Merge branch 'TandoorRecipes:develop' into develop

This commit is contained in:
nough 2022-08-27 21:57:02 +01:00 committed by GitHub
commit c020bea41e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
123 changed files with 5540 additions and 4302 deletions

View File

@ -68,6 +68,10 @@ SHOPPING_MIN_AUTOSYNC_INTERVAL=5
# when unset: 1 (true) - this is temporary until an appropriate amount of time has passed for everyone to migrate # when unset: 1 (true) - this is temporary until an appropriate amount of time has passed for everyone to migrate
GUNICORN_MEDIA=0 GUNICORN_MEDIA=0
# GUNICORN SERVER RELATED SETTINGS (see https://docs.gunicorn.org/en/stable/design.html#how-many-workers for recommended settings)
# GUNICORN_WORKERS=1
# GUNICORN_THREADS=1
# S3 Media settings: store mediafiles in s3 or any compatible storage backend (e.g. minio) # S3 Media settings: store mediafiles in s3 or any compatible storage backend (e.g. minio)
# as long as S3_ACCESS_KEY is not set S3 features are disabled # as long as S3_ACCESS_KEY is not set S3 features are disabled
# S3_ACCESS_KEY= # S3_ACCESS_KEY=
@ -77,6 +81,7 @@ GUNICORN_MEDIA=0
# S3_QUERYSTRING_AUTH=1 # default true, set to 0 to serve media from a public bucket without signed urls # S3_QUERYSTRING_AUTH=1 # default true, set to 0 to serve media from a public bucket without signed urls
# S3_QUERYSTRING_EXPIRE=3600 # number of seconds querystring are valid for # S3_QUERYSTRING_EXPIRE=3600 # number of seconds querystring are valid for
# S3_ENDPOINT_URL= # when using a custom endpoint like minio # S3_ENDPOINT_URL= # when using a custom endpoint like minio
# S3_CUSTOM_DOMAIN= # when using a CDN/proxy to S3 (see https://github.com/TandoorRecipes/recipes/issues/1943)
# Email Settings, see https://docs.djangoproject.com/en/3.2/ref/settings/#email-host # Email Settings, see https://docs.djangoproject.com/en/3.2/ref/settings/#email-host
# Required for email confirmation and password reset (automatically activates if host is set) # Required for email confirmation and password reset (automatically activates if host is set)

View File

@ -2,6 +2,8 @@
source venv/bin/activate source venv/bin/activate
TANDOOR_PORT="${TANDOOR_PORT:-8080}" TANDOOR_PORT="${TANDOOR_PORT:-8080}"
GUNICORN_WORKERS="${GUNICORN_WORKERS}"
GUNICORN_THREADS="${GUNICORN_THREADS}"
NGINX_CONF_FILE=/opt/recipes/nginx/conf.d/Recipes.conf NGINX_CONF_FILE=/opt/recipes/nginx/conf.d/Recipes.conf
display_warning() { display_warning() {
@ -63,4 +65,4 @@ echo "Done"
chmod -R 755 /opt/recipes/mediafiles chmod -R 755 /opt/recipes/mediafiles
exec gunicorn -b :$TANDOOR_PORT --access-logfile - --error-logfile - --log-level INFO recipes.wsgi exec gunicorn -b :$TANDOOR_PORT --workers $GUNICORN_WORKERS --threads $GUNICORN_THREADS --access-logfile - --error-logfile - --log-level INFO recipes.wsgi

View File

@ -15,7 +15,7 @@ from .models import (BookmarkletImport, Comment, CookLog, Food, FoodInheritField
Recipe, RecipeBook, RecipeBookEntry, RecipeImport, SearchPreference, ShareLink, Recipe, RecipeBook, RecipeBookEntry, RecipeImport, SearchPreference, ShareLink,
ShoppingList, ShoppingListEntry, ShoppingListRecipe, Space, Step, Storage, ShoppingList, ShoppingListEntry, ShoppingListRecipe, Space, Step, Storage,
Supermarket, SupermarketCategory, SupermarketCategoryRelation, Sync, SyncLog, Supermarket, SupermarketCategory, SupermarketCategoryRelation, Sync, SyncLog,
TelegramBot, Unit, UserFile, UserPreference, ViewLog, Automation) TelegramBot, Unit, UserFile, UserPreference, ViewLog, Automation, UserSpace)
class CustomUserAdmin(UserAdmin): class CustomUserAdmin(UserAdmin):
@ -46,15 +46,23 @@ class SpaceAdmin(admin.ModelAdmin):
admin.site.register(Space, SpaceAdmin) admin.site.register(Space, SpaceAdmin)
class UserSpaceAdmin(admin.ModelAdmin):
list_display = ('user', 'space',)
search_fields = ('user', 'space',)
admin.site.register(UserSpace, UserSpaceAdmin)
class UserPreferenceAdmin(admin.ModelAdmin): class UserPreferenceAdmin(admin.ModelAdmin):
list_display = ('name', 'theme', 'nav_color', 'default_page', 'search_style',) # TODO add new fields list_display = ('name', 'theme', 'nav_color', 'default_page',)
search_fields = ('user__username',) search_fields = ('user__username',)
list_filter = ('theme', 'nav_color', 'default_page', 'search_style') list_filter = ('theme', 'nav_color', 'default_page',)
date_hierarchy = 'created_at' date_hierarchy = 'created_at'
@staticmethod @staticmethod
def name(obj): def name(obj):
return obj.user.get_user_name() return obj.user.get_user_display_name()
admin.site.register(UserPreference, UserPreferenceAdmin) admin.site.register(UserPreference, UserPreferenceAdmin)
@ -67,7 +75,7 @@ class SearchPreferenceAdmin(admin.ModelAdmin):
@staticmethod @staticmethod
def name(obj): def name(obj):
return obj.user.get_user_name() return obj.user.get_user_display_name()
admin.site.register(SearchPreference, SearchPreferenceAdmin) admin.site.register(SearchPreference, SearchPreferenceAdmin)
@ -169,7 +177,7 @@ class RecipeAdmin(admin.ModelAdmin):
@staticmethod @staticmethod
def created_by(obj): def created_by(obj):
return obj.created_by.get_user_name() return obj.created_by.get_user_display_name()
if settings.DATABASES['default']['ENGINE'] in ['django.db.backends.postgresql_psycopg2', 'django.db.backends.postgresql']: if settings.DATABASES['default']['ENGINE'] in ['django.db.backends.postgresql_psycopg2', 'django.db.backends.postgresql']:
actions = [rebuild_index] actions = [rebuild_index]
@ -208,7 +216,7 @@ class CommentAdmin(admin.ModelAdmin):
@staticmethod @staticmethod
def name(obj): def name(obj):
return obj.created_by.get_user_name() return obj.created_by.get_user_display_name()
admin.site.register(Comment, CommentAdmin) admin.site.register(Comment, CommentAdmin)
@ -227,7 +235,7 @@ class RecipeBookAdmin(admin.ModelAdmin):
@staticmethod @staticmethod
def user_name(obj): def user_name(obj):
return obj.created_by.get_user_name() return obj.created_by.get_user_display_name()
admin.site.register(RecipeBook, RecipeBookAdmin) admin.site.register(RecipeBook, RecipeBookAdmin)
@ -245,7 +253,7 @@ class MealPlanAdmin(admin.ModelAdmin):
@staticmethod @staticmethod
def user(obj): def user(obj):
return obj.created_by.get_user_name() return obj.created_by.get_user_display_name()
admin.site.register(MealPlan, MealPlanAdmin) admin.site.register(MealPlan, MealPlanAdmin)

View File

@ -1,62 +0,0 @@
import django_filters
from django.conf import settings
from django.contrib.postgres.search import TrigramSimilarity
from django.db.models import Q
from django.utils.translation import gettext as _
from django_scopes import scopes_disabled
from cookbook.forms import MultiSelectWidget
from cookbook.models import Food, Keyword, Recipe
with scopes_disabled():
class RecipeFilter(django_filters.FilterSet):
name = django_filters.CharFilter(method='filter_name')
keywords = django_filters.ModelMultipleChoiceFilter(
queryset=Keyword.objects.none(),
widget=MultiSelectWidget,
method='filter_keywords'
)
foods = django_filters.ModelMultipleChoiceFilter(
queryset=Food.objects.none(),
widget=MultiSelectWidget,
method='filter_foods',
label=_('Ingredients')
)
def __init__(self, data=None, *args, **kwargs):
space = kwargs.pop('space')
super().__init__(data, *args, **kwargs)
self.filters['foods'].queryset = Food.objects.filter(space=space).all()
self.filters['keywords'].queryset = Keyword.objects.filter(space=space).all()
@staticmethod
def filter_keywords(queryset, name, value):
if not name == 'keywords':
return queryset
for x in value:
queryset = queryset.filter(keywords=x)
return queryset
@staticmethod
def filter_foods(queryset, name, value):
if not name == 'foods':
return queryset
for x in value:
queryset = queryset.filter(steps__ingredients__food__name=x).distinct()
return queryset
@staticmethod
def filter_name(queryset, name, value):
if not name == 'name':
return queryset
if settings.DATABASES['default']['ENGINE'] in ['django.db.backends.postgresql_psycopg2',
'django.db.backends.postgresql']:
queryset = queryset.annotate(similarity=TrigramSimilarity('name', value), ).filter(
Q(similarity__gt=0.1) | Q(name__unaccent__icontains=value)).order_by('-similarity')
else:
queryset = queryset.filter(name__icontains=value)
return queryset
class Meta:
model = Recipe
fields = ['name', 'keywords', 'foods', 'internal']

View File

@ -45,8 +45,7 @@ class UserPreferenceForm(forms.ModelForm):
model = UserPreference model = UserPreference
fields = ( fields = (
'default_unit', 'use_fractions', 'use_kj', 'theme', 'nav_color', 'default_unit', 'use_fractions', 'use_kj', 'theme', 'nav_color',
'sticky_navbar', 'default_page', 'show_recent', 'search_style', 'sticky_navbar', 'default_page', 'plan_share', 'ingredient_decimals', 'comments', 'left_handed',
'plan_share', 'ingredient_decimals', 'comments', 'left_handed',
) )
labels = { labels = {
@ -57,8 +56,6 @@ class UserPreferenceForm(forms.ModelForm):
'nav_color': _('Navbar color'), 'nav_color': _('Navbar color'),
'sticky_navbar': _('Sticky navbar'), 'sticky_navbar': _('Sticky navbar'),
'default_page': _('Default page'), 'default_page': _('Default page'),
'show_recent': _('Show recent recipes'),
'search_style': _('Search style'),
'plan_share': _('Plan sharing'), 'plan_share': _('Plan sharing'),
'ingredient_decimals': _('Ingredient decimal places'), 'ingredient_decimals': _('Ingredient decimal places'),
'shopping_auto_sync': _('Shopping list auto sync period'), 'shopping_auto_sync': _('Shopping list auto sync period'),
@ -68,23 +65,21 @@ class UserPreferenceForm(forms.ModelForm):
help_texts = { help_texts = {
'nav_color': _('Color of the top navigation bar. Not all colors work with all themes, just try them out!'), 'nav_color': _('Color of the top navigation bar. Not all colors work with all themes, just try them out!'),
# noqa: E501
'default_unit': _('Default Unit to be used when inserting a new ingredient into a recipe.'), # noqa: E501 'default_unit': _('Default Unit to be used when inserting a new ingredient into a recipe.'),
'use_fractions': _( 'use_fractions': _(
'Enables support for fractions in ingredient amounts (e.g. convert decimals to fractions automatically)'), 'Enables support for fractions in ingredient amounts (e.g. convert decimals to fractions automatically)'),
# noqa: E501
'use_kj': _('Display nutritional energy amounts in joules instead of calories'), # noqa: E501 '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.'), 'plan_share': _('Users with whom newly created meal plans should be shared by default.'),
'shopping_share': _('Users with whom to share shopping lists.'), 'shopping_share': _('Users with whom to share shopping lists.'),
# noqa: E501 'ingredient_decimals': _('Number of decimals to round ingredients.'),
'show_recent': _('Show recently viewed recipes on search page.'), # noqa: E501 'comments': _('If you want to be able to create and see comments underneath recipes.'),
'ingredient_decimals': _('Number of decimals to round ingredients.'), # noqa: E501
'comments': _('If you want to be able to create and see comments underneath recipes.'), # noqa: E501
'shopping_auto_sync': _( '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 ' # noqa: E501 '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.' # noqa: E501 '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.'), # noqa: E501 'sticky_navbar': _('Makes the navbar stick to the top of the page.'),
'mealplan_autoadd_shopping': _('Automatically add meal plan ingredients to shopping list.'), 'mealplan_autoadd_shopping': _('Automatically add meal plan ingredients to shopping list.'),
'mealplan_autoexclude_onhand': _('Exclude ingredients that are on hand.'), 'mealplan_autoexclude_onhand': _('Exclude ingredients that are on hand.'),
'left_handed': _('Will optimize the UI for use with your left hand.') 'left_handed': _('Will optimize the UI for use with your left hand.')
@ -336,9 +331,9 @@ class MealPlanForm(forms.ModelForm):
) )
help_texts = { help_texts = {
'shared': _('You can list default users to share recipes with in the settings.'), # noqa: E501 'shared': _('You can list default users to share recipes with in the settings.'),
'note': _('You can use markdown to format this field. See the <a href="/docs/markdown/">docs here</a>') 'note': _('You can use markdown to format this field. See the <a href="/docs/markdown/">docs here</a>')
# noqa: E501
} }
widgets = { widgets = {
@ -493,8 +488,8 @@ class ShoppingPreferenceForm(forms.ModelForm):
help_texts = { help_texts = {
'shopping_share': _('Users will see all items you add to your shopping list. They must add you to see items on their list.'), 'shopping_share': _('Users will see all items you add to your shopping list. They must add you to see items on their list.'),
'shopping_auto_sync': _( '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 ' # noqa: E501 '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.' # noqa: E501 'of mobile data. If lower than instance limit it is reset when saving.'
), ),
'mealplan_autoadd_shopping': _('Automatically add meal plan ingredients to shopping list.'), 'mealplan_autoadd_shopping': _('Automatically add meal plan ingredients to shopping list.'),
'mealplan_autoinclude_related': _('When adding a meal plan to the shopping list (manually or automatically), include all related recipes.'), 'mealplan_autoinclude_related': _('When adding a meal plan to the shopping list (manually or automatically), include all related recipes.'),

View File

@ -14,7 +14,7 @@ class AllAuthCustomAdapter(DefaultAccountAdapter):
def is_open_for_signup(self, request): def is_open_for_signup(self, request):
""" """
Whether to allow sign ups. Whether to allow sign-ups.
""" """
signup_token = False signup_token = False
if 'signup_token' in request.session and InviteLink.objects.filter(valid_until__gte=datetime.datetime.today(), used_by=None, uuid=request.session['signup_token']).exists(): if 'signup_token' in request.session and InviteLink.objects.filter(valid_until__gte=datetime.datetime.today(), used_by=None, uuid=request.session['signup_token']).exists():
@ -31,7 +31,10 @@ class AllAuthCustomAdapter(DefaultAccountAdapter):
default = datetime.datetime.now() default = datetime.datetime.now()
c = caches['default'].get_or_set(email, default, timeout=360) c = caches['default'].get_or_set(email, default, timeout=360)
if c == default: if c == default:
super(AllAuthCustomAdapter, self).send_mail(template_prefix, email, context) try:
super(AllAuthCustomAdapter, self).send_mail(template_prefix, email, context)
except Exception: # dont fail signup just because confirmation mail could not be send
pass
else: else:
messages.add_message(self.request, messages.ERROR, _('In order to prevent spam, the requested email was not send. Please wait a few minutes and try again.')) messages.add_message(self.request, messages.ERROR, _('In order to prevent spam, the requested email was not send. Please wait a few minutes and try again.'))
else: else:

View File

@ -10,4 +10,5 @@ def context_settings(request):
'TERMS_URL': settings.TERMS_URL, 'TERMS_URL': settings.TERMS_URL,
'PRIVACY_URL': settings.PRIVACY_URL, 'PRIVACY_URL': settings.PRIVACY_URL,
'IMPRINT_URL': settings.IMPRINT_URL, 'IMPRINT_URL': settings.IMPRINT_URL,
'SHOPPING_MIN_AUTOSYNC_INTERVAL': settings.SHOPPING_MIN_AUTOSYNC_INTERVAL,
} }

View File

@ -6,10 +6,12 @@ from django.core.exceptions import ValidationError, ObjectDoesNotExist
from django.http import HttpResponseRedirect from django.http import HttpResponseRedirect
from django.urls import reverse, reverse_lazy from django.urls import reverse, reverse_lazy
from django.utils.translation import gettext as _ from django.utils.translation import gettext as _
from oauth2_provider.contrib.rest_framework import TokenHasScope, TokenHasReadWriteScope
from oauth2_provider.models import AccessToken
from rest_framework import permissions from rest_framework import permissions
from rest_framework.permissions import SAFE_METHODS from rest_framework.permissions import SAFE_METHODS
from cookbook.models import ShareLink, Recipe, UserPreference, UserSpace from cookbook.models import ShareLink, Recipe, UserSpace
def get_allowed_groups(groups_required): def get_allowed_groups(groups_required):
@ -299,6 +301,73 @@ class CustomIsShare(permissions.BasePermission):
return False return False
class CustomRecipePermission(permissions.BasePermission):
"""
Custom permission class for recipe api endpoint
"""
message = _('You do not have the required permissions to view this page!')
def has_permission(self, request, view): # user is either at least a guest or a share link is given and the request is safe
share = request.query_params.get('share', None)
return has_group_permission(request.user, ['guest']) or (share and request.method in SAFE_METHODS and 'pk' in view.kwargs)
def has_object_permission(self, request, view, obj):
share = request.query_params.get('share', None)
if share:
return share_link_valid(obj, share)
else:
if obj.private:
return ((obj.created_by == request.user) or (request.user in obj.shared.all())) and obj.space == request.space
else:
return has_group_permission(request.user, ['guest']) and obj.space == request.space
class CustomUserPermission(permissions.BasePermission):
"""
Custom permission class for user api endpoint
"""
message = _('You do not have the required permissions to view this page!')
def has_permission(self, request, view): # a space filtered user list is visible for everyone
return has_group_permission(request.user, ['guest'])
def has_object_permission(self, request, view, obj): # object write permissions are only available for user
if request.method in SAFE_METHODS and 'pk' in view.kwargs and has_group_permission(request.user, ['guest']) and request.space in obj.userspace_set.all():
return True
elif request.user == obj:
return True
else:
return False
class CustomTokenHasScope(TokenHasScope):
"""
Custom implementation of Django OAuth Toolkit TokenHasScope class
Only difference: if any other authentication method except OAuth2Authentication is used the scope check is ignored
IMPORTANT: do not use this class without any other permission class as it will not check anything besides token scopes
"""
def has_permission(self, request, view):
if type(request.auth) == AccessToken:
return super().has_permission(request, view)
else:
return request.user.is_authenticated
class CustomTokenHasReadWriteScope(TokenHasReadWriteScope):
"""
Custom implementation of Django OAuth Toolkit TokenHasReadWriteScope class
Only difference: if any other authentication method except OAuth2Authentication is used the scope check is ignored
IMPORTANT: do not use this class without any other permission class as it will not check anything besides token scopes
"""
def has_permission(self, request, view):
if type(request.auth) == AccessToken:
return super().has_permission(request, view)
else:
return True
def above_space_limit(space): # TODO add file storage limit def above_space_limit(space): # TODO add file storage limit
""" """
Test if the space has reached any limit (e.g. max recipes, users, ..) Test if the space has reached any limit (e.g. max recipes, users, ..)

View File

@ -1,189 +1,191 @@
import json # import json
import re # import re
from json import JSONDecodeError # from json import JSONDecodeError
from urllib.parse import unquote # from urllib.parse import unquote
from bs4 import BeautifulSoup # from bs4 import BeautifulSoup
from bs4.element import Tag # from bs4.element import Tag
from recipe_scrapers import scrape_html, scrape_me # from recipe_scrapers import scrape_html, scrape_me
from recipe_scrapers._exceptions import NoSchemaFoundInWildMode # from recipe_scrapers._exceptions import NoSchemaFoundInWildMode
from recipe_scrapers._utils import get_host_name, normalize_string # from recipe_scrapers._utils import get_host_name, normalize_string
from cookbook.helper import recipe_url_import as helper # from cookbook.helper import recipe_url_import as helper
from cookbook.helper.scrapers.scrapers import text_scraper # from cookbook.helper.scrapers.scrapers import text_scraper
def get_recipe_from_source(text, url, request): # def get_recipe_from_source(text, url, request):
def build_node(k, v): # def build_node(k, v):
if isinstance(v, dict): # if isinstance(v, dict):
node = { # node = {
'name': k, # 'name': k,
'value': k, # 'value': k,
'children': get_children_dict(v) # 'children': get_children_dict(v)
} # }
elif isinstance(v, list): # elif isinstance(v, list):
node = { # node = {
'name': k, # 'name': k,
'value': k, # 'value': k,
'children': get_children_list(v) # 'children': get_children_list(v)
} # }
else: # else:
node = { # node = {
'name': k + ": " + normalize_string(str(v)), # 'name': k + ": " + normalize_string(str(v)),
'value': normalize_string(str(v)) # 'value': normalize_string(str(v))
} # }
return node # return node
def get_children_dict(children): # def get_children_dict(children):
kid_list = [] # kid_list = []
for k, v in children.items(): # for k, v in children.items():
kid_list.append(build_node(k, v)) # kid_list.append(build_node(k, v))
return kid_list # return kid_list
def get_children_list(children): # def get_children_list(children):
kid_list = [] # kid_list = []
for kid in children: # for kid in children:
if type(kid) == list: # if type(kid) == list:
node = { # node = {
'name': "unknown list", # 'name': "unknown list",
'value': "unknown list", # 'value': "unknown list",
'children': get_children_list(kid) # 'children': get_children_list(kid)
} # }
kid_list.append(node) # kid_list.append(node)
elif type(kid) == dict: # elif type(kid) == dict:
for k, v in kid.items(): # for k, v in kid.items():
kid_list.append(build_node(k, v)) # kid_list.append(build_node(k, v))
else: # else:
kid_list.append({ # kid_list.append({
'name': normalize_string(str(kid)), # 'name': normalize_string(str(kid)),
'value': normalize_string(str(kid)) # 'value': normalize_string(str(kid))
}) # })
return kid_list # return kid_list
recipe_tree = [] # recipe_tree = []
parse_list = [] # parse_list = []
soup = BeautifulSoup(text, "html.parser") # soup = BeautifulSoup(text, "html.parser")
html_data = get_from_html(soup) # html_data = get_from_html(soup)
images = get_images_from_source(soup, url) # images = get_images_from_source(soup, url)
text = unquote(text) # text = unquote(text)
scrape = None # scrape = None
if url: # if url and not text:
try: # try:
scrape = scrape_me(url_path=url, wild_mode=True) # scrape = scrape_me(url_path=url, wild_mode=True)
except(NoSchemaFoundInWildMode): # except(NoSchemaFoundInWildMode):
pass # pass
if not scrape:
try:
parse_list.append(remove_graph(json.loads(text)))
if not url and 'url' in parse_list[0]:
url = parse_list[0]['url']
scrape = text_scraper("<script type='application/ld+json'>" + text + "</script>", url=url)
except JSONDecodeError: # if not scrape:
for el in soup.find_all('script', type='application/ld+json'): # try:
el = remove_graph(el) # parse_list.append(remove_graph(json.loads(text)))
if not url and 'url' in el: # if not url and 'url' in parse_list[0]:
url = el['url'] # url = parse_list[0]['url']
if type(el) == list: # scrape = text_scraper("<script type='application/ld+json'>" + text + "</script>", url=url)
for le in el:
parse_list.append(le)
elif type(el) == dict:
parse_list.append(el)
for el in soup.find_all(type='application/json'):
el = remove_graph(el)
if type(el) == list:
for le in el:
parse_list.append(le)
elif type(el) == dict:
parse_list.append(el)
scrape = text_scraper(text, url=url)
recipe_json = helper.get_from_scraper(scrape, request) # except JSONDecodeError:
# for el in soup.find_all('script', type='application/ld+json'):
# el = remove_graph(el)
# if not url and 'url' in el:
# url = el['url']
# if type(el) == list:
# for le in el:
# parse_list.append(le)
# elif type(el) == dict:
# parse_list.append(el)
# for el in soup.find_all(type='application/json'):
# el = remove_graph(el)
# if type(el) == list:
# for le in el:
# parse_list.append(le)
# elif type(el) == dict:
# parse_list.append(el)
# scrape = text_scraper(text, url=url)
for el in parse_list: # recipe_json = helper.get_from_scraper(scrape, request)
temp_tree = []
if isinstance(el, Tag):
try:
el = json.loads(el.string)
except TypeError:
continue
for k, v in el.items(): # # TODO: DEPRECATE recipe_tree & html_data. first validate it isn't used anywhere
if isinstance(v, dict): # for el in parse_list:
node = { # temp_tree = []
'name': k, # if isinstance(el, Tag):
'value': k, # try:
'children': get_children_dict(v) # el = json.loads(el.string)
} # except TypeError:
elif isinstance(v, list): # continue
node = {
'name': k,
'value': k,
'children': get_children_list(v)
}
else:
node = {
'name': k + ": " + normalize_string(str(v)),
'value': normalize_string(str(v))
}
temp_tree.append(node)
if '@type' in el and el['@type'] == 'Recipe': # for k, v in el.items():
recipe_tree += [{'name': 'ld+json', 'children': temp_tree}] # if isinstance(v, dict):
else: # node = {
recipe_tree += [{'name': 'json', 'children': temp_tree}] # 'name': k,
# 'value': k,
# 'children': get_children_dict(v)
# }
# elif isinstance(v, list):
# node = {
# 'name': k,
# 'value': k,
# 'children': get_children_list(v)
# }
# else:
# node = {
# 'name': k + ": " + normalize_string(str(v)),
# 'value': normalize_string(str(v))
# }
# temp_tree.append(node)
return recipe_json, recipe_tree, html_data, images # if '@type' in el and el['@type'] == 'Recipe':
# recipe_tree += [{'name': 'ld+json', 'children': temp_tree}]
# else:
# recipe_tree += [{'name': 'json', 'children': temp_tree}]
# return recipe_json, recipe_tree, html_data, images
def get_from_html(soup): # def get_from_html(soup):
INVISIBLE_ELEMS = ('style', 'script', 'head', 'title') # INVISIBLE_ELEMS = ('style', 'script', 'head', 'title')
html = [] # html = []
for s in soup.strings: # for s in soup.strings:
if ((s.parent.name not in INVISIBLE_ELEMS) and (len(s.strip()) > 0)): # if ((s.parent.name not in INVISIBLE_ELEMS) and (len(s.strip()) > 0)):
html.append(s) # html.append(s)
return html # return html
def get_images_from_source(soup, url): # def get_images_from_source(soup, url):
sources = ['src', 'srcset', 'data-src'] # sources = ['src', 'srcset', 'data-src']
images = [] # images = []
img_tags = soup.find_all('img') # img_tags = soup.find_all('img')
if url: # if url:
site = get_host_name(url) # site = get_host_name(url)
prot = url.split(':')[0] # prot = url.split(':')[0]
urls = [] # urls = []
for img in img_tags: # for img in img_tags:
for src in sources: # for src in sources:
try: # try:
urls.append(img[src]) # urls.append(img[src])
except KeyError: # except KeyError:
pass # pass
for u in urls: # for u in urls:
u = u.split('?')[0] # u = u.split('?')[0]
filename = re.search(r'/([\w_-]+[.](jpg|jpeg|gif|png))$', u) # filename = re.search(r'/([\w_-]+[.](jpg|jpeg|gif|png))$', u)
if filename: # if filename:
if (('http' not in u) and (url)): # if (('http' not in u) and (url)):
# sometimes an image source can be relative # # sometimes an image source can be relative
# if it is provide the base url # # if it is provide the base url
u = '{}://{}{}'.format(prot, site, u) # u = '{}://{}{}'.format(prot, site, u)
if 'http' in u: # if 'http' in u:
images.append(u) # images.append(u)
return images # return images
def remove_graph(el): # def remove_graph(el):
# recipes type might be wrapped in @graph type # # recipes type might be wrapped in @graph type
if isinstance(el, Tag): # if isinstance(el, Tag):
try: # try:
el = json.loads(el.string) # el = json.loads(el.string)
if '@graph' in el: # if '@graph' in el:
for x in el['@graph']: # for x in el['@graph']:
if '@type' in x and x['@type'] == 'Recipe': # if '@type' in x and x['@type'] == 'Recipe':
el = x # el = x
except (TypeError, JSONDecodeError): # except (TypeError, JSONDecodeError):
pass # pass
return el # return el

View File

@ -4,17 +4,14 @@ from datetime import date, timedelta
from django.contrib.postgres.search import SearchQuery, SearchRank, SearchVector, TrigramSimilarity from django.contrib.postgres.search import SearchQuery, SearchRank, SearchVector, TrigramSimilarity
from django.core.cache import caches from django.core.cache import caches
from django.db.models import (Avg, Case, Count, Exists, F, Func, Max, OuterRef, Q, Subquery, Sum, from django.db.models import (Avg, Case, Count, Exists, F, Func, Max, OuterRef, Q, Subquery, Value, When)
Value, When)
from django.db.models.functions import Coalesce, Lower, Substr from django.db.models.functions import Coalesce, Lower, Substr
from django.utils import timezone, translation from django.utils import timezone, translation
from django.utils.translation import gettext as _ from django.utils.translation import gettext as _
from cookbook.filters import RecipeFilter
from cookbook.helper.HelperFunctions import Round, str2bool from cookbook.helper.HelperFunctions import Round, str2bool
from cookbook.helper.permission_helper import has_group_permission
from cookbook.managers import DICTIONARY from cookbook.managers import DICTIONARY
from cookbook.models import (CookLog, CustomFilter, Food, Keyword, Recipe, RecipeBook, SearchFields, from cookbook.models import (CookLog, CustomFilter, Food, Keyword, Recipe, SearchFields,
SearchPreference, ViewLog) SearchPreference, ViewLog)
from recipes import settings from recipes import settings
@ -759,12 +756,3 @@ class RecipeFacet():
else: else:
return queryset.filter(depth__lte=depth).values('id', 'name', 'numchild').order_by(Lower('name').asc()) return queryset.filter(depth__lte=depth).values('id', 'name', 'numchild').order_by(Lower('name').asc())
def old_search(request):
if has_group_permission(request.user, ('guest',)):
params = dict(request.GET)
params['internal'] = None
f = RecipeFilter(params,
queryset=Recipe.objects.filter(space=request.space).all().order_by(Lower('name').asc()),
space=request.space)
return f.qs

View File

@ -1,21 +1,19 @@
import random import random
import re import re
from html import unescape from html import unescape
from pytube import YouTube
from unicodedata import decomposition from unicodedata import decomposition
from django.utils.dateparse import parse_duration from django.utils.dateparse import parse_duration
from django.utils.translation import gettext as _ from django.utils.translation import gettext as _
from isodate import parse_duration as iso_parse_duration from isodate import parse_duration as iso_parse_duration
from isodate.isoerror import ISO8601Error from isodate.isoerror import ISO8601Error
from recipe_scrapers._utils import get_minutes from pytube import YouTube
from recipe_scrapers._utils import get_host_name, get_minutes
from cookbook.helper import recipe_url_import as helper from cookbook.helper import recipe_url_import as helper
from cookbook.helper.ingredient_parser import IngredientParser from cookbook.helper.ingredient_parser import IngredientParser
from cookbook.models import Keyword from cookbook.models import Keyword
# from recipe_scrapers._utils import get_minutes ## temporary until/unless upstream incorporates get_minutes() PR # from recipe_scrapers._utils import get_minutes ## temporary until/unless upstream incorporates get_minutes() PR
@ -369,3 +367,32 @@ def iso_duration_to_minutes(string):
string string
).groupdict() ).groupdict()
return int(match['days'] or 0) * 24 * 60 + int(match['hours'] or 0) * 60 + int(match['minutes'] or 0) return int(match['days'] or 0) * 24 * 60 + int(match['hours'] or 0) * 60 + int(match['minutes'] or 0)
def get_images_from_soup(soup, url):
sources = ['src', 'srcset', 'data-src']
images = []
img_tags = soup.find_all('img')
if url:
site = get_host_name(url)
prot = url.split(':')[0]
urls = []
for img in img_tags:
for src in sources:
try:
urls.append(img[src])
except KeyError:
pass
for u in urls:
u = u.split('?')[0]
filename = re.search(r'/([\w_-]+[.](jpg|jpeg|gif|png))$', u)
if filename:
if (('http' not in u) and (url)):
# sometimes an image source can be relative
# if it is provide the base url
u = '{}://{}{}'.format(prot, site, u)
if 'http' in u:
images.append(u)
return images

View File

@ -1,5 +1,6 @@
from django.urls import reverse from django.urls import reverse
from django_scopes import scope, scopes_disabled from django_scopes import scope, scopes_disabled
from oauth2_provider.contrib.rest_framework import OAuth2Authentication
from rest_framework.authentication import TokenAuthentication from rest_framework.authentication import TokenAuthentication
from rest_framework.authtoken.models import Token from rest_framework.authtoken.models import Token
from rest_framework.exceptions import AuthenticationFailed from rest_framework.exceptions import AuthenticationFailed
@ -55,7 +56,7 @@ class ScopeMiddleware:
else: else:
if request.path.startswith(prefix + '/api/'): if request.path.startswith(prefix + '/api/'):
try: try:
if auth := TokenAuthentication().authenticate(request): if auth := OAuth2Authentication().authenticate(request):
user_space = auth[0].userspace_set.filter(active=True).first() user_space = auth[0].userspace_set.filter(active=True).first()
if user_space: if user_space:
request.space = user_space.space request.space = user_space.space

View File

@ -1,6 +1,7 @@
from bs4 import BeautifulSoup
from json import JSONDecodeError from json import JSONDecodeError
from recipe_scrapers import SCRAPERS
from bs4 import BeautifulSoup
from recipe_scrapers import SCRAPERS, get_host_name
from recipe_scrapers._factory import SchemaScraperFactory from recipe_scrapers._factory import SchemaScraperFactory
from recipe_scrapers._schemaorg import SchemaOrg from recipe_scrapers._schemaorg import SchemaOrg
@ -15,22 +16,28 @@ SCRAPERS.update(CUSTOM_SCRAPERS)
def text_scraper(text, url=None): def text_scraper(text, url=None):
scraper_class = SchemaScraperFactory.SchemaScraper domain = None
if url:
domain = get_host_name(url)
if domain in SCRAPERS:
scraper_class = SCRAPERS[domain]
else:
scraper_class = SchemaScraperFactory.SchemaScraper
class TextScraper(scraper_class): class TextScraper(scraper_class):
def __init__( def __init__(
self, self,
page_data, html=None,
url=None url=None,
): ):
self.wild_mode = False self.wild_mode = False
self.meta_http_equiv = False self.meta_http_equiv = False
self.soup = BeautifulSoup(page_data, "html.parser") self.soup = BeautifulSoup(html, "html.parser")
self.url = url self.url = url
self.recipe = None self.recipe = None
try: try:
self.schema = SchemaOrg(page_data) self.schema = SchemaOrg(html)
except (JSONDecodeError, AttributeError): except (JSONDecodeError, AttributeError):
pass pass
return TextScraper(text, url) return TextScraper(url=url, html=text)

View File

@ -10,8 +10,9 @@ import validators
import yaml import yaml
from cookbook.helper.ingredient_parser import IngredientParser from cookbook.helper.ingredient_parser import IngredientParser
from cookbook.helper.recipe_html_import import get_recipe_from_source from cookbook.helper.recipe_url_import import (get_from_scraper, get_images_from_soup,
from cookbook.helper.recipe_url_import import iso_duration_to_minutes iso_duration_to_minutes)
from cookbook.helper.scrapers.scrapers import text_scraper
from cookbook.integration.integration import Integration from cookbook.integration.integration import Integration
from cookbook.models import Ingredient, Keyword, Recipe, Step from cookbook.models import Ingredient, Keyword, Recipe, Step
@ -24,7 +25,10 @@ class CookBookApp(Integration):
def get_recipe_from_file(self, file): def get_recipe_from_file(self, file):
recipe_html = file.getvalue().decode("utf-8") recipe_html = file.getvalue().decode("utf-8")
recipe_json, recipe_tree, html_data, images = get_recipe_from_source(recipe_html, 'CookBookApp', self.request) # recipe_json, recipe_tree, html_data, images = get_recipe_from_source(recipe_html, 'CookBookApp', self.request)
scrape = text_scraper(text=recipe_html)
recipe_json = get_from_scraper(scrape, self.request)
images = list(dict.fromkeys(get_images_from_soup(scrape.soup, None)))
recipe = Recipe.objects.create( recipe = Recipe.objects.create(
name=recipe_json['name'].strip(), name=recipe_json['name'].strip(),
@ -42,7 +46,8 @@ class CookBookApp(Integration):
except Exception: except Exception:
pass pass
step = Step.objects.create(instruction=recipe_json['recipeInstructions'], space=self.request.space, ) # assuming import files only contain single step
step = Step.objects.create(instruction=recipe_json['steps'][0]['instruction'], space=self.request.space, )
if 'nutrition' in recipe_json: if 'nutrition' in recipe_json:
step.instruction = step.instruction + '\n\n' + recipe_json['nutrition'] step.instruction = step.instruction + '\n\n' + recipe_json['nutrition']
@ -51,11 +56,13 @@ class CookBookApp(Integration):
recipe.steps.add(step) recipe.steps.add(step)
ingredient_parser = IngredientParser(self.request, True) ingredient_parser = IngredientParser(self.request, True)
for ingredient in recipe_json['recipeIngredient']: for ingredient in recipe_json['steps'][0]['ingredients']:
f = ingredient_parser.get_food(ingredient['ingredient']['text']) f = ingredient_parser.get_food(ingredient['food']['name'])
u = ingredient_parser.get_unit(ingredient['unit']['text']) u = None
if unit := ingredient.get('unit', None):
u = ingredient_parser.get_unit(unit.get('name', None))
step.ingredients.add(Ingredient.objects.create( step.ingredients.add(Ingredient.objects.create(
food=f, unit=u, amount=ingredient['amount'], note=ingredient['note'], space=self.request.space, food=f, unit=u, amount=ingredient.get('amount', None), note=ingredient.get('note', None), original_text=ingredient.get('original_text', None), space=self.request.space,
)) ))
if len(images) > 0: if len(images) > 0:

View File

@ -2,11 +2,10 @@ import re
from io import BytesIO from io import BytesIO
from zipfile import ZipFile from zipfile import ZipFile
from bs4 import BeautifulSoup from bs4 import BeautifulSoup, Tag
from django.utils.translation import gettext as _ from django.utils.translation import gettext as _
from cookbook.helper.ingredient_parser import IngredientParser from cookbook.helper.ingredient_parser import IngredientParser
from cookbook.helper.recipe_html_import import get_recipe_from_source
from cookbook.helper.recipe_url_import import iso_duration_to_minutes, parse_servings from cookbook.helper.recipe_url_import import iso_duration_to_minutes, parse_servings
from cookbook.integration.integration import Integration from cookbook.integration.integration import Integration
from cookbook.models import Ingredient, Keyword, Recipe, Step from cookbook.models import Ingredient, Keyword, Recipe, Step
@ -22,18 +21,21 @@ class CopyMeThat(Integration):
def get_recipe_from_file(self, file): def get_recipe_from_file(self, file):
# 'file' comes is as a beautifulsoup object # 'file' comes is as a beautifulsoup object
recipe = Recipe.objects.create(name=file.find("div", {"id": "name"}).text.strip(), created_by=self.request.user, internal=True, space=self.request.space, ) try:
source = file.find("a", {"id": "original_link"}).text
except AttributeError:
source = None
recipe = Recipe.objects.create(name=file.find("div", {"id": "name"}).text.strip()[:128], source_url=source, created_by=self.request.user, internal=True, space=self.request.space, )
for category in file.find_all("span", {"class": "recipeCategory"}): for category in file.find_all("span", {"class": "recipeCategory"}):
keyword, created = Keyword.objects.get_or_create(name=category.text, space=self.request.space) keyword, created = Keyword.objects.get_or_create(name=category.text, space=self.request.space)
recipe.keywords.add(keyword) recipe.keywords.add(keyword)
try: try:
recipe.servings = parse_servings(file.find("a", {"id": "recipeYield"}).text.strip()) recipe.servings = parse_servings(file.find("a", {"id": "recipeYield"}).text.strip())
recipe.working_time = iso_duration_to_minutes(file.find("span", {"meta": "prepTime"}).text.strip()) recipe.working_time = iso_duration_to_minutes(file.find("span", {"meta": "prepTime"}).text.strip())
recipe.waiting_time = iso_duration_to_minutes(file.find("span", {"meta": "cookTime"}).text.strip()) recipe.waiting_time = iso_duration_to_minutes(file.find("span", {"meta": "cookTime"}).text.strip())
recipe.description = (file.find("div ", {"id": "description"}).text.strip())[:512]
except AttributeError: except AttributeError:
pass pass
@ -43,36 +45,65 @@ class CopyMeThat(Integration):
except AttributeError: except AttributeError:
pass pass
step = Step.objects.create(instruction='', space=self.request.space, )
ingredient_parser = IngredientParser(self.request, True)
for ingredient in file.find_all("li", {"class": "recipeIngredient"}):
if ingredient.text == "":
continue
amount, unit, food, note = ingredient_parser.parse(ingredient.text.strip())
f = ingredient_parser.get_food(food)
u = ingredient_parser.get_unit(unit)
step.ingredients.add(Ingredient.objects.create(
food=f, unit=u, amount=amount, note=note, original_text=ingredient.text.strip(), space=self.request.space,
))
for s in file.find_all("li", {"class": "instruction"}):
if s.text == "":
continue
step.instruction += s.text.strip() + ' \n\n'
for s in file.find_all("li", {"class": "recipeNote"}):
if s.text == "":
continue
step.instruction += s.text.strip() + ' \n\n'
try: try:
if file.find("a", {"id": "original_link"}).text != '': if len(file.find("span", {"id": "made_this"}).text.strip()) > 0:
step.instruction += "\n\n" + _("Imported from") + ": " + file.find("a", {"id": "original_link"}).text recipe.keywords.add(Keyword.objects.get_or_create(space=self.request.space, name=_('I made this'))[0])
step.save()
except AttributeError: except AttributeError:
pass pass
step = Step.objects.create(instruction='', space=self.request.space, )
ingredient_parser = IngredientParser(self.request, True)
ingredients = file.find("ul", {"id": "recipeIngredients"})
if isinstance(ingredients, Tag):
for ingredient in ingredients.children:
if not isinstance(ingredient, Tag) or not ingredient.text.strip() or "recipeIngredient_spacer" in ingredient['class']:
continue
if any(x in ingredient['class'] for x in ["recipeIngredient_subheader", "recipeIngredient_note"]):
step.ingredients.add(Ingredient.objects.create(is_header=True, note=ingredient.text.strip()[:256], original_text=ingredient.text.strip(), space=self.request.space, ))
else:
amount, unit, food, note = ingredient_parser.parse(ingredient.text.strip())
f = ingredient_parser.get_food(food)
u = ingredient_parser.get_unit(unit)
step.ingredients.add(Ingredient.objects.create(food=f, unit=u, amount=amount, note=note, original_text=ingredient.text.strip(), space=self.request.space, ))
instructions = file.find("ol", {"id": "recipeInstructions"})
if isinstance(instructions, Tag):
for instruction in instructions.children:
if not isinstance(instruction, Tag) or instruction.text == "":
continue
if "instruction_subheader" in instruction['class']:
if step.instruction:
step.save()
recipe.steps.add(step)
step = Step.objects.create(instruction='', space=self.request.space, )
step.name = instruction.text.strip()[:128]
else:
step.instruction += instruction.text.strip() + ' \n\n'
notes = file.find_all("li", {"class": "recipeNote"})
if notes:
step.instruction += '*Notes:* \n\n'
for n in notes:
if n.text == "":
continue
step.instruction += '*' + n.text.strip() + '* \n\n'
description = ''
try:
description = file.find("div", {"id": "description"}).text.strip()
except AttributeError:
pass
if len(description) <= 512:
recipe.description = description
else:
recipe.description = description[:480] + ' ... (full description below)'
step.instruction += '*Description:* \n\n*' + description + '* \n\n'
step.save()
recipe.steps.add(step) recipe.steps.add(step)
# import the Primary recipe image that is stored in the Zip # import the Primary recipe image that is stored in the Zip

View File

@ -43,7 +43,7 @@ class Integration:
self.export_type = export_type self.export_type = export_type
self.ignored_recipes = [] self.ignored_recipes = []
description = f'Imported by {request.user.get_user_name()} at {date_format(datetime.datetime.now(), "DATETIME_FORMAT")}. Type: {export_type}' description = f'Imported by {request.user.get_user_display_name()} at {date_format(datetime.datetime.now(), "DATETIME_FORMAT")}. Type: {export_type}'
icon = '📥' icon = '📥'
try: try:
@ -169,7 +169,7 @@ class Integration:
for z in file_list: for z in file_list:
try: try:
if not hasattr(z, 'filename'): if not hasattr(z, 'filename') or type(z) == Tag:
recipe = self.get_recipe_from_file(z) recipe = self.get_recipe_from_file(z)
else: else:
recipe = self.get_recipe_from_file(BytesIO(import_zip.read(z.filename))) recipe = self.get_recipe_from_file(BytesIO(import_zip.read(z.filename)))

Binary file not shown.

View File

@ -12,7 +12,7 @@ msgid ""
msgstr "" 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: 2022-06-26 12:09+0200\n" "POT-Creation-Date: 2022-07-12 19:20+0200\n"
"PO-Revision-Date: 2022-05-22 11:20+0000\n" "PO-Revision-Date: 2022-05-22 11:20+0000\n"
"Last-Translator: Ramon Aixa Juan <juanramonaixa@gmail.com>\n" "Last-Translator: Ramon Aixa Juan <juanramonaixa@gmail.com>\n"
"Language-Team: Catalan <http://translate.tandoor.dev/projects/tandoor/" "Language-Team: Catalan <http://translate.tandoor.dev/projects/tandoor/"
@ -535,7 +535,7 @@ msgstr "Has arribat al nombre màxim de receptes per al vostre espai."
msgid "You have more users than allowed in your space." msgid "You have more users than allowed in your space."
msgstr "Tens més usuaris dels permesos al teu espai." msgstr "Tens més usuaris dels permesos al teu espai."
#: .\cookbook\helper\recipe_search.py:560 #: .\cookbook\helper\recipe_search.py:565
msgid "One of queryset or hash_key must be provided" msgid "One of queryset or hash_key must be provided"
msgstr "S'ha de proporcionar una de queryset o hash_key" msgstr "S'ha de proporcionar una de queryset o hash_key"
@ -548,12 +548,12 @@ msgstr "Heu de proporcionar una mida de porcions"
msgid "Could not parse template code." msgid "Could not parse template code."
msgstr "No s'ha pogut analitzar el codi de la plantilla." msgstr "No s'ha pogut analitzar el codi de la plantilla."
#: .\cookbook\integration\copymethat.py:42 #: .\cookbook\integration\copymethat.py:41
#: .\cookbook\integration\melarecipes.py:37 #: .\cookbook\integration\melarecipes.py:37
msgid "Favorite" msgid "Favorite"
msgstr "" msgstr ""
#: .\cookbook\integration\copymethat.py:71 #: .\cookbook\integration\copymethat.py:70
#: .\cookbook\integration\recettetek.py:54 #: .\cookbook\integration\recettetek.py:54
#: .\cookbook\integration\recipekeeper.py:63 #: .\cookbook\integration\recipekeeper.py:63
msgid "Imported from" msgid "Imported from"
@ -692,104 +692,104 @@ msgstr "Nova"
msgid " is part of a recipe step and cannot be deleted" msgid " is part of a recipe step and cannot be deleted"
msgstr " forma part d'un pas de recepta i no es pot suprimir" msgstr " forma part d'un pas de recepta i no es pot suprimir"
#: .\cookbook\models.py:1160 .\cookbook\templates\search_info.html:28 #: .\cookbook\models.py:1162 .\cookbook\templates\search_info.html:28
msgid "Simple" msgid "Simple"
msgstr "Simple" msgstr "Simple"
#: .\cookbook\models.py:1161 .\cookbook\templates\search_info.html:33 #: .\cookbook\models.py:1163 .\cookbook\templates\search_info.html:33
msgid "Phrase" msgid "Phrase"
msgstr "Frase" msgstr "Frase"
#: .\cookbook\models.py:1162 .\cookbook\templates\search_info.html:38 #: .\cookbook\models.py:1164 .\cookbook\templates\search_info.html:38
msgid "Web" msgid "Web"
msgstr "Web" msgstr "Web"
#: .\cookbook\models.py:1163 .\cookbook\templates\search_info.html:47 #: .\cookbook\models.py:1165 .\cookbook\templates\search_info.html:47
msgid "Raw" msgid "Raw"
msgstr "Cru" msgstr "Cru"
#: .\cookbook\models.py:1201 #: .\cookbook\models.py:1203
msgid "Food Alias" msgid "Food Alias"
msgstr "Alies Menjar" msgstr "Alies Menjar"
#: .\cookbook\models.py:1201 #: .\cookbook\models.py:1203
msgid "Unit Alias" msgid "Unit Alias"
msgstr "Àlies Unitat" msgstr "Àlies Unitat"
#: .\cookbook\models.py:1201 #: .\cookbook\models.py:1203
msgid "Keyword Alias" msgid "Keyword Alias"
msgstr "Àlies Paraula clau" msgstr "Àlies Paraula clau"
#: .\cookbook\models.py:1225 #: .\cookbook\models.py:1227
#: .\cookbook\templates\include\recipe_open_modal.html:7 #: .\cookbook\templates\include\recipe_open_modal.html:7
#: .\cookbook\views\delete.py:36 .\cookbook\views\edit.py:251 #: .\cookbook\views\delete.py:36 .\cookbook\views\edit.py:251
#: .\cookbook\views\new.py:48 #: .\cookbook\views\new.py:48
msgid "Recipe" msgid "Recipe"
msgstr "Recepta" msgstr "Recepta"
#: .\cookbook\models.py:1226 #: .\cookbook\models.py:1228
#, fuzzy #, fuzzy
#| msgid "Foods" #| msgid "Foods"
msgid "Food" msgid "Food"
msgstr "Menjars" msgstr "Menjars"
#: .\cookbook\models.py:1227 .\cookbook\templates\base.html:138 #: .\cookbook\models.py:1229 .\cookbook\templates\base.html:138
msgid "Keyword" msgid "Keyword"
msgstr "Paraula Clau" msgstr "Paraula Clau"
#: .\cookbook\serializer.py:204 #: .\cookbook\serializer.py:207
msgid "Cannot modify Space owner permission." msgid "Cannot modify Space owner permission."
msgstr "" msgstr ""
#: .\cookbook\serializer.py:273 #: .\cookbook\serializer.py:290
msgid "File uploads are not enabled for this Space." msgid "File uploads are not enabled for this Space."
msgstr "Càrregues de fitxers no habilitades en aquest espai." msgstr "Càrregues de fitxers no habilitades en aquest espai."
#: .\cookbook\serializer.py:284 #: .\cookbook\serializer.py:301
msgid "You have reached your file upload limit." msgid "You have reached your file upload limit."
msgstr "Límit de càrrega de fitxers Assolit." msgstr "Límit de càrrega de fitxers Assolit."
#: .\cookbook\serializer.py:1051 #: .\cookbook\serializer.py:1081
msgid "Hello" msgid "Hello"
msgstr "Hola" msgstr "Hola"
#: .\cookbook\serializer.py:1051 #: .\cookbook\serializer.py:1081
msgid "You have been invited by " msgid "You have been invited by "
msgstr "Convidat per " msgstr "Convidat per "
#: .\cookbook\serializer.py:1052 #: .\cookbook\serializer.py:1082
msgid " to join their Tandoor Recipes space " msgid " to join their Tandoor Recipes space "
msgstr " per unir-se al seu espai de Receptes " msgstr " per unir-se al seu espai de Receptes "
#: .\cookbook\serializer.py:1053 #: .\cookbook\serializer.py:1083
msgid "Click the following link to activate your account: " msgid "Click the following link to activate your account: "
msgstr "Click per activar el teu compte: " msgstr "Click per activar el teu compte: "
#: .\cookbook\serializer.py:1054 #: .\cookbook\serializer.py:1084
msgid "" msgid ""
"If the link does not work use the following code to manually join the space: " "If the link does not work use the following code to manually join the space: "
msgstr "" msgstr ""
"Si l'enllaç no funciona, utilitzeu el codi següent per unir-vos a l'espai: " "Si l'enllaç no funciona, utilitzeu el codi següent per unir-vos a l'espai: "
#: .\cookbook\serializer.py:1055 #: .\cookbook\serializer.py:1085
msgid "The invitation is valid until " msgid "The invitation is valid until "
msgstr "Invitació vàlida fins " msgstr "Invitació vàlida fins "
#: .\cookbook\serializer.py:1056 #: .\cookbook\serializer.py:1086
msgid "" msgid ""
"Tandoor Recipes is an Open Source recipe manager. Check it out on GitHub " "Tandoor Recipes is an Open Source recipe manager. Check it out on GitHub "
msgstr "" msgstr ""
"Tandoor Recipes és un gestor de receptes de codi obert. Comprova a GitHub " "Tandoor Recipes és un gestor de receptes de codi obert. Comprova a GitHub "
#: .\cookbook\serializer.py:1059 #: .\cookbook\serializer.py:1089
msgid "Tandoor Recipes Invite" msgid "Tandoor Recipes Invite"
msgstr "Invitació de receptes Tandoor" msgstr "Invitació de receptes Tandoor"
#: .\cookbook\serializer.py:1179 #: .\cookbook\serializer.py:1209
msgid "Existing shopping list to update" msgid "Existing shopping list to update"
msgstr "Llista de la compra existent a actualitzar" msgstr "Llista de la compra existent a actualitzar"
#: .\cookbook\serializer.py:1181 #: .\cookbook\serializer.py:1211
msgid "" msgid ""
"List of ingredient IDs from the recipe to add, if not provided all " "List of ingredient IDs from the recipe to add, if not provided all "
"ingredients will be added." "ingredients will be added."
@ -797,22 +797,22 @@ msgstr ""
"Llista d'ingredients IDs de la recepta per afegir, si no es proporciona, " "Llista d'ingredients IDs de la recepta per afegir, si no es proporciona, "
"s'afegiran tots els ingredients." "s'afegiran tots els ingredients."
#: .\cookbook\serializer.py:1183 #: .\cookbook\serializer.py:1213
msgid "" msgid ""
"Providing a list_recipe ID and servings of 0 will delete that shopping list." "Providing a list_recipe ID and servings of 0 will delete that shopping list."
msgstr "" msgstr ""
"Proporcionant un list_recipe ID i porcions de 0, se suprimirà aquesta llista " "Proporcionant un list_recipe ID i porcions de 0, se suprimirà aquesta llista "
"de la compra." "de la compra."
#: .\cookbook\serializer.py:1192 #: .\cookbook\serializer.py:1222
msgid "Amount of food to add to the shopping list" msgid "Amount of food to add to the shopping list"
msgstr "Quantitat de menjar per afegir a la llista de la compra" msgstr "Quantitat de menjar per afegir a la llista de la compra"
#: .\cookbook\serializer.py:1194 #: .\cookbook\serializer.py:1224
msgid "ID of unit to use for the shopping list" msgid "ID of unit to use for the shopping list"
msgstr "ID de la unitat a utilitzar per a la llista de la compra" msgstr "ID de la unitat a utilitzar per a la llista de la compra"
#: .\cookbook\serializer.py:1196 #: .\cookbook\serializer.py:1226
msgid "When set to true will delete all food from active shopping lists." msgid "When set to true will delete all food from active shopping lists."
msgstr "" msgstr ""
"Quan s'estableix a true, se suprimirà tots els aliments de les llistes de " "Quan s'estableix a true, se suprimirà tots els aliments de les llistes de "
@ -956,7 +956,7 @@ msgstr ""
"confirmació d'email</a>." "confirmació d'email</a>."
#: .\cookbook\templates\account\login.html:8 #: .\cookbook\templates\account\login.html:8
#: .\cookbook\templates\base.html:339 .\cookbook\templates\openid\login.html:8 #: .\cookbook\templates\base.html:340 .\cookbook\templates\openid\login.html:8
msgid "Login" msgid "Login"
msgstr "Iniciar Sessió" msgstr "Iniciar Sessió"
@ -1123,7 +1123,7 @@ msgstr "Inicis Tancats"
msgid "We are sorry, but the sign up is currently closed." msgid "We are sorry, but the sign up is currently closed."
msgstr "Inicis de Sessió tancats temporalment." msgstr "Inicis de Sessió tancats temporalment."
#: .\cookbook\templates\api_info.html:5 .\cookbook\templates\base.html:329 #: .\cookbook\templates\api_info.html:5 .\cookbook\templates\base.html:330
#: .\cookbook\templates\rest_framework\api.html:11 #: .\cookbook\templates\rest_framework\api.html:11
msgid "API Documentation" msgid "API Documentation"
msgstr "Documentació API" msgstr "Documentació API"
@ -1220,36 +1220,36 @@ msgstr "Admin"
msgid "Your Spaces" msgid "Your Spaces"
msgstr "Sense Espai" msgstr "Sense Espai"
#: .\cookbook\templates\base.html:319 #: .\cookbook\templates\base.html:320
#: .\cookbook\templates\space_overview.html:6 #: .\cookbook\templates\space_overview.html:6
msgid "Overview" msgid "Overview"
msgstr "" msgstr ""
#: .\cookbook\templates\base.html:323 #: .\cookbook\templates\base.html:324
msgid "Markdown Guide" msgid "Markdown Guide"
msgstr "Guia Markdown" msgstr "Guia Markdown"
#: .\cookbook\templates\base.html:325 #: .\cookbook\templates\base.html:326
msgid "GitHub" msgid "GitHub"
msgstr "GitHub" msgstr "GitHub"
#: .\cookbook\templates\base.html:327 #: .\cookbook\templates\base.html:328
msgid "Translate Tandoor" msgid "Translate Tandoor"
msgstr "Tradueix Tandoor" msgstr "Tradueix Tandoor"
#: .\cookbook\templates\base.html:331 #: .\cookbook\templates\base.html:332
msgid "API Browser" msgid "API Browser"
msgstr "Navegador API" msgstr "Navegador API"
#: .\cookbook\templates\base.html:334 #: .\cookbook\templates\base.html:335
msgid "Log out" msgid "Log out"
msgstr "Tanca sessió" msgstr "Tanca sessió"
#: .\cookbook\templates\base.html:356 #: .\cookbook\templates\base.html:357
msgid "You are using the free version of Tandor" msgid "You are using the free version of Tandor"
msgstr "" msgstr ""
#: .\cookbook\templates\base.html:357 #: .\cookbook\templates\base.html:358
msgid "Upgrade Now" msgid "Upgrade Now"
msgstr "" msgstr ""
@ -2263,19 +2263,11 @@ msgstr "Receptes sense paraules clau"
msgid "Internal Recipes" msgid "Internal Recipes"
msgstr "Receptes Internes" msgstr "Receptes Internes"
#: .\cookbook\templates\system.html:21 .\cookbook\views\lists.py:76 #: .\cookbook\templates\system.html:20
msgid "Invite Links"
msgstr "Enllaços Invitació"
#: .\cookbook\templates\system.html:22
msgid "Show Links"
msgstr "Mostra Enllaços"
#: .\cookbook\templates\system.html:32
msgid "System Information" msgid "System Information"
msgstr "Informació de Sistema" msgstr "Informació de Sistema"
#: .\cookbook\templates\system.html:34 #: .\cookbook\templates\system.html:22
msgid "" msgid ""
"\n" "\n"
" Django Recipes is an open source free software application. It can " " Django Recipes is an open source free software application. It can "
@ -2293,21 +2285,21 @@ msgstr ""
"com/vabene1111/recipes/releases\">aquí</a>.\n" "com/vabene1111/recipes/releases\">aquí</a>.\n"
" " " "
#: .\cookbook\templates\system.html:48 #: .\cookbook\templates\system.html:36
msgid "Media Serving" msgid "Media Serving"
msgstr "Servei Mitjans" msgstr "Servei Mitjans"
#: .\cookbook\templates\system.html:49 .\cookbook\templates\system.html:64 #: .\cookbook\templates\system.html:37 .\cookbook\templates\system.html:52
#: .\cookbook\templates\system.html:80 #: .\cookbook\templates\system.html:68
msgid "Warning" msgid "Warning"
msgstr "Advertència" msgstr "Advertència"
#: .\cookbook\templates\system.html:49 .\cookbook\templates\system.html:64 #: .\cookbook\templates\system.html:37 .\cookbook\templates\system.html:52
#: .\cookbook\templates\system.html:80 .\cookbook\templates\system.html:95 #: .\cookbook\templates\system.html:68 .\cookbook\templates\system.html:83
msgid "Ok" msgid "Ok"
msgstr "Ok" msgstr "Ok"
#: .\cookbook\templates\system.html:51 #: .\cookbook\templates\system.html:39
msgid "" msgid ""
"Serving media files directly using gunicorn/python is <b>not recommend</b>!\n" "Serving media files directly using gunicorn/python is <b>not recommend</b>!\n"
" Please follow the steps described\n" " Please follow the steps described\n"
@ -2323,16 +2315,16 @@ msgstr ""
"a> per actualitzar\n" "a> per actualitzar\n"
"la vostra instal·lació." "la vostra instal·lació."
#: .\cookbook\templates\system.html:57 .\cookbook\templates\system.html:73 #: .\cookbook\templates\system.html:45 .\cookbook\templates\system.html:61
#: .\cookbook\templates\system.html:88 .\cookbook\templates\system.html:102 #: .\cookbook\templates\system.html:76 .\cookbook\templates\system.html:90
msgid "Everything is fine!" msgid "Everything is fine!"
msgstr "Tot està bé!" msgstr "Tot està bé!"
#: .\cookbook\templates\system.html:62 #: .\cookbook\templates\system.html:50
msgid "Secret Key" msgid "Secret Key"
msgstr "Paraula Clau" msgstr "Paraula Clau"
#: .\cookbook\templates\system.html:66 #: .\cookbook\templates\system.html:54
msgid "" msgid ""
"\n" "\n"
" You do not have a <code>SECRET_KEY</code> configured in your " " You do not have a <code>SECRET_KEY</code> configured in your "
@ -2352,11 +2344,11 @@ msgstr ""
"Estableix-ho\n" "Estableix-ho\n"
"<code>SECRET_KEY</code> al fitxer de configuració<code> .env.</code>" "<code>SECRET_KEY</code> al fitxer de configuració<code> .env.</code>"
#: .\cookbook\templates\system.html:78 #: .\cookbook\templates\system.html:66
msgid "Debug Mode" msgid "Debug Mode"
msgstr "Mode Depuració" msgstr "Mode Depuració"
#: .\cookbook\templates\system.html:82 #: .\cookbook\templates\system.html:70
msgid "" msgid ""
"\n" "\n"
" This application is still running in debug mode. This is most " " This application is still running in debug mode. This is most "
@ -2372,15 +2364,15 @@ msgstr ""
"configuració\n" "configuració\n"
"<code>DEBUG = 0</code> al fitxer de configuració<code> .env.</code>" "<code>DEBUG = 0</code> al fitxer de configuració<code> .env.</code>"
#: .\cookbook\templates\system.html:93 #: .\cookbook\templates\system.html:81
msgid "Database" msgid "Database"
msgstr "Base de Dades" msgstr "Base de Dades"
#: .\cookbook\templates\system.html:95 #: .\cookbook\templates\system.html:83
msgid "Info" msgid "Info"
msgstr "Info" msgstr "Info"
#: .\cookbook\templates\system.html:97 #: .\cookbook\templates\system.html:85
msgid "" msgid ""
"\n" "\n"
" This application is not running with a Postgres database " " This application is not running with a Postgres database "
@ -2397,72 +2389,72 @@ msgstr ""
msgid "URL Import" msgid "URL Import"
msgstr "Importació dURL" msgstr "Importació dURL"
#: .\cookbook\views\api.py:97 .\cookbook\views\api.py:189 #: .\cookbook\views\api.py:105 .\cookbook\views\api.py:197
msgid "Parameter updated_at incorrectly formatted" msgid "Parameter updated_at incorrectly formatted"
msgstr "El paràmetre updated_at té un format incorrecte" msgstr "El paràmetre updated_at té un format incorrecte"
#: .\cookbook\views\api.py:209 .\cookbook\views\api.py:312 #: .\cookbook\views\api.py:217 .\cookbook\views\api.py:320
msgid "No {self.basename} with id {pk} exists" msgid "No {self.basename} with id {pk} exists"
msgstr "No {self.basename} amb id {pk} existeix" msgstr "No {self.basename} amb id {pk} existeix"
#: .\cookbook\views\api.py:213 #: .\cookbook\views\api.py:221
msgid "Cannot merge with the same object!" msgid "Cannot merge with the same object!"
msgstr "No es pot fusionar amb el mateix objecte!" msgstr "No es pot fusionar amb el mateix objecte!"
#: .\cookbook\views\api.py:220 #: .\cookbook\views\api.py:228
msgid "No {self.basename} with id {target} exists" msgid "No {self.basename} with id {target} exists"
msgstr "No {self.basename} amb id {target} existeix" msgstr "No {self.basename} amb id {target} existeix"
#: .\cookbook\views\api.py:225 #: .\cookbook\views\api.py:233
msgid "Cannot merge with child object!" msgid "Cannot merge with child object!"
msgstr "No es pot combinar amb l'objecte fill!" msgstr "No es pot combinar amb l'objecte fill!"
#: .\cookbook\views\api.py:258 #: .\cookbook\views\api.py:266
msgid "{source.name} was merged successfully with {target.name}" msgid "{source.name} was merged successfully with {target.name}"
msgstr "{source.name} s'ha fusionat amb {target.name}" msgstr "{source.name} s'ha fusionat amb {target.name}"
#: .\cookbook\views\api.py:263 #: .\cookbook\views\api.py:271
msgid "An error occurred attempting to merge {source.name} with {target.name}" msgid "An error occurred attempting to merge {source.name} with {target.name}"
msgstr "Error en intentar combinar {source.name} amb {target.name}" msgstr "Error en intentar combinar {source.name} amb {target.name}"
#: .\cookbook\views\api.py:321 #: .\cookbook\views\api.py:329
msgid "{child.name} was moved successfully to the root." msgid "{child.name} was moved successfully to the root."
msgstr "{child.name} s'ha mogut correctament a l'arrel." msgstr "{child.name} s'ha mogut correctament a l'arrel."
#: .\cookbook\views\api.py:324 .\cookbook\views\api.py:342 #: .\cookbook\views\api.py:332 .\cookbook\views\api.py:350
msgid "An error occurred attempting to move " msgid "An error occurred attempting to move "
msgstr "Error a l'intentar moure " msgstr "Error a l'intentar moure "
#: .\cookbook\views\api.py:327 #: .\cookbook\views\api.py:335
msgid "Cannot move an object to itself!" msgid "Cannot move an object to itself!"
msgstr "No es pot moure un objecte cap a si mateix!" msgstr "No es pot moure un objecte cap a si mateix!"
#: .\cookbook\views\api.py:333 #: .\cookbook\views\api.py:341
msgid "No {self.basename} with id {parent} exists" msgid "No {self.basename} with id {parent} exists"
msgstr "No existeix {self.basename} amb identificador {parent}" msgstr "No existeix {self.basename} amb identificador {parent}"
#: .\cookbook\views\api.py:339 #: .\cookbook\views\api.py:347
msgid "{child.name} was moved successfully to parent {parent.name}" msgid "{child.name} was moved successfully to parent {parent.name}"
msgstr "{child.name} s'ha mogut correctament al pare {parent.name}" msgstr "{child.name} s'ha mogut correctament al pare {parent.name}"
#: .\cookbook\views\api.py:534 #: .\cookbook\views\api.py:542
msgid "{obj.name} was removed from the shopping list." msgid "{obj.name} was removed from the shopping list."
msgstr "{obj.name} eliminat de la llista de la compra." msgstr "{obj.name} eliminat de la llista de la compra."
#: .\cookbook\views\api.py:539 .\cookbook\views\api.py:871 #: .\cookbook\views\api.py:547 .\cookbook\views\api.py:879
#: .\cookbook\views\api.py:884 #: .\cookbook\views\api.py:892
msgid "{obj.name} was added to the shopping list." msgid "{obj.name} was added to the shopping list."
msgstr "Afegit {obj.name} a la llista de la compra." msgstr "Afegit {obj.name} a la llista de la compra."
#: .\cookbook\views\api.py:666 #: .\cookbook\views\api.py:674
msgid "ID of recipe a step is part of. For multiple repeat parameter." msgid "ID of recipe a step is part of. For multiple repeat parameter."
msgstr "ID de recepta forma part d'un pas. Per a múltiples repeteix paràmetre." msgstr "ID de recepta forma part d'un pas. Per a múltiples repeteix paràmetre."
#: .\cookbook\views\api.py:668 #: .\cookbook\views\api.py:676
msgid "Query string matched (fuzzy) against object name." msgid "Query string matched (fuzzy) against object name."
msgstr "La cadena de consulta coincideix (difusa) amb el nom de l'objecte." msgstr "La cadena de consulta coincideix (difusa) amb el nom de l'objecte."
#: .\cookbook\views\api.py:712 #: .\cookbook\views\api.py:720
msgid "" msgid ""
"Query string matched (fuzzy) against recipe name. In the future also " "Query string matched (fuzzy) against recipe name. In the future also "
"fulltext search." "fulltext search."
@ -2470,7 +2462,7 @@ msgstr ""
"Cadena de consulta coincideix (difusa) amb el nom de la recepta. En el futur " "Cadena de consulta coincideix (difusa) amb el nom de la recepta. En el futur "
"també cerca text complet." "també cerca text complet."
#: .\cookbook\views\api.py:714 #: .\cookbook\views\api.py:722
#, fuzzy #, fuzzy
#| msgid "ID of keyword a recipe should have. For multiple repeat parameter." #| msgid "ID of keyword a recipe should have. For multiple repeat parameter."
msgid "" msgid ""
@ -2480,173 +2472,173 @@ msgstr ""
"ID de la paraula clau que hauria de tenir una recepta. Per a múltiples " "ID de la paraula clau que hauria de tenir una recepta. Per a múltiples "
"repeteix paràmetre." "repeteix paràmetre."
#: .\cookbook\views\api.py:717 #: .\cookbook\views\api.py:725
msgid "" msgid ""
"Keyword IDs, repeat for multiple. Return recipes with any of the keywords" "Keyword IDs, repeat for multiple. Return recipes with any of the keywords"
msgstr "" msgstr ""
#: .\cookbook\views\api.py:720 #: .\cookbook\views\api.py:728
msgid "" msgid ""
"Keyword IDs, repeat for multiple. Return recipes with all of the keywords." "Keyword IDs, repeat for multiple. Return recipes with all of the keywords."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:723 #: .\cookbook\views\api.py:731
msgid "" msgid ""
"Keyword IDs, repeat for multiple. Exclude recipes with any of the keywords." "Keyword IDs, repeat for multiple. Exclude recipes with any of the keywords."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:726 #: .\cookbook\views\api.py:734
msgid "" msgid ""
"Keyword IDs, repeat for multiple. Exclude recipes with all of the keywords." "Keyword IDs, repeat for multiple. Exclude recipes with all of the keywords."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:728 #: .\cookbook\views\api.py:736
msgid "ID of food a recipe should have. For multiple repeat parameter." msgid "ID of food a recipe should have. For multiple repeat parameter."
msgstr "" msgstr ""
"ID d'aliments que ha de tenir una recepta. Per a múltiples repeteix " "ID d'aliments que ha de tenir una recepta. Per a múltiples repeteix "
"paràmetres." "paràmetres."
#: .\cookbook\views\api.py:731 #: .\cookbook\views\api.py:739
msgid "Food IDs, repeat for multiple. Return recipes with any of the foods" msgid "Food IDs, repeat for multiple. Return recipes with any of the foods"
msgstr "" msgstr ""
#: .\cookbook\views\api.py:733 #: .\cookbook\views\api.py:741
msgid "Food IDs, repeat for multiple. Return recipes with all of the foods." msgid "Food IDs, repeat for multiple. Return recipes with all of the foods."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:735 #: .\cookbook\views\api.py:743
msgid "Food IDs, repeat for multiple. Exclude recipes with any of the foods." msgid "Food IDs, repeat for multiple. Exclude recipes with any of the foods."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:737 #: .\cookbook\views\api.py:745
msgid "Food IDs, repeat for multiple. Exclude recipes with all of the foods." msgid "Food IDs, repeat for multiple. Exclude recipes with all of the foods."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:738 #: .\cookbook\views\api.py:746
msgid "ID of unit a recipe should have." msgid "ID of unit a recipe should have."
msgstr "ID d'unitat que hauria de tenir una recepta." msgstr "ID d'unitat que hauria de tenir una recepta."
#: .\cookbook\views\api.py:740 #: .\cookbook\views\api.py:748
msgid "" msgid ""
"Rating a recipe should have or greater. [0 - 5] Negative value filters " "Rating a recipe should have or greater. [0 - 5] Negative value filters "
"rating less than." "rating less than."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:741 #: .\cookbook\views\api.py:749
msgid "ID of book a recipe should be in. For multiple repeat parameter." msgid "ID of book a recipe should be in. For multiple repeat parameter."
msgstr "" msgstr ""
"ID del llibre hauria d'haver-hi en una recepta. Per al paràmetre de " "ID del llibre hauria d'haver-hi en una recepta. Per al paràmetre de "
"repetició múltiple." "repetició múltiple."
#: .\cookbook\views\api.py:743 #: .\cookbook\views\api.py:751
msgid "Book IDs, repeat for multiple. Return recipes with any of the books" msgid "Book IDs, repeat for multiple. Return recipes with any of the books"
msgstr "" msgstr ""
#: .\cookbook\views\api.py:745 #: .\cookbook\views\api.py:753
msgid "Book IDs, repeat for multiple. Return recipes with all of the books." msgid "Book IDs, repeat for multiple. Return recipes with all of the books."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:747 #: .\cookbook\views\api.py:755
msgid "Book IDs, repeat for multiple. Exclude recipes with any of the books." msgid "Book IDs, repeat for multiple. Exclude recipes with any of the books."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:749 #: .\cookbook\views\api.py:757
msgid "Book IDs, repeat for multiple. Exclude recipes with all of the books." msgid "Book IDs, repeat for multiple. Exclude recipes with all of the books."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:751 #: .\cookbook\views\api.py:759
msgid "If only internal recipes should be returned. [true/<b>false</b>]" msgid "If only internal recipes should be returned. [true/<b>false</b>]"
msgstr "" msgstr ""
#: .\cookbook\views\api.py:753 #: .\cookbook\views\api.py:761
msgid "Returns the results in randomized order. [true/<b>false</b>]" msgid "Returns the results in randomized order. [true/<b>false</b>]"
msgstr "" msgstr ""
#: .\cookbook\views\api.py:755 #: .\cookbook\views\api.py:763
msgid "Returns new results first in search results. [true/<b>false</b>]" msgid "Returns new results first in search results. [true/<b>false</b>]"
msgstr "" msgstr ""
#: .\cookbook\views\api.py:757 #: .\cookbook\views\api.py:765
msgid "" msgid ""
"Filter recipes cooked X times or more. Negative values returns cooked less " "Filter recipes cooked X times or more. Negative values returns cooked less "
"than X times" "than X times"
msgstr "" msgstr ""
#: .\cookbook\views\api.py:759 #: .\cookbook\views\api.py:767
msgid "" msgid ""
"Filter recipes last cooked on or after YYYY-MM-DD. Prepending - filters on " "Filter recipes last cooked on or after YYYY-MM-DD. Prepending - filters on "
"or before date." "or before date."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:761 #: .\cookbook\views\api.py:769
msgid "" msgid ""
"Filter recipes created on or after YYYY-MM-DD. Prepending - filters on or " "Filter recipes created on or after YYYY-MM-DD. Prepending - filters on or "
"before date." "before date."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:763 #: .\cookbook\views\api.py:771
msgid "" msgid ""
"Filter recipes updated on or after YYYY-MM-DD. Prepending - filters on or " "Filter recipes updated on or after YYYY-MM-DD. Prepending - filters on or "
"before date." "before date."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:765 #: .\cookbook\views\api.py:773
msgid "" msgid ""
"Filter recipes lasts viewed on or after YYYY-MM-DD. Prepending - filters on " "Filter recipes lasts viewed on or after YYYY-MM-DD. Prepending - filters on "
"or before date." "or before date."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:767 #: .\cookbook\views\api.py:775
msgid "Filter recipes that can be made with OnHand food. [true/<b>false</b>]" msgid "Filter recipes that can be made with OnHand food. [true/<b>false</b>]"
msgstr "" msgstr ""
#: .\cookbook\views\api.py:929 #: .\cookbook\views\api.py:937
msgid "" msgid ""
"Returns the shopping list entry with a primary key of id. Multiple values " "Returns the shopping list entry with a primary key of id. Multiple values "
"allowed." "allowed."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:934 #: .\cookbook\views\api.py:942
msgid "" msgid ""
"Filter shopping list entries on checked. [true, false, both, <b>recent</b>]" "Filter shopping list entries on checked. [true, false, both, <b>recent</b>]"
"<br> - recent includes unchecked items and recently completed items." "<br> - recent includes unchecked items and recently completed items."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:937 #: .\cookbook\views\api.py:945
msgid "Returns the shopping list entries sorted by supermarket category order." msgid "Returns the shopping list entries sorted by supermarket category order."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:1134 #: .\cookbook\views\api.py:1140
msgid "Nothing to do." msgid "Nothing to do."
msgstr "Res a fer." msgstr "Res a fer."
#: .\cookbook\views\api.py:1153 #: .\cookbook\views\api.py:1160
msgid "Invalid Url" msgid "Invalid Url"
msgstr "" msgstr ""
#: .\cookbook\views\api.py:1158 #: .\cookbook\views\api.py:1167
msgid "Connection Refused." msgid "Connection Refused."
msgstr "Connexió Refusada." msgstr "Connexió Refusada."
#: .\cookbook\views\api.py:1163 #: .\cookbook\views\api.py:1172
msgid "Bad URL Schema." msgid "Bad URL Schema."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:1170 #: .\cookbook\views\api.py:1195
msgid "No usable data could be found." msgid "No usable data could be found."
msgstr "No s'han trobat dades utilitzables." msgstr "No s'han trobat dades utilitzables."
#: .\cookbook\views\api.py:1260 .\cookbook\views\data.py:28 #: .\cookbook\views\api.py:1303 .\cookbook\views\data.py:28
#: .\cookbook\views\edit.py:120 .\cookbook\views\new.py:90 #: .\cookbook\views\edit.py:120 .\cookbook\views\new.py:90
msgid "This feature is not yet available in the hosted version of tandoor!" msgid "This feature is not yet available in the hosted version of tandoor!"
msgstr "" msgstr ""
"Aquesta funció encara no està disponible a la versió allotjada de tandoor!" "Aquesta funció encara no està disponible a la versió allotjada de tandoor!"
#: .\cookbook\views\api.py:1282 #: .\cookbook\views\api.py:1325
msgid "Sync successful!" msgid "Sync successful!"
msgstr "Sincronització correcte" msgstr "Sincronització correcte"
#: .\cookbook\views\api.py:1287 #: .\cookbook\views\api.py:1330
msgid "Error synchronizing with Storage" msgid "Error synchronizing with Storage"
msgstr "Error de sincronització amb emmagatzematge" msgstr "Error de sincronització amb emmagatzematge"
@ -2736,6 +2728,10 @@ msgstr "Descobriment"
msgid "Shopping List" msgid "Shopping List"
msgstr "Llista de la Compra" msgstr "Llista de la Compra"
#: .\cookbook\views\lists.py:76
msgid "Invite Links"
msgstr "Enllaços Invitació"
#: .\cookbook\views\lists.py:139 #: .\cookbook\views\lists.py:139
msgid "Supermarkets" msgid "Supermarkets"
msgstr "Supermercats" msgstr "Supermercats"
@ -2844,6 +2840,9 @@ msgstr ""
"L'enllaç per compartir receptes s'ha desactivat! Per obtenir informació " "L'enllaç per compartir receptes s'ha desactivat! Per obtenir informació "
"addicional, poseu-vos en contacte amb l'administrador." "addicional, poseu-vos en contacte amb l'administrador."
#~ msgid "Show Links"
#~ msgstr "Mostra Enllaços"
#~ msgid "A user is required" #~ msgid "A user is required"
#~ msgstr "Usuari requerit" #~ msgstr "Usuari requerit"

View File

@ -8,7 +8,7 @@ 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: 2022-04-29 18:42+0200\n" "POT-Creation-Date: 2022-04-29 18:42+0200\n"
"PO-Revision-Date: 2022-05-10 15:32+0000\n" "PO-Revision-Date: 2022-08-18 14:32+0000\n"
"Last-Translator: Mathias Rasmussen <math625f@gmail.com>\n" "Last-Translator: Mathias Rasmussen <math625f@gmail.com>\n"
"Language-Team: Danish <http://translate.tandoor.dev/projects/tandoor/" "Language-Team: Danish <http://translate.tandoor.dev/projects/tandoor/"
"recipes-backend/da/>\n" "recipes-backend/da/>\n"
@ -2377,9 +2377,9 @@ msgid ""
" " " "
msgstr "" msgstr ""
"At servere mediefiler direkte med gunicorn/python er <b>ikke anbefalet</b>!\n" "At servere mediefiler direkte med gunicorn/python er <b>ikke anbefalet</b>!\n"
" Følg venligst trinne beskrevet\n" " Følg venligst trinnene beskrevet\n"
" <a href=\"https://github.com/vabene1111/recipes/releases/tag/0.8.1\"" " <a href=\"https://github.com/vabene1111/recipes/releases/tag/0.8.1\""
">here</a> for at opdtere\n" ">her</a> for at opdatere\n"
" din installation.\n" " din installation.\n"
" " " "

File diff suppressed because it is too large Load Diff

View File

@ -8,7 +8,7 @@ msgid ""
msgstr "" 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: 2022-06-26 12:09+0200\n" "POT-Creation-Date: 2022-07-12 19:20+0200\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n" "Language-Team: LANGUAGE <LL@li.org>\n"
@ -471,7 +471,7 @@ msgstr ""
msgid "You have more users than allowed in your space." msgid "You have more users than allowed in your space."
msgstr "" msgstr ""
#: .\cookbook\helper\recipe_search.py:560 #: .\cookbook\helper\recipe_search.py:565
msgid "One of queryset or hash_key must be provided" msgid "One of queryset or hash_key must be provided"
msgstr "" msgstr ""
@ -484,12 +484,12 @@ msgstr ""
msgid "Could not parse template code." msgid "Could not parse template code."
msgstr "" msgstr ""
#: .\cookbook\integration\copymethat.py:42 #: .\cookbook\integration\copymethat.py:41
#: .\cookbook\integration\melarecipes.py:37 #: .\cookbook\integration\melarecipes.py:37
msgid "Favorite" msgid "Favorite"
msgstr "" msgstr ""
#: .\cookbook\integration\copymethat.py:71 #: .\cookbook\integration\copymethat.py:70
#: .\cookbook\integration\recettetek.py:54 #: .\cookbook\integration\recettetek.py:54
#: .\cookbook\integration\recipekeeper.py:63 #: .\cookbook\integration\recipekeeper.py:63
msgid "Imported from" msgid "Imported from"
@ -620,119 +620,119 @@ msgstr ""
msgid " is part of a recipe step and cannot be deleted" msgid " is part of a recipe step and cannot be deleted"
msgstr "" msgstr ""
#: .\cookbook\models.py:1160 .\cookbook\templates\search_info.html:28 #: .\cookbook\models.py:1162 .\cookbook\templates\search_info.html:28
msgid "Simple" msgid "Simple"
msgstr "" msgstr ""
#: .\cookbook\models.py:1161 .\cookbook\templates\search_info.html:33 #: .\cookbook\models.py:1163 .\cookbook\templates\search_info.html:33
msgid "Phrase" msgid "Phrase"
msgstr "" msgstr ""
#: .\cookbook\models.py:1162 .\cookbook\templates\search_info.html:38 #: .\cookbook\models.py:1164 .\cookbook\templates\search_info.html:38
msgid "Web" msgid "Web"
msgstr "" msgstr ""
#: .\cookbook\models.py:1163 .\cookbook\templates\search_info.html:47 #: .\cookbook\models.py:1165 .\cookbook\templates\search_info.html:47
msgid "Raw" msgid "Raw"
msgstr "" msgstr ""
#: .\cookbook\models.py:1201 #: .\cookbook\models.py:1203
msgid "Food Alias" msgid "Food Alias"
msgstr "" msgstr ""
#: .\cookbook\models.py:1201 #: .\cookbook\models.py:1203
msgid "Unit Alias" msgid "Unit Alias"
msgstr "" msgstr ""
#: .\cookbook\models.py:1201 #: .\cookbook\models.py:1203
msgid "Keyword Alias" msgid "Keyword Alias"
msgstr "" msgstr ""
#: .\cookbook\models.py:1225 #: .\cookbook\models.py:1227
#: .\cookbook\templates\include\recipe_open_modal.html:7 #: .\cookbook\templates\include\recipe_open_modal.html:7
#: .\cookbook\views\delete.py:36 .\cookbook\views\edit.py:251 #: .\cookbook\views\delete.py:36 .\cookbook\views\edit.py:251
#: .\cookbook\views\new.py:48 #: .\cookbook\views\new.py:48
msgid "Recipe" msgid "Recipe"
msgstr "" msgstr ""
#: .\cookbook\models.py:1226 #: .\cookbook\models.py:1228
msgid "Food" msgid "Food"
msgstr "" msgstr ""
#: .\cookbook\models.py:1227 .\cookbook\templates\base.html:138 #: .\cookbook\models.py:1229 .\cookbook\templates\base.html:138
msgid "Keyword" msgid "Keyword"
msgstr "" msgstr ""
#: .\cookbook\serializer.py:204 #: .\cookbook\serializer.py:207
msgid "Cannot modify Space owner permission." msgid "Cannot modify Space owner permission."
msgstr "" msgstr ""
#: .\cookbook\serializer.py:273 #: .\cookbook\serializer.py:290
msgid "File uploads are not enabled for this Space." msgid "File uploads are not enabled for this Space."
msgstr "" msgstr ""
#: .\cookbook\serializer.py:284 #: .\cookbook\serializer.py:301
msgid "You have reached your file upload limit." msgid "You have reached your file upload limit."
msgstr "" msgstr ""
#: .\cookbook\serializer.py:1051 #: .\cookbook\serializer.py:1081
msgid "Hello" msgid "Hello"
msgstr "" msgstr ""
#: .\cookbook\serializer.py:1051 #: .\cookbook\serializer.py:1081
msgid "You have been invited by " msgid "You have been invited by "
msgstr "" msgstr ""
#: .\cookbook\serializer.py:1052 #: .\cookbook\serializer.py:1082
msgid " to join their Tandoor Recipes space " msgid " to join their Tandoor Recipes space "
msgstr "" msgstr ""
#: .\cookbook\serializer.py:1053 #: .\cookbook\serializer.py:1083
msgid "Click the following link to activate your account: " msgid "Click the following link to activate your account: "
msgstr "" msgstr ""
#: .\cookbook\serializer.py:1054 #: .\cookbook\serializer.py:1084
msgid "" msgid ""
"If the link does not work use the following code to manually join the space: " "If the link does not work use the following code to manually join the space: "
msgstr "" msgstr ""
#: .\cookbook\serializer.py:1055 #: .\cookbook\serializer.py:1085
msgid "The invitation is valid until " msgid "The invitation is valid until "
msgstr "" msgstr ""
#: .\cookbook\serializer.py:1056 #: .\cookbook\serializer.py:1086
msgid "" msgid ""
"Tandoor Recipes is an Open Source recipe manager. Check it out on GitHub " "Tandoor Recipes is an Open Source recipe manager. Check it out on GitHub "
msgstr "" msgstr ""
#: .\cookbook\serializer.py:1059 #: .\cookbook\serializer.py:1089
msgid "Tandoor Recipes Invite" msgid "Tandoor Recipes Invite"
msgstr "" msgstr ""
#: .\cookbook\serializer.py:1179 #: .\cookbook\serializer.py:1209
msgid "Existing shopping list to update" msgid "Existing shopping list to update"
msgstr "" msgstr ""
#: .\cookbook\serializer.py:1181 #: .\cookbook\serializer.py:1211
msgid "" msgid ""
"List of ingredient IDs from the recipe to add, if not provided all " "List of ingredient IDs from the recipe to add, if not provided all "
"ingredients will be added." "ingredients will be added."
msgstr "" msgstr ""
#: .\cookbook\serializer.py:1183 #: .\cookbook\serializer.py:1213
msgid "" msgid ""
"Providing a list_recipe ID and servings of 0 will delete that shopping list." "Providing a list_recipe ID and servings of 0 will delete that shopping list."
msgstr "" msgstr ""
#: .\cookbook\serializer.py:1192 #: .\cookbook\serializer.py:1222
msgid "Amount of food to add to the shopping list" msgid "Amount of food to add to the shopping list"
msgstr "" msgstr ""
#: .\cookbook\serializer.py:1194 #: .\cookbook\serializer.py:1224
msgid "ID of unit to use for the shopping list" msgid "ID of unit to use for the shopping list"
msgstr "" msgstr ""
#: .\cookbook\serializer.py:1196 #: .\cookbook\serializer.py:1226
msgid "When set to true will delete all food from active shopping lists." msgid "When set to true will delete all food from active shopping lists."
msgstr "" msgstr ""
@ -864,7 +864,7 @@ msgid ""
msgstr "" msgstr ""
#: .\cookbook\templates\account\login.html:8 #: .\cookbook\templates\account\login.html:8
#: .\cookbook\templates\base.html:339 .\cookbook\templates\openid\login.html:8 #: .\cookbook\templates\base.html:340 .\cookbook\templates\openid\login.html:8
msgid "Login" msgid "Login"
msgstr "" msgstr ""
@ -1028,7 +1028,7 @@ msgstr ""
msgid "We are sorry, but the sign up is currently closed." msgid "We are sorry, but the sign up is currently closed."
msgstr "" msgstr ""
#: .\cookbook\templates\api_info.html:5 .\cookbook\templates\base.html:329 #: .\cookbook\templates\api_info.html:5 .\cookbook\templates\base.html:330
#: .\cookbook\templates\rest_framework\api.html:11 #: .\cookbook\templates\rest_framework\api.html:11
msgid "API Documentation" msgid "API Documentation"
msgstr "" msgstr ""
@ -1121,36 +1121,36 @@ msgstr ""
msgid "Your Spaces" msgid "Your Spaces"
msgstr "" msgstr ""
#: .\cookbook\templates\base.html:319 #: .\cookbook\templates\base.html:320
#: .\cookbook\templates\space_overview.html:6 #: .\cookbook\templates\space_overview.html:6
msgid "Overview" msgid "Overview"
msgstr "" msgstr ""
#: .\cookbook\templates\base.html:323 #: .\cookbook\templates\base.html:324
msgid "Markdown Guide" msgid "Markdown Guide"
msgstr "" msgstr ""
#: .\cookbook\templates\base.html:325 #: .\cookbook\templates\base.html:326
msgid "GitHub" msgid "GitHub"
msgstr "" msgstr ""
#: .\cookbook\templates\base.html:327 #: .\cookbook\templates\base.html:328
msgid "Translate Tandoor" msgid "Translate Tandoor"
msgstr "" msgstr ""
#: .\cookbook\templates\base.html:331 #: .\cookbook\templates\base.html:332
msgid "API Browser" msgid "API Browser"
msgstr "" msgstr ""
#: .\cookbook\templates\base.html:334 #: .\cookbook\templates\base.html:335
msgid "Log out" msgid "Log out"
msgstr "" msgstr ""
#: .\cookbook\templates\base.html:356 #: .\cookbook\templates\base.html:357
msgid "You are using the free version of Tandor" msgid "You are using the free version of Tandor"
msgstr "" msgstr ""
#: .\cookbook\templates\base.html:357 #: .\cookbook\templates\base.html:358
msgid "Upgrade Now" msgid "Upgrade Now"
msgstr "" msgstr ""
@ -2091,19 +2091,11 @@ msgstr ""
msgid "Internal Recipes" msgid "Internal Recipes"
msgstr "" msgstr ""
#: .\cookbook\templates\system.html:21 .\cookbook\views\lists.py:76 #: .\cookbook\templates\system.html:20
msgid "Invite Links"
msgstr ""
#: .\cookbook\templates\system.html:22
msgid "Show Links"
msgstr ""
#: .\cookbook\templates\system.html:32
msgid "System Information" msgid "System Information"
msgstr "" msgstr ""
#: .\cookbook\templates\system.html:34 #: .\cookbook\templates\system.html:22
msgid "" msgid ""
"\n" "\n"
" Django Recipes is an open source free software application. It can " " Django Recipes is an open source free software application. It can "
@ -2114,21 +2106,21 @@ msgid ""
" " " "
msgstr "" msgstr ""
#: .\cookbook\templates\system.html:48 #: .\cookbook\templates\system.html:36
msgid "Media Serving" msgid "Media Serving"
msgstr "" msgstr ""
#: .\cookbook\templates\system.html:49 .\cookbook\templates\system.html:64 #: .\cookbook\templates\system.html:37 .\cookbook\templates\system.html:52
#: .\cookbook\templates\system.html:80 #: .\cookbook\templates\system.html:68
msgid "Warning" msgid "Warning"
msgstr "" msgstr ""
#: .\cookbook\templates\system.html:49 .\cookbook\templates\system.html:64 #: .\cookbook\templates\system.html:37 .\cookbook\templates\system.html:52
#: .\cookbook\templates\system.html:80 .\cookbook\templates\system.html:95 #: .\cookbook\templates\system.html:68 .\cookbook\templates\system.html:83
msgid "Ok" msgid "Ok"
msgstr "" msgstr ""
#: .\cookbook\templates\system.html:51 #: .\cookbook\templates\system.html:39
msgid "" msgid ""
"Serving media files directly using gunicorn/python is <b>not recommend</b>!\n" "Serving media files directly using gunicorn/python is <b>not recommend</b>!\n"
" Please follow the steps described\n" " Please follow the steps described\n"
@ -2138,16 +2130,16 @@ msgid ""
" " " "
msgstr "" msgstr ""
#: .\cookbook\templates\system.html:57 .\cookbook\templates\system.html:73 #: .\cookbook\templates\system.html:45 .\cookbook\templates\system.html:61
#: .\cookbook\templates\system.html:88 .\cookbook\templates\system.html:102 #: .\cookbook\templates\system.html:76 .\cookbook\templates\system.html:90
msgid "Everything is fine!" msgid "Everything is fine!"
msgstr "" msgstr ""
#: .\cookbook\templates\system.html:62 #: .\cookbook\templates\system.html:50
msgid "Secret Key" msgid "Secret Key"
msgstr "" msgstr ""
#: .\cookbook\templates\system.html:66 #: .\cookbook\templates\system.html:54
msgid "" msgid ""
"\n" "\n"
" You do not have a <code>SECRET_KEY</code> configured in your " " You do not have a <code>SECRET_KEY</code> configured in your "
@ -2160,11 +2152,11 @@ msgid ""
" " " "
msgstr "" msgstr ""
#: .\cookbook\templates\system.html:78 #: .\cookbook\templates\system.html:66
msgid "Debug Mode" msgid "Debug Mode"
msgstr "" msgstr ""
#: .\cookbook\templates\system.html:82 #: .\cookbook\templates\system.html:70
msgid "" msgid ""
"\n" "\n"
" This application is still running in debug mode. This is most " " This application is still running in debug mode. This is most "
@ -2175,15 +2167,15 @@ msgid ""
" " " "
msgstr "" msgstr ""
#: .\cookbook\templates\system.html:93 #: .\cookbook\templates\system.html:81
msgid "Database" msgid "Database"
msgstr "" msgstr ""
#: .\cookbook\templates\system.html:95 #: .\cookbook\templates\system.html:83
msgid "Info" msgid "Info"
msgstr "" msgstr ""
#: .\cookbook\templates\system.html:97 #: .\cookbook\templates\system.html:85
msgid "" msgid ""
"\n" "\n"
" This application is not running with a Postgres database " " This application is not running with a Postgres database "
@ -2196,245 +2188,245 @@ msgstr ""
msgid "URL Import" msgid "URL Import"
msgstr "" msgstr ""
#: .\cookbook\views\api.py:97 .\cookbook\views\api.py:189 #: .\cookbook\views\api.py:105 .\cookbook\views\api.py:197
msgid "Parameter updated_at incorrectly formatted" msgid "Parameter updated_at incorrectly formatted"
msgstr "" msgstr ""
#: .\cookbook\views\api.py:209 .\cookbook\views\api.py:312 #: .\cookbook\views\api.py:217 .\cookbook\views\api.py:320
msgid "No {self.basename} with id {pk} exists" msgid "No {self.basename} with id {pk} exists"
msgstr "" msgstr ""
#: .\cookbook\views\api.py:213 #: .\cookbook\views\api.py:221
msgid "Cannot merge with the same object!" msgid "Cannot merge with the same object!"
msgstr "" msgstr ""
#: .\cookbook\views\api.py:220 #: .\cookbook\views\api.py:228
msgid "No {self.basename} with id {target} exists" msgid "No {self.basename} with id {target} exists"
msgstr "" msgstr ""
#: .\cookbook\views\api.py:225 #: .\cookbook\views\api.py:233
msgid "Cannot merge with child object!" msgid "Cannot merge with child object!"
msgstr "" msgstr ""
#: .\cookbook\views\api.py:258 #: .\cookbook\views\api.py:266
msgid "{source.name} was merged successfully with {target.name}" msgid "{source.name} was merged successfully with {target.name}"
msgstr "" msgstr ""
#: .\cookbook\views\api.py:263 #: .\cookbook\views\api.py:271
msgid "An error occurred attempting to merge {source.name} with {target.name}" msgid "An error occurred attempting to merge {source.name} with {target.name}"
msgstr "" msgstr ""
#: .\cookbook\views\api.py:321 #: .\cookbook\views\api.py:329
msgid "{child.name} was moved successfully to the root." msgid "{child.name} was moved successfully to the root."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:324 .\cookbook\views\api.py:342 #: .\cookbook\views\api.py:332 .\cookbook\views\api.py:350
msgid "An error occurred attempting to move " msgid "An error occurred attempting to move "
msgstr "" msgstr ""
#: .\cookbook\views\api.py:327 #: .\cookbook\views\api.py:335
msgid "Cannot move an object to itself!" msgid "Cannot move an object to itself!"
msgstr "" msgstr ""
#: .\cookbook\views\api.py:333 #: .\cookbook\views\api.py:341
msgid "No {self.basename} with id {parent} exists" msgid "No {self.basename} with id {parent} exists"
msgstr "" msgstr ""
#: .\cookbook\views\api.py:339 #: .\cookbook\views\api.py:347
msgid "{child.name} was moved successfully to parent {parent.name}" msgid "{child.name} was moved successfully to parent {parent.name}"
msgstr "" msgstr ""
#: .\cookbook\views\api.py:534 #: .\cookbook\views\api.py:542
msgid "{obj.name} was removed from the shopping list." msgid "{obj.name} was removed from the shopping list."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:539 .\cookbook\views\api.py:871 #: .\cookbook\views\api.py:547 .\cookbook\views\api.py:879
#: .\cookbook\views\api.py:884 #: .\cookbook\views\api.py:892
msgid "{obj.name} was added to the shopping list." msgid "{obj.name} was added to the shopping list."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:666 #: .\cookbook\views\api.py:674
msgid "ID of recipe a step is part of. For multiple repeat parameter." msgid "ID of recipe a step is part of. For multiple repeat parameter."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:668 #: .\cookbook\views\api.py:676
msgid "Query string matched (fuzzy) against object name." msgid "Query string matched (fuzzy) against object name."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:712 #: .\cookbook\views\api.py:720
msgid "" msgid ""
"Query string matched (fuzzy) against recipe name. In the future also " "Query string matched (fuzzy) against recipe name. In the future also "
"fulltext search." "fulltext search."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:714 #: .\cookbook\views\api.py:722
msgid "" msgid ""
"ID of keyword a recipe should have. For multiple repeat parameter. " "ID of keyword a recipe should have. For multiple repeat parameter. "
"Equivalent to keywords_or" "Equivalent to keywords_or"
msgstr "" msgstr ""
#: .\cookbook\views\api.py:717 #: .\cookbook\views\api.py:725
msgid "" msgid ""
"Keyword IDs, repeat for multiple. Return recipes with any of the keywords" "Keyword IDs, repeat for multiple. Return recipes with any of the keywords"
msgstr "" msgstr ""
#: .\cookbook\views\api.py:720 #: .\cookbook\views\api.py:728
msgid "" msgid ""
"Keyword IDs, repeat for multiple. Return recipes with all of the keywords." "Keyword IDs, repeat for multiple. Return recipes with all of the keywords."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:723 #: .\cookbook\views\api.py:731
msgid "" msgid ""
"Keyword IDs, repeat for multiple. Exclude recipes with any of the keywords." "Keyword IDs, repeat for multiple. Exclude recipes with any of the keywords."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:726 #: .\cookbook\views\api.py:734
msgid "" msgid ""
"Keyword IDs, repeat for multiple. Exclude recipes with all of the keywords." "Keyword IDs, repeat for multiple. Exclude recipes with all of the keywords."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:728 #: .\cookbook\views\api.py:736
msgid "ID of food a recipe should have. For multiple repeat parameter." msgid "ID of food a recipe should have. For multiple repeat parameter."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:731 #: .\cookbook\views\api.py:739
msgid "Food IDs, repeat for multiple. Return recipes with any of the foods" msgid "Food IDs, repeat for multiple. Return recipes with any of the foods"
msgstr "" msgstr ""
#: .\cookbook\views\api.py:733 #: .\cookbook\views\api.py:741
msgid "Food IDs, repeat for multiple. Return recipes with all of the foods." msgid "Food IDs, repeat for multiple. Return recipes with all of the foods."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:735 #: .\cookbook\views\api.py:743
msgid "Food IDs, repeat for multiple. Exclude recipes with any of the foods." msgid "Food IDs, repeat for multiple. Exclude recipes with any of the foods."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:737 #: .\cookbook\views\api.py:745
msgid "Food IDs, repeat for multiple. Exclude recipes with all of the foods." msgid "Food IDs, repeat for multiple. Exclude recipes with all of the foods."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:738 #: .\cookbook\views\api.py:746
msgid "ID of unit a recipe should have." msgid "ID of unit a recipe should have."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:740 #: .\cookbook\views\api.py:748
msgid "" msgid ""
"Rating a recipe should have or greater. [0 - 5] Negative value filters " "Rating a recipe should have or greater. [0 - 5] Negative value filters "
"rating less than." "rating less than."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:741 #: .\cookbook\views\api.py:749
msgid "ID of book a recipe should be in. For multiple repeat parameter." msgid "ID of book a recipe should be in. For multiple repeat parameter."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:743 #: .\cookbook\views\api.py:751
msgid "Book IDs, repeat for multiple. Return recipes with any of the books" msgid "Book IDs, repeat for multiple. Return recipes with any of the books"
msgstr "" msgstr ""
#: .\cookbook\views\api.py:745 #: .\cookbook\views\api.py:753
msgid "Book IDs, repeat for multiple. Return recipes with all of the books." msgid "Book IDs, repeat for multiple. Return recipes with all of the books."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:747 #: .\cookbook\views\api.py:755
msgid "Book IDs, repeat for multiple. Exclude recipes with any of the books." msgid "Book IDs, repeat for multiple. Exclude recipes with any of the books."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:749 #: .\cookbook\views\api.py:757
msgid "Book IDs, repeat for multiple. Exclude recipes with all of the books." msgid "Book IDs, repeat for multiple. Exclude recipes with all of the books."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:751 #: .\cookbook\views\api.py:759
msgid "If only internal recipes should be returned. [true/<b>false</b>]" msgid "If only internal recipes should be returned. [true/<b>false</b>]"
msgstr "" msgstr ""
#: .\cookbook\views\api.py:753 #: .\cookbook\views\api.py:761
msgid "Returns the results in randomized order. [true/<b>false</b>]" msgid "Returns the results in randomized order. [true/<b>false</b>]"
msgstr "" msgstr ""
#: .\cookbook\views\api.py:755 #: .\cookbook\views\api.py:763
msgid "Returns new results first in search results. [true/<b>false</b>]" msgid "Returns new results first in search results. [true/<b>false</b>]"
msgstr "" msgstr ""
#: .\cookbook\views\api.py:757 #: .\cookbook\views\api.py:765
msgid "" msgid ""
"Filter recipes cooked X times or more. Negative values returns cooked less " "Filter recipes cooked X times or more. Negative values returns cooked less "
"than X times" "than X times"
msgstr "" msgstr ""
#: .\cookbook\views\api.py:759 #: .\cookbook\views\api.py:767
msgid "" msgid ""
"Filter recipes last cooked on or after YYYY-MM-DD. Prepending - filters on " "Filter recipes last cooked on or after YYYY-MM-DD. Prepending - filters on "
"or before date." "or before date."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:761 #: .\cookbook\views\api.py:769
msgid "" msgid ""
"Filter recipes created on or after YYYY-MM-DD. Prepending - filters on or " "Filter recipes created on or after YYYY-MM-DD. Prepending - filters on or "
"before date." "before date."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:763 #: .\cookbook\views\api.py:771
msgid "" msgid ""
"Filter recipes updated on or after YYYY-MM-DD. Prepending - filters on or " "Filter recipes updated on or after YYYY-MM-DD. Prepending - filters on or "
"before date." "before date."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:765 #: .\cookbook\views\api.py:773
msgid "" msgid ""
"Filter recipes lasts viewed on or after YYYY-MM-DD. Prepending - filters on " "Filter recipes lasts viewed on or after YYYY-MM-DD. Prepending - filters on "
"or before date." "or before date."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:767 #: .\cookbook\views\api.py:775
msgid "Filter recipes that can be made with OnHand food. [true/<b>false</b>]" msgid "Filter recipes that can be made with OnHand food. [true/<b>false</b>]"
msgstr "" msgstr ""
#: .\cookbook\views\api.py:929 #: .\cookbook\views\api.py:937
msgid "" msgid ""
"Returns the shopping list entry with a primary key of id. Multiple values " "Returns the shopping list entry with a primary key of id. Multiple values "
"allowed." "allowed."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:934 #: .\cookbook\views\api.py:942
msgid "" msgid ""
"Filter shopping list entries on checked. [true, false, both, <b>recent</b>]" "Filter shopping list entries on checked. [true, false, both, <b>recent</b>]"
"<br> - recent includes unchecked items and recently completed items." "<br> - recent includes unchecked items and recently completed items."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:937 #: .\cookbook\views\api.py:945
msgid "Returns the shopping list entries sorted by supermarket category order." msgid "Returns the shopping list entries sorted by supermarket category order."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:1134 #: .\cookbook\views\api.py:1140
msgid "Nothing to do." msgid "Nothing to do."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:1153 #: .\cookbook\views\api.py:1160
msgid "Invalid Url" msgid "Invalid Url"
msgstr "" msgstr ""
#: .\cookbook\views\api.py:1158 #: .\cookbook\views\api.py:1167
msgid "Connection Refused." msgid "Connection Refused."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:1163 #: .\cookbook\views\api.py:1172
msgid "Bad URL Schema." msgid "Bad URL Schema."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:1170 #: .\cookbook\views\api.py:1195
msgid "No usable data could be found." msgid "No usable data could be found."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:1260 .\cookbook\views\data.py:28 #: .\cookbook\views\api.py:1303 .\cookbook\views\data.py:28
#: .\cookbook\views\edit.py:120 .\cookbook\views\new.py:90 #: .\cookbook\views\edit.py:120 .\cookbook\views\new.py:90
msgid "This feature is not yet available in the hosted version of tandoor!" msgid "This feature is not yet available in the hosted version of tandoor!"
msgstr "" msgstr ""
#: .\cookbook\views\api.py:1282 #: .\cookbook\views\api.py:1325
msgid "Sync successful!" msgid "Sync successful!"
msgstr "" msgstr ""
#: .\cookbook\views\api.py:1287 #: .\cookbook\views\api.py:1330
msgid "Error synchronizing with Storage" msgid "Error synchronizing with Storage"
msgstr "" msgstr ""
@ -2517,6 +2509,10 @@ msgstr ""
msgid "Shopping List" msgid "Shopping List"
msgstr "" msgstr ""
#: .\cookbook\views\lists.py:76
msgid "Invite Links"
msgstr ""
#: .\cookbook\views\lists.py:139 #: .\cookbook\views\lists.py:139
msgid "Supermarkets" msgid "Supermarkets"
msgstr "" msgstr ""

View File

@ -13,9 +13,9 @@ msgid ""
msgstr "" 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: 2022-06-26 12:09+0200\n" "POT-Creation-Date: 2022-07-12 19:20+0200\n"
"PO-Revision-Date: 2022-06-25 17:32+0000\n" "PO-Revision-Date: 2022-08-12 21:32+0000\n"
"Last-Translator: César Blanco Guillamon <cesarblancg97@gmail.com>\n" "Last-Translator: Thorin <thorin8@hotmail.com>\n"
"Language-Team: Spanish <http://translate.tandoor.dev/projects/tandoor/" "Language-Team: Spanish <http://translate.tandoor.dev/projects/tandoor/"
"recipes-backend/es/>\n" "recipes-backend/es/>\n"
"Language: es\n" "Language: es\n"
@ -68,7 +68,7 @@ msgstr "Estilo de búsqueda"
#: .\cookbook\forms.py:62 #: .\cookbook\forms.py:62
msgid "Plan sharing" msgid "Plan sharing"
msgstr "" msgstr "Compartir régimen"
#: .\cookbook\forms.py:63 #: .\cookbook\forms.py:63
msgid "Ingredient decimal places" msgid "Ingredient decimal places"
@ -518,7 +518,7 @@ msgstr ""
msgid "You have more users than allowed in your space." msgid "You have more users than allowed in your space."
msgstr "" msgstr ""
#: .\cookbook\helper\recipe_search.py:560 #: .\cookbook\helper\recipe_search.py:565
msgid "One of queryset or hash_key must be provided" msgid "One of queryset or hash_key must be provided"
msgstr "" msgstr ""
@ -531,12 +531,12 @@ msgstr "Debe proporcionar un tamaño de porción"
msgid "Could not parse template code." msgid "Could not parse template code."
msgstr "" msgstr ""
#: .\cookbook\integration\copymethat.py:42 #: .\cookbook\integration\copymethat.py:41
#: .\cookbook\integration\melarecipes.py:37 #: .\cookbook\integration\melarecipes.py:37
msgid "Favorite" msgid "Favorite"
msgstr "" msgstr ""
#: .\cookbook\integration\copymethat.py:71 #: .\cookbook\integration\copymethat.py:70
#: .\cookbook\integration\recettetek.py:54 #: .\cookbook\integration\recettetek.py:54
#: .\cookbook\integration\recipekeeper.py:63 #: .\cookbook\integration\recipekeeper.py:63
msgid "Imported from" msgid "Imported from"
@ -669,125 +669,125 @@ msgstr "Nuevo"
msgid " is part of a recipe step and cannot be deleted" msgid " is part of a recipe step and cannot be deleted"
msgstr "" msgstr ""
#: .\cookbook\models.py:1160 .\cookbook\templates\search_info.html:28 #: .\cookbook\models.py:1162 .\cookbook\templates\search_info.html:28
msgid "Simple" msgid "Simple"
msgstr "" msgstr ""
#: .\cookbook\models.py:1161 .\cookbook\templates\search_info.html:33 #: .\cookbook\models.py:1163 .\cookbook\templates\search_info.html:33
msgid "Phrase" msgid "Phrase"
msgstr "" msgstr ""
#: .\cookbook\models.py:1162 .\cookbook\templates\search_info.html:38 #: .\cookbook\models.py:1164 .\cookbook\templates\search_info.html:38
msgid "Web" msgid "Web"
msgstr "" msgstr ""
#: .\cookbook\models.py:1163 .\cookbook\templates\search_info.html:47 #: .\cookbook\models.py:1165 .\cookbook\templates\search_info.html:47
msgid "Raw" msgid "Raw"
msgstr "" msgstr ""
#: .\cookbook\models.py:1201 #: .\cookbook\models.py:1203
msgid "Food Alias" msgid "Food Alias"
msgstr "Alias de la Comida" msgstr "Alias de la Comida"
#: .\cookbook\models.py:1201 #: .\cookbook\models.py:1203
#, fuzzy #, fuzzy
#| msgid "Units" #| msgid "Units"
msgid "Unit Alias" msgid "Unit Alias"
msgstr "Unidades" msgstr "Unidades"
#: .\cookbook\models.py:1201 #: .\cookbook\models.py:1203
#, fuzzy #, fuzzy
#| msgid "Keywords" #| msgid "Keywords"
msgid "Keyword Alias" msgid "Keyword Alias"
msgstr "Palabras clave" msgstr "Palabras clave"
#: .\cookbook\models.py:1225 #: .\cookbook\models.py:1227
#: .\cookbook\templates\include\recipe_open_modal.html:7 #: .\cookbook\templates\include\recipe_open_modal.html:7
#: .\cookbook\views\delete.py:36 .\cookbook\views\edit.py:251 #: .\cookbook\views\delete.py:36 .\cookbook\views\edit.py:251
#: .\cookbook\views\new.py:48 #: .\cookbook\views\new.py:48
msgid "Recipe" msgid "Recipe"
msgstr "Receta" msgstr "Receta"
#: .\cookbook\models.py:1226 #: .\cookbook\models.py:1228
#, fuzzy #, fuzzy
#| msgid "Food" #| msgid "Food"
msgid "Food" msgid "Food"
msgstr "Comida" msgstr "Comida"
#: .\cookbook\models.py:1227 .\cookbook\templates\base.html:138 #: .\cookbook\models.py:1229 .\cookbook\templates\base.html:138
msgid "Keyword" msgid "Keyword"
msgstr "Palabra clave" msgstr "Palabra clave"
#: .\cookbook\serializer.py:204 #: .\cookbook\serializer.py:207
msgid "Cannot modify Space owner permission." msgid "Cannot modify Space owner permission."
msgstr "" msgstr ""
#: .\cookbook\serializer.py:273 #: .\cookbook\serializer.py:290
msgid "File uploads are not enabled for this Space." msgid "File uploads are not enabled for this Space."
msgstr "" msgstr ""
#: .\cookbook\serializer.py:284 #: .\cookbook\serializer.py:301
msgid "You have reached your file upload limit." msgid "You have reached your file upload limit."
msgstr "" msgstr ""
#: .\cookbook\serializer.py:1051 #: .\cookbook\serializer.py:1081
msgid "Hello" msgid "Hello"
msgstr "" msgstr ""
#: .\cookbook\serializer.py:1051 #: .\cookbook\serializer.py:1081
msgid "You have been invited by " msgid "You have been invited by "
msgstr "" msgstr ""
#: .\cookbook\serializer.py:1052 #: .\cookbook\serializer.py:1082
msgid " to join their Tandoor Recipes space " msgid " to join their Tandoor Recipes space "
msgstr "" msgstr ""
#: .\cookbook\serializer.py:1053 #: .\cookbook\serializer.py:1083
msgid "Click the following link to activate your account: " msgid "Click the following link to activate your account: "
msgstr "" msgstr ""
#: .\cookbook\serializer.py:1054 #: .\cookbook\serializer.py:1084
msgid "" msgid ""
"If the link does not work use the following code to manually join the space: " "If the link does not work use the following code to manually join the space: "
msgstr "" msgstr ""
#: .\cookbook\serializer.py:1055 #: .\cookbook\serializer.py:1085
msgid "The invitation is valid until " msgid "The invitation is valid until "
msgstr "" msgstr ""
#: .\cookbook\serializer.py:1056 #: .\cookbook\serializer.py:1086
msgid "" msgid ""
"Tandoor Recipes is an Open Source recipe manager. Check it out on GitHub " "Tandoor Recipes is an Open Source recipe manager. Check it out on GitHub "
msgstr "" msgstr ""
#: .\cookbook\serializer.py:1059 #: .\cookbook\serializer.py:1089
msgid "Tandoor Recipes Invite" msgid "Tandoor Recipes Invite"
msgstr "" msgstr ""
#: .\cookbook\serializer.py:1179 #: .\cookbook\serializer.py:1209
msgid "Existing shopping list to update" msgid "Existing shopping list to update"
msgstr "" msgstr ""
#: .\cookbook\serializer.py:1181 #: .\cookbook\serializer.py:1211
msgid "" msgid ""
"List of ingredient IDs from the recipe to add, if not provided all " "List of ingredient IDs from the recipe to add, if not provided all "
"ingredients will be added." "ingredients will be added."
msgstr "" msgstr ""
#: .\cookbook\serializer.py:1183 #: .\cookbook\serializer.py:1213
msgid "" msgid ""
"Providing a list_recipe ID and servings of 0 will delete that shopping list." "Providing a list_recipe ID and servings of 0 will delete that shopping list."
msgstr "" msgstr ""
#: .\cookbook\serializer.py:1192 #: .\cookbook\serializer.py:1222
msgid "Amount of food to add to the shopping list" msgid "Amount of food to add to the shopping list"
msgstr "" msgstr ""
#: .\cookbook\serializer.py:1194 #: .\cookbook\serializer.py:1224
msgid "ID of unit to use for the shopping list" msgid "ID of unit to use for the shopping list"
msgstr "" msgstr ""
#: .\cookbook\serializer.py:1196 #: .\cookbook\serializer.py:1226
msgid "When set to true will delete all food from active shopping lists." msgid "When set to true will delete all food from active shopping lists."
msgstr "" msgstr ""
@ -921,7 +921,7 @@ msgid ""
msgstr "" msgstr ""
#: .\cookbook\templates\account\login.html:8 #: .\cookbook\templates\account\login.html:8
#: .\cookbook\templates\base.html:339 .\cookbook\templates\openid\login.html:8 #: .\cookbook\templates\base.html:340 .\cookbook\templates\openid\login.html:8
msgid "Login" msgid "Login"
msgstr "Iniciar sesión" msgstr "Iniciar sesión"
@ -1096,7 +1096,7 @@ msgstr ""
msgid "We are sorry, but the sign up is currently closed." msgid "We are sorry, but the sign up is currently closed."
msgstr "" msgstr ""
#: .\cookbook\templates\api_info.html:5 .\cookbook\templates\base.html:329 #: .\cookbook\templates\api_info.html:5 .\cookbook\templates\base.html:330
#: .\cookbook\templates\rest_framework\api.html:11 #: .\cookbook\templates\rest_framework\api.html:11
msgid "API Documentation" msgid "API Documentation"
msgstr "Documentación de API" msgstr "Documentación de API"
@ -1203,36 +1203,36 @@ msgstr "Administrador"
msgid "Your Spaces" msgid "Your Spaces"
msgstr "Crear Usuario" msgstr "Crear Usuario"
#: .\cookbook\templates\base.html:319 #: .\cookbook\templates\base.html:320
#: .\cookbook\templates\space_overview.html:6 #: .\cookbook\templates\space_overview.html:6
msgid "Overview" msgid "Overview"
msgstr "" msgstr ""
#: .\cookbook\templates\base.html:323 #: .\cookbook\templates\base.html:324
msgid "Markdown Guide" msgid "Markdown Guide"
msgstr "Guia Markdown" msgstr "Guia Markdown"
#: .\cookbook\templates\base.html:325 #: .\cookbook\templates\base.html:326
msgid "GitHub" msgid "GitHub"
msgstr "GitHub" msgstr "GitHub"
#: .\cookbook\templates\base.html:327 #: .\cookbook\templates\base.html:328
msgid "Translate Tandoor" msgid "Translate Tandoor"
msgstr "" msgstr ""
#: .\cookbook\templates\base.html:331 #: .\cookbook\templates\base.html:332
msgid "API Browser" msgid "API Browser"
msgstr "Explorador de API" msgstr "Explorador de API"
#: .\cookbook\templates\base.html:334 #: .\cookbook\templates\base.html:335
msgid "Log out" msgid "Log out"
msgstr "" msgstr ""
#: .\cookbook\templates\base.html:356 #: .\cookbook\templates\base.html:357
msgid "You are using the free version of Tandor" msgid "You are using the free version of Tandor"
msgstr "" msgstr ""
#: .\cookbook\templates\base.html:357 #: .\cookbook\templates\base.html:358
msgid "Upgrade Now" msgid "Upgrade Now"
msgstr "" msgstr ""
@ -2278,19 +2278,11 @@ msgstr "Recetas sin palabras clave"
msgid "Internal Recipes" msgid "Internal Recipes"
msgstr "Recetas Internas" msgstr "Recetas Internas"
#: .\cookbook\templates\system.html:21 .\cookbook\views\lists.py:76 #: .\cookbook\templates\system.html:20
msgid "Invite Links"
msgstr "Enlaces de Invitación"
#: .\cookbook\templates\system.html:22
msgid "Show Links"
msgstr "Mostrar Enlaces"
#: .\cookbook\templates\system.html:32
msgid "System Information" msgid "System Information"
msgstr "Información del Sistema" msgstr "Información del Sistema"
#: .\cookbook\templates\system.html:34 #: .\cookbook\templates\system.html:22
msgid "" msgid ""
"\n" "\n"
" Django Recipes is an open source free software application. It can " " Django Recipes is an open source free software application. It can "
@ -2308,21 +2300,21 @@ msgstr ""
"github.com/vabene1111/recipes/releases\">aquí</a>.\n" "github.com/vabene1111/recipes/releases\">aquí</a>.\n"
" " " "
#: .\cookbook\templates\system.html:48 #: .\cookbook\templates\system.html:36
msgid "Media Serving" msgid "Media Serving"
msgstr "Servidor multimedia" msgstr "Servidor multimedia"
#: .\cookbook\templates\system.html:49 .\cookbook\templates\system.html:64 #: .\cookbook\templates\system.html:37 .\cookbook\templates\system.html:52
#: .\cookbook\templates\system.html:80 #: .\cookbook\templates\system.html:68
msgid "Warning" msgid "Warning"
msgstr "Advertencia" msgstr "Advertencia"
#: .\cookbook\templates\system.html:49 .\cookbook\templates\system.html:64 #: .\cookbook\templates\system.html:37 .\cookbook\templates\system.html:52
#: .\cookbook\templates\system.html:80 .\cookbook\templates\system.html:95 #: .\cookbook\templates\system.html:68 .\cookbook\templates\system.html:83
msgid "Ok" msgid "Ok"
msgstr "Ok" msgstr "Ok"
#: .\cookbook\templates\system.html:51 #: .\cookbook\templates\system.html:39
msgid "" msgid ""
"Serving media files directly using gunicorn/python is <b>not recommend</b>!\n" "Serving media files directly using gunicorn/python is <b>not recommend</b>!\n"
" Please follow the steps described\n" " Please follow the steps described\n"
@ -2339,16 +2331,16 @@ msgstr ""
" tu instalación.\n" " tu instalación.\n"
" " " "
#: .\cookbook\templates\system.html:57 .\cookbook\templates\system.html:73 #: .\cookbook\templates\system.html:45 .\cookbook\templates\system.html:61
#: .\cookbook\templates\system.html:88 .\cookbook\templates\system.html:102 #: .\cookbook\templates\system.html:76 .\cookbook\templates\system.html:90
msgid "Everything is fine!" msgid "Everything is fine!"
msgstr "¡Todo va bien!" msgstr "¡Todo va bien!"
#: .\cookbook\templates\system.html:62 #: .\cookbook\templates\system.html:50
msgid "Secret Key" msgid "Secret Key"
msgstr "Clave Secreta" msgstr "Clave Secreta"
#: .\cookbook\templates\system.html:66 #: .\cookbook\templates\system.html:54
msgid "" msgid ""
"\n" "\n"
" You do not have a <code>SECRET_KEY</code> configured in your " " You do not have a <code>SECRET_KEY</code> configured in your "
@ -2370,11 +2362,11 @@ msgstr ""
"env</code>.\n" "env</code>.\n"
" " " "
#: .\cookbook\templates\system.html:78 #: .\cookbook\templates\system.html:66
msgid "Debug Mode" msgid "Debug Mode"
msgstr "Modo Depuración" msgstr "Modo Depuración"
#: .\cookbook\templates\system.html:82 #: .\cookbook\templates\system.html:70
msgid "" msgid ""
"\n" "\n"
" This application is still running in debug mode. This is most " " This application is still running in debug mode. This is most "
@ -2392,15 +2384,15 @@ msgstr ""
"code>.\n" "code>.\n"
" " " "
#: .\cookbook\templates\system.html:93 #: .\cookbook\templates\system.html:81
msgid "Database" msgid "Database"
msgstr "Base de Datos" msgstr "Base de Datos"
#: .\cookbook\templates\system.html:95 #: .\cookbook\templates\system.html:83
msgid "Info" msgid "Info"
msgstr "Información" msgstr "Información"
#: .\cookbook\templates\system.html:97 #: .\cookbook\templates\system.html:85
msgid "" msgid ""
"\n" "\n"
" This application is not running with a Postgres database " " This application is not running with a Postgres database "
@ -2418,253 +2410,253 @@ msgstr ""
msgid "URL Import" msgid "URL Import"
msgstr "Importar URL" msgstr "Importar URL"
#: .\cookbook\views\api.py:97 .\cookbook\views\api.py:189 #: .\cookbook\views\api.py:105 .\cookbook\views\api.py:197
#, fuzzy #, fuzzy
#| msgid "Parameter filter_list incorrectly formatted" #| msgid "Parameter filter_list incorrectly formatted"
msgid "Parameter updated_at incorrectly formatted" msgid "Parameter updated_at incorrectly formatted"
msgstr "Parámetro filter_list formateado incorrectamente" msgstr "Parámetro filter_list formateado incorrectamente"
#: .\cookbook\views\api.py:209 .\cookbook\views\api.py:312 #: .\cookbook\views\api.py:217 .\cookbook\views\api.py:320
msgid "No {self.basename} with id {pk} exists" msgid "No {self.basename} with id {pk} exists"
msgstr "" msgstr ""
#: .\cookbook\views\api.py:213 #: .\cookbook\views\api.py:221
msgid "Cannot merge with the same object!" msgid "Cannot merge with the same object!"
msgstr "¡No se puede unir con el mismo objeto!" msgstr "¡No se puede unir con el mismo objeto!"
#: .\cookbook\views\api.py:220 #: .\cookbook\views\api.py:228
msgid "No {self.basename} with id {target} exists" msgid "No {self.basename} with id {target} exists"
msgstr "" msgstr ""
#: .\cookbook\views\api.py:225 #: .\cookbook\views\api.py:233
#, fuzzy #, fuzzy
#| msgid "Cannot merge with the same object!" #| msgid "Cannot merge with the same object!"
msgid "Cannot merge with child object!" msgid "Cannot merge with child object!"
msgstr "¡No se puede unir con el mismo objeto!" msgstr "¡No se puede unir con el mismo objeto!"
#: .\cookbook\views\api.py:258 #: .\cookbook\views\api.py:266
msgid "{source.name} was merged successfully with {target.name}" msgid "{source.name} was merged successfully with {target.name}"
msgstr "" msgstr ""
#: .\cookbook\views\api.py:263 #: .\cookbook\views\api.py:271
msgid "An error occurred attempting to merge {source.name} with {target.name}" msgid "An error occurred attempting to merge {source.name} with {target.name}"
msgstr "" msgstr ""
#: .\cookbook\views\api.py:321 #: .\cookbook\views\api.py:329
msgid "{child.name} was moved successfully to the root." msgid "{child.name} was moved successfully to the root."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:324 .\cookbook\views\api.py:342 #: .\cookbook\views\api.py:332 .\cookbook\views\api.py:350
msgid "An error occurred attempting to move " msgid "An error occurred attempting to move "
msgstr "" msgstr ""
#: .\cookbook\views\api.py:327 #: .\cookbook\views\api.py:335
msgid "Cannot move an object to itself!" msgid "Cannot move an object to itself!"
msgstr "" msgstr ""
#: .\cookbook\views\api.py:333 #: .\cookbook\views\api.py:341
msgid "No {self.basename} with id {parent} exists" msgid "No {self.basename} with id {parent} exists"
msgstr "" msgstr ""
#: .\cookbook\views\api.py:339 #: .\cookbook\views\api.py:347
msgid "{child.name} was moved successfully to parent {parent.name}" msgid "{child.name} was moved successfully to parent {parent.name}"
msgstr "" msgstr ""
#: .\cookbook\views\api.py:534 #: .\cookbook\views\api.py:542
msgid "{obj.name} was removed from the shopping list." msgid "{obj.name} was removed from the shopping list."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:539 .\cookbook\views\api.py:871 #: .\cookbook\views\api.py:547 .\cookbook\views\api.py:879
#: .\cookbook\views\api.py:884 #: .\cookbook\views\api.py:892
msgid "{obj.name} was added to the shopping list." msgid "{obj.name} was added to the shopping list."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:666 #: .\cookbook\views\api.py:674
msgid "ID of recipe a step is part of. For multiple repeat parameter." msgid "ID of recipe a step is part of. For multiple repeat parameter."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:668 #: .\cookbook\views\api.py:676
msgid "Query string matched (fuzzy) against object name." msgid "Query string matched (fuzzy) against object name."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:712 #: .\cookbook\views\api.py:720
msgid "" msgid ""
"Query string matched (fuzzy) against recipe name. In the future also " "Query string matched (fuzzy) against recipe name. In the future also "
"fulltext search." "fulltext search."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:714 #: .\cookbook\views\api.py:722
msgid "" msgid ""
"ID of keyword a recipe should have. For multiple repeat parameter. " "ID of keyword a recipe should have. For multiple repeat parameter. "
"Equivalent to keywords_or" "Equivalent to keywords_or"
msgstr "" msgstr ""
#: .\cookbook\views\api.py:717 #: .\cookbook\views\api.py:725
msgid "" msgid ""
"Keyword IDs, repeat for multiple. Return recipes with any of the keywords" "Keyword IDs, repeat for multiple. Return recipes with any of the keywords"
msgstr "" msgstr ""
#: .\cookbook\views\api.py:720 #: .\cookbook\views\api.py:728
msgid "" msgid ""
"Keyword IDs, repeat for multiple. Return recipes with all of the keywords." "Keyword IDs, repeat for multiple. Return recipes with all of the keywords."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:723 #: .\cookbook\views\api.py:731
msgid "" msgid ""
"Keyword IDs, repeat for multiple. Exclude recipes with any of the keywords." "Keyword IDs, repeat for multiple. Exclude recipes with any of the keywords."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:726 #: .\cookbook\views\api.py:734
msgid "" msgid ""
"Keyword IDs, repeat for multiple. Exclude recipes with all of the keywords." "Keyword IDs, repeat for multiple. Exclude recipes with all of the keywords."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:728 #: .\cookbook\views\api.py:736
msgid "ID of food a recipe should have. For multiple repeat parameter." msgid "ID of food a recipe should have. For multiple repeat parameter."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:731 #: .\cookbook\views\api.py:739
msgid "Food IDs, repeat for multiple. Return recipes with any of the foods" msgid "Food IDs, repeat for multiple. Return recipes with any of the foods"
msgstr "" msgstr ""
#: .\cookbook\views\api.py:733 #: .\cookbook\views\api.py:741
msgid "Food IDs, repeat for multiple. Return recipes with all of the foods." msgid "Food IDs, repeat for multiple. Return recipes with all of the foods."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:735 #: .\cookbook\views\api.py:743
msgid "Food IDs, repeat for multiple. Exclude recipes with any of the foods." msgid "Food IDs, repeat for multiple. Exclude recipes with any of the foods."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:737 #: .\cookbook\views\api.py:745
msgid "Food IDs, repeat for multiple. Exclude recipes with all of the foods." msgid "Food IDs, repeat for multiple. Exclude recipes with all of the foods."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:738 #: .\cookbook\views\api.py:746
msgid "ID of unit a recipe should have." msgid "ID of unit a recipe should have."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:740 #: .\cookbook\views\api.py:748
msgid "" msgid ""
"Rating a recipe should have or greater. [0 - 5] Negative value filters " "Rating a recipe should have or greater. [0 - 5] Negative value filters "
"rating less than." "rating less than."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:741 #: .\cookbook\views\api.py:749
msgid "ID of book a recipe should be in. For multiple repeat parameter." msgid "ID of book a recipe should be in. For multiple repeat parameter."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:743 #: .\cookbook\views\api.py:751
msgid "Book IDs, repeat for multiple. Return recipes with any of the books" msgid "Book IDs, repeat for multiple. Return recipes with any of the books"
msgstr "" msgstr ""
#: .\cookbook\views\api.py:745 #: .\cookbook\views\api.py:753
msgid "Book IDs, repeat for multiple. Return recipes with all of the books." msgid "Book IDs, repeat for multiple. Return recipes with all of the books."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:747 #: .\cookbook\views\api.py:755
msgid "Book IDs, repeat for multiple. Exclude recipes with any of the books." msgid "Book IDs, repeat for multiple. Exclude recipes with any of the books."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:749 #: .\cookbook\views\api.py:757
msgid "Book IDs, repeat for multiple. Exclude recipes with all of the books." msgid "Book IDs, repeat for multiple. Exclude recipes with all of the books."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:751 #: .\cookbook\views\api.py:759
msgid "If only internal recipes should be returned. [true/<b>false</b>]" msgid "If only internal recipes should be returned. [true/<b>false</b>]"
msgstr "" msgstr ""
#: .\cookbook\views\api.py:753 #: .\cookbook\views\api.py:761
msgid "Returns the results in randomized order. [true/<b>false</b>]" msgid "Returns the results in randomized order. [true/<b>false</b>]"
msgstr "" msgstr ""
#: .\cookbook\views\api.py:755 #: .\cookbook\views\api.py:763
msgid "Returns new results first in search results. [true/<b>false</b>]" msgid "Returns new results first in search results. [true/<b>false</b>]"
msgstr "" msgstr ""
#: .\cookbook\views\api.py:757 #: .\cookbook\views\api.py:765
msgid "" msgid ""
"Filter recipes cooked X times or more. Negative values returns cooked less " "Filter recipes cooked X times or more. Negative values returns cooked less "
"than X times" "than X times"
msgstr "" msgstr ""
#: .\cookbook\views\api.py:759 #: .\cookbook\views\api.py:767
msgid "" msgid ""
"Filter recipes last cooked on or after YYYY-MM-DD. Prepending - filters on " "Filter recipes last cooked on or after YYYY-MM-DD. Prepending - filters on "
"or before date." "or before date."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:761 #: .\cookbook\views\api.py:769
msgid "" msgid ""
"Filter recipes created on or after YYYY-MM-DD. Prepending - filters on or " "Filter recipes created on or after YYYY-MM-DD. Prepending - filters on or "
"before date." "before date."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:763 #: .\cookbook\views\api.py:771
msgid "" msgid ""
"Filter recipes updated on or after YYYY-MM-DD. Prepending - filters on or " "Filter recipes updated on or after YYYY-MM-DD. Prepending - filters on or "
"before date." "before date."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:765 #: .\cookbook\views\api.py:773
msgid "" msgid ""
"Filter recipes lasts viewed on or after YYYY-MM-DD. Prepending - filters on " "Filter recipes lasts viewed on or after YYYY-MM-DD. Prepending - filters on "
"or before date." "or before date."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:767 #: .\cookbook\views\api.py:775
msgid "Filter recipes that can be made with OnHand food. [true/<b>false</b>]" msgid "Filter recipes that can be made with OnHand food. [true/<b>false</b>]"
msgstr "" msgstr ""
#: .\cookbook\views\api.py:929 #: .\cookbook\views\api.py:937
msgid "" msgid ""
"Returns the shopping list entry with a primary key of id. Multiple values " "Returns the shopping list entry with a primary key of id. Multiple values "
"allowed." "allowed."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:934 #: .\cookbook\views\api.py:942
msgid "" msgid ""
"Filter shopping list entries on checked. [true, false, both, <b>recent</b>]" "Filter shopping list entries on checked. [true, false, both, <b>recent</b>]"
"<br> - recent includes unchecked items and recently completed items." "<br> - recent includes unchecked items and recently completed items."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:937 #: .\cookbook\views\api.py:945
msgid "Returns the shopping list entries sorted by supermarket category order." msgid "Returns the shopping list entries sorted by supermarket category order."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:1134 #: .\cookbook\views\api.py:1140
msgid "Nothing to do." msgid "Nothing to do."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:1153 #: .\cookbook\views\api.py:1160
msgid "Invalid Url" msgid "Invalid Url"
msgstr "" msgstr ""
#: .\cookbook\views\api.py:1158 #: .\cookbook\views\api.py:1167
msgid "Connection Refused." msgid "Connection Refused."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:1163 #: .\cookbook\views\api.py:1172
msgid "Bad URL Schema." msgid "Bad URL Schema."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:1170 #: .\cookbook\views\api.py:1195
#, fuzzy #, fuzzy
#| msgid "The requested page could not be found." #| msgid "The requested page could not be found."
msgid "No usable data could be found." msgid "No usable data could be found."
msgstr "La página solicitada no pudo ser encontrada." msgstr "La página solicitada no pudo ser encontrada."
#: .\cookbook\views\api.py:1260 .\cookbook\views\data.py:28 #: .\cookbook\views\api.py:1303 .\cookbook\views\data.py:28
#: .\cookbook\views\edit.py:120 .\cookbook\views\new.py:90 #: .\cookbook\views\edit.py:120 .\cookbook\views\new.py:90
#, fuzzy #, fuzzy
#| msgid "This feature is not available in the demo version!" #| msgid "This feature is not available in the demo version!"
msgid "This feature is not yet available in the hosted version of tandoor!" msgid "This feature is not yet available in the hosted version of tandoor!"
msgstr "¡Esta funcionalidad no está disponible en la versión demo!" msgstr "¡Esta funcionalidad no está disponible en la versión demo!"
#: .\cookbook\views\api.py:1282 #: .\cookbook\views\api.py:1325
msgid "Sync successful!" msgid "Sync successful!"
msgstr "¡Sincronización exitosa!" msgstr "¡Sincronización exitosa!"
#: .\cookbook\views\api.py:1287 #: .\cookbook\views\api.py:1330
msgid "Error synchronizing with Storage" msgid "Error synchronizing with Storage"
msgstr "Error de sincronización con el almacenamiento" msgstr "Error de sincronización con el almacenamiento"
@ -2749,6 +2741,10 @@ msgstr "Descubrimiento"
msgid "Shopping List" msgid "Shopping List"
msgstr "Lista de la Compra" msgstr "Lista de la Compra"
#: .\cookbook\views\lists.py:76
msgid "Invite Links"
msgstr "Enlaces de Invitación"
#: .\cookbook\views\lists.py:139 #: .\cookbook\views\lists.py:139
#, fuzzy #, fuzzy
#| msgid "Supermarket" #| msgid "Supermarket"
@ -2853,6 +2849,9 @@ msgid ""
"contact the page administrator." "contact the page administrator."
msgstr "" msgstr ""
#~ msgid "Show Links"
#~ msgstr "Mostrar Enlaces"
#, fuzzy #, fuzzy
#~| msgid "Invite Links" #~| msgid "Invite Links"
#~ msgid "Invite User" #~ msgid "Invite User"

View File

@ -13,7 +13,7 @@ msgid ""
msgstr "" 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: 2022-06-26 12:09+0200\n" "POT-Creation-Date: 2022-07-12 19:20+0200\n"
"PO-Revision-Date: 2022-02-09 01:31+0000\n" "PO-Revision-Date: 2022-02-09 01:31+0000\n"
"Last-Translator: Marion Kämpfer <marion@murphyslantech.de>\n" "Last-Translator: Marion Kämpfer <marion@murphyslantech.de>\n"
"Language-Team: French <http://translate.tandoor.dev/projects/tandoor/recipes-" "Language-Team: French <http://translate.tandoor.dev/projects/tandoor/recipes-"
@ -562,7 +562,7 @@ msgstr ""
"Le nombre dutilisateurs dans votre groupe dépasse le nombre dutilisateurs " "Le nombre dutilisateurs dans votre groupe dépasse le nombre dutilisateurs "
"autorisé." "autorisé."
#: .\cookbook\helper\recipe_search.py:560 #: .\cookbook\helper\recipe_search.py:565
msgid "One of queryset or hash_key must be provided" msgid "One of queryset or hash_key must be provided"
msgstr "" msgstr ""
@ -577,12 +577,12 @@ msgstr "Vous devez fournir une information créé_par"
msgid "Could not parse template code." msgid "Could not parse template code."
msgstr "Impossible danalyser le code du modèle." msgstr "Impossible danalyser le code du modèle."
#: .\cookbook\integration\copymethat.py:42 #: .\cookbook\integration\copymethat.py:41
#: .\cookbook\integration\melarecipes.py:37 #: .\cookbook\integration\melarecipes.py:37
msgid "Favorite" msgid "Favorite"
msgstr "" msgstr ""
#: .\cookbook\integration\copymethat.py:71 #: .\cookbook\integration\copymethat.py:70
#: .\cookbook\integration\recettetek.py:54 #: .\cookbook\integration\recettetek.py:54
#: .\cookbook\integration\recipekeeper.py:63 #: .\cookbook\integration\recipekeeper.py:63
msgid "Imported from" msgid "Imported from"
@ -723,125 +723,125 @@ msgstr "Nouveau"
msgid " is part of a recipe step and cannot be deleted" msgid " is part of a recipe step and cannot be deleted"
msgstr " fait partie dune étape de la recette et ne peut être supprimé(e)" msgstr " fait partie dune étape de la recette et ne peut être supprimé(e)"
#: .\cookbook\models.py:1160 .\cookbook\templates\search_info.html:28 #: .\cookbook\models.py:1162 .\cookbook\templates\search_info.html:28
msgid "Simple" msgid "Simple"
msgstr "Simple" msgstr "Simple"
#: .\cookbook\models.py:1161 .\cookbook\templates\search_info.html:33 #: .\cookbook\models.py:1163 .\cookbook\templates\search_info.html:33
msgid "Phrase" msgid "Phrase"
msgstr "Phrase" msgstr "Phrase"
#: .\cookbook\models.py:1162 .\cookbook\templates\search_info.html:38 #: .\cookbook\models.py:1164 .\cookbook\templates\search_info.html:38
msgid "Web" msgid "Web"
msgstr "Internet" msgstr "Internet"
#: .\cookbook\models.py:1163 .\cookbook\templates\search_info.html:47 #: .\cookbook\models.py:1165 .\cookbook\templates\search_info.html:47
msgid "Raw" msgid "Raw"
msgstr "Brut" msgstr "Brut"
#: .\cookbook\models.py:1201 #: .\cookbook\models.py:1203
msgid "Food Alias" msgid "Food Alias"
msgstr "Aliment équivalent" msgstr "Aliment équivalent"
#: .\cookbook\models.py:1201 #: .\cookbook\models.py:1203
msgid "Unit Alias" msgid "Unit Alias"
msgstr "Unité équivalente" msgstr "Unité équivalente"
#: .\cookbook\models.py:1201 #: .\cookbook\models.py:1203
msgid "Keyword Alias" msgid "Keyword Alias"
msgstr "Mot-clé équivalent" msgstr "Mot-clé équivalent"
#: .\cookbook\models.py:1225 #: .\cookbook\models.py:1227
#: .\cookbook\templates\include\recipe_open_modal.html:7 #: .\cookbook\templates\include\recipe_open_modal.html:7
#: .\cookbook\views\delete.py:36 .\cookbook\views\edit.py:251 #: .\cookbook\views\delete.py:36 .\cookbook\views\edit.py:251
#: .\cookbook\views\new.py:48 #: .\cookbook\views\new.py:48
msgid "Recipe" msgid "Recipe"
msgstr "Recette" msgstr "Recette"
#: .\cookbook\models.py:1226 #: .\cookbook\models.py:1228
#, fuzzy #, fuzzy
#| msgid "Foods" #| msgid "Foods"
msgid "Food" msgid "Food"
msgstr "Aliments" msgstr "Aliments"
#: .\cookbook\models.py:1227 .\cookbook\templates\base.html:138 #: .\cookbook\models.py:1229 .\cookbook\templates\base.html:138
msgid "Keyword" msgid "Keyword"
msgstr "Mot-clé" msgstr "Mot-clé"
#: .\cookbook\serializer.py:204 #: .\cookbook\serializer.py:207
msgid "Cannot modify Space owner permission." msgid "Cannot modify Space owner permission."
msgstr "" msgstr ""
#: .\cookbook\serializer.py:273 #: .\cookbook\serializer.py:290
msgid "File uploads are not enabled for this Space." msgid "File uploads are not enabled for this Space."
msgstr "Le téléversement de fichiers nest pas autorisé pour ce groupe." msgstr "Le téléversement de fichiers nest pas autorisé pour ce groupe."
#: .\cookbook\serializer.py:284 #: .\cookbook\serializer.py:301
msgid "You have reached your file upload limit." msgid "You have reached your file upload limit."
msgstr "Vous avez atteint votre limite de téléversement de fichiers." msgstr "Vous avez atteint votre limite de téléversement de fichiers."
#: .\cookbook\serializer.py:1051 #: .\cookbook\serializer.py:1081
msgid "Hello" msgid "Hello"
msgstr "Bonjour" msgstr "Bonjour"
#: .\cookbook\serializer.py:1051 #: .\cookbook\serializer.py:1081
msgid "You have been invited by " msgid "You have been invited by "
msgstr "Vous avez été invité par " msgstr "Vous avez été invité par "
#: .\cookbook\serializer.py:1052 #: .\cookbook\serializer.py:1082
msgid " to join their Tandoor Recipes space " msgid " to join their Tandoor Recipes space "
msgstr " pour rejoindre leur groupe Tandoor Recipes " msgstr " pour rejoindre leur groupe Tandoor Recipes "
#: .\cookbook\serializer.py:1053 #: .\cookbook\serializer.py:1083
msgid "Click the following link to activate your account: " msgid "Click the following link to activate your account: "
msgstr "Cliquez le lien suivant pour activer votre compte : " msgstr "Cliquez le lien suivant pour activer votre compte : "
#: .\cookbook\serializer.py:1054 #: .\cookbook\serializer.py:1084
msgid "" msgid ""
"If the link does not work use the following code to manually join the space: " "If the link does not work use the following code to manually join the space: "
msgstr "" msgstr ""
"Si le lien ne fonctionne pas, utilisez le code suivant pour rejoindre le " "Si le lien ne fonctionne pas, utilisez le code suivant pour rejoindre le "
"groupe manuellement : " "groupe manuellement : "
#: .\cookbook\serializer.py:1055 #: .\cookbook\serializer.py:1085
msgid "The invitation is valid until " msgid "The invitation is valid until "
msgstr "Linvitation est valide jusquau " msgstr "Linvitation est valide jusquau "
#: .\cookbook\serializer.py:1056 #: .\cookbook\serializer.py:1086
msgid "" msgid ""
"Tandoor Recipes is an Open Source recipe manager. Check it out on GitHub " "Tandoor Recipes is an Open Source recipe manager. Check it out on GitHub "
msgstr "" msgstr ""
"Tandoor Recipes est un gestionnaire de recettes open source. Venez-voir " "Tandoor Recipes est un gestionnaire de recettes open source. Venez-voir "
"notre Github " "notre Github "
#: .\cookbook\serializer.py:1059 #: .\cookbook\serializer.py:1089
msgid "Tandoor Recipes Invite" msgid "Tandoor Recipes Invite"
msgstr "Invitation Tandoor Recipes" msgstr "Invitation Tandoor Recipes"
#: .\cookbook\serializer.py:1179 #: .\cookbook\serializer.py:1209
msgid "Existing shopping list to update" msgid "Existing shopping list to update"
msgstr "Liste de courses existante à mettre à jour" msgstr "Liste de courses existante à mettre à jour"
#: .\cookbook\serializer.py:1181 #: .\cookbook\serializer.py:1211
msgid "" msgid ""
"List of ingredient IDs from the recipe to add, if not provided all " "List of ingredient IDs from the recipe to add, if not provided all "
"ingredients will be added." "ingredients will be added."
msgstr "" msgstr ""
#: .\cookbook\serializer.py:1183 #: .\cookbook\serializer.py:1213
msgid "" msgid ""
"Providing a list_recipe ID and servings of 0 will delete that shopping list." "Providing a list_recipe ID and servings of 0 will delete that shopping list."
msgstr "" msgstr ""
#: .\cookbook\serializer.py:1192 #: .\cookbook\serializer.py:1222
msgid "Amount of food to add to the shopping list" msgid "Amount of food to add to the shopping list"
msgstr "Quantité daliments à ajouter à la liste de courses" msgstr "Quantité daliments à ajouter à la liste de courses"
#: .\cookbook\serializer.py:1194 #: .\cookbook\serializer.py:1224
msgid "ID of unit to use for the shopping list" msgid "ID of unit to use for the shopping list"
msgstr "ID de lunité à utiliser pour la liste de courses" msgstr "ID de lunité à utiliser pour la liste de courses"
#: .\cookbook\serializer.py:1196 #: .\cookbook\serializer.py:1226
msgid "When set to true will delete all food from active shopping lists." msgid "When set to true will delete all food from active shopping lists."
msgstr "" msgstr ""
@ -981,7 +981,7 @@ msgstr ""
"par mail</a>." "par mail</a>."
#: .\cookbook\templates\account\login.html:8 #: .\cookbook\templates\account\login.html:8
#: .\cookbook\templates\base.html:339 .\cookbook\templates\openid\login.html:8 #: .\cookbook\templates\base.html:340 .\cookbook\templates\openid\login.html:8
msgid "Login" msgid "Login"
msgstr "Connexion" msgstr "Connexion"
@ -1154,7 +1154,7 @@ msgstr "Inscriptions closes"
msgid "We are sorry, but the sign up is currently closed." msgid "We are sorry, but the sign up is currently closed."
msgstr "Nous sommes désolés, mais les inscriptions sont closes pour le moment." msgstr "Nous sommes désolés, mais les inscriptions sont closes pour le moment."
#: .\cookbook\templates\api_info.html:5 .\cookbook\templates\base.html:329 #: .\cookbook\templates\api_info.html:5 .\cookbook\templates\base.html:330
#: .\cookbook\templates\rest_framework\api.html:11 #: .\cookbook\templates\rest_framework\api.html:11
msgid "API Documentation" msgid "API Documentation"
msgstr "Documentation API" msgstr "Documentation API"
@ -1251,36 +1251,36 @@ msgstr "Admin"
msgid "Your Spaces" msgid "Your Spaces"
msgstr "Aucun groupe" msgstr "Aucun groupe"
#: .\cookbook\templates\base.html:319 #: .\cookbook\templates\base.html:320
#: .\cookbook\templates\space_overview.html:6 #: .\cookbook\templates\space_overview.html:6
msgid "Overview" msgid "Overview"
msgstr "" msgstr ""
#: .\cookbook\templates\base.html:323 #: .\cookbook\templates\base.html:324
msgid "Markdown Guide" msgid "Markdown Guide"
msgstr "Guide Markdown" msgstr "Guide Markdown"
#: .\cookbook\templates\base.html:325 #: .\cookbook\templates\base.html:326
msgid "GitHub" msgid "GitHub"
msgstr "GitHub" msgstr "GitHub"
#: .\cookbook\templates\base.html:327 #: .\cookbook\templates\base.html:328
msgid "Translate Tandoor" msgid "Translate Tandoor"
msgstr "Traduire Tandoor" msgstr "Traduire Tandoor"
#: .\cookbook\templates\base.html:331 #: .\cookbook\templates\base.html:332
msgid "API Browser" msgid "API Browser"
msgstr "Navigateur API" msgstr "Navigateur API"
#: .\cookbook\templates\base.html:334 #: .\cookbook\templates\base.html:335
msgid "Log out" msgid "Log out"
msgstr "Déconnexion" msgstr "Déconnexion"
#: .\cookbook\templates\base.html:356 #: .\cookbook\templates\base.html:357
msgid "You are using the free version of Tandor" msgid "You are using the free version of Tandor"
msgstr "" msgstr ""
#: .\cookbook\templates\base.html:357 #: .\cookbook\templates\base.html:358
msgid "Upgrade Now" msgid "Upgrade Now"
msgstr "" msgstr ""
@ -2473,19 +2473,11 @@ msgstr "Recettes sans mots-clés"
msgid "Internal Recipes" msgid "Internal Recipes"
msgstr "Recettes internes" msgstr "Recettes internes"
#: .\cookbook\templates\system.html:21 .\cookbook\views\lists.py:76 #: .\cookbook\templates\system.html:20
msgid "Invite Links"
msgstr "Liens dinvitation"
#: .\cookbook\templates\system.html:22
msgid "Show Links"
msgstr "Afficher les liens"
#: .\cookbook\templates\system.html:32
msgid "System Information" msgid "System Information"
msgstr "Informations système" msgstr "Informations système"
#: .\cookbook\templates\system.html:34 #: .\cookbook\templates\system.html:22
msgid "" msgid ""
"\n" "\n"
" Django Recipes is an open source free software application. It can " " Django Recipes is an open source free software application. It can "
@ -2503,21 +2495,21 @@ msgstr ""
"github.com/vabene1111/recipes/releases\">ici</a>.\n" "github.com/vabene1111/recipes/releases\">ici</a>.\n"
" " " "
#: .\cookbook\templates\system.html:48 #: .\cookbook\templates\system.html:36
msgid "Media Serving" msgid "Media Serving"
msgstr "Publication des médias" msgstr "Publication des médias"
#: .\cookbook\templates\system.html:49 .\cookbook\templates\system.html:64 #: .\cookbook\templates\system.html:37 .\cookbook\templates\system.html:52
#: .\cookbook\templates\system.html:80 #: .\cookbook\templates\system.html:68
msgid "Warning" msgid "Warning"
msgstr "Avertissement" msgstr "Avertissement"
#: .\cookbook\templates\system.html:49 .\cookbook\templates\system.html:64 #: .\cookbook\templates\system.html:37 .\cookbook\templates\system.html:52
#: .\cookbook\templates\system.html:80 .\cookbook\templates\system.html:95 #: .\cookbook\templates\system.html:68 .\cookbook\templates\system.html:83
msgid "Ok" msgid "Ok"
msgstr "OK" msgstr "OK"
#: .\cookbook\templates\system.html:51 #: .\cookbook\templates\system.html:39
msgid "" msgid ""
"Serving media files directly using gunicorn/python is <b>not recommend</b>!\n" "Serving media files directly using gunicorn/python is <b>not recommend</b>!\n"
" Please follow the steps described\n" " Please follow the steps described\n"
@ -2533,16 +2525,16 @@ msgstr ""
" pour mettre à jour votre installation.\n" " pour mettre à jour votre installation.\n"
" " " "
#: .\cookbook\templates\system.html:57 .\cookbook\templates\system.html:73 #: .\cookbook\templates\system.html:45 .\cookbook\templates\system.html:61
#: .\cookbook\templates\system.html:88 .\cookbook\templates\system.html:102 #: .\cookbook\templates\system.html:76 .\cookbook\templates\system.html:90
msgid "Everything is fine!" msgid "Everything is fine!"
msgstr "Tout est en ordre !" msgstr "Tout est en ordre !"
#: .\cookbook\templates\system.html:62 #: .\cookbook\templates\system.html:50
msgid "Secret Key" msgid "Secret Key"
msgstr "Clé secrète" msgstr "Clé secrète"
#: .\cookbook\templates\system.html:66 #: .\cookbook\templates\system.html:54
msgid "" msgid ""
"\n" "\n"
" You do not have a <code>SECRET_KEY</code> configured in your " " You do not have a <code>SECRET_KEY</code> configured in your "
@ -2563,11 +2555,11 @@ msgstr ""
"env</code>\n" "env</code>\n"
" " " "
#: .\cookbook\templates\system.html:78 #: .\cookbook\templates\system.html:66
msgid "Debug Mode" msgid "Debug Mode"
msgstr "Mode debug" msgstr "Mode debug"
#: .\cookbook\templates\system.html:82 #: .\cookbook\templates\system.html:70
msgid "" msgid ""
"\n" "\n"
" This application is still running in debug mode. This is most " " This application is still running in debug mode. This is most "
@ -2584,15 +2576,15 @@ msgstr ""
"code>.\n" "code>.\n"
" " " "
#: .\cookbook\templates\system.html:93 #: .\cookbook\templates\system.html:81
msgid "Database" msgid "Database"
msgstr "Base de données" msgstr "Base de données"
#: .\cookbook\templates\system.html:95 #: .\cookbook\templates\system.html:83
msgid "Info" msgid "Info"
msgstr "Info" msgstr "Info"
#: .\cookbook\templates\system.html:97 #: .\cookbook\templates\system.html:85
msgid "" msgid ""
"\n" "\n"
" This application is not running with a Postgres database " " This application is not running with a Postgres database "
@ -2611,251 +2603,251 @@ msgstr ""
msgid "URL Import" msgid "URL Import"
msgstr "Import URL" msgstr "Import URL"
#: .\cookbook\views\api.py:97 .\cookbook\views\api.py:189 #: .\cookbook\views\api.py:105 .\cookbook\views\api.py:197
msgid "Parameter updated_at incorrectly formatted" msgid "Parameter updated_at incorrectly formatted"
msgstr "Le paramètre «update_at» n'est pas correctement formaté" msgstr "Le paramètre «update_at» n'est pas correctement formaté"
#: .\cookbook\views\api.py:209 .\cookbook\views\api.py:312 #: .\cookbook\views\api.py:217 .\cookbook\views\api.py:320
msgid "No {self.basename} with id {pk} exists" msgid "No {self.basename} with id {pk} exists"
msgstr "Il nexiste aucun(e) {self.basename} avec lidentifiant {pk}" msgstr "Il nexiste aucun(e) {self.basename} avec lidentifiant {pk}"
#: .\cookbook\views\api.py:213 #: .\cookbook\views\api.py:221
msgid "Cannot merge with the same object!" msgid "Cannot merge with the same object!"
msgstr "Impossible de fusionner un objet avec lui-même !" msgstr "Impossible de fusionner un objet avec lui-même !"
#: .\cookbook\views\api.py:220 #: .\cookbook\views\api.py:228
msgid "No {self.basename} with id {target} exists" msgid "No {self.basename} with id {target} exists"
msgstr "Il nexiste aucun(e) {self.basename} avec lid {target}" msgstr "Il nexiste aucun(e) {self.basename} avec lid {target}"
#: .\cookbook\views\api.py:225 #: .\cookbook\views\api.py:233
msgid "Cannot merge with child object!" msgid "Cannot merge with child object!"
msgstr "Impossible de fusionner avec lobjet enfant !" msgstr "Impossible de fusionner avec lobjet enfant !"
#: .\cookbook\views\api.py:258 #: .\cookbook\views\api.py:266
msgid "{source.name} was merged successfully with {target.name}" msgid "{source.name} was merged successfully with {target.name}"
msgstr "{source.name} a été fusionné avec succès avec {target.name}" msgstr "{source.name} a été fusionné avec succès avec {target.name}"
#: .\cookbook\views\api.py:263 #: .\cookbook\views\api.py:271
msgid "An error occurred attempting to merge {source.name} with {target.name}" msgid "An error occurred attempting to merge {source.name} with {target.name}"
msgstr "" msgstr ""
"Une erreur est survenue lors de la tentative de fusion de {source.name} avec " "Une erreur est survenue lors de la tentative de fusion de {source.name} avec "
"{target.name}" "{target.name}"
#: .\cookbook\views\api.py:321 #: .\cookbook\views\api.py:329
msgid "{child.name} was moved successfully to the root." msgid "{child.name} was moved successfully to the root."
msgstr "{child.name} a été déplacé avec succès vers la racine." msgstr "{child.name} a été déplacé avec succès vers la racine."
#: .\cookbook\views\api.py:324 .\cookbook\views\api.py:342 #: .\cookbook\views\api.py:332 .\cookbook\views\api.py:350
msgid "An error occurred attempting to move " msgid "An error occurred attempting to move "
msgstr "Une erreur est survenue en essayant de déplacer " msgstr "Une erreur est survenue en essayant de déplacer "
#: .\cookbook\views\api.py:327 #: .\cookbook\views\api.py:335
msgid "Cannot move an object to itself!" msgid "Cannot move an object to itself!"
msgstr "Impossible de déplacer un objet vers lui-même !" msgstr "Impossible de déplacer un objet vers lui-même !"
#: .\cookbook\views\api.py:333 #: .\cookbook\views\api.py:341
msgid "No {self.basename} with id {parent} exists" msgid "No {self.basename} with id {parent} exists"
msgstr "Il nexiste aucun(e) {self.basename} avec lid {parent}" msgstr "Il nexiste aucun(e) {self.basename} avec lid {parent}"
#: .\cookbook\views\api.py:339 #: .\cookbook\views\api.py:347
msgid "{child.name} was moved successfully to parent {parent.name}" msgid "{child.name} was moved successfully to parent {parent.name}"
msgstr "{child.name} a été déplacé avec succès vers le parent {parent.name}" msgstr "{child.name} a été déplacé avec succès vers le parent {parent.name}"
#: .\cookbook\views\api.py:534 #: .\cookbook\views\api.py:542
msgid "{obj.name} was removed from the shopping list." msgid "{obj.name} was removed from the shopping list."
msgstr "{obj.name} a été supprimé(e) de la liste de courses." msgstr "{obj.name} a été supprimé(e) de la liste de courses."
#: .\cookbook\views\api.py:539 .\cookbook\views\api.py:871 #: .\cookbook\views\api.py:547 .\cookbook\views\api.py:879
#: .\cookbook\views\api.py:884 #: .\cookbook\views\api.py:892
msgid "{obj.name} was added to the shopping list." msgid "{obj.name} was added to the shopping list."
msgstr "{obj.name} a été ajouté(e) à la liste de courses." msgstr "{obj.name} a été ajouté(e) à la liste de courses."
#: .\cookbook\views\api.py:666 #: .\cookbook\views\api.py:674
msgid "ID of recipe a step is part of. For multiple repeat parameter." msgid "ID of recipe a step is part of. For multiple repeat parameter."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:668 #: .\cookbook\views\api.py:676
msgid "Query string matched (fuzzy) against object name." msgid "Query string matched (fuzzy) against object name."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:712 #: .\cookbook\views\api.py:720
msgid "" msgid ""
"Query string matched (fuzzy) against recipe name. In the future also " "Query string matched (fuzzy) against recipe name. In the future also "
"fulltext search." "fulltext search."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:714 #: .\cookbook\views\api.py:722
msgid "" msgid ""
"ID of keyword a recipe should have. For multiple repeat parameter. " "ID of keyword a recipe should have. For multiple repeat parameter. "
"Equivalent to keywords_or" "Equivalent to keywords_or"
msgstr "" msgstr ""
#: .\cookbook\views\api.py:717 #: .\cookbook\views\api.py:725
msgid "" msgid ""
"Keyword IDs, repeat for multiple. Return recipes with any of the keywords" "Keyword IDs, repeat for multiple. Return recipes with any of the keywords"
msgstr "" msgstr ""
#: .\cookbook\views\api.py:720 #: .\cookbook\views\api.py:728
msgid "" msgid ""
"Keyword IDs, repeat for multiple. Return recipes with all of the keywords." "Keyword IDs, repeat for multiple. Return recipes with all of the keywords."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:723 #: .\cookbook\views\api.py:731
msgid "" msgid ""
"Keyword IDs, repeat for multiple. Exclude recipes with any of the keywords." "Keyword IDs, repeat for multiple. Exclude recipes with any of the keywords."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:726 #: .\cookbook\views\api.py:734
msgid "" msgid ""
"Keyword IDs, repeat for multiple. Exclude recipes with all of the keywords." "Keyword IDs, repeat for multiple. Exclude recipes with all of the keywords."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:728 #: .\cookbook\views\api.py:736
msgid "ID of food a recipe should have. For multiple repeat parameter." msgid "ID of food a recipe should have. For multiple repeat parameter."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:731 #: .\cookbook\views\api.py:739
msgid "Food IDs, repeat for multiple. Return recipes with any of the foods" msgid "Food IDs, repeat for multiple. Return recipes with any of the foods"
msgstr "" msgstr ""
#: .\cookbook\views\api.py:733 #: .\cookbook\views\api.py:741
msgid "Food IDs, repeat for multiple. Return recipes with all of the foods." msgid "Food IDs, repeat for multiple. Return recipes with all of the foods."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:735 #: .\cookbook\views\api.py:743
msgid "Food IDs, repeat for multiple. Exclude recipes with any of the foods." msgid "Food IDs, repeat for multiple. Exclude recipes with any of the foods."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:737 #: .\cookbook\views\api.py:745
msgid "Food IDs, repeat for multiple. Exclude recipes with all of the foods." msgid "Food IDs, repeat for multiple. Exclude recipes with all of the foods."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:738 #: .\cookbook\views\api.py:746
msgid "ID of unit a recipe should have." msgid "ID of unit a recipe should have."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:740 #: .\cookbook\views\api.py:748
msgid "" msgid ""
"Rating a recipe should have or greater. [0 - 5] Negative value filters " "Rating a recipe should have or greater. [0 - 5] Negative value filters "
"rating less than." "rating less than."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:741 #: .\cookbook\views\api.py:749
msgid "ID of book a recipe should be in. For multiple repeat parameter." msgid "ID of book a recipe should be in. For multiple repeat parameter."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:743 #: .\cookbook\views\api.py:751
msgid "Book IDs, repeat for multiple. Return recipes with any of the books" msgid "Book IDs, repeat for multiple. Return recipes with any of the books"
msgstr "" msgstr ""
#: .\cookbook\views\api.py:745 #: .\cookbook\views\api.py:753
msgid "Book IDs, repeat for multiple. Return recipes with all of the books." msgid "Book IDs, repeat for multiple. Return recipes with all of the books."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:747 #: .\cookbook\views\api.py:755
msgid "Book IDs, repeat for multiple. Exclude recipes with any of the books." msgid "Book IDs, repeat for multiple. Exclude recipes with any of the books."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:749 #: .\cookbook\views\api.py:757
msgid "Book IDs, repeat for multiple. Exclude recipes with all of the books." msgid "Book IDs, repeat for multiple. Exclude recipes with all of the books."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:751 #: .\cookbook\views\api.py:759
msgid "If only internal recipes should be returned. [true/<b>false</b>]" msgid "If only internal recipes should be returned. [true/<b>false</b>]"
msgstr "" msgstr ""
#: .\cookbook\views\api.py:753 #: .\cookbook\views\api.py:761
msgid "Returns the results in randomized order. [true/<b>false</b>]" msgid "Returns the results in randomized order. [true/<b>false</b>]"
msgstr "" msgstr ""
#: .\cookbook\views\api.py:755 #: .\cookbook\views\api.py:763
msgid "Returns new results first in search results. [true/<b>false</b>]" msgid "Returns new results first in search results. [true/<b>false</b>]"
msgstr "" msgstr ""
#: .\cookbook\views\api.py:757 #: .\cookbook\views\api.py:765
msgid "" msgid ""
"Filter recipes cooked X times or more. Negative values returns cooked less " "Filter recipes cooked X times or more. Negative values returns cooked less "
"than X times" "than X times"
msgstr "" msgstr ""
#: .\cookbook\views\api.py:759 #: .\cookbook\views\api.py:767
msgid "" msgid ""
"Filter recipes last cooked on or after YYYY-MM-DD. Prepending - filters on " "Filter recipes last cooked on or after YYYY-MM-DD. Prepending - filters on "
"or before date." "or before date."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:761 #: .\cookbook\views\api.py:769
msgid "" msgid ""
"Filter recipes created on or after YYYY-MM-DD. Prepending - filters on or " "Filter recipes created on or after YYYY-MM-DD. Prepending - filters on or "
"before date." "before date."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:763 #: .\cookbook\views\api.py:771
msgid "" msgid ""
"Filter recipes updated on or after YYYY-MM-DD. Prepending - filters on or " "Filter recipes updated on or after YYYY-MM-DD. Prepending - filters on or "
"before date." "before date."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:765 #: .\cookbook\views\api.py:773
msgid "" msgid ""
"Filter recipes lasts viewed on or after YYYY-MM-DD. Prepending - filters on " "Filter recipes lasts viewed on or after YYYY-MM-DD. Prepending - filters on "
"or before date." "or before date."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:767 #: .\cookbook\views\api.py:775
msgid "Filter recipes that can be made with OnHand food. [true/<b>false</b>]" msgid "Filter recipes that can be made with OnHand food. [true/<b>false</b>]"
msgstr "" msgstr ""
#: .\cookbook\views\api.py:929 #: .\cookbook\views\api.py:937
msgid "" msgid ""
"Returns the shopping list entry with a primary key of id. Multiple values " "Returns the shopping list entry with a primary key of id. Multiple values "
"allowed." "allowed."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:934 #: .\cookbook\views\api.py:942
msgid "" msgid ""
"Filter shopping list entries on checked. [true, false, both, <b>recent</b>]" "Filter shopping list entries on checked. [true, false, both, <b>recent</b>]"
"<br> - recent includes unchecked items and recently completed items." "<br> - recent includes unchecked items and recently completed items."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:937 #: .\cookbook\views\api.py:945
msgid "Returns the shopping list entries sorted by supermarket category order." msgid "Returns the shopping list entries sorted by supermarket category order."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:1134 #: .\cookbook\views\api.py:1140
msgid "Nothing to do." msgid "Nothing to do."
msgstr "Rien à faire." msgstr "Rien à faire."
#: .\cookbook\views\api.py:1153 #: .\cookbook\views\api.py:1160
msgid "Invalid Url" msgid "Invalid Url"
msgstr "" msgstr ""
#: .\cookbook\views\api.py:1158 #: .\cookbook\views\api.py:1167
msgid "Connection Refused." msgid "Connection Refused."
msgstr "Connexion refusée." msgstr "Connexion refusée."
#: .\cookbook\views\api.py:1163 #: .\cookbook\views\api.py:1172
msgid "Bad URL Schema." msgid "Bad URL Schema."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:1170 #: .\cookbook\views\api.py:1195
#, fuzzy #, fuzzy
#| msgid "No useable data could be found." #| msgid "No useable data could be found."
msgid "No usable data could be found." msgid "No usable data could be found."
msgstr "Aucune information utilisable n'a été trouvée." msgstr "Aucune information utilisable n'a été trouvée."
#: .\cookbook\views\api.py:1260 .\cookbook\views\data.py:28 #: .\cookbook\views\api.py:1303 .\cookbook\views\data.py:28
#: .\cookbook\views\edit.py:120 .\cookbook\views\new.py:90 #: .\cookbook\views\edit.py:120 .\cookbook\views\new.py:90
msgid "This feature is not yet available in the hosted version of tandoor!" msgid "This feature is not yet available in the hosted version of tandoor!"
msgstr "" msgstr ""
"Cette fonctionnalité nest pas encore disponible dans la version hébergée de " "Cette fonctionnalité nest pas encore disponible dans la version hébergée de "
"Tandoor !" "Tandoor !"
#: .\cookbook\views\api.py:1282 #: .\cookbook\views\api.py:1325
msgid "Sync successful!" msgid "Sync successful!"
msgstr "Synchro réussie !" msgstr "Synchro réussie !"
#: .\cookbook\views\api.py:1287 #: .\cookbook\views\api.py:1330
msgid "Error synchronizing with Storage" msgid "Error synchronizing with Storage"
msgstr "Erreur lors de la synchronisation avec le stockage" msgstr "Erreur lors de la synchronisation avec le stockage"
@ -2943,6 +2935,10 @@ msgstr "Découverte"
msgid "Shopping List" msgid "Shopping List"
msgstr "Liste de courses" msgstr "Liste de courses"
#: .\cookbook\views\lists.py:76
msgid "Invite Links"
msgstr "Liens dinvitation"
#: .\cookbook\views\lists.py:139 #: .\cookbook\views\lists.py:139
msgid "Supermarkets" msgid "Supermarkets"
msgstr "Supermarchés" msgstr "Supermarchés"
@ -3055,6 +3051,9 @@ msgstr ""
"Le lien de partage de la recette a été désactivé ! Pour plus dinformations, " "Le lien de partage de la recette a été désactivé ! Pour plus dinformations, "
"veuillez contacter ladministrateur de la page." "veuillez contacter ladministrateur de la page."
#~ msgid "Show Links"
#~ msgstr "Afficher les liens"
#~ msgid "A user is required" #~ msgid "A user is required"
#~ msgstr "Un utilisateur est requis" #~ msgstr "Un utilisateur est requis"

View File

@ -10,7 +10,7 @@ msgid ""
msgstr "" 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: 2022-06-26 12:09+0200\n" "POT-Creation-Date: 2022-07-12 19:20+0200\n"
"PO-Revision-Date: 2022-05-24 20:32+0000\n" "PO-Revision-Date: 2022-05-24 20:32+0000\n"
"Last-Translator: Krisztian Doka <master@dnome.hu>\n" "Last-Translator: Krisztian Doka <master@dnome.hu>\n"
"Language-Team: Hungarian <http://translate.tandoor.dev/projects/tandoor/" "Language-Team: Hungarian <http://translate.tandoor.dev/projects/tandoor/"
@ -547,7 +547,7 @@ msgstr "Elérte a maximális számú receptet a helyén."
msgid "You have more users than allowed in your space." msgid "You have more users than allowed in your space."
msgstr "Több felhasználója van, mint amennyit engedélyeztek a térben." msgstr "Több felhasználója van, mint amennyit engedélyeztek a térben."
#: .\cookbook\helper\recipe_search.py:560 #: .\cookbook\helper\recipe_search.py:565
msgid "One of queryset or hash_key must be provided" msgid "One of queryset or hash_key must be provided"
msgstr "A queryset vagy a hash_key valamelyikét meg kell adni" msgstr "A queryset vagy a hash_key valamelyikét meg kell adni"
@ -562,12 +562,12 @@ msgstr "Meg kell adnia egy created_by"
msgid "Could not parse template code." msgid "Could not parse template code."
msgstr "Nem sikerült elemezni a sablon kódját." msgstr "Nem sikerült elemezni a sablon kódját."
#: .\cookbook\integration\copymethat.py:42 #: .\cookbook\integration\copymethat.py:41
#: .\cookbook\integration\melarecipes.py:37 #: .\cookbook\integration\melarecipes.py:37
msgid "Favorite" msgid "Favorite"
msgstr "" msgstr ""
#: .\cookbook\integration\copymethat.py:71 #: .\cookbook\integration\copymethat.py:70
#: .\cookbook\integration\recettetek.py:54 #: .\cookbook\integration\recettetek.py:54
#: .\cookbook\integration\recipekeeper.py:63 #: .\cookbook\integration\recipekeeper.py:63
#, fuzzy #, fuzzy
@ -710,105 +710,105 @@ msgstr "Új"
msgid " is part of a recipe step and cannot be deleted" msgid " is part of a recipe step and cannot be deleted"
msgstr " egy recept része, ezért nem törölhető" msgstr " egy recept része, ezért nem törölhető"
#: .\cookbook\models.py:1160 .\cookbook\templates\search_info.html:28 #: .\cookbook\models.py:1162 .\cookbook\templates\search_info.html:28
msgid "Simple" msgid "Simple"
msgstr "Egyszerű" msgstr "Egyszerű"
#: .\cookbook\models.py:1161 .\cookbook\templates\search_info.html:33 #: .\cookbook\models.py:1163 .\cookbook\templates\search_info.html:33
msgid "Phrase" msgid "Phrase"
msgstr "Kifejezés" msgstr "Kifejezés"
#: .\cookbook\models.py:1162 .\cookbook\templates\search_info.html:38 #: .\cookbook\models.py:1164 .\cookbook\templates\search_info.html:38
msgid "Web" msgid "Web"
msgstr "Web" msgstr "Web"
#: .\cookbook\models.py:1163 .\cookbook\templates\search_info.html:47 #: .\cookbook\models.py:1165 .\cookbook\templates\search_info.html:47
msgid "Raw" msgid "Raw"
msgstr "Nyers" msgstr "Nyers"
#: .\cookbook\models.py:1201 #: .\cookbook\models.py:1203
msgid "Food Alias" msgid "Food Alias"
msgstr "Élelmiszer álneve" msgstr "Élelmiszer álneve"
#: .\cookbook\models.py:1201 #: .\cookbook\models.py:1203
msgid "Unit Alias" msgid "Unit Alias"
msgstr "Egység álneve" msgstr "Egység álneve"
#: .\cookbook\models.py:1201 #: .\cookbook\models.py:1203
msgid "Keyword Alias" msgid "Keyword Alias"
msgstr "Kulcsszó álneve" msgstr "Kulcsszó álneve"
#: .\cookbook\models.py:1225 #: .\cookbook\models.py:1227
#: .\cookbook\templates\include\recipe_open_modal.html:7 #: .\cookbook\templates\include\recipe_open_modal.html:7
#: .\cookbook\views\delete.py:36 .\cookbook\views\edit.py:251 #: .\cookbook\views\delete.py:36 .\cookbook\views\edit.py:251
#: .\cookbook\views\new.py:48 #: .\cookbook\views\new.py:48
msgid "Recipe" msgid "Recipe"
msgstr "Recept" msgstr "Recept"
#: .\cookbook\models.py:1226 #: .\cookbook\models.py:1228
#, fuzzy #, fuzzy
#| msgid "Foods" #| msgid "Foods"
msgid "Food" msgid "Food"
msgstr "Ételek" msgstr "Ételek"
#: .\cookbook\models.py:1227 .\cookbook\templates\base.html:138 #: .\cookbook\models.py:1229 .\cookbook\templates\base.html:138
msgid "Keyword" msgid "Keyword"
msgstr "Kulcsszó" msgstr "Kulcsszó"
#: .\cookbook\serializer.py:204 #: .\cookbook\serializer.py:207
msgid "Cannot modify Space owner permission." msgid "Cannot modify Space owner permission."
msgstr "" msgstr ""
#: .\cookbook\serializer.py:273 #: .\cookbook\serializer.py:290
msgid "File uploads are not enabled for this Space." msgid "File uploads are not enabled for this Space."
msgstr "A fájlok feltöltése nem engedélyezett ezen a tárhelyen." msgstr "A fájlok feltöltése nem engedélyezett ezen a tárhelyen."
#: .\cookbook\serializer.py:284 #: .\cookbook\serializer.py:301
msgid "You have reached your file upload limit." msgid "You have reached your file upload limit."
msgstr "Elérte a fájlfeltöltési limitet." msgstr "Elérte a fájlfeltöltési limitet."
#: .\cookbook\serializer.py:1051 #: .\cookbook\serializer.py:1081
msgid "Hello" msgid "Hello"
msgstr "Helló" msgstr "Helló"
#: .\cookbook\serializer.py:1051 #: .\cookbook\serializer.py:1081
msgid "You have been invited by " msgid "You have been invited by "
msgstr "Önt meghívta " msgstr "Önt meghívta "
#: .\cookbook\serializer.py:1052 #: .\cookbook\serializer.py:1082
msgid " to join their Tandoor Recipes space " msgid " to join their Tandoor Recipes space "
msgstr " hogy csatlakozzon a Tandoor Receptek helyhez " msgstr " hogy csatlakozzon a Tandoor Receptek helyhez "
#: .\cookbook\serializer.py:1053 #: .\cookbook\serializer.py:1083
msgid "Click the following link to activate your account: " msgid "Click the following link to activate your account: "
msgstr "Kattintson az alábbi linkre fiókja aktiválásához: " msgstr "Kattintson az alábbi linkre fiókja aktiválásához: "
#: .\cookbook\serializer.py:1054 #: .\cookbook\serializer.py:1084
msgid "" msgid ""
"If the link does not work use the following code to manually join the space: " "If the link does not work use the following code to manually join the space: "
msgstr "" msgstr ""
"Ha a link nem működik, használja a következő kódot, hogy manuálisan " "Ha a link nem működik, használja a következő kódot, hogy manuálisan "
"csatlakozzon a térhez: " "csatlakozzon a térhez: "
#: .\cookbook\serializer.py:1055 #: .\cookbook\serializer.py:1085
msgid "The invitation is valid until " msgid "The invitation is valid until "
msgstr "A meghívó a következő időpontig érvényes " msgstr "A meghívó a következő időpontig érvényes "
#: .\cookbook\serializer.py:1056 #: .\cookbook\serializer.py:1086
msgid "" msgid ""
"Tandoor Recipes is an Open Source recipe manager. Check it out on GitHub " "Tandoor Recipes is an Open Source recipe manager. Check it out on GitHub "
msgstr "" msgstr ""
"A Tandoor Receptek egy nyílt forráskódú receptkezelő. Nézze meg a GitHubon " "A Tandoor Receptek egy nyílt forráskódú receptkezelő. Nézze meg a GitHubon "
#: .\cookbook\serializer.py:1059 #: .\cookbook\serializer.py:1089
msgid "Tandoor Recipes Invite" msgid "Tandoor Recipes Invite"
msgstr "Tandoor receptek meghívó" msgstr "Tandoor receptek meghívó"
#: .\cookbook\serializer.py:1179 #: .\cookbook\serializer.py:1209
msgid "Existing shopping list to update" msgid "Existing shopping list to update"
msgstr "Meglévő bevásárlólista frissítése" msgstr "Meglévő bevásárlólista frissítése"
#: .\cookbook\serializer.py:1181 #: .\cookbook\serializer.py:1211
msgid "" msgid ""
"List of ingredient IDs from the recipe to add, if not provided all " "List of ingredient IDs from the recipe to add, if not provided all "
"ingredients will be added." "ingredients will be added."
@ -816,20 +816,20 @@ msgstr ""
"A hozzáadandó összetevők azonosítóinak listája a receptből, ha nincs " "A hozzáadandó összetevők azonosítóinak listája a receptből, ha nincs "
"megadva, az összes összetevő hozzáadásra kerül." "megadva, az összes összetevő hozzáadásra kerül."
#: .\cookbook\serializer.py:1183 #: .\cookbook\serializer.py:1213
msgid "" msgid ""
"Providing a list_recipe ID and servings of 0 will delete that shopping list." "Providing a list_recipe ID and servings of 0 will delete that shopping list."
msgstr "A list_recipe azonosító és a 0 adag megadása törli a bevásárlólistát." msgstr "A list_recipe azonosító és a 0 adag megadása törli a bevásárlólistát."
#: .\cookbook\serializer.py:1192 #: .\cookbook\serializer.py:1222
msgid "Amount of food to add to the shopping list" msgid "Amount of food to add to the shopping list"
msgstr "A bevásárlólistához hozzáadandó élelmiszerek mennyisége" msgstr "A bevásárlólistához hozzáadandó élelmiszerek mennyisége"
#: .\cookbook\serializer.py:1194 #: .\cookbook\serializer.py:1224
msgid "ID of unit to use for the shopping list" msgid "ID of unit to use for the shopping list"
msgstr "A bevásárlólistához használandó egység azonosítója" msgstr "A bevásárlólistához használandó egység azonosítója"
#: .\cookbook\serializer.py:1196 #: .\cookbook\serializer.py:1226
msgid "When set to true will delete all food from active shopping lists." msgid "When set to true will delete all food from active shopping lists."
msgstr "" msgstr ""
"Ha igazra van állítva, akkor minden élelmiszert töröl az aktív " "Ha igazra van állítva, akkor minden élelmiszert töröl az aktív "
@ -972,7 +972,7 @@ msgstr ""
"kérelmet</a>." "kérelmet</a>."
#: .\cookbook\templates\account\login.html:8 #: .\cookbook\templates\account\login.html:8
#: .\cookbook\templates\base.html:339 .\cookbook\templates\openid\login.html:8 #: .\cookbook\templates\base.html:340 .\cookbook\templates\openid\login.html:8
msgid "Login" msgid "Login"
msgstr "Bejelentkezés" msgstr "Bejelentkezés"
@ -1144,7 +1144,7 @@ msgstr "Regisztráció lezárva"
msgid "We are sorry, but the sign up is currently closed." msgid "We are sorry, but the sign up is currently closed."
msgstr "Sajnáljuk, de a regisztráció jelenleg zárva van." msgstr "Sajnáljuk, de a regisztráció jelenleg zárva van."
#: .\cookbook\templates\api_info.html:5 .\cookbook\templates\base.html:329 #: .\cookbook\templates\api_info.html:5 .\cookbook\templates\base.html:330
#: .\cookbook\templates\rest_framework\api.html:11 #: .\cookbook\templates\rest_framework\api.html:11
msgid "API Documentation" msgid "API Documentation"
msgstr "API dokumentáció" msgstr "API dokumentáció"
@ -1241,36 +1241,36 @@ msgstr "Admin"
msgid "Your Spaces" msgid "Your Spaces"
msgstr "Nincs hely" msgstr "Nincs hely"
#: .\cookbook\templates\base.html:319 #: .\cookbook\templates\base.html:320
#: .\cookbook\templates\space_overview.html:6 #: .\cookbook\templates\space_overview.html:6
msgid "Overview" msgid "Overview"
msgstr "" msgstr ""
#: .\cookbook\templates\base.html:323 #: .\cookbook\templates\base.html:324
msgid "Markdown Guide" msgid "Markdown Guide"
msgstr "Markdown útmutató" msgstr "Markdown útmutató"
#: .\cookbook\templates\base.html:325 #: .\cookbook\templates\base.html:326
msgid "GitHub" msgid "GitHub"
msgstr "GitHub" msgstr "GitHub"
#: .\cookbook\templates\base.html:327 #: .\cookbook\templates\base.html:328
msgid "Translate Tandoor" msgid "Translate Tandoor"
msgstr "Tandoor fordítása" msgstr "Tandoor fordítása"
#: .\cookbook\templates\base.html:331 #: .\cookbook\templates\base.html:332
msgid "API Browser" msgid "API Browser"
msgstr "API böngésző" msgstr "API böngésző"
#: .\cookbook\templates\base.html:334 #: .\cookbook\templates\base.html:335
msgid "Log out" msgid "Log out"
msgstr "Kijelentkezés" msgstr "Kijelentkezés"
#: .\cookbook\templates\base.html:356 #: .\cookbook\templates\base.html:357
msgid "You are using the free version of Tandor" msgid "You are using the free version of Tandor"
msgstr "" msgstr ""
#: .\cookbook\templates\base.html:357 #: .\cookbook\templates\base.html:358
msgid "Upgrade Now" msgid "Upgrade Now"
msgstr "" msgstr ""
@ -2444,19 +2444,11 @@ msgstr "Receptek kulcsszavak nélkül"
msgid "Internal Recipes" msgid "Internal Recipes"
msgstr "Belső receptek" msgstr "Belső receptek"
#: .\cookbook\templates\system.html:21 .\cookbook\views\lists.py:76 #: .\cookbook\templates\system.html:20
msgid "Invite Links"
msgstr "Meghívó linkek"
#: .\cookbook\templates\system.html:22
msgid "Show Links"
msgstr "Linkek megjelenítése"
#: .\cookbook\templates\system.html:32
msgid "System Information" msgid "System Information"
msgstr "Rendszerinformáció" msgstr "Rendszerinformáció"
#: .\cookbook\templates\system.html:34 #: .\cookbook\templates\system.html:22
msgid "" msgid ""
"\n" "\n"
" Django Recipes is an open source free software application. It can " " Django Recipes is an open source free software application. It can "
@ -2474,21 +2466,21 @@ msgstr ""
"vabene1111/recipes/releases\">itt</a>.\n" "vabene1111/recipes/releases\">itt</a>.\n"
" " " "
#: .\cookbook\templates\system.html:48 #: .\cookbook\templates\system.html:36
msgid "Media Serving" msgid "Media Serving"
msgstr "Média kiszolgáló" msgstr "Média kiszolgáló"
#: .\cookbook\templates\system.html:49 .\cookbook\templates\system.html:64 #: .\cookbook\templates\system.html:37 .\cookbook\templates\system.html:52
#: .\cookbook\templates\system.html:80 #: .\cookbook\templates\system.html:68
msgid "Warning" msgid "Warning"
msgstr "Figyelmeztetés" msgstr "Figyelmeztetés"
#: .\cookbook\templates\system.html:49 .\cookbook\templates\system.html:64 #: .\cookbook\templates\system.html:37 .\cookbook\templates\system.html:52
#: .\cookbook\templates\system.html:80 .\cookbook\templates\system.html:95 #: .\cookbook\templates\system.html:68 .\cookbook\templates\system.html:83
msgid "Ok" msgid "Ok"
msgstr "Rendben" msgstr "Rendben"
#: .\cookbook\templates\system.html:51 #: .\cookbook\templates\system.html:39
msgid "" msgid ""
"Serving media files directly using gunicorn/python is <b>not recommend</b>!\n" "Serving media files directly using gunicorn/python is <b>not recommend</b>!\n"
" Please follow the steps described\n" " Please follow the steps described\n"
@ -2505,16 +2497,16 @@ msgstr ""
" frissítéshez.\n" " frissítéshez.\n"
" " " "
#: .\cookbook\templates\system.html:57 .\cookbook\templates\system.html:73 #: .\cookbook\templates\system.html:45 .\cookbook\templates\system.html:61
#: .\cookbook\templates\system.html:88 .\cookbook\templates\system.html:102 #: .\cookbook\templates\system.html:76 .\cookbook\templates\system.html:90
msgid "Everything is fine!" msgid "Everything is fine!"
msgstr "Minden rendben van!" msgstr "Minden rendben van!"
#: .\cookbook\templates\system.html:62 #: .\cookbook\templates\system.html:50
msgid "Secret Key" msgid "Secret Key"
msgstr "Titkos kulcs" msgstr "Titkos kulcs"
#: .\cookbook\templates\system.html:66 #: .\cookbook\templates\system.html:54
msgid "" msgid ""
"\n" "\n"
" You do not have a <code>SECRET_KEY</code> configured in your " " You do not have a <code>SECRET_KEY</code> configured in your "
@ -2536,11 +2528,11 @@ msgstr ""
"fájlban.\n" "fájlban.\n"
" " " "
#: .\cookbook\templates\system.html:78 #: .\cookbook\templates\system.html:66
msgid "Debug Mode" msgid "Debug Mode"
msgstr "Hibakeresési mód" msgstr "Hibakeresési mód"
#: .\cookbook\templates\system.html:82 #: .\cookbook\templates\system.html:70
msgid "" msgid ""
"\n" "\n"
" This application is still running in debug mode. This is most " " This application is still running in debug mode. This is most "
@ -2558,15 +2550,15 @@ msgstr ""
"konfigurációs fájlban.\n" "konfigurációs fájlban.\n"
" " " "
#: .\cookbook\templates\system.html:93 #: .\cookbook\templates\system.html:81
msgid "Database" msgid "Database"
msgstr "Adatbázis" msgstr "Adatbázis"
#: .\cookbook\templates\system.html:95 #: .\cookbook\templates\system.html:83
msgid "Info" msgid "Info"
msgstr "Információ" msgstr "Információ"
#: .\cookbook\templates\system.html:97 #: .\cookbook\templates\system.html:85
msgid "" msgid ""
"\n" "\n"
" This application is not running with a Postgres database " " This application is not running with a Postgres database "
@ -2584,74 +2576,74 @@ msgstr ""
msgid "URL Import" msgid "URL Import"
msgstr "URL importálása" msgstr "URL importálása"
#: .\cookbook\views\api.py:97 .\cookbook\views\api.py:189 #: .\cookbook\views\api.py:105 .\cookbook\views\api.py:197
msgid "Parameter updated_at incorrectly formatted" msgid "Parameter updated_at incorrectly formatted"
msgstr "Az updated_at paraméter helytelenül van formázva" msgstr "Az updated_at paraméter helytelenül van formázva"
#: .\cookbook\views\api.py:209 .\cookbook\views\api.py:312 #: .\cookbook\views\api.py:217 .\cookbook\views\api.py:320
msgid "No {self.basename} with id {pk} exists" msgid "No {self.basename} with id {pk} exists"
msgstr "Nem létezik {self.basename} azonosítóval {pk}" msgstr "Nem létezik {self.basename} azonosítóval {pk}"
#: .\cookbook\views\api.py:213 #: .\cookbook\views\api.py:221
msgid "Cannot merge with the same object!" msgid "Cannot merge with the same object!"
msgstr "Nem egyesíthető ugyanazzal az objektummal!" msgstr "Nem egyesíthető ugyanazzal az objektummal!"
#: .\cookbook\views\api.py:220 #: .\cookbook\views\api.py:228
msgid "No {self.basename} with id {target} exists" msgid "No {self.basename} with id {target} exists"
msgstr "Nem létezik {self.basename} azonosítóval {target}" msgstr "Nem létezik {self.basename} azonosítóval {target}"
#: .\cookbook\views\api.py:225 #: .\cookbook\views\api.py:233
msgid "Cannot merge with child object!" msgid "Cannot merge with child object!"
msgstr "Nem lehet egyesíteni a gyermekobjektummal!" msgstr "Nem lehet egyesíteni a gyermekobjektummal!"
#: .\cookbook\views\api.py:258 #: .\cookbook\views\api.py:266
msgid "{source.name} was merged successfully with {target.name}" msgid "{source.name} was merged successfully with {target.name}"
msgstr "{source.name} sikeresen egyesült a {target.name} -vel" msgstr "{source.name} sikeresen egyesült a {target.name} -vel"
#: .\cookbook\views\api.py:263 #: .\cookbook\views\api.py:271
msgid "An error occurred attempting to merge {source.name} with {target.name}" msgid "An error occurred attempting to merge {source.name} with {target.name}"
msgstr "Hiba történt a {source.name} és a {target.name} egyesítése során" msgstr "Hiba történt a {source.name} és a {target.name} egyesítése során"
#: .\cookbook\views\api.py:321 #: .\cookbook\views\api.py:329
msgid "{child.name} was moved successfully to the root." msgid "{child.name} was moved successfully to the root."
msgstr "{child.name} sikeresen átkerült a gyökérbe." msgstr "{child.name} sikeresen átkerült a gyökérbe."
#: .\cookbook\views\api.py:324 .\cookbook\views\api.py:342 #: .\cookbook\views\api.py:332 .\cookbook\views\api.py:350
msgid "An error occurred attempting to move " msgid "An error occurred attempting to move "
msgstr "Hiba történt az áthelyezés közben " msgstr "Hiba történt az áthelyezés közben "
#: .\cookbook\views\api.py:327 #: .\cookbook\views\api.py:335
msgid "Cannot move an object to itself!" msgid "Cannot move an object to itself!"
msgstr "Nem lehet egy objektumot önmagába mozgatni!" msgstr "Nem lehet egy objektumot önmagába mozgatni!"
#: .\cookbook\views\api.py:333 #: .\cookbook\views\api.py:341
msgid "No {self.basename} with id {parent} exists" msgid "No {self.basename} with id {parent} exists"
msgstr "Nem létezik {self.basename} azonosítóval {parent}" msgstr "Nem létezik {self.basename} azonosítóval {parent}"
#: .\cookbook\views\api.py:339 #: .\cookbook\views\api.py:347
msgid "{child.name} was moved successfully to parent {parent.name}" msgid "{child.name} was moved successfully to parent {parent.name}"
msgstr "{child.name} sikeresen átkerült a {parent.name} szülőhöz" msgstr "{child.name} sikeresen átkerült a {parent.name} szülőhöz"
#: .\cookbook\views\api.py:534 #: .\cookbook\views\api.py:542
msgid "{obj.name} was removed from the shopping list." msgid "{obj.name} was removed from the shopping list."
msgstr "{obj.name} lekerült a bevásárlólistáról." msgstr "{obj.name} lekerült a bevásárlólistáról."
#: .\cookbook\views\api.py:539 .\cookbook\views\api.py:871 #: .\cookbook\views\api.py:547 .\cookbook\views\api.py:879
#: .\cookbook\views\api.py:884 #: .\cookbook\views\api.py:892
msgid "{obj.name} was added to the shopping list." msgid "{obj.name} was added to the shopping list."
msgstr "{obj.name} hozzá lett adva a bevásárlólistához." msgstr "{obj.name} hozzá lett adva a bevásárlólistához."
#: .\cookbook\views\api.py:666 #: .\cookbook\views\api.py:674
msgid "ID of recipe a step is part of. For multiple repeat parameter." msgid "ID of recipe a step is part of. For multiple repeat parameter."
msgstr "" msgstr ""
"A recept azonosítója, amelynek egy lépés része. Többszörös ismétlés esetén " "A recept azonosítója, amelynek egy lépés része. Többszörös ismétlés esetén "
"paraméter." "paraméter."
#: .\cookbook\views\api.py:668 #: .\cookbook\views\api.py:676
msgid "Query string matched (fuzzy) against object name." msgid "Query string matched (fuzzy) against object name."
msgstr "A lekérdezés karakterlánca az objektum nevével összevetve (fuzzy)." msgstr "A lekérdezés karakterlánca az objektum nevével összevetve (fuzzy)."
#: .\cookbook\views\api.py:712 #: .\cookbook\views\api.py:720
msgid "" msgid ""
"Query string matched (fuzzy) against recipe name. In the future also " "Query string matched (fuzzy) against recipe name. In the future also "
"fulltext search." "fulltext search."
@ -2659,7 +2651,7 @@ msgstr ""
"A lekérdezési karakterláncot a recept nevével összevetve (fuzzy). A jövőben " "A lekérdezési karakterláncot a recept nevével összevetve (fuzzy). A jövőben "
"teljes szöveges keresés is." "teljes szöveges keresés is."
#: .\cookbook\views\api.py:714 #: .\cookbook\views\api.py:722
#, fuzzy #, fuzzy
#| msgid "ID of keyword a recipe should have. For multiple repeat parameter." #| msgid "ID of keyword a recipe should have. For multiple repeat parameter."
msgid "" msgid ""
@ -2668,132 +2660,132 @@ msgid ""
msgstr "" msgstr ""
"A recept kulcsszavának azonosítója. Többszörös ismétlődő paraméter esetén." "A recept kulcsszavának azonosítója. Többszörös ismétlődő paraméter esetén."
#: .\cookbook\views\api.py:717 #: .\cookbook\views\api.py:725
msgid "" msgid ""
"Keyword IDs, repeat for multiple. Return recipes with any of the keywords" "Keyword IDs, repeat for multiple. Return recipes with any of the keywords"
msgstr "" msgstr ""
#: .\cookbook\views\api.py:720 #: .\cookbook\views\api.py:728
msgid "" msgid ""
"Keyword IDs, repeat for multiple. Return recipes with all of the keywords." "Keyword IDs, repeat for multiple. Return recipes with all of the keywords."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:723 #: .\cookbook\views\api.py:731
msgid "" msgid ""
"Keyword IDs, repeat for multiple. Exclude recipes with any of the keywords." "Keyword IDs, repeat for multiple. Exclude recipes with any of the keywords."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:726 #: .\cookbook\views\api.py:734
msgid "" msgid ""
"Keyword IDs, repeat for multiple. Exclude recipes with all of the keywords." "Keyword IDs, repeat for multiple. Exclude recipes with all of the keywords."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:728 #: .\cookbook\views\api.py:736
msgid "ID of food a recipe should have. For multiple repeat parameter." msgid "ID of food a recipe should have. For multiple repeat parameter."
msgstr "" msgstr ""
"Az ételek azonosítója egy receptnek tartalmaznia kell. Többszörös ismétlődő " "Az ételek azonosítója egy receptnek tartalmaznia kell. Többszörös ismétlődő "
"paraméter esetén." "paraméter esetén."
#: .\cookbook\views\api.py:731 #: .\cookbook\views\api.py:739
msgid "Food IDs, repeat for multiple. Return recipes with any of the foods" msgid "Food IDs, repeat for multiple. Return recipes with any of the foods"
msgstr "" msgstr ""
#: .\cookbook\views\api.py:733 #: .\cookbook\views\api.py:741
msgid "Food IDs, repeat for multiple. Return recipes with all of the foods." msgid "Food IDs, repeat for multiple. Return recipes with all of the foods."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:735 #: .\cookbook\views\api.py:743
msgid "Food IDs, repeat for multiple. Exclude recipes with any of the foods." msgid "Food IDs, repeat for multiple. Exclude recipes with any of the foods."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:737 #: .\cookbook\views\api.py:745
msgid "Food IDs, repeat for multiple. Exclude recipes with all of the foods." msgid "Food IDs, repeat for multiple. Exclude recipes with all of the foods."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:738 #: .\cookbook\views\api.py:746
msgid "ID of unit a recipe should have." msgid "ID of unit a recipe should have."
msgstr "Az egység azonosítója, amellyel a receptnek rendelkeznie kell." msgstr "Az egység azonosítója, amellyel a receptnek rendelkeznie kell."
#: .\cookbook\views\api.py:740 #: .\cookbook\views\api.py:748
msgid "" msgid ""
"Rating a recipe should have or greater. [0 - 5] Negative value filters " "Rating a recipe should have or greater. [0 - 5] Negative value filters "
"rating less than." "rating less than."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:741 #: .\cookbook\views\api.py:749
msgid "ID of book a recipe should be in. For multiple repeat parameter." msgid "ID of book a recipe should be in. For multiple repeat parameter."
msgstr "" msgstr ""
"A könyv azonosítója, amelyben a receptnek szerepelnie kell. Többszörös " "A könyv azonosítója, amelyben a receptnek szerepelnie kell. Többszörös "
"ismétlés esetén paraméter." "ismétlés esetén paraméter."
#: .\cookbook\views\api.py:743 #: .\cookbook\views\api.py:751
msgid "Book IDs, repeat for multiple. Return recipes with any of the books" msgid "Book IDs, repeat for multiple. Return recipes with any of the books"
msgstr "" msgstr ""
#: .\cookbook\views\api.py:745 #: .\cookbook\views\api.py:753
msgid "Book IDs, repeat for multiple. Return recipes with all of the books." msgid "Book IDs, repeat for multiple. Return recipes with all of the books."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:747 #: .\cookbook\views\api.py:755
msgid "Book IDs, repeat for multiple. Exclude recipes with any of the books." msgid "Book IDs, repeat for multiple. Exclude recipes with any of the books."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:749 #: .\cookbook\views\api.py:757
msgid "Book IDs, repeat for multiple. Exclude recipes with all of the books." msgid "Book IDs, repeat for multiple. Exclude recipes with all of the books."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:751 #: .\cookbook\views\api.py:759
msgid "If only internal recipes should be returned. [true/<b>false</b>]" msgid "If only internal recipes should be returned. [true/<b>false</b>]"
msgstr "Ha csak a belső recepteket kell visszaadni. [true/<b>false</b>]" msgstr "Ha csak a belső recepteket kell visszaadni. [true/<b>false</b>]"
#: .\cookbook\views\api.py:753 #: .\cookbook\views\api.py:761
msgid "Returns the results in randomized order. [true/<b>false</b>]" msgid "Returns the results in randomized order. [true/<b>false</b>]"
msgstr "" msgstr ""
"Az eredményeket véletlenszerű sorrendben adja vissza. [true/<b>false</b>]" "Az eredményeket véletlenszerű sorrendben adja vissza. [true/<b>false</b>]"
#: .\cookbook\views\api.py:755 #: .\cookbook\views\api.py:763
msgid "Returns new results first in search results. [true/<b>false</b>]" msgid "Returns new results first in search results. [true/<b>false</b>]"
msgstr "" msgstr ""
"Az új találatokat adja vissza először a keresési eredmények között. [true/" "Az új találatokat adja vissza először a keresési eredmények között. [true/"
"<b>false</b>]" "<b>false</b>]"
#: .\cookbook\views\api.py:757 #: .\cookbook\views\api.py:765
msgid "" msgid ""
"Filter recipes cooked X times or more. Negative values returns cooked less " "Filter recipes cooked X times or more. Negative values returns cooked less "
"than X times" "than X times"
msgstr "" msgstr ""
#: .\cookbook\views\api.py:759 #: .\cookbook\views\api.py:767
msgid "" msgid ""
"Filter recipes last cooked on or after YYYY-MM-DD. Prepending - filters on " "Filter recipes last cooked on or after YYYY-MM-DD. Prepending - filters on "
"or before date." "or before date."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:761 #: .\cookbook\views\api.py:769
msgid "" msgid ""
"Filter recipes created on or after YYYY-MM-DD. Prepending - filters on or " "Filter recipes created on or after YYYY-MM-DD. Prepending - filters on or "
"before date." "before date."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:763 #: .\cookbook\views\api.py:771
msgid "" msgid ""
"Filter recipes updated on or after YYYY-MM-DD. Prepending - filters on or " "Filter recipes updated on or after YYYY-MM-DD. Prepending - filters on or "
"before date." "before date."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:765 #: .\cookbook\views\api.py:773
msgid "" msgid ""
"Filter recipes lasts viewed on or after YYYY-MM-DD. Prepending - filters on " "Filter recipes lasts viewed on or after YYYY-MM-DD. Prepending - filters on "
"or before date." "or before date."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:767 #: .\cookbook\views\api.py:775
#, fuzzy #, fuzzy
#| msgid "If only internal recipes should be returned. [true/<b>false</b>]" #| msgid "If only internal recipes should be returned. [true/<b>false</b>]"
msgid "Filter recipes that can be made with OnHand food. [true/<b>false</b>]" msgid "Filter recipes that can be made with OnHand food. [true/<b>false</b>]"
msgstr "Ha csak a belső recepteket kell visszaadni. [true/<b>false</b>]" msgstr "Ha csak a belső recepteket kell visszaadni. [true/<b>false</b>]"
#: .\cookbook\views\api.py:929 #: .\cookbook\views\api.py:937
msgid "" msgid ""
"Returns the shopping list entry with a primary key of id. Multiple values " "Returns the shopping list entry with a primary key of id. Multiple values "
"allowed." "allowed."
@ -2801,7 +2793,7 @@ msgstr ""
"Visszaadja az id elsődleges kulccsal rendelkező bevásárlólista-bejegyzést. " "Visszaadja az id elsődleges kulccsal rendelkező bevásárlólista-bejegyzést. "
"Több érték megengedett." "Több érték megengedett."
#: .\cookbook\views\api.py:934 #: .\cookbook\views\api.py:942
msgid "" msgid ""
"Filter shopping list entries on checked. [true, false, both, <b>recent</b>]" "Filter shopping list entries on checked. [true, false, both, <b>recent</b>]"
"<br> - recent includes unchecked items and recently completed items." "<br> - recent includes unchecked items and recently completed items."
@ -2810,44 +2802,44 @@ msgstr ""
"mindkettő, <b>legutóbbi</b>]<br> a legutóbbi a nem bejelölt és a nemrég " "mindkettő, <b>legutóbbi</b>]<br> a legutóbbi a nem bejelölt és a nemrég "
"befejezett elemeket tartalmazza." "befejezett elemeket tartalmazza."
#: .\cookbook\views\api.py:937 #: .\cookbook\views\api.py:945
msgid "Returns the shopping list entries sorted by supermarket category order." msgid "Returns the shopping list entries sorted by supermarket category order."
msgstr "" msgstr ""
"Visszaadja a bevásárlólista bejegyzéseit szupermarket kategóriák szerinti " "Visszaadja a bevásárlólista bejegyzéseit szupermarket kategóriák szerinti "
"sorrendben." "sorrendben."
#: .\cookbook\views\api.py:1134 #: .\cookbook\views\api.py:1140
msgid "Nothing to do." msgid "Nothing to do."
msgstr "Semmi feladat." msgstr "Semmi feladat."
#: .\cookbook\views\api.py:1153 #: .\cookbook\views\api.py:1160
msgid "Invalid Url" msgid "Invalid Url"
msgstr "" msgstr ""
#: .\cookbook\views\api.py:1158 #: .\cookbook\views\api.py:1167
msgid "Connection Refused." msgid "Connection Refused."
msgstr "Kapcsolat megtagadva." msgstr "Kapcsolat megtagadva."
#: .\cookbook\views\api.py:1163 #: .\cookbook\views\api.py:1172
msgid "Bad URL Schema." msgid "Bad URL Schema."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:1170 #: .\cookbook\views\api.py:1195
#, fuzzy #, fuzzy
#| msgid "No useable data could be found." #| msgid "No useable data could be found."
msgid "No usable data could be found." msgid "No usable data could be found."
msgstr "Nem találtam használható adatokat." msgstr "Nem találtam használható adatokat."
#: .\cookbook\views\api.py:1260 .\cookbook\views\data.py:28 #: .\cookbook\views\api.py:1303 .\cookbook\views\data.py:28
#: .\cookbook\views\edit.py:120 .\cookbook\views\new.py:90 #: .\cookbook\views\edit.py:120 .\cookbook\views\new.py:90
msgid "This feature is not yet available in the hosted version of tandoor!" msgid "This feature is not yet available in the hosted version of tandoor!"
msgstr "Ez a funkció még nem érhető el a tandoor hosztolt verziójában!" msgstr "Ez a funkció még nem érhető el a tandoor hosztolt verziójában!"
#: .\cookbook\views\api.py:1282 #: .\cookbook\views\api.py:1325
msgid "Sync successful!" msgid "Sync successful!"
msgstr "Szinkronizálás sikeres!" msgstr "Szinkronizálás sikeres!"
#: .\cookbook\views\api.py:1287 #: .\cookbook\views\api.py:1330
msgid "Error synchronizing with Storage" msgid "Error synchronizing with Storage"
msgstr "Hiba szinkronizálás közben a tárolóval" msgstr "Hiba szinkronizálás közben a tárolóval"
@ -2936,6 +2928,10 @@ msgstr "Felfedezés"
msgid "Shopping List" msgid "Shopping List"
msgstr "Bevásárlólista" msgstr "Bevásárlólista"
#: .\cookbook\views\lists.py:76
msgid "Invite Links"
msgstr "Meghívó linkek"
#: .\cookbook\views\lists.py:139 #: .\cookbook\views\lists.py:139
msgid "Supermarkets" msgid "Supermarkets"
msgstr "Szupermarketek" msgstr "Szupermarketek"
@ -3044,6 +3040,9 @@ msgstr ""
"A receptmegosztó linket letiltották! További információkért kérjük, " "A receptmegosztó linket letiltották! További információkért kérjük, "
"forduljon az oldal adminisztrátorához." "forduljon az oldal adminisztrátorához."
#~ msgid "Show Links"
#~ msgstr "Linkek megjelenítése"
#~ msgid "A user is required" #~ msgid "A user is required"
#~ msgstr "Egy felhasználó szükséges" #~ msgstr "Egy felhasználó szükséges"

View File

@ -11,8 +11,8 @@ msgid ""
msgstr "" 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: 2022-06-26 12:09+0200\n" "POT-Creation-Date: 2022-07-12 19:20+0200\n"
"PO-Revision-Date: 2022-06-01 22:32+0000\n" "PO-Revision-Date: 2022-08-04 11:32+0000\n"
"Last-Translator: Oliver Cervera <olivercervera@yahoo.it>\n" "Last-Translator: Oliver Cervera <olivercervera@yahoo.it>\n"
"Language-Team: Italian <http://translate.tandoor.dev/projects/tandoor/" "Language-Team: Italian <http://translate.tandoor.dev/projects/tandoor/"
"recipes-backend/it/>\n" "recipes-backend/it/>\n"
@ -330,12 +330,16 @@ msgid ""
"Fields to search ignoring accents. Selecting this option can improve or " "Fields to search ignoring accents. Selecting this option can improve or "
"degrade search quality depending on language" "degrade search quality depending on language"
msgstr "" msgstr ""
"Campi da cercare ignorando gli accenti. A seconda alla lingua utilizzata, "
"questa opzione può migliorare o peggiorare la ricerca"
#: .\cookbook\forms.py:453 #: .\cookbook\forms.py:453
msgid "" msgid ""
"Fields to search for partial matches. (e.g. searching for 'Pie' will return " "Fields to search for partial matches. (e.g. searching for 'Pie' will return "
"'pie' and 'piece' and 'soapie')" "'pie' and 'piece' and 'soapie')"
msgstr "" msgstr ""
"Campi da cercare con corrispondenza parziale. (ad esempio, cercando \"Torta"
"\" verranno mostrati \"torta\", \"tortino\" e \"contorta\")"
#: .\cookbook\forms.py:455 #: .\cookbook\forms.py:455
msgid "" msgid ""
@ -543,7 +547,7 @@ msgstr "Hai raggiunto il numero massimo di ricette nella tua istanza."
msgid "You have more users than allowed in your space." msgid "You have more users than allowed in your space."
msgstr "Hai più utenti di quanti permessi nella tua istanza." msgstr "Hai più utenti di quanti permessi nella tua istanza."
#: .\cookbook\helper\recipe_search.py:560 #: .\cookbook\helper\recipe_search.py:565
msgid "One of queryset or hash_key must be provided" msgid "One of queryset or hash_key must be provided"
msgstr "" msgstr ""
@ -558,12 +562,12 @@ msgstr "Devi fornire almeno una ricetta o un titolo."
msgid "Could not parse template code." msgid "Could not parse template code."
msgstr "Impossibile elaborare il codice del template." msgstr "Impossibile elaborare il codice del template."
#: .\cookbook\integration\copymethat.py:42 #: .\cookbook\integration\copymethat.py:41
#: .\cookbook\integration\melarecipes.py:37 #: .\cookbook\integration\melarecipes.py:37
msgid "Favorite" msgid "Favorite"
msgstr "" msgstr ""
#: .\cookbook\integration\copymethat.py:71 #: .\cookbook\integration\copymethat.py:70
#: .\cookbook\integration\recettetek.py:54 #: .\cookbook\integration\recettetek.py:54
#: .\cookbook\integration\recipekeeper.py:63 #: .\cookbook\integration\recipekeeper.py:63
msgid "Imported from" msgid "Imported from"
@ -704,125 +708,125 @@ msgstr "Nuovo"
msgid " is part of a recipe step and cannot be deleted" msgid " is part of a recipe step and cannot be deleted"
msgstr " è parte dello step di una ricetta e non può essere eliminato" msgstr " è parte dello step di una ricetta e non può essere eliminato"
#: .\cookbook\models.py:1160 .\cookbook\templates\search_info.html:28 #: .\cookbook\models.py:1162 .\cookbook\templates\search_info.html:28
msgid "Simple" msgid "Simple"
msgstr "Semplice" msgstr "Semplice"
#: .\cookbook\models.py:1161 .\cookbook\templates\search_info.html:33 #: .\cookbook\models.py:1163 .\cookbook\templates\search_info.html:33
msgid "Phrase" msgid "Phrase"
msgstr "Frase" msgstr "Frase"
#: .\cookbook\models.py:1162 .\cookbook\templates\search_info.html:38 #: .\cookbook\models.py:1164 .\cookbook\templates\search_info.html:38
msgid "Web" msgid "Web"
msgstr "Web" msgstr "Web"
#: .\cookbook\models.py:1163 .\cookbook\templates\search_info.html:47 #: .\cookbook\models.py:1165 .\cookbook\templates\search_info.html:47
msgid "Raw" msgid "Raw"
msgstr "Raw" msgstr "Raw"
#: .\cookbook\models.py:1201 #: .\cookbook\models.py:1203
msgid "Food Alias" msgid "Food Alias"
msgstr "Alias Alimento" msgstr "Alias Alimento"
#: .\cookbook\models.py:1201 #: .\cookbook\models.py:1203
msgid "Unit Alias" msgid "Unit Alias"
msgstr "Alias Unità" msgstr "Alias Unità"
#: .\cookbook\models.py:1201 #: .\cookbook\models.py:1203
msgid "Keyword Alias" msgid "Keyword Alias"
msgstr "Alias Parola Chiave" msgstr "Alias Parola Chiave"
#: .\cookbook\models.py:1225 #: .\cookbook\models.py:1227
#: .\cookbook\templates\include\recipe_open_modal.html:7 #: .\cookbook\templates\include\recipe_open_modal.html:7
#: .\cookbook\views\delete.py:36 .\cookbook\views\edit.py:251 #: .\cookbook\views\delete.py:36 .\cookbook\views\edit.py:251
#: .\cookbook\views\new.py:48 #: .\cookbook\views\new.py:48
msgid "Recipe" msgid "Recipe"
msgstr "Ricetta" msgstr "Ricetta"
#: .\cookbook\models.py:1226 #: .\cookbook\models.py:1228
#, fuzzy #, fuzzy
#| msgid "Foods" #| msgid "Foods"
msgid "Food" msgid "Food"
msgstr "Alimenti" msgstr "Alimenti"
#: .\cookbook\models.py:1227 .\cookbook\templates\base.html:138 #: .\cookbook\models.py:1229 .\cookbook\templates\base.html:138
msgid "Keyword" msgid "Keyword"
msgstr "Parola chiave" msgstr "Parola chiave"
#: .\cookbook\serializer.py:204 #: .\cookbook\serializer.py:207
msgid "Cannot modify Space owner permission." msgid "Cannot modify Space owner permission."
msgstr "" msgstr ""
#: .\cookbook\serializer.py:273 #: .\cookbook\serializer.py:290
msgid "File uploads are not enabled for this Space." msgid "File uploads are not enabled for this Space."
msgstr "Il caricamento dei file non è abilitato in questa istanza." msgstr "Il caricamento dei file non è abilitato in questa istanza."
#: .\cookbook\serializer.py:284 #: .\cookbook\serializer.py:301
msgid "You have reached your file upload limit." msgid "You have reached your file upload limit."
msgstr "Hai raggiungo il limite per il caricamento dei file." msgstr "Hai raggiungo il limite per il caricamento dei file."
#: .\cookbook\serializer.py:1051 #: .\cookbook\serializer.py:1081
msgid "Hello" msgid "Hello"
msgstr "Ciao" msgstr "Ciao"
#: .\cookbook\serializer.py:1051 #: .\cookbook\serializer.py:1081
msgid "You have been invited by " msgid "You have been invited by "
msgstr "Sei stato invitato da " msgstr "Sei stato invitato da "
#: .\cookbook\serializer.py:1052 #: .\cookbook\serializer.py:1082
msgid " to join their Tandoor Recipes space " msgid " to join their Tandoor Recipes space "
msgstr " per entrare nella sua istanza di Tandoor Recipes " msgstr " per entrare nella sua istanza di Tandoor Recipes "
#: .\cookbook\serializer.py:1053 #: .\cookbook\serializer.py:1083
msgid "Click the following link to activate your account: " msgid "Click the following link to activate your account: "
msgstr "Clicca il link qui di seguito per attivare il tuo account: " msgstr "Clicca il link qui di seguito per attivare il tuo account: "
#: .\cookbook\serializer.py:1054 #: .\cookbook\serializer.py:1084
msgid "" msgid ""
"If the link does not work use the following code to manually join the space: " "If the link does not work use the following code to manually join the space: "
msgstr "" msgstr ""
"Se il link non funziona, usa il seguente codice per entrare manualmente " "Se il link non funziona, usa il seguente codice per entrare manualmente "
"nell'istanza: " "nell'istanza: "
#: .\cookbook\serializer.py:1055 #: .\cookbook\serializer.py:1085
msgid "The invitation is valid until " msgid "The invitation is valid until "
msgstr "L'invito è valido fino al " msgstr "L'invito è valido fino al "
#: .\cookbook\serializer.py:1056 #: .\cookbook\serializer.py:1086
msgid "" msgid ""
"Tandoor Recipes is an Open Source recipe manager. Check it out on GitHub " "Tandoor Recipes is an Open Source recipe manager. Check it out on GitHub "
msgstr "" msgstr ""
"Tandoor Recipes è un gestore di ricette Open Source. Dagli una occhiata su " "Tandoor Recipes è un gestore di ricette Open Source. Dagli una occhiata su "
"GitHub " "GitHub "
#: .\cookbook\serializer.py:1059 #: .\cookbook\serializer.py:1089
msgid "Tandoor Recipes Invite" msgid "Tandoor Recipes Invite"
msgstr "Invito per Tandoor Recipes" msgstr "Invito per Tandoor Recipes"
#: .\cookbook\serializer.py:1179 #: .\cookbook\serializer.py:1209
msgid "Existing shopping list to update" msgid "Existing shopping list to update"
msgstr "" msgstr ""
#: .\cookbook\serializer.py:1181 #: .\cookbook\serializer.py:1211
msgid "" msgid ""
"List of ingredient IDs from the recipe to add, if not provided all " "List of ingredient IDs from the recipe to add, if not provided all "
"ingredients will be added." "ingredients will be added."
msgstr "" msgstr ""
#: .\cookbook\serializer.py:1183 #: .\cookbook\serializer.py:1213
msgid "" msgid ""
"Providing a list_recipe ID and servings of 0 will delete that shopping list." "Providing a list_recipe ID and servings of 0 will delete that shopping list."
msgstr "" msgstr ""
#: .\cookbook\serializer.py:1192 #: .\cookbook\serializer.py:1222
msgid "Amount of food to add to the shopping list" msgid "Amount of food to add to the shopping list"
msgstr "" msgstr ""
#: .\cookbook\serializer.py:1194 #: .\cookbook\serializer.py:1224
msgid "ID of unit to use for the shopping list" msgid "ID of unit to use for the shopping list"
msgstr "" msgstr ""
#: .\cookbook\serializer.py:1196 #: .\cookbook\serializer.py:1226
msgid "When set to true will delete all food from active shopping lists." msgid "When set to true will delete all food from active shopping lists."
msgstr "" msgstr ""
@ -963,7 +967,7 @@ msgstr ""
"a>." "a>."
#: .\cookbook\templates\account\login.html:8 #: .\cookbook\templates\account\login.html:8
#: .\cookbook\templates\base.html:339 .\cookbook\templates\openid\login.html:8 #: .\cookbook\templates\base.html:340 .\cookbook\templates\openid\login.html:8
msgid "Login" msgid "Login"
msgstr "Login" msgstr "Login"
@ -1135,7 +1139,7 @@ msgstr "Iscrizioni chiuse"
msgid "We are sorry, but the sign up is currently closed." msgid "We are sorry, but the sign up is currently closed."
msgstr "Spiacenti, al momento le iscrizioni sono chiuse." msgstr "Spiacenti, al momento le iscrizioni sono chiuse."
#: .\cookbook\templates\api_info.html:5 .\cookbook\templates\base.html:329 #: .\cookbook\templates\api_info.html:5 .\cookbook\templates\base.html:330
#: .\cookbook\templates\rest_framework\api.html:11 #: .\cookbook\templates\rest_framework\api.html:11
msgid "API Documentation" msgid "API Documentation"
msgstr "Documentazione API" msgstr "Documentazione API"
@ -1232,36 +1236,36 @@ msgstr "Amministratore"
msgid "Your Spaces" msgid "Your Spaces"
msgstr "Nessuna istanza" msgstr "Nessuna istanza"
#: .\cookbook\templates\base.html:319 #: .\cookbook\templates\base.html:320
#: .\cookbook\templates\space_overview.html:6 #: .\cookbook\templates\space_overview.html:6
msgid "Overview" msgid "Overview"
msgstr "" msgstr ""
#: .\cookbook\templates\base.html:323 #: .\cookbook\templates\base.html:324
msgid "Markdown Guide" msgid "Markdown Guide"
msgstr "Informazioni su Markdown" msgstr "Informazioni su Markdown"
#: .\cookbook\templates\base.html:325 #: .\cookbook\templates\base.html:326
msgid "GitHub" msgid "GitHub"
msgstr "GitHub" msgstr "GitHub"
#: .\cookbook\templates\base.html:327 #: .\cookbook\templates\base.html:328
msgid "Translate Tandoor" msgid "Translate Tandoor"
msgstr "Traduci Tandoor" msgstr "Traduci Tandoor"
#: .\cookbook\templates\base.html:331 #: .\cookbook\templates\base.html:332
msgid "API Browser" msgid "API Browser"
msgstr "Browser API" msgstr "Browser API"
#: .\cookbook\templates\base.html:334 #: .\cookbook\templates\base.html:335
msgid "Log out" msgid "Log out"
msgstr "Esci" msgstr "Esci"
#: .\cookbook\templates\base.html:356 #: .\cookbook\templates\base.html:357
msgid "You are using the free version of Tandor" msgid "You are using the free version of Tandor"
msgstr "" msgstr ""
#: .\cookbook\templates\base.html:357 #: .\cookbook\templates\base.html:358
msgid "Upgrade Now" msgid "Upgrade Now"
msgstr "" msgstr ""
@ -2296,19 +2300,11 @@ msgstr "Ricette senza parole chiave"
msgid "Internal Recipes" msgid "Internal Recipes"
msgstr "Ricette interne" msgstr "Ricette interne"
#: .\cookbook\templates\system.html:21 .\cookbook\views\lists.py:76 #: .\cookbook\templates\system.html:20
msgid "Invite Links"
msgstr "Link di invito"
#: .\cookbook\templates\system.html:22
msgid "Show Links"
msgstr "Mostra link"
#: .\cookbook\templates\system.html:32
msgid "System Information" msgid "System Information"
msgstr "Informazioni di sistema" msgstr "Informazioni di sistema"
#: .\cookbook\templates\system.html:34 #: .\cookbook\templates\system.html:22
msgid "" msgid ""
"\n" "\n"
" Django Recipes is an open source free software application. It can " " Django Recipes is an open source free software application. It can "
@ -2324,21 +2320,21 @@ msgstr ""
"Le ultime novità sono disponibili <a href=\"https://github.com/vabene1111/" "Le ultime novità sono disponibili <a href=\"https://github.com/vabene1111/"
"recipes/releases\">qui</a>." "recipes/releases\">qui</a>."
#: .\cookbook\templates\system.html:48 #: .\cookbook\templates\system.html:36
msgid "Media Serving" msgid "Media Serving"
msgstr "File multimediali" msgstr "File multimediali"
#: .\cookbook\templates\system.html:49 .\cookbook\templates\system.html:64 #: .\cookbook\templates\system.html:37 .\cookbook\templates\system.html:52
#: .\cookbook\templates\system.html:80 #: .\cookbook\templates\system.html:68
msgid "Warning" msgid "Warning"
msgstr "Avviso" msgstr "Avviso"
#: .\cookbook\templates\system.html:49 .\cookbook\templates\system.html:64 #: .\cookbook\templates\system.html:37 .\cookbook\templates\system.html:52
#: .\cookbook\templates\system.html:80 .\cookbook\templates\system.html:95 #: .\cookbook\templates\system.html:68 .\cookbook\templates\system.html:83
msgid "Ok" msgid "Ok"
msgstr "Ok" msgstr "Ok"
#: .\cookbook\templates\system.html:51 #: .\cookbook\templates\system.html:39
msgid "" msgid ""
"Serving media files directly using gunicorn/python is <b>not recommend</b>!\n" "Serving media files directly using gunicorn/python is <b>not recommend</b>!\n"
" Please follow the steps described\n" " Please follow the steps described\n"
@ -2353,16 +2349,16 @@ msgstr ""
"<a href=\"https://github.com/vabene1111/recipes/releases/tag/0.8.1\">qui</a> " "<a href=\"https://github.com/vabene1111/recipes/releases/tag/0.8.1\">qui</a> "
"per aggiornare la tua installazione." "per aggiornare la tua installazione."
#: .\cookbook\templates\system.html:57 .\cookbook\templates\system.html:73 #: .\cookbook\templates\system.html:45 .\cookbook\templates\system.html:61
#: .\cookbook\templates\system.html:88 .\cookbook\templates\system.html:102 #: .\cookbook\templates\system.html:76 .\cookbook\templates\system.html:90
msgid "Everything is fine!" msgid "Everything is fine!"
msgstr "È tutto ok!" msgstr "È tutto ok!"
#: .\cookbook\templates\system.html:62 #: .\cookbook\templates\system.html:50
msgid "Secret Key" msgid "Secret Key"
msgstr "Chiave segreta" msgstr "Chiave segreta"
#: .\cookbook\templates\system.html:66 #: .\cookbook\templates\system.html:54
msgid "" msgid ""
"\n" "\n"
" You do not have a <code>SECRET_KEY</code> configured in your " " You do not have a <code>SECRET_KEY</code> configured in your "
@ -2380,11 +2376,11 @@ msgstr ""
"dell'installazione che è pubblica e insicura! Sei pregato di aggiungere una\n" "dell'installazione che è pubblica e insicura! Sei pregato di aggiungere una\n"
"<code>SECRET_KEY</code> nel file di configurazione <code>.env</code>." "<code>SECRET_KEY</code> nel file di configurazione <code>.env</code>."
#: .\cookbook\templates\system.html:78 #: .\cookbook\templates\system.html:66
msgid "Debug Mode" msgid "Debug Mode"
msgstr "Modalità di debug" msgstr "Modalità di debug"
#: .\cookbook\templates\system.html:82 #: .\cookbook\templates\system.html:70
msgid "" msgid ""
"\n" "\n"
" This application is still running in debug mode. This is most " " This application is still running in debug mode. This is most "
@ -2400,15 +2396,15 @@ msgstr ""
"configurando\n" "configurando\n"
"<code>DEBUG=0</code> nel file di configurazione<code>.env</code>." "<code>DEBUG=0</code> nel file di configurazione<code>.env</code>."
#: .\cookbook\templates\system.html:93 #: .\cookbook\templates\system.html:81
msgid "Database" msgid "Database"
msgstr "Database" msgstr "Database"
#: .\cookbook\templates\system.html:95 #: .\cookbook\templates\system.html:83
msgid "Info" msgid "Info"
msgstr "Info" msgstr "Info"
#: .\cookbook\templates\system.html:97 #: .\cookbook\templates\system.html:85
msgid "" msgid ""
"\n" "\n"
" This application is not running with a Postgres database " " This application is not running with a Postgres database "
@ -2425,251 +2421,251 @@ msgstr ""
msgid "URL Import" msgid "URL Import"
msgstr "Importa da URL" msgstr "Importa da URL"
#: .\cookbook\views\api.py:97 .\cookbook\views\api.py:189 #: .\cookbook\views\api.py:105 .\cookbook\views\api.py:197
msgid "Parameter updated_at incorrectly formatted" msgid "Parameter updated_at incorrectly formatted"
msgstr "Il parametro updated_at non è formattato correttamente" msgstr "Il parametro updated_at non è formattato correttamente"
#: .\cookbook\views\api.py:209 .\cookbook\views\api.py:312 #: .\cookbook\views\api.py:217 .\cookbook\views\api.py:320
msgid "No {self.basename} with id {pk} exists" msgid "No {self.basename} with id {pk} exists"
msgstr "Non esiste nessun {self.basename} con id {pk}" msgstr "Non esiste nessun {self.basename} con id {pk}"
#: .\cookbook\views\api.py:213 #: .\cookbook\views\api.py:221
msgid "Cannot merge with the same object!" msgid "Cannot merge with the same object!"
msgstr "Non è possibile unirlo con lo stesso oggetto!" msgstr "Non è possibile unirlo con lo stesso oggetto!"
#: .\cookbook\views\api.py:220 #: .\cookbook\views\api.py:228
msgid "No {self.basename} with id {target} exists" msgid "No {self.basename} with id {target} exists"
msgstr "Non esiste nessun {self.basename} con id {target}" msgstr "Non esiste nessun {self.basename} con id {target}"
#: .\cookbook\views\api.py:225 #: .\cookbook\views\api.py:233
msgid "Cannot merge with child object!" msgid "Cannot merge with child object!"
msgstr "Non è possibile unirlo con un oggetto secondario!" msgstr "Non è possibile unirlo con un oggetto secondario!"
#: .\cookbook\views\api.py:258 #: .\cookbook\views\api.py:266
msgid "{source.name} was merged successfully with {target.name}" msgid "{source.name} was merged successfully with {target.name}"
msgstr "{source.name} è stato unito con successo a {target.name}" msgstr "{source.name} è stato unito con successo a {target.name}"
#: .\cookbook\views\api.py:263 #: .\cookbook\views\api.py:271
msgid "An error occurred attempting to merge {source.name} with {target.name}" msgid "An error occurred attempting to merge {source.name} with {target.name}"
msgstr "" msgstr ""
"Si è verificato un errore durante l'unione di {source.name} con {target.name}" "Si è verificato un errore durante l'unione di {source.name} con {target.name}"
#: .\cookbook\views\api.py:321 #: .\cookbook\views\api.py:329
msgid "{child.name} was moved successfully to the root." msgid "{child.name} was moved successfully to the root."
msgstr "{child.name} è stato spostato con successo alla radice." msgstr "{child.name} è stato spostato con successo alla radice."
#: .\cookbook\views\api.py:324 .\cookbook\views\api.py:342 #: .\cookbook\views\api.py:332 .\cookbook\views\api.py:350
msgid "An error occurred attempting to move " msgid "An error occurred attempting to move "
msgstr "Si è verificato un errore durante lo spostamento " msgstr "Si è verificato un errore durante lo spostamento "
#: .\cookbook\views\api.py:327 #: .\cookbook\views\api.py:335
msgid "Cannot move an object to itself!" msgid "Cannot move an object to itself!"
msgstr "Non è possibile muovere un oggetto a sé stesso!" msgstr "Non è possibile muovere un oggetto a sé stesso!"
#: .\cookbook\views\api.py:333 #: .\cookbook\views\api.py:341
msgid "No {self.basename} with id {parent} exists" msgid "No {self.basename} with id {parent} exists"
msgstr "Non esiste nessun {self.basename} con id {parent}" msgstr "Non esiste nessun {self.basename} con id {parent}"
#: .\cookbook\views\api.py:339 #: .\cookbook\views\api.py:347
msgid "{child.name} was moved successfully to parent {parent.name}" msgid "{child.name} was moved successfully to parent {parent.name}"
msgstr "{child.name} è stato spostato con successo al primario {parent.name}" msgstr "{child.name} è stato spostato con successo al primario {parent.name}"
#: .\cookbook\views\api.py:534 #: .\cookbook\views\api.py:542
msgid "{obj.name} was removed from the shopping list." msgid "{obj.name} was removed from the shopping list."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:539 .\cookbook\views\api.py:871 #: .\cookbook\views\api.py:547 .\cookbook\views\api.py:879
#: .\cookbook\views\api.py:884 #: .\cookbook\views\api.py:892
msgid "{obj.name} was added to the shopping list." msgid "{obj.name} was added to the shopping list."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:666 #: .\cookbook\views\api.py:674
msgid "ID of recipe a step is part of. For multiple repeat parameter." msgid "ID of recipe a step is part of. For multiple repeat parameter."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:668 #: .\cookbook\views\api.py:676
msgid "Query string matched (fuzzy) against object name." msgid "Query string matched (fuzzy) against object name."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:712 #: .\cookbook\views\api.py:720
msgid "" msgid ""
"Query string matched (fuzzy) against recipe name. In the future also " "Query string matched (fuzzy) against recipe name. In the future also "
"fulltext search." "fulltext search."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:714 #: .\cookbook\views\api.py:722
msgid "" msgid ""
"ID of keyword a recipe should have. For multiple repeat parameter. " "ID of keyword a recipe should have. For multiple repeat parameter. "
"Equivalent to keywords_or" "Equivalent to keywords_or"
msgstr "" msgstr ""
#: .\cookbook\views\api.py:717 #: .\cookbook\views\api.py:725
msgid "" msgid ""
"Keyword IDs, repeat for multiple. Return recipes with any of the keywords" "Keyword IDs, repeat for multiple. Return recipes with any of the keywords"
msgstr "" msgstr ""
#: .\cookbook\views\api.py:720 #: .\cookbook\views\api.py:728
msgid "" msgid ""
"Keyword IDs, repeat for multiple. Return recipes with all of the keywords." "Keyword IDs, repeat for multiple. Return recipes with all of the keywords."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:723 #: .\cookbook\views\api.py:731
msgid "" msgid ""
"Keyword IDs, repeat for multiple. Exclude recipes with any of the keywords." "Keyword IDs, repeat for multiple. Exclude recipes with any of the keywords."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:726 #: .\cookbook\views\api.py:734
msgid "" msgid ""
"Keyword IDs, repeat for multiple. Exclude recipes with all of the keywords." "Keyword IDs, repeat for multiple. Exclude recipes with all of the keywords."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:728 #: .\cookbook\views\api.py:736
msgid "ID of food a recipe should have. For multiple repeat parameter." msgid "ID of food a recipe should have. For multiple repeat parameter."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:731 #: .\cookbook\views\api.py:739
msgid "Food IDs, repeat for multiple. Return recipes with any of the foods" msgid "Food IDs, repeat for multiple. Return recipes with any of the foods"
msgstr "" msgstr ""
#: .\cookbook\views\api.py:733 #: .\cookbook\views\api.py:741
msgid "Food IDs, repeat for multiple. Return recipes with all of the foods." msgid "Food IDs, repeat for multiple. Return recipes with all of the foods."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:735 #: .\cookbook\views\api.py:743
msgid "Food IDs, repeat for multiple. Exclude recipes with any of the foods." msgid "Food IDs, repeat for multiple. Exclude recipes with any of the foods."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:737 #: .\cookbook\views\api.py:745
msgid "Food IDs, repeat for multiple. Exclude recipes with all of the foods." msgid "Food IDs, repeat for multiple. Exclude recipes with all of the foods."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:738 #: .\cookbook\views\api.py:746
msgid "ID of unit a recipe should have." msgid "ID of unit a recipe should have."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:740 #: .\cookbook\views\api.py:748
msgid "" msgid ""
"Rating a recipe should have or greater. [0 - 5] Negative value filters " "Rating a recipe should have or greater. [0 - 5] Negative value filters "
"rating less than." "rating less than."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:741 #: .\cookbook\views\api.py:749
msgid "ID of book a recipe should be in. For multiple repeat parameter." msgid "ID of book a recipe should be in. For multiple repeat parameter."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:743 #: .\cookbook\views\api.py:751
msgid "Book IDs, repeat for multiple. Return recipes with any of the books" msgid "Book IDs, repeat for multiple. Return recipes with any of the books"
msgstr "" msgstr ""
#: .\cookbook\views\api.py:745 #: .\cookbook\views\api.py:753
msgid "Book IDs, repeat for multiple. Return recipes with all of the books." msgid "Book IDs, repeat for multiple. Return recipes with all of the books."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:747 #: .\cookbook\views\api.py:755
msgid "Book IDs, repeat for multiple. Exclude recipes with any of the books." msgid "Book IDs, repeat for multiple. Exclude recipes with any of the books."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:749 #: .\cookbook\views\api.py:757
msgid "Book IDs, repeat for multiple. Exclude recipes with all of the books." msgid "Book IDs, repeat for multiple. Exclude recipes with all of the books."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:751 #: .\cookbook\views\api.py:759
msgid "If only internal recipes should be returned. [true/<b>false</b>]" msgid "If only internal recipes should be returned. [true/<b>false</b>]"
msgstr "" msgstr ""
#: .\cookbook\views\api.py:753 #: .\cookbook\views\api.py:761
msgid "Returns the results in randomized order. [true/<b>false</b>]" msgid "Returns the results in randomized order. [true/<b>false</b>]"
msgstr "" msgstr ""
#: .\cookbook\views\api.py:755 #: .\cookbook\views\api.py:763
msgid "Returns new results first in search results. [true/<b>false</b>]" msgid "Returns new results first in search results. [true/<b>false</b>]"
msgstr "" msgstr ""
#: .\cookbook\views\api.py:757 #: .\cookbook\views\api.py:765
msgid "" msgid ""
"Filter recipes cooked X times or more. Negative values returns cooked less " "Filter recipes cooked X times or more. Negative values returns cooked less "
"than X times" "than X times"
msgstr "" msgstr ""
#: .\cookbook\views\api.py:759 #: .\cookbook\views\api.py:767
msgid "" msgid ""
"Filter recipes last cooked on or after YYYY-MM-DD. Prepending - filters on " "Filter recipes last cooked on or after YYYY-MM-DD. Prepending - filters on "
"or before date." "or before date."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:761 #: .\cookbook\views\api.py:769
msgid "" msgid ""
"Filter recipes created on or after YYYY-MM-DD. Prepending - filters on or " "Filter recipes created on or after YYYY-MM-DD. Prepending - filters on or "
"before date." "before date."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:763 #: .\cookbook\views\api.py:771
msgid "" msgid ""
"Filter recipes updated on or after YYYY-MM-DD. Prepending - filters on or " "Filter recipes updated on or after YYYY-MM-DD. Prepending - filters on or "
"before date." "before date."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:765 #: .\cookbook\views\api.py:773
msgid "" msgid ""
"Filter recipes lasts viewed on or after YYYY-MM-DD. Prepending - filters on " "Filter recipes lasts viewed on or after YYYY-MM-DD. Prepending - filters on "
"or before date." "or before date."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:767 #: .\cookbook\views\api.py:775
msgid "Filter recipes that can be made with OnHand food. [true/<b>false</b>]" msgid "Filter recipes that can be made with OnHand food. [true/<b>false</b>]"
msgstr "" msgstr ""
"Filtra le ricette che possono essere preparate con alimenti già disponibili. " "Filtra le ricette che possono essere preparate con alimenti già disponibili. "
"[true/<b>false</b>]" "[true/<b>false</b>]"
#: .\cookbook\views\api.py:929 #: .\cookbook\views\api.py:937
msgid "" msgid ""
"Returns the shopping list entry with a primary key of id. Multiple values " "Returns the shopping list entry with a primary key of id. Multiple values "
"allowed." "allowed."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:934 #: .\cookbook\views\api.py:942
msgid "" msgid ""
"Filter shopping list entries on checked. [true, false, both, <b>recent</b>]" "Filter shopping list entries on checked. [true, false, both, <b>recent</b>]"
"<br> - recent includes unchecked items and recently completed items." "<br> - recent includes unchecked items and recently completed items."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:937 #: .\cookbook\views\api.py:945
msgid "Returns the shopping list entries sorted by supermarket category order." msgid "Returns the shopping list entries sorted by supermarket category order."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:1134 #: .\cookbook\views\api.py:1140
msgid "Nothing to do." msgid "Nothing to do."
msgstr "Nulla da fare." msgstr "Nulla da fare."
#: .\cookbook\views\api.py:1153 #: .\cookbook\views\api.py:1160
msgid "Invalid Url" msgid "Invalid Url"
msgstr "" msgstr ""
#: .\cookbook\views\api.py:1158 #: .\cookbook\views\api.py:1167
msgid "Connection Refused." msgid "Connection Refused."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:1163 #: .\cookbook\views\api.py:1172
msgid "Bad URL Schema." msgid "Bad URL Schema."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:1170 #: .\cookbook\views\api.py:1195
#, fuzzy #, fuzzy
#| msgid "No useable data could be found." #| msgid "No useable data could be found."
msgid "No usable data could be found." msgid "No usable data could be found."
msgstr "Nessuna informazione utilizzabile è stata trovata." msgstr "Nessuna informazione utilizzabile è stata trovata."
#: .\cookbook\views\api.py:1260 .\cookbook\views\data.py:28 #: .\cookbook\views\api.py:1303 .\cookbook\views\data.py:28
#: .\cookbook\views\edit.py:120 .\cookbook\views\new.py:90 #: .\cookbook\views\edit.py:120 .\cookbook\views\new.py:90
msgid "This feature is not yet available in the hosted version of tandoor!" msgid "This feature is not yet available in the hosted version of tandoor!"
msgstr "" msgstr ""
"Questa funzione non è ancora disponibile nella versione hostata di Tandor!" "Questa funzione non è ancora disponibile nella versione hostata di Tandor!"
#: .\cookbook\views\api.py:1282 #: .\cookbook\views\api.py:1325
msgid "Sync successful!" msgid "Sync successful!"
msgstr "Sincronizzazione completata con successo!" msgstr "Sincronizzazione completata con successo!"
#: .\cookbook\views\api.py:1287 #: .\cookbook\views\api.py:1330
msgid "Error synchronizing with Storage" msgid "Error synchronizing with Storage"
msgstr "Errore di sincronizzazione con questo backend" msgstr "Errore di sincronizzazione con questo backend"
@ -2759,6 +2755,10 @@ msgstr "Trovate"
msgid "Shopping List" msgid "Shopping List"
msgstr "Lista della spesa" msgstr "Lista della spesa"
#: .\cookbook\views\lists.py:76
msgid "Invite Links"
msgstr "Link di invito"
#: .\cookbook\views\lists.py:139 #: .\cookbook\views\lists.py:139
msgid "Supermarkets" msgid "Supermarkets"
msgstr "Supermercati" msgstr "Supermercati"
@ -2867,6 +2867,9 @@ msgstr ""
"Il link per la condivisione delle ricette è stato disabilitato! Per maggiori " "Il link per la condivisione delle ricette è stato disabilitato! Per maggiori "
"informazioni contatta l'amministratore." "informazioni contatta l'amministratore."
#~ msgid "Show Links"
#~ msgstr "Mostra link"
#~ msgid "Invite User" #~ msgid "Invite User"
#~ msgstr "Invita utente" #~ msgstr "Invita utente"

View File

@ -11,7 +11,7 @@ msgid ""
msgstr "" 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: 2022-06-26 12:09+0200\n" "POT-Creation-Date: 2022-07-12 19:20+0200\n"
"PO-Revision-Date: 2020-06-02 19:28+0000\n" "PO-Revision-Date: 2020-06-02 19:28+0000\n"
"Last-Translator: vabene1111 <vabene1234@googlemail.com>, 2021\n" "Last-Translator: vabene1111 <vabene1234@googlemail.com>, 2021\n"
"Language-Team: Latvian (https://www.transifex.com/django-recipes/" "Language-Team: Latvian (https://www.transifex.com/django-recipes/"
@ -525,7 +525,7 @@ msgstr ""
msgid "You have more users than allowed in your space." msgid "You have more users than allowed in your space."
msgstr "" msgstr ""
#: .\cookbook\helper\recipe_search.py:560 #: .\cookbook\helper\recipe_search.py:565
msgid "One of queryset or hash_key must be provided" msgid "One of queryset or hash_key must be provided"
msgstr "" msgstr ""
@ -540,12 +540,12 @@ msgstr "Jums jānorāda vismaz recepte vai nosaukums."
msgid "Could not parse template code." msgid "Could not parse template code."
msgstr "" msgstr ""
#: .\cookbook\integration\copymethat.py:42 #: .\cookbook\integration\copymethat.py:41
#: .\cookbook\integration\melarecipes.py:37 #: .\cookbook\integration\melarecipes.py:37
msgid "Favorite" msgid "Favorite"
msgstr "" msgstr ""
#: .\cookbook\integration\copymethat.py:71 #: .\cookbook\integration\copymethat.py:70
#: .\cookbook\integration\recettetek.py:54 #: .\cookbook\integration\recettetek.py:54
#: .\cookbook\integration\recipekeeper.py:63 #: .\cookbook\integration\recipekeeper.py:63
msgid "Imported from" msgid "Imported from"
@ -681,127 +681,127 @@ msgstr "Jauns"
msgid " is part of a recipe step and cannot be deleted" msgid " is part of a recipe step and cannot be deleted"
msgstr "" msgstr ""
#: .\cookbook\models.py:1160 .\cookbook\templates\search_info.html:28 #: .\cookbook\models.py:1162 .\cookbook\templates\search_info.html:28
msgid "Simple" msgid "Simple"
msgstr "" msgstr ""
#: .\cookbook\models.py:1161 .\cookbook\templates\search_info.html:33 #: .\cookbook\models.py:1163 .\cookbook\templates\search_info.html:33
msgid "Phrase" msgid "Phrase"
msgstr "" msgstr ""
#: .\cookbook\models.py:1162 .\cookbook\templates\search_info.html:38 #: .\cookbook\models.py:1164 .\cookbook\templates\search_info.html:38
msgid "Web" msgid "Web"
msgstr "" msgstr ""
#: .\cookbook\models.py:1163 .\cookbook\templates\search_info.html:47 #: .\cookbook\models.py:1165 .\cookbook\templates\search_info.html:47
msgid "Raw" msgid "Raw"
msgstr "" msgstr ""
#: .\cookbook\models.py:1201 #: .\cookbook\models.py:1203
#, fuzzy #, fuzzy
#| msgid "Food" #| msgid "Food"
msgid "Food Alias" msgid "Food Alias"
msgstr "Ēdiens" msgstr "Ēdiens"
#: .\cookbook\models.py:1201 #: .\cookbook\models.py:1203
#, fuzzy #, fuzzy
#| msgid "Units" #| msgid "Units"
msgid "Unit Alias" msgid "Unit Alias"
msgstr "Vienības" msgstr "Vienības"
#: .\cookbook\models.py:1201 #: .\cookbook\models.py:1203
#, fuzzy #, fuzzy
#| msgid "Keywords" #| msgid "Keywords"
msgid "Keyword Alias" msgid "Keyword Alias"
msgstr "Atslēgvārdi" msgstr "Atslēgvārdi"
#: .\cookbook\models.py:1225 #: .\cookbook\models.py:1227
#: .\cookbook\templates\include\recipe_open_modal.html:7 #: .\cookbook\templates\include\recipe_open_modal.html:7
#: .\cookbook\views\delete.py:36 .\cookbook\views\edit.py:251 #: .\cookbook\views\delete.py:36 .\cookbook\views\edit.py:251
#: .\cookbook\views\new.py:48 #: .\cookbook\views\new.py:48
msgid "Recipe" msgid "Recipe"
msgstr "Recepte" msgstr "Recepte"
#: .\cookbook\models.py:1226 #: .\cookbook\models.py:1228
#, fuzzy #, fuzzy
#| msgid "Food" #| msgid "Food"
msgid "Food" msgid "Food"
msgstr "Ēdiens" msgstr "Ēdiens"
#: .\cookbook\models.py:1227 .\cookbook\templates\base.html:138 #: .\cookbook\models.py:1229 .\cookbook\templates\base.html:138
msgid "Keyword" msgid "Keyword"
msgstr "Atslēgvārds" msgstr "Atslēgvārds"
#: .\cookbook\serializer.py:204 #: .\cookbook\serializer.py:207
msgid "Cannot modify Space owner permission." msgid "Cannot modify Space owner permission."
msgstr "" msgstr ""
#: .\cookbook\serializer.py:273 #: .\cookbook\serializer.py:290
msgid "File uploads are not enabled for this Space." msgid "File uploads are not enabled for this Space."
msgstr "" msgstr ""
#: .\cookbook\serializer.py:284 #: .\cookbook\serializer.py:301
msgid "You have reached your file upload limit." msgid "You have reached your file upload limit."
msgstr "" msgstr ""
#: .\cookbook\serializer.py:1051 #: .\cookbook\serializer.py:1081
msgid "Hello" msgid "Hello"
msgstr "" msgstr ""
#: .\cookbook\serializer.py:1051 #: .\cookbook\serializer.py:1081
msgid "You have been invited by " msgid "You have been invited by "
msgstr "" msgstr ""
#: .\cookbook\serializer.py:1052 #: .\cookbook\serializer.py:1082
msgid " to join their Tandoor Recipes space " msgid " to join their Tandoor Recipes space "
msgstr "" msgstr ""
#: .\cookbook\serializer.py:1053 #: .\cookbook\serializer.py:1083
msgid "Click the following link to activate your account: " msgid "Click the following link to activate your account: "
msgstr "" msgstr ""
#: .\cookbook\serializer.py:1054 #: .\cookbook\serializer.py:1084
msgid "" msgid ""
"If the link does not work use the following code to manually join the space: " "If the link does not work use the following code to manually join the space: "
msgstr "" msgstr ""
#: .\cookbook\serializer.py:1055 #: .\cookbook\serializer.py:1085
msgid "The invitation is valid until " msgid "The invitation is valid until "
msgstr "" msgstr ""
#: .\cookbook\serializer.py:1056 #: .\cookbook\serializer.py:1086
msgid "" msgid ""
"Tandoor Recipes is an Open Source recipe manager. Check it out on GitHub " "Tandoor Recipes is an Open Source recipe manager. Check it out on GitHub "
msgstr "" msgstr ""
#: .\cookbook\serializer.py:1059 #: .\cookbook\serializer.py:1089
msgid "Tandoor Recipes Invite" msgid "Tandoor Recipes Invite"
msgstr "" msgstr ""
#: .\cookbook\serializer.py:1179 #: .\cookbook\serializer.py:1209
msgid "Existing shopping list to update" msgid "Existing shopping list to update"
msgstr "" msgstr ""
#: .\cookbook\serializer.py:1181 #: .\cookbook\serializer.py:1211
msgid "" msgid ""
"List of ingredient IDs from the recipe to add, if not provided all " "List of ingredient IDs from the recipe to add, if not provided all "
"ingredients will be added." "ingredients will be added."
msgstr "" msgstr ""
#: .\cookbook\serializer.py:1183 #: .\cookbook\serializer.py:1213
msgid "" msgid ""
"Providing a list_recipe ID and servings of 0 will delete that shopping list." "Providing a list_recipe ID and servings of 0 will delete that shopping list."
msgstr "" msgstr ""
#: .\cookbook\serializer.py:1192 #: .\cookbook\serializer.py:1222
msgid "Amount of food to add to the shopping list" msgid "Amount of food to add to the shopping list"
msgstr "" msgstr ""
#: .\cookbook\serializer.py:1194 #: .\cookbook\serializer.py:1224
msgid "ID of unit to use for the shopping list" msgid "ID of unit to use for the shopping list"
msgstr "" msgstr ""
#: .\cookbook\serializer.py:1196 #: .\cookbook\serializer.py:1226
msgid "When set to true will delete all food from active shopping lists." msgid "When set to true will delete all food from active shopping lists."
msgstr "" msgstr ""
@ -937,7 +937,7 @@ msgid ""
msgstr "" msgstr ""
#: .\cookbook\templates\account\login.html:8 #: .\cookbook\templates\account\login.html:8
#: .\cookbook\templates\base.html:339 .\cookbook\templates\openid\login.html:8 #: .\cookbook\templates\base.html:340 .\cookbook\templates\openid\login.html:8
msgid "Login" msgid "Login"
msgstr "Pieslēgties" msgstr "Pieslēgties"
@ -1111,7 +1111,7 @@ msgstr ""
msgid "We are sorry, but the sign up is currently closed." msgid "We are sorry, but the sign up is currently closed."
msgstr "" msgstr ""
#: .\cookbook\templates\api_info.html:5 .\cookbook\templates\base.html:329 #: .\cookbook\templates\api_info.html:5 .\cookbook\templates\base.html:330
#: .\cookbook\templates\rest_framework\api.html:11 #: .\cookbook\templates\rest_framework\api.html:11
msgid "API Documentation" msgid "API Documentation"
msgstr "API dokumentācija" msgstr "API dokumentācija"
@ -1218,36 +1218,36 @@ msgstr "Administrators"
msgid "Your Spaces" msgid "Your Spaces"
msgstr "Izveidot lietotāju" msgstr "Izveidot lietotāju"
#: .\cookbook\templates\base.html:319 #: .\cookbook\templates\base.html:320
#: .\cookbook\templates\space_overview.html:6 #: .\cookbook\templates\space_overview.html:6
msgid "Overview" msgid "Overview"
msgstr "" msgstr ""
#: .\cookbook\templates\base.html:323 #: .\cookbook\templates\base.html:324
msgid "Markdown Guide" msgid "Markdown Guide"
msgstr "Markdown rokasgrāmata" msgstr "Markdown rokasgrāmata"
#: .\cookbook\templates\base.html:325 #: .\cookbook\templates\base.html:326
msgid "GitHub" msgid "GitHub"
msgstr "Github" msgstr "Github"
#: .\cookbook\templates\base.html:327 #: .\cookbook\templates\base.html:328
msgid "Translate Tandoor" msgid "Translate Tandoor"
msgstr "" msgstr ""
#: .\cookbook\templates\base.html:331 #: .\cookbook\templates\base.html:332
msgid "API Browser" msgid "API Browser"
msgstr "API pārlūks" msgstr "API pārlūks"
#: .\cookbook\templates\base.html:334 #: .\cookbook\templates\base.html:335
msgid "Log out" msgid "Log out"
msgstr "" msgstr ""
#: .\cookbook\templates\base.html:356 #: .\cookbook\templates\base.html:357
msgid "You are using the free version of Tandor" msgid "You are using the free version of Tandor"
msgstr "" msgstr ""
#: .\cookbook\templates\base.html:357 #: .\cookbook\templates\base.html:358
msgid "Upgrade Now" msgid "Upgrade Now"
msgstr "" msgstr ""
@ -2273,19 +2273,11 @@ msgstr "Receptes bez atslēgas vārdiem"
msgid "Internal Recipes" msgid "Internal Recipes"
msgstr "Iekšējās receptes" msgstr "Iekšējās receptes"
#: .\cookbook\templates\system.html:21 .\cookbook\views\lists.py:76 #: .\cookbook\templates\system.html:20
msgid "Invite Links"
msgstr "Uzaicinājuma saites"
#: .\cookbook\templates\system.html:22
msgid "Show Links"
msgstr "Rādīt saites"
#: .\cookbook\templates\system.html:32
msgid "System Information" msgid "System Information"
msgstr "Sistēmas informācija" msgstr "Sistēmas informācija"
#: .\cookbook\templates\system.html:34 #: .\cookbook\templates\system.html:22
msgid "" msgid ""
"\n" "\n"
" Django Recipes is an open source free software application. It can " " Django Recipes is an open source free software application. It can "
@ -2303,21 +2295,21 @@ msgstr ""
"recipes/releases\">šeit</a>.\n" "recipes/releases\">šeit</a>.\n"
" " " "
#: .\cookbook\templates\system.html:48 #: .\cookbook\templates\system.html:36
msgid "Media Serving" msgid "Media Serving"
msgstr "Multivides rādīšana" msgstr "Multivides rādīšana"
#: .\cookbook\templates\system.html:49 .\cookbook\templates\system.html:64 #: .\cookbook\templates\system.html:37 .\cookbook\templates\system.html:52
#: .\cookbook\templates\system.html:80 #: .\cookbook\templates\system.html:68
msgid "Warning" msgid "Warning"
msgstr "Brīdinājums" msgstr "Brīdinājums"
#: .\cookbook\templates\system.html:49 .\cookbook\templates\system.html:64 #: .\cookbook\templates\system.html:37 .\cookbook\templates\system.html:52
#: .\cookbook\templates\system.html:80 .\cookbook\templates\system.html:95 #: .\cookbook\templates\system.html:68 .\cookbook\templates\system.html:83
msgid "Ok" msgid "Ok"
msgstr "Ok" msgstr "Ok"
#: .\cookbook\templates\system.html:51 #: .\cookbook\templates\system.html:39
msgid "" msgid ""
"Serving media files directly using gunicorn/python is <b>not recommend</b>!\n" "Serving media files directly using gunicorn/python is <b>not recommend</b>!\n"
" Please follow the steps described\n" " Please follow the steps described\n"
@ -2334,16 +2326,16 @@ msgstr ""
" jūsu instalāciju.\n" " jūsu instalāciju.\n"
" " " "
#: .\cookbook\templates\system.html:57 .\cookbook\templates\system.html:73 #: .\cookbook\templates\system.html:45 .\cookbook\templates\system.html:61
#: .\cookbook\templates\system.html:88 .\cookbook\templates\system.html:102 #: .\cookbook\templates\system.html:76 .\cookbook\templates\system.html:90
msgid "Everything is fine!" msgid "Everything is fine!"
msgstr "Viss ir kārtībā!" msgstr "Viss ir kārtībā!"
#: .\cookbook\templates\system.html:62 #: .\cookbook\templates\system.html:50
msgid "Secret Key" msgid "Secret Key"
msgstr "Slepenā atslēga" msgstr "Slepenā atslēga"
#: .\cookbook\templates\system.html:66 #: .\cookbook\templates\system.html:54
msgid "" msgid ""
"\n" "\n"
" You do not have a <code>SECRET_KEY</code> configured in your " " You do not have a <code>SECRET_KEY</code> configured in your "
@ -2365,11 +2357,11 @@ msgstr ""
"code>.\n" "code>.\n"
" " " "
#: .\cookbook\templates\system.html:78 #: .\cookbook\templates\system.html:66
msgid "Debug Mode" msgid "Debug Mode"
msgstr "Atkļūdošanas režīms" msgstr "Atkļūdošanas režīms"
#: .\cookbook\templates\system.html:82 #: .\cookbook\templates\system.html:70
msgid "" msgid ""
"\n" "\n"
" This application is still running in debug mode. This is most " " This application is still running in debug mode. This is most "
@ -2386,15 +2378,15 @@ msgstr ""
" <code>DEBUG = 0</code> konfigurācijas failā <code>.env</code>.\n" " <code>DEBUG = 0</code> konfigurācijas failā <code>.env</code>.\n"
" " " "
#: .\cookbook\templates\system.html:93 #: .\cookbook\templates\system.html:81
msgid "Database" msgid "Database"
msgstr "Datubāze" msgstr "Datubāze"
#: .\cookbook\templates\system.html:95 #: .\cookbook\templates\system.html:83
msgid "Info" msgid "Info"
msgstr "Info" msgstr "Info"
#: .\cookbook\templates\system.html:97 #: .\cookbook\templates\system.html:85
msgid "" msgid ""
"\n" "\n"
" This application is not running with a Postgres database " " This application is not running with a Postgres database "
@ -2412,249 +2404,249 @@ msgstr ""
msgid "URL Import" msgid "URL Import"
msgstr "URL importēšana" msgstr "URL importēšana"
#: .\cookbook\views\api.py:97 .\cookbook\views\api.py:189 #: .\cookbook\views\api.py:105 .\cookbook\views\api.py:197
#, fuzzy #, fuzzy
#| msgid "Parameter filter_list incorrectly formatted" #| msgid "Parameter filter_list incorrectly formatted"
msgid "Parameter updated_at incorrectly formatted" msgid "Parameter updated_at incorrectly formatted"
msgstr "Parametrs filter_list ir nepareizi formatēts" msgstr "Parametrs filter_list ir nepareizi formatēts"
#: .\cookbook\views\api.py:209 .\cookbook\views\api.py:312 #: .\cookbook\views\api.py:217 .\cookbook\views\api.py:320
msgid "No {self.basename} with id {pk} exists" msgid "No {self.basename} with id {pk} exists"
msgstr "" msgstr ""
#: .\cookbook\views\api.py:213 #: .\cookbook\views\api.py:221
msgid "Cannot merge with the same object!" msgid "Cannot merge with the same object!"
msgstr "" msgstr ""
#: .\cookbook\views\api.py:220 #: .\cookbook\views\api.py:228
msgid "No {self.basename} with id {target} exists" msgid "No {self.basename} with id {target} exists"
msgstr "" msgstr ""
#: .\cookbook\views\api.py:225 #: .\cookbook\views\api.py:233
msgid "Cannot merge with child object!" msgid "Cannot merge with child object!"
msgstr "" msgstr ""
#: .\cookbook\views\api.py:258 #: .\cookbook\views\api.py:266
msgid "{source.name} was merged successfully with {target.name}" msgid "{source.name} was merged successfully with {target.name}"
msgstr "" msgstr ""
#: .\cookbook\views\api.py:263 #: .\cookbook\views\api.py:271
msgid "An error occurred attempting to merge {source.name} with {target.name}" msgid "An error occurred attempting to merge {source.name} with {target.name}"
msgstr "" msgstr ""
#: .\cookbook\views\api.py:321 #: .\cookbook\views\api.py:329
msgid "{child.name} was moved successfully to the root." msgid "{child.name} was moved successfully to the root."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:324 .\cookbook\views\api.py:342 #: .\cookbook\views\api.py:332 .\cookbook\views\api.py:350
msgid "An error occurred attempting to move " msgid "An error occurred attempting to move "
msgstr "" msgstr ""
#: .\cookbook\views\api.py:327 #: .\cookbook\views\api.py:335
msgid "Cannot move an object to itself!" msgid "Cannot move an object to itself!"
msgstr "" msgstr ""
#: .\cookbook\views\api.py:333 #: .\cookbook\views\api.py:341
msgid "No {self.basename} with id {parent} exists" msgid "No {self.basename} with id {parent} exists"
msgstr "" msgstr ""
#: .\cookbook\views\api.py:339 #: .\cookbook\views\api.py:347
msgid "{child.name} was moved successfully to parent {parent.name}" msgid "{child.name} was moved successfully to parent {parent.name}"
msgstr "" msgstr ""
#: .\cookbook\views\api.py:534 #: .\cookbook\views\api.py:542
msgid "{obj.name} was removed from the shopping list." msgid "{obj.name} was removed from the shopping list."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:539 .\cookbook\views\api.py:871 #: .\cookbook\views\api.py:547 .\cookbook\views\api.py:879
#: .\cookbook\views\api.py:884 #: .\cookbook\views\api.py:892
msgid "{obj.name} was added to the shopping list." msgid "{obj.name} was added to the shopping list."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:666 #: .\cookbook\views\api.py:674
msgid "ID of recipe a step is part of. For multiple repeat parameter." msgid "ID of recipe a step is part of. For multiple repeat parameter."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:668 #: .\cookbook\views\api.py:676
msgid "Query string matched (fuzzy) against object name." msgid "Query string matched (fuzzy) against object name."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:712 #: .\cookbook\views\api.py:720
msgid "" msgid ""
"Query string matched (fuzzy) against recipe name. In the future also " "Query string matched (fuzzy) against recipe name. In the future also "
"fulltext search." "fulltext search."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:714 #: .\cookbook\views\api.py:722
msgid "" msgid ""
"ID of keyword a recipe should have. For multiple repeat parameter. " "ID of keyword a recipe should have. For multiple repeat parameter. "
"Equivalent to keywords_or" "Equivalent to keywords_or"
msgstr "" msgstr ""
#: .\cookbook\views\api.py:717 #: .\cookbook\views\api.py:725
msgid "" msgid ""
"Keyword IDs, repeat for multiple. Return recipes with any of the keywords" "Keyword IDs, repeat for multiple. Return recipes with any of the keywords"
msgstr "" msgstr ""
#: .\cookbook\views\api.py:720 #: .\cookbook\views\api.py:728
msgid "" msgid ""
"Keyword IDs, repeat for multiple. Return recipes with all of the keywords." "Keyword IDs, repeat for multiple. Return recipes with all of the keywords."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:723 #: .\cookbook\views\api.py:731
msgid "" msgid ""
"Keyword IDs, repeat for multiple. Exclude recipes with any of the keywords." "Keyword IDs, repeat for multiple. Exclude recipes with any of the keywords."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:726 #: .\cookbook\views\api.py:734
msgid "" msgid ""
"Keyword IDs, repeat for multiple. Exclude recipes with all of the keywords." "Keyword IDs, repeat for multiple. Exclude recipes with all of the keywords."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:728 #: .\cookbook\views\api.py:736
msgid "ID of food a recipe should have. For multiple repeat parameter." msgid "ID of food a recipe should have. For multiple repeat parameter."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:731 #: .\cookbook\views\api.py:739
msgid "Food IDs, repeat for multiple. Return recipes with any of the foods" msgid "Food IDs, repeat for multiple. Return recipes with any of the foods"
msgstr "" msgstr ""
#: .\cookbook\views\api.py:733 #: .\cookbook\views\api.py:741
msgid "Food IDs, repeat for multiple. Return recipes with all of the foods." msgid "Food IDs, repeat for multiple. Return recipes with all of the foods."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:735 #: .\cookbook\views\api.py:743
msgid "Food IDs, repeat for multiple. Exclude recipes with any of the foods." msgid "Food IDs, repeat for multiple. Exclude recipes with any of the foods."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:737 #: .\cookbook\views\api.py:745
msgid "Food IDs, repeat for multiple. Exclude recipes with all of the foods." msgid "Food IDs, repeat for multiple. Exclude recipes with all of the foods."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:738 #: .\cookbook\views\api.py:746
msgid "ID of unit a recipe should have." msgid "ID of unit a recipe should have."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:740 #: .\cookbook\views\api.py:748
msgid "" msgid ""
"Rating a recipe should have or greater. [0 - 5] Negative value filters " "Rating a recipe should have or greater. [0 - 5] Negative value filters "
"rating less than." "rating less than."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:741 #: .\cookbook\views\api.py:749
msgid "ID of book a recipe should be in. For multiple repeat parameter." msgid "ID of book a recipe should be in. For multiple repeat parameter."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:743 #: .\cookbook\views\api.py:751
msgid "Book IDs, repeat for multiple. Return recipes with any of the books" msgid "Book IDs, repeat for multiple. Return recipes with any of the books"
msgstr "" msgstr ""
#: .\cookbook\views\api.py:745 #: .\cookbook\views\api.py:753
msgid "Book IDs, repeat for multiple. Return recipes with all of the books." msgid "Book IDs, repeat for multiple. Return recipes with all of the books."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:747 #: .\cookbook\views\api.py:755
msgid "Book IDs, repeat for multiple. Exclude recipes with any of the books." msgid "Book IDs, repeat for multiple. Exclude recipes with any of the books."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:749 #: .\cookbook\views\api.py:757
msgid "Book IDs, repeat for multiple. Exclude recipes with all of the books." msgid "Book IDs, repeat for multiple. Exclude recipes with all of the books."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:751 #: .\cookbook\views\api.py:759
msgid "If only internal recipes should be returned. [true/<b>false</b>]" msgid "If only internal recipes should be returned. [true/<b>false</b>]"
msgstr "" msgstr ""
#: .\cookbook\views\api.py:753 #: .\cookbook\views\api.py:761
msgid "Returns the results in randomized order. [true/<b>false</b>]" msgid "Returns the results in randomized order. [true/<b>false</b>]"
msgstr "" msgstr ""
#: .\cookbook\views\api.py:755 #: .\cookbook\views\api.py:763
msgid "Returns new results first in search results. [true/<b>false</b>]" msgid "Returns new results first in search results. [true/<b>false</b>]"
msgstr "" msgstr ""
#: .\cookbook\views\api.py:757 #: .\cookbook\views\api.py:765
msgid "" msgid ""
"Filter recipes cooked X times or more. Negative values returns cooked less " "Filter recipes cooked X times or more. Negative values returns cooked less "
"than X times" "than X times"
msgstr "" msgstr ""
#: .\cookbook\views\api.py:759 #: .\cookbook\views\api.py:767
msgid "" msgid ""
"Filter recipes last cooked on or after YYYY-MM-DD. Prepending - filters on " "Filter recipes last cooked on or after YYYY-MM-DD. Prepending - filters on "
"or before date." "or before date."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:761 #: .\cookbook\views\api.py:769
msgid "" msgid ""
"Filter recipes created on or after YYYY-MM-DD. Prepending - filters on or " "Filter recipes created on or after YYYY-MM-DD. Prepending - filters on or "
"before date." "before date."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:763 #: .\cookbook\views\api.py:771
msgid "" msgid ""
"Filter recipes updated on or after YYYY-MM-DD. Prepending - filters on or " "Filter recipes updated on or after YYYY-MM-DD. Prepending - filters on or "
"before date." "before date."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:765 #: .\cookbook\views\api.py:773
msgid "" msgid ""
"Filter recipes lasts viewed on or after YYYY-MM-DD. Prepending - filters on " "Filter recipes lasts viewed on or after YYYY-MM-DD. Prepending - filters on "
"or before date." "or before date."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:767 #: .\cookbook\views\api.py:775
msgid "Filter recipes that can be made with OnHand food. [true/<b>false</b>]" msgid "Filter recipes that can be made with OnHand food. [true/<b>false</b>]"
msgstr "" msgstr ""
#: .\cookbook\views\api.py:929 #: .\cookbook\views\api.py:937
msgid "" msgid ""
"Returns the shopping list entry with a primary key of id. Multiple values " "Returns the shopping list entry with a primary key of id. Multiple values "
"allowed." "allowed."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:934 #: .\cookbook\views\api.py:942
msgid "" msgid ""
"Filter shopping list entries on checked. [true, false, both, <b>recent</b>]" "Filter shopping list entries on checked. [true, false, both, <b>recent</b>]"
"<br> - recent includes unchecked items and recently completed items." "<br> - recent includes unchecked items and recently completed items."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:937 #: .\cookbook\views\api.py:945
msgid "Returns the shopping list entries sorted by supermarket category order." msgid "Returns the shopping list entries sorted by supermarket category order."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:1134 #: .\cookbook\views\api.py:1140
msgid "Nothing to do." msgid "Nothing to do."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:1153 #: .\cookbook\views\api.py:1160
msgid "Invalid Url" msgid "Invalid Url"
msgstr "" msgstr ""
#: .\cookbook\views\api.py:1158 #: .\cookbook\views\api.py:1167
msgid "Connection Refused." msgid "Connection Refused."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:1163 #: .\cookbook\views\api.py:1172
msgid "Bad URL Schema." msgid "Bad URL Schema."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:1170 #: .\cookbook\views\api.py:1195
#, fuzzy #, fuzzy
#| msgid "The requested page could not be found." #| msgid "The requested page could not be found."
msgid "No usable data could be found." msgid "No usable data could be found."
msgstr "Pieprasīto lapu nevarēja atrast." msgstr "Pieprasīto lapu nevarēja atrast."
#: .\cookbook\views\api.py:1260 .\cookbook\views\data.py:28 #: .\cookbook\views\api.py:1303 .\cookbook\views\data.py:28
#: .\cookbook\views\edit.py:120 .\cookbook\views\new.py:90 #: .\cookbook\views\edit.py:120 .\cookbook\views\new.py:90
msgid "This feature is not yet available in the hosted version of tandoor!" msgid "This feature is not yet available in the hosted version of tandoor!"
msgstr "" msgstr ""
#: .\cookbook\views\api.py:1282 #: .\cookbook\views\api.py:1325
msgid "Sync successful!" msgid "Sync successful!"
msgstr "Sinhronizācija ir veiksmīga!" msgstr "Sinhronizācija ir veiksmīga!"
#: .\cookbook\views\api.py:1287 #: .\cookbook\views\api.py:1330
msgid "Error synchronizing with Storage" msgid "Error synchronizing with Storage"
msgstr "Sinhronizējot ar krātuvi, radās kļūda" msgstr "Sinhronizējot ar krātuvi, radās kļūda"
@ -2740,6 +2732,10 @@ msgstr "Atklāšana"
msgid "Shopping List" msgid "Shopping List"
msgstr "Iepirkumu saraksts" msgstr "Iepirkumu saraksts"
#: .\cookbook\views\lists.py:76
msgid "Invite Links"
msgstr "Uzaicinājuma saites"
#: .\cookbook\views\lists.py:139 #: .\cookbook\views\lists.py:139
msgid "Supermarkets" msgid "Supermarkets"
msgstr "" msgstr ""
@ -2842,6 +2838,9 @@ msgid ""
"contact the page administrator." "contact the page administrator."
msgstr "" msgstr ""
#~ msgid "Show Links"
#~ msgstr "Rādīt saites"
#, fuzzy #, fuzzy
#~| msgid "Invite Links" #~| msgid "Invite Links"
#~ msgid "Invite User" #~ msgid "Invite User"

View File

@ -12,7 +12,7 @@ msgid ""
msgstr "" 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: 2022-06-26 12:09+0200\n" "POT-Creation-Date: 2022-07-12 19:20+0200\n"
"PO-Revision-Date: 2022-05-31 08:32+0000\n" "PO-Revision-Date: 2022-05-31 08:32+0000\n"
"Last-Translator: Jesse <jesse.kamps@pm.me>\n" "Last-Translator: Jesse <jesse.kamps@pm.me>\n"
"Language-Team: Dutch <http://translate.tandoor.dev/projects/tandoor/recipes-" "Language-Team: Dutch <http://translate.tandoor.dev/projects/tandoor/recipes-"
@ -531,7 +531,7 @@ msgstr "Je hebt het maximaal aantal recepten voor jouw ruimte bereikt."
msgid "You have more users than allowed in your space." msgid "You have more users than allowed in your space."
msgstr "Je hebt meer gebruikers dan toegestaan in jouw ruimte." msgstr "Je hebt meer gebruikers dan toegestaan in jouw ruimte."
#: .\cookbook\helper\recipe_search.py:560 #: .\cookbook\helper\recipe_search.py:565
msgid "One of queryset or hash_key must be provided" msgid "One of queryset or hash_key must be provided"
msgstr "Er moet een queryset of hash_key opgegeven worden" msgstr "Er moet een queryset of hash_key opgegeven worden"
@ -544,12 +544,12 @@ msgstr "Je moet een portiegrootte aanleveren"
msgid "Could not parse template code." msgid "Could not parse template code."
msgstr "Sjablooncode kon niet verwerkt worden." msgstr "Sjablooncode kon niet verwerkt worden."
#: .\cookbook\integration\copymethat.py:42 #: .\cookbook\integration\copymethat.py:41
#: .\cookbook\integration\melarecipes.py:37 #: .\cookbook\integration\melarecipes.py:37
msgid "Favorite" msgid "Favorite"
msgstr "Favoriet" msgstr "Favoriet"
#: .\cookbook\integration\copymethat.py:71 #: .\cookbook\integration\copymethat.py:70
#: .\cookbook\integration\recettetek.py:54 #: .\cookbook\integration\recettetek.py:54
#: .\cookbook\integration\recipekeeper.py:63 #: .\cookbook\integration\recipekeeper.py:63
msgid "Imported from" msgid "Imported from"
@ -687,103 +687,103 @@ msgstr "Nieuw"
msgid " is part of a recipe step and cannot be deleted" msgid " is part of a recipe step and cannot be deleted"
msgstr " is deel van een receptstap en kan niet verwijderd worden" msgstr " is deel van een receptstap en kan niet verwijderd worden"
#: .\cookbook\models.py:1160 .\cookbook\templates\search_info.html:28 #: .\cookbook\models.py:1162 .\cookbook\templates\search_info.html:28
msgid "Simple" msgid "Simple"
msgstr "Simpel" msgstr "Simpel"
#: .\cookbook\models.py:1161 .\cookbook\templates\search_info.html:33 #: .\cookbook\models.py:1163 .\cookbook\templates\search_info.html:33
msgid "Phrase" msgid "Phrase"
msgstr "Zin" msgstr "Zin"
#: .\cookbook\models.py:1162 .\cookbook\templates\search_info.html:38 #: .\cookbook\models.py:1164 .\cookbook\templates\search_info.html:38
msgid "Web" msgid "Web"
msgstr "Web" msgstr "Web"
#: .\cookbook\models.py:1163 .\cookbook\templates\search_info.html:47 #: .\cookbook\models.py:1165 .\cookbook\templates\search_info.html:47
msgid "Raw" msgid "Raw"
msgstr "Rauw" msgstr "Rauw"
#: .\cookbook\models.py:1201 #: .\cookbook\models.py:1203
msgid "Food Alias" msgid "Food Alias"
msgstr "Ingrediënt alias" msgstr "Ingrediënt alias"
#: .\cookbook\models.py:1201 #: .\cookbook\models.py:1203
msgid "Unit Alias" msgid "Unit Alias"
msgstr "Eenheid alias" msgstr "Eenheid alias"
#: .\cookbook\models.py:1201 #: .\cookbook\models.py:1203
msgid "Keyword Alias" msgid "Keyword Alias"
msgstr "Etiket alias" msgstr "Etiket alias"
#: .\cookbook\models.py:1225 #: .\cookbook\models.py:1227
#: .\cookbook\templates\include\recipe_open_modal.html:7 #: .\cookbook\templates\include\recipe_open_modal.html:7
#: .\cookbook\views\delete.py:36 .\cookbook\views\edit.py:251 #: .\cookbook\views\delete.py:36 .\cookbook\views\edit.py:251
#: .\cookbook\views\new.py:48 #: .\cookbook\views\new.py:48
msgid "Recipe" msgid "Recipe"
msgstr "Recept" msgstr "Recept"
#: .\cookbook\models.py:1226 #: .\cookbook\models.py:1228
msgid "Food" msgid "Food"
msgstr "Ingrediënt" msgstr "Ingrediënt"
#: .\cookbook\models.py:1227 .\cookbook\templates\base.html:138 #: .\cookbook\models.py:1229 .\cookbook\templates\base.html:138
msgid "Keyword" msgid "Keyword"
msgstr "Etiket" msgstr "Etiket"
#: .\cookbook\serializer.py:204 #: .\cookbook\serializer.py:207
msgid "Cannot modify Space owner permission." msgid "Cannot modify Space owner permission."
msgstr "" msgstr ""
#: .\cookbook\serializer.py:273 #: .\cookbook\serializer.py:290
msgid "File uploads are not enabled for this Space." msgid "File uploads are not enabled for this Space."
msgstr "Bestandsuploads zijn niet ingeschakeld voor deze Ruimte." msgstr "Bestandsuploads zijn niet ingeschakeld voor deze Ruimte."
#: .\cookbook\serializer.py:284 #: .\cookbook\serializer.py:301
msgid "You have reached your file upload limit." msgid "You have reached your file upload limit."
msgstr "U heeft de uploadlimiet bereikt." msgstr "U heeft de uploadlimiet bereikt."
#: .\cookbook\serializer.py:1051 #: .\cookbook\serializer.py:1081
msgid "Hello" msgid "Hello"
msgstr "Hallo" msgstr "Hallo"
#: .\cookbook\serializer.py:1051 #: .\cookbook\serializer.py:1081
msgid "You have been invited by " msgid "You have been invited by "
msgstr "Je bent uitgenodigd door " msgstr "Je bent uitgenodigd door "
#: .\cookbook\serializer.py:1052 #: .\cookbook\serializer.py:1082
msgid " to join their Tandoor Recipes space " msgid " to join their Tandoor Recipes space "
msgstr " om zijn/haar Tandoor Recepten ruimte " msgstr " om zijn/haar Tandoor Recepten ruimte "
#: .\cookbook\serializer.py:1053 #: .\cookbook\serializer.py:1083
msgid "Click the following link to activate your account: " msgid "Click the following link to activate your account: "
msgstr "Klik om de volgende link om je account te activeren: " msgstr "Klik om de volgende link om je account te activeren: "
#: .\cookbook\serializer.py:1054 #: .\cookbook\serializer.py:1084
msgid "" msgid ""
"If the link does not work use the following code to manually join the space: " "If the link does not work use the following code to manually join the space: "
msgstr "" msgstr ""
"Als de linkt niet werkt, gebruik dan de volgende code om handmatig tot de " "Als de linkt niet werkt, gebruik dan de volgende code om handmatig tot de "
"ruimte toe te treden: " "ruimte toe te treden: "
#: .\cookbook\serializer.py:1055 #: .\cookbook\serializer.py:1085
msgid "The invitation is valid until " msgid "The invitation is valid until "
msgstr "De uitnodiging is geldig tot " msgstr "De uitnodiging is geldig tot "
#: .\cookbook\serializer.py:1056 #: .\cookbook\serializer.py:1086
msgid "" msgid ""
"Tandoor Recipes is an Open Source recipe manager. Check it out on GitHub " "Tandoor Recipes is an Open Source recipe manager. Check it out on GitHub "
msgstr "" msgstr ""
"Tandoor Recepten is een Open Source recepten manager. Bekijk het op GitHub " "Tandoor Recepten is een Open Source recepten manager. Bekijk het op GitHub "
#: .\cookbook\serializer.py:1059 #: .\cookbook\serializer.py:1089
msgid "Tandoor Recipes Invite" msgid "Tandoor Recipes Invite"
msgstr "Tandoor Recepten uitnodiging" msgstr "Tandoor Recepten uitnodiging"
#: .\cookbook\serializer.py:1179 #: .\cookbook\serializer.py:1209
msgid "Existing shopping list to update" msgid "Existing shopping list to update"
msgstr "Bestaande boodschappenlijst is bijgewerkt" msgstr "Bestaande boodschappenlijst is bijgewerkt"
#: .\cookbook\serializer.py:1181 #: .\cookbook\serializer.py:1211
msgid "" msgid ""
"List of ingredient IDs from the recipe to add, if not provided all " "List of ingredient IDs from the recipe to add, if not provided all "
"ingredients will be added." "ingredients will be added."
@ -791,22 +791,22 @@ msgstr ""
"Lijst van ingrediënten ID's van het toe te voegen recept, als deze niet " "Lijst van ingrediënten ID's van het toe te voegen recept, als deze niet "
"opgegeven worden worden alle ingrediënten toegevoegd." "opgegeven worden worden alle ingrediënten toegevoegd."
#: .\cookbook\serializer.py:1183 #: .\cookbook\serializer.py:1213
msgid "" msgid ""
"Providing a list_recipe ID and servings of 0 will delete that shopping list." "Providing a list_recipe ID and servings of 0 will delete that shopping list."
msgstr "" msgstr ""
"Als je een list_recipe ID en portiegrootte van 0 opgeeft wordt dat " "Als je een list_recipe ID en portiegrootte van 0 opgeeft wordt dat "
"boodschappenlijstje verwijderd." "boodschappenlijstje verwijderd."
#: .\cookbook\serializer.py:1192 #: .\cookbook\serializer.py:1222
msgid "Amount of food to add to the shopping list" msgid "Amount of food to add to the shopping list"
msgstr "Hoeveelheid eten om aan het boodschappenlijstje toe te voegen" msgstr "Hoeveelheid eten om aan het boodschappenlijstje toe te voegen"
#: .\cookbook\serializer.py:1194 #: .\cookbook\serializer.py:1224
msgid "ID of unit to use for the shopping list" msgid "ID of unit to use for the shopping list"
msgstr "ID of eenheid om te gebruik voor het boodschappenlijstje" msgstr "ID of eenheid om te gebruik voor het boodschappenlijstje"
#: .\cookbook\serializer.py:1196 #: .\cookbook\serializer.py:1226
msgid "When set to true will delete all food from active shopping lists." msgid "When set to true will delete all food from active shopping lists."
msgstr "" msgstr ""
"Wanneer ingesteld op waar, wordt al het voedsel van actieve " "Wanneer ingesteld op waar, wordt al het voedsel van actieve "
@ -948,7 +948,7 @@ msgstr ""
"<a href=\"%(email_url)s\">Vraag een nieuwe bevestigingslink aan</a>." "<a href=\"%(email_url)s\">Vraag een nieuwe bevestigingslink aan</a>."
#: .\cookbook\templates\account\login.html:8 #: .\cookbook\templates\account\login.html:8
#: .\cookbook\templates\base.html:339 .\cookbook\templates\openid\login.html:8 #: .\cookbook\templates\base.html:340 .\cookbook\templates\openid\login.html:8
msgid "Login" msgid "Login"
msgstr "Inloggen" msgstr "Inloggen"
@ -1120,7 +1120,7 @@ msgstr "Registratie gesloten"
msgid "We are sorry, but the sign up is currently closed." msgid "We are sorry, but the sign up is currently closed."
msgstr "Excuses, registratie is op dit moment gesloten." msgstr "Excuses, registratie is op dit moment gesloten."
#: .\cookbook\templates\api_info.html:5 .\cookbook\templates\base.html:329 #: .\cookbook\templates\api_info.html:5 .\cookbook\templates\base.html:330
#: .\cookbook\templates\rest_framework\api.html:11 #: .\cookbook\templates\rest_framework\api.html:11
msgid "API Documentation" msgid "API Documentation"
msgstr "API documentatie" msgstr "API documentatie"
@ -1215,36 +1215,36 @@ msgstr "Beheer"
msgid "Your Spaces" msgid "Your Spaces"
msgstr "Geen ruimte" msgstr "Geen ruimte"
#: .\cookbook\templates\base.html:319 #: .\cookbook\templates\base.html:320
#: .\cookbook\templates\space_overview.html:6 #: .\cookbook\templates\space_overview.html:6
msgid "Overview" msgid "Overview"
msgstr "" msgstr ""
#: .\cookbook\templates\base.html:323 #: .\cookbook\templates\base.html:324
msgid "Markdown Guide" msgid "Markdown Guide"
msgstr "Markdown gids" msgstr "Markdown gids"
#: .\cookbook\templates\base.html:325 #: .\cookbook\templates\base.html:326
msgid "GitHub" msgid "GitHub"
msgstr "GitHub" msgstr "GitHub"
#: .\cookbook\templates\base.html:327 #: .\cookbook\templates\base.html:328
msgid "Translate Tandoor" msgid "Translate Tandoor"
msgstr "Vertaal Tandoor" msgstr "Vertaal Tandoor"
#: .\cookbook\templates\base.html:331 #: .\cookbook\templates\base.html:332
msgid "API Browser" msgid "API Browser"
msgstr "API Browser" msgstr "API Browser"
#: .\cookbook\templates\base.html:334 #: .\cookbook\templates\base.html:335
msgid "Log out" msgid "Log out"
msgstr "Uitloggen" msgstr "Uitloggen"
#: .\cookbook\templates\base.html:356 #: .\cookbook\templates\base.html:357
msgid "You are using the free version of Tandor" msgid "You are using the free version of Tandor"
msgstr "Je gebruikt de gratis versie van Tandoor" msgstr "Je gebruikt de gratis versie van Tandoor"
#: .\cookbook\templates\base.html:357 #: .\cookbook\templates\base.html:358
msgid "Upgrade Now" msgid "Upgrade Now"
msgstr "Upgrade nu" msgstr "Upgrade nu"
@ -2365,19 +2365,11 @@ msgstr "Recepten zonder etiketten"
msgid "Internal Recipes" msgid "Internal Recipes"
msgstr "Interne recepten" msgstr "Interne recepten"
#: .\cookbook\templates\system.html:21 .\cookbook\views\lists.py:76 #: .\cookbook\templates\system.html:20
msgid "Invite Links"
msgstr "Uitnodigingslink"
#: .\cookbook\templates\system.html:22
msgid "Show Links"
msgstr "Toon links"
#: .\cookbook\templates\system.html:32
msgid "System Information" msgid "System Information"
msgstr "Systeeminformatie" msgstr "Systeeminformatie"
#: .\cookbook\templates\system.html:34 #: .\cookbook\templates\system.html:22
msgid "" msgid ""
"\n" "\n"
" Django Recipes is an open source free software application. It can " " Django Recipes is an open source free software application. It can "
@ -2395,21 +2387,21 @@ msgstr ""
"recipes/releases\">hier</a> gevonden worden.\n" "recipes/releases\">hier</a> gevonden worden.\n"
" " " "
#: .\cookbook\templates\system.html:48 #: .\cookbook\templates\system.html:36
msgid "Media Serving" msgid "Media Serving"
msgstr "Media aanbieder" msgstr "Media aanbieder"
#: .\cookbook\templates\system.html:49 .\cookbook\templates\system.html:64 #: .\cookbook\templates\system.html:37 .\cookbook\templates\system.html:52
#: .\cookbook\templates\system.html:80 #: .\cookbook\templates\system.html:68
msgid "Warning" msgid "Warning"
msgstr "Waarschuwing" msgstr "Waarschuwing"
#: .\cookbook\templates\system.html:49 .\cookbook\templates\system.html:64 #: .\cookbook\templates\system.html:37 .\cookbook\templates\system.html:52
#: .\cookbook\templates\system.html:80 .\cookbook\templates\system.html:95 #: .\cookbook\templates\system.html:68 .\cookbook\templates\system.html:83
msgid "Ok" msgid "Ok"
msgstr "Oké" msgstr "Oké"
#: .\cookbook\templates\system.html:51 #: .\cookbook\templates\system.html:39
msgid "" msgid ""
"Serving media files directly using gunicorn/python is <b>not recommend</b>!\n" "Serving media files directly using gunicorn/python is <b>not recommend</b>!\n"
" Please follow the steps described\n" " Please follow the steps described\n"
@ -2424,16 +2416,16 @@ msgstr ""
"releases/tag/0.8.1\">hier</a> beschreven om je installatie te updaten.\n" "releases/tag/0.8.1\">hier</a> beschreven om je installatie te updaten.\n"
" " " "
#: .\cookbook\templates\system.html:57 .\cookbook\templates\system.html:73 #: .\cookbook\templates\system.html:45 .\cookbook\templates\system.html:61
#: .\cookbook\templates\system.html:88 .\cookbook\templates\system.html:102 #: .\cookbook\templates\system.html:76 .\cookbook\templates\system.html:90
msgid "Everything is fine!" msgid "Everything is fine!"
msgstr "Alles is in orde!" msgstr "Alles is in orde!"
#: .\cookbook\templates\system.html:62 #: .\cookbook\templates\system.html:50
msgid "Secret Key" msgid "Secret Key"
msgstr "Geheime sleutel" msgstr "Geheime sleutel"
#: .\cookbook\templates\system.html:66 #: .\cookbook\templates\system.html:54
msgid "" msgid ""
"\n" "\n"
" You do not have a <code>SECRET_KEY</code> configured in your " " You do not have a <code>SECRET_KEY</code> configured in your "
@ -2453,11 +2445,11 @@ msgstr ""
"configuratiebestand.\n" "configuratiebestand.\n"
" " " "
#: .\cookbook\templates\system.html:78 #: .\cookbook\templates\system.html:66
msgid "Debug Mode" msgid "Debug Mode"
msgstr "Debug modus" msgstr "Debug modus"
#: .\cookbook\templates\system.html:82 #: .\cookbook\templates\system.html:70
msgid "" msgid ""
"\n" "\n"
" This application is still running in debug mode. This is most " " This application is still running in debug mode. This is most "
@ -2475,15 +2467,15 @@ msgstr ""
"passen.\n" "passen.\n"
" " " "
#: .\cookbook\templates\system.html:93 #: .\cookbook\templates\system.html:81
msgid "Database" msgid "Database"
msgstr "Database" msgstr "Database"
#: .\cookbook\templates\system.html:95 #: .\cookbook\templates\system.html:83
msgid "Info" msgid "Info"
msgstr "Info" msgstr "Info"
#: .\cookbook\templates\system.html:97 #: .\cookbook\templates\system.html:85
msgid "" msgid ""
"\n" "\n"
" This application is not running with a Postgres database " " This application is not running with a Postgres database "
@ -2501,76 +2493,76 @@ msgstr ""
msgid "URL Import" msgid "URL Import"
msgstr "Importeer URL" msgstr "Importeer URL"
#: .\cookbook\views\api.py:97 .\cookbook\views\api.py:189 #: .\cookbook\views\api.py:105 .\cookbook\views\api.py:197
msgid "Parameter updated_at incorrectly formatted" msgid "Parameter updated_at incorrectly formatted"
msgstr "Parameter updatet_at is onjuist geformateerd" msgstr "Parameter updatet_at is onjuist geformateerd"
#: .\cookbook\views\api.py:209 .\cookbook\views\api.py:312 #: .\cookbook\views\api.py:217 .\cookbook\views\api.py:320
msgid "No {self.basename} with id {pk} exists" msgid "No {self.basename} with id {pk} exists"
msgstr "Er bestaat geen {self.basename} met id {pk}" msgstr "Er bestaat geen {self.basename} met id {pk}"
#: .\cookbook\views\api.py:213 #: .\cookbook\views\api.py:221
msgid "Cannot merge with the same object!" msgid "Cannot merge with the same object!"
msgstr "Kan niet met hetzelfde object samenvoegen!" msgstr "Kan niet met hetzelfde object samenvoegen!"
#: .\cookbook\views\api.py:220 #: .\cookbook\views\api.py:228
msgid "No {self.basename} with id {target} exists" msgid "No {self.basename} with id {target} exists"
msgstr "Er bestaat geen {self.basename} met id {target}" msgstr "Er bestaat geen {self.basename} met id {target}"
#: .\cookbook\views\api.py:225 #: .\cookbook\views\api.py:233
msgid "Cannot merge with child object!" msgid "Cannot merge with child object!"
msgstr "Kan niet met kindobject samenvoegen!" msgstr "Kan niet met kindobject samenvoegen!"
#: .\cookbook\views\api.py:258 #: .\cookbook\views\api.py:266
msgid "{source.name} was merged successfully with {target.name}" msgid "{source.name} was merged successfully with {target.name}"
msgstr "{source.name} is succesvol samengevoegd met {target.name}" msgstr "{source.name} is succesvol samengevoegd met {target.name}"
#: .\cookbook\views\api.py:263 #: .\cookbook\views\api.py:271
msgid "An error occurred attempting to merge {source.name} with {target.name}" msgid "An error occurred attempting to merge {source.name} with {target.name}"
msgstr "" msgstr ""
"Er is een error opgetreden bij het samenvoegen van {source.name} met {target." "Er is een error opgetreden bij het samenvoegen van {source.name} met {target."
"name}" "name}"
#: .\cookbook\views\api.py:321 #: .\cookbook\views\api.py:329
msgid "{child.name} was moved successfully to the root." msgid "{child.name} was moved successfully to the root."
msgstr "{child.name} is succesvol verplaatst naar het hoogste niveau." msgstr "{child.name} is succesvol verplaatst naar het hoogste niveau."
#: .\cookbook\views\api.py:324 .\cookbook\views\api.py:342 #: .\cookbook\views\api.py:332 .\cookbook\views\api.py:350
msgid "An error occurred attempting to move " msgid "An error occurred attempting to move "
msgstr "Er is een error opgetreden bij het verplaatsen " msgstr "Er is een error opgetreden bij het verplaatsen "
#: .\cookbook\views\api.py:327 #: .\cookbook\views\api.py:335
msgid "Cannot move an object to itself!" msgid "Cannot move an object to itself!"
msgstr "Kan object niet verplaatsen naar zichzelf!" msgstr "Kan object niet verplaatsen naar zichzelf!"
#: .\cookbook\views\api.py:333 #: .\cookbook\views\api.py:341
msgid "No {self.basename} with id {parent} exists" msgid "No {self.basename} with id {parent} exists"
msgstr "Er bestaat geen {self.basename} met id {parent}" msgstr "Er bestaat geen {self.basename} met id {parent}"
#: .\cookbook\views\api.py:339 #: .\cookbook\views\api.py:347
msgid "{child.name} was moved successfully to parent {parent.name}" msgid "{child.name} was moved successfully to parent {parent.name}"
msgstr "{child.name} is succesvol verplaatst naar {parent.name}" msgstr "{child.name} is succesvol verplaatst naar {parent.name}"
#: .\cookbook\views\api.py:534 #: .\cookbook\views\api.py:542
msgid "{obj.name} was removed from the shopping list." msgid "{obj.name} was removed from the shopping list."
msgstr "{obj.name} is verwijderd van het boodschappenlijstje." msgstr "{obj.name} is verwijderd van het boodschappenlijstje."
#: .\cookbook\views\api.py:539 .\cookbook\views\api.py:871 #: .\cookbook\views\api.py:547 .\cookbook\views\api.py:879
#: .\cookbook\views\api.py:884 #: .\cookbook\views\api.py:892
msgid "{obj.name} was added to the shopping list." msgid "{obj.name} was added to the shopping list."
msgstr "{obj.name} is toegevoegd aan het boodschappenlijstje." msgstr "{obj.name} is toegevoegd aan het boodschappenlijstje."
#: .\cookbook\views\api.py:666 #: .\cookbook\views\api.py:674
msgid "ID of recipe a step is part of. For multiple repeat parameter." msgid "ID of recipe a step is part of. For multiple repeat parameter."
msgstr "" msgstr ""
"ID van het recept waar de stap onderdeel van is. Herhaal parameter voor " "ID van het recept waar de stap onderdeel van is. Herhaal parameter voor "
"meerdere." "meerdere."
#: .\cookbook\views\api.py:668 #: .\cookbook\views\api.py:676
msgid "Query string matched (fuzzy) against object name." msgid "Query string matched (fuzzy) against object name."
msgstr "Zoekterm komt overeen (fuzzy) met object naam." msgstr "Zoekterm komt overeen (fuzzy) met object naam."
#: .\cookbook\views\api.py:712 #: .\cookbook\views\api.py:720
msgid "" msgid ""
"Query string matched (fuzzy) against recipe name. In the future also " "Query string matched (fuzzy) against recipe name. In the future also "
"fulltext search." "fulltext search."
@ -2578,7 +2570,7 @@ msgstr ""
"Zoekterm komt overeen (fuzzy) met recept naam. In de toekomst wordt zoeken " "Zoekterm komt overeen (fuzzy) met recept naam. In de toekomst wordt zoeken "
"op volledige tekst ondersteund." "op volledige tekst ondersteund."
#: .\cookbook\views\api.py:714 #: .\cookbook\views\api.py:722
msgid "" msgid ""
"ID of keyword a recipe should have. For multiple repeat parameter. " "ID of keyword a recipe should have. For multiple repeat parameter. "
"Equivalent to keywords_or" "Equivalent to keywords_or"
@ -2586,109 +2578,109 @@ msgstr ""
"ID van etiket dat een recept moet hebben. Herhaal parameter voor meerdere. " "ID van etiket dat een recept moet hebben. Herhaal parameter voor meerdere. "
"Gelijkwaardig aan keywords_or" "Gelijkwaardig aan keywords_or"
#: .\cookbook\views\api.py:717 #: .\cookbook\views\api.py:725
msgid "" msgid ""
"Keyword IDs, repeat for multiple. Return recipes with any of the keywords" "Keyword IDs, repeat for multiple. Return recipes with any of the keywords"
msgstr "" msgstr ""
"Etiket ID, herhaal voor meerdere. Geeft recepten met elk geselecteerd etiket " "Etiket ID, herhaal voor meerdere. Geeft recepten met elk geselecteerd etiket "
"weer" "weer"
#: .\cookbook\views\api.py:720 #: .\cookbook\views\api.py:728
msgid "" msgid ""
"Keyword IDs, repeat for multiple. Return recipes with all of the keywords." "Keyword IDs, repeat for multiple. Return recipes with all of the keywords."
msgstr "" msgstr ""
"Etiket ID, herhaal voor meerdere. Geeft recepten met alle geselecteerde " "Etiket ID, herhaal voor meerdere. Geeft recepten met alle geselecteerde "
"etiketten weer." "etiketten weer."
#: .\cookbook\views\api.py:723 #: .\cookbook\views\api.py:731
msgid "" msgid ""
"Keyword IDs, repeat for multiple. Exclude recipes with any of the keywords." "Keyword IDs, repeat for multiple. Exclude recipes with any of the keywords."
msgstr "" msgstr ""
"Etiket ID, herhaal voor meerdere. Sluit recepten met één van de etiketten " "Etiket ID, herhaal voor meerdere. Sluit recepten met één van de etiketten "
"uit." "uit."
#: .\cookbook\views\api.py:726 #: .\cookbook\views\api.py:734
msgid "" msgid ""
"Keyword IDs, repeat for multiple. Exclude recipes with all of the keywords." "Keyword IDs, repeat for multiple. Exclude recipes with all of the keywords."
msgstr "" msgstr ""
"Etiket ID, herhaal voor meerdere. Sluit recepten met alle etiketten uit." "Etiket ID, herhaal voor meerdere. Sluit recepten met alle etiketten uit."
#: .\cookbook\views\api.py:728 #: .\cookbook\views\api.py:736
msgid "ID of food a recipe should have. For multiple repeat parameter." msgid "ID of food a recipe should have. For multiple repeat parameter."
msgstr "" msgstr ""
"ID van ingrediënt dat een recept moet hebben. Herhaal parameter voor " "ID van ingrediënt dat een recept moet hebben. Herhaal parameter voor "
"meerdere." "meerdere."
#: .\cookbook\views\api.py:731 #: .\cookbook\views\api.py:739
msgid "Food IDs, repeat for multiple. Return recipes with any of the foods" msgid "Food IDs, repeat for multiple. Return recipes with any of the foods"
msgstr "" msgstr ""
"Ingrediënt ID, herhaal voor meerdere. Geeft recepten met elk ingrediënt weer" "Ingrediënt ID, herhaal voor meerdere. Geeft recepten met elk ingrediënt weer"
#: .\cookbook\views\api.py:733 #: .\cookbook\views\api.py:741
msgid "Food IDs, repeat for multiple. Return recipes with all of the foods." msgid "Food IDs, repeat for multiple. Return recipes with all of the foods."
msgstr "" msgstr ""
"Ingrediënt ID, herhaal voor meerdere. Geef recepten met alle ingrediënten " "Ingrediënt ID, herhaal voor meerdere. Geef recepten met alle ingrediënten "
"weer." "weer."
#: .\cookbook\views\api.py:735 #: .\cookbook\views\api.py:743
msgid "Food IDs, repeat for multiple. Exclude recipes with any of the foods." msgid "Food IDs, repeat for multiple. Exclude recipes with any of the foods."
msgstr "" msgstr ""
"Ingrediënt ID, herhaal voor meerdere. sluit recepten met één van de " "Ingrediënt ID, herhaal voor meerdere. sluit recepten met één van de "
"ingrediënten uit." "ingrediënten uit."
#: .\cookbook\views\api.py:737 #: .\cookbook\views\api.py:745
msgid "Food IDs, repeat for multiple. Exclude recipes with all of the foods." msgid "Food IDs, repeat for multiple. Exclude recipes with all of the foods."
msgstr "" msgstr ""
"Ingrediënt ID, herhaal voor meerdere. Sluit recepten met alle ingrediënten " "Ingrediënt ID, herhaal voor meerdere. Sluit recepten met alle ingrediënten "
"uit." "uit."
#: .\cookbook\views\api.py:738 #: .\cookbook\views\api.py:746
msgid "ID of unit a recipe should have." msgid "ID of unit a recipe should have."
msgstr "ID van eenheid dat een recept moet hebben." msgstr "ID van eenheid dat een recept moet hebben."
#: .\cookbook\views\api.py:740 #: .\cookbook\views\api.py:748
msgid "" msgid ""
"Rating a recipe should have or greater. [0 - 5] Negative value filters " "Rating a recipe should have or greater. [0 - 5] Negative value filters "
"rating less than." "rating less than."
msgstr "Een waardering van een recept gaat van 0 tot 5." msgstr "Een waardering van een recept gaat van 0 tot 5."
#: .\cookbook\views\api.py:741 #: .\cookbook\views\api.py:749
msgid "ID of book a recipe should be in. For multiple repeat parameter." msgid "ID of book a recipe should be in. For multiple repeat parameter."
msgstr "" msgstr ""
"ID van boek dat een recept moet hebben. Herhaal parameter voor meerdere." "ID van boek dat een recept moet hebben. Herhaal parameter voor meerdere."
#: .\cookbook\views\api.py:743 #: .\cookbook\views\api.py:751
msgid "Book IDs, repeat for multiple. Return recipes with any of the books" msgid "Book IDs, repeat for multiple. Return recipes with any of the books"
msgstr "Boek ID, herhaal voor meerdere. Geeft recepten uit alle boeken weer" msgstr "Boek ID, herhaal voor meerdere. Geeft recepten uit alle boeken weer"
#: .\cookbook\views\api.py:745 #: .\cookbook\views\api.py:753
msgid "Book IDs, repeat for multiple. Return recipes with all of the books." msgid "Book IDs, repeat for multiple. Return recipes with all of the books."
msgstr "Boek IDs, herhaal voor meerdere. Geeft recepten weer uit alle boeken." msgstr "Boek IDs, herhaal voor meerdere. Geeft recepten weer uit alle boeken."
#: .\cookbook\views\api.py:747 #: .\cookbook\views\api.py:755
msgid "Book IDs, repeat for multiple. Exclude recipes with any of the books." msgid "Book IDs, repeat for multiple. Exclude recipes with any of the books."
msgstr "" msgstr ""
"Boek IDs, herhaal voor meerdere. Sluit recepten uit elk van de boeken uit." "Boek IDs, herhaal voor meerdere. Sluit recepten uit elk van de boeken uit."
#: .\cookbook\views\api.py:749 #: .\cookbook\views\api.py:757
msgid "Book IDs, repeat for multiple. Exclude recipes with all of the books." msgid "Book IDs, repeat for multiple. Exclude recipes with all of the books."
msgstr "Boek IDs, herhaal voor meerdere. Sluit recepten uit alle boeken uit." msgstr "Boek IDs, herhaal voor meerdere. Sluit recepten uit alle boeken uit."
#: .\cookbook\views\api.py:751 #: .\cookbook\views\api.py:759
msgid "If only internal recipes should be returned. [true/<b>false</b>]" msgid "If only internal recipes should be returned. [true/<b>false</b>]"
msgstr "" msgstr ""
"Wanneer alleen interne recepten gevonden moeten worden. [waar/<b>onwaar</b>]" "Wanneer alleen interne recepten gevonden moeten worden. [waar/<b>onwaar</b>]"
#: .\cookbook\views\api.py:753 #: .\cookbook\views\api.py:761
msgid "Returns the results in randomized order. [true/<b>false</b>]" msgid "Returns the results in randomized order. [true/<b>false</b>]"
msgstr "" msgstr ""
"Geeft de resultaten in willekeurige volgorde weer. [waar/<b>onwaar</b>]" "Geeft de resultaten in willekeurige volgorde weer. [waar/<b>onwaar</b>]"
#: .\cookbook\views\api.py:755 #: .\cookbook\views\api.py:763
msgid "Returns new results first in search results. [true/<b>false</b>]" msgid "Returns new results first in search results. [true/<b>false</b>]"
msgstr "Geeft nieuwe resultaten eerst weer. [waar/<b>onwaar</b>]" msgstr "Geeft nieuwe resultaten eerst weer. [waar/<b>onwaar</b>]"
#: .\cookbook\views\api.py:757 #: .\cookbook\views\api.py:765
msgid "" msgid ""
"Filter recipes cooked X times or more. Negative values returns cooked less " "Filter recipes cooked X times or more. Negative values returns cooked less "
"than X times" "than X times"
@ -2696,7 +2688,7 @@ msgstr ""
"Filter recepten X maal of meer bereid. Negatieve waarden geven minder dan X " "Filter recepten X maal of meer bereid. Negatieve waarden geven minder dan X "
"keer bereide recepten weer" "keer bereide recepten weer"
#: .\cookbook\views\api.py:759 #: .\cookbook\views\api.py:767
msgid "" msgid ""
"Filter recipes last cooked on or after YYYY-MM-DD. Prepending - filters on " "Filter recipes last cooked on or after YYYY-MM-DD. Prepending - filters on "
"or before date." "or before date."
@ -2704,7 +2696,7 @@ msgstr ""
"Filter recepten op laatst bereid op of na JJJJ-MM-DD. Voorafgaand - filters " "Filter recepten op laatst bereid op of na JJJJ-MM-DD. Voorafgaand - filters "
"op of voor datum." "op of voor datum."
#: .\cookbook\views\api.py:761 #: .\cookbook\views\api.py:769
msgid "" msgid ""
"Filter recipes created on or after YYYY-MM-DD. Prepending - filters on or " "Filter recipes created on or after YYYY-MM-DD. Prepending - filters on or "
"before date." "before date."
@ -2712,7 +2704,7 @@ msgstr ""
"Filter recepten aangemaakt op of na JJJJ-MM-DD. Voorafgaand - filters op of " "Filter recepten aangemaakt op of na JJJJ-MM-DD. Voorafgaand - filters op of "
"voor datum." "voor datum."
#: .\cookbook\views\api.py:763 #: .\cookbook\views\api.py:771
msgid "" msgid ""
"Filter recipes updated on or after YYYY-MM-DD. Prepending - filters on or " "Filter recipes updated on or after YYYY-MM-DD. Prepending - filters on or "
"before date." "before date."
@ -2720,7 +2712,7 @@ msgstr ""
"Filter recepten op geüpdatet op of na JJJJ-MM-DD. Voorafgaand - filters op " "Filter recepten op geüpdatet op of na JJJJ-MM-DD. Voorafgaand - filters op "
"of voor datum." "of voor datum."
#: .\cookbook\views\api.py:765 #: .\cookbook\views\api.py:773
msgid "" msgid ""
"Filter recipes lasts viewed on or after YYYY-MM-DD. Prepending - filters on " "Filter recipes lasts viewed on or after YYYY-MM-DD. Prepending - filters on "
"or before date." "or before date."
@ -2728,13 +2720,13 @@ msgstr ""
"Filter recepten op laatst bekeken op of na JJJJ-MM-DD. Voorafgaand - filters " "Filter recepten op laatst bekeken op of na JJJJ-MM-DD. Voorafgaand - filters "
"op of voor datum." "op of voor datum."
#: .\cookbook\views\api.py:767 #: .\cookbook\views\api.py:775
msgid "Filter recipes that can be made with OnHand food. [true/<b>false</b>]" msgid "Filter recipes that can be made with OnHand food. [true/<b>false</b>]"
msgstr "" msgstr ""
"Filter recepten die bereid kunnen worden met ingrediënten die op voorraad " "Filter recepten die bereid kunnen worden met ingrediënten die op voorraad "
"zijn. [waar/<b>onwaar</b>]" "zijn. [waar/<b>onwaar</b>]"
#: .\cookbook\views\api.py:929 #: .\cookbook\views\api.py:937
msgid "" msgid ""
"Returns the shopping list entry with a primary key of id. Multiple values " "Returns the shopping list entry with a primary key of id. Multiple values "
"allowed." "allowed."
@ -2742,7 +2734,7 @@ msgstr ""
"Geeft het boodschappenlijstje item met een primaire sleutel van id. " "Geeft het boodschappenlijstje item met een primaire sleutel van id. "
"Meerdere waarden toegestaan." "Meerdere waarden toegestaan."
#: .\cookbook\views\api.py:934 #: .\cookbook\views\api.py:942
msgid "" msgid ""
"Filter shopping list entries on checked. [true, false, both, <b>recent</b>]" "Filter shopping list entries on checked. [true, false, both, <b>recent</b>]"
"<br> - recent includes unchecked items and recently completed items." "<br> - recent includes unchecked items and recently completed items."
@ -2750,41 +2742,41 @@ msgstr ""
"Filter boodschappenlijstjes op aangevinkt. [waar,onwaar,beide,<b>recent</b>]" "Filter boodschappenlijstjes op aangevinkt. [waar,onwaar,beide,<b>recent</b>]"
"<br> - recent bevat niet aangevinkte en recent voltooide items." "<br> - recent bevat niet aangevinkte en recent voltooide items."
#: .\cookbook\views\api.py:937 #: .\cookbook\views\api.py:945
msgid "Returns the shopping list entries sorted by supermarket category order." msgid "Returns the shopping list entries sorted by supermarket category order."
msgstr "" msgstr ""
"Geeft items op boodschappenlijstjes gesorteerd per supermarktcategorie weer." "Geeft items op boodschappenlijstjes gesorteerd per supermarktcategorie weer."
#: .\cookbook\views\api.py:1134 #: .\cookbook\views\api.py:1140
msgid "Nothing to do." msgid "Nothing to do."
msgstr "Niks te doen." msgstr "Niks te doen."
#: .\cookbook\views\api.py:1153 #: .\cookbook\views\api.py:1160
msgid "Invalid Url" msgid "Invalid Url"
msgstr "" msgstr ""
#: .\cookbook\views\api.py:1158 #: .\cookbook\views\api.py:1167
msgid "Connection Refused." msgid "Connection Refused."
msgstr "Verbinding geweigerd." msgstr "Verbinding geweigerd."
#: .\cookbook\views\api.py:1163 #: .\cookbook\views\api.py:1172
msgid "Bad URL Schema." msgid "Bad URL Schema."
msgstr "Verkeerd URL schema." msgstr "Verkeerd URL schema."
#: .\cookbook\views\api.py:1170 #: .\cookbook\views\api.py:1195
msgid "No usable data could be found." msgid "No usable data could be found."
msgstr "Er is geen bruikbare data gevonden." msgstr "Er is geen bruikbare data gevonden."
#: .\cookbook\views\api.py:1260 .\cookbook\views\data.py:28 #: .\cookbook\views\api.py:1303 .\cookbook\views\data.py:28
#: .\cookbook\views\edit.py:120 .\cookbook\views\new.py:90 #: .\cookbook\views\edit.py:120 .\cookbook\views\new.py:90
msgid "This feature is not yet available in the hosted version of tandoor!" msgid "This feature is not yet available in the hosted version of tandoor!"
msgstr "Deze optie is nog niet beschikbaar in de gehoste versie van Tandoor!" msgstr "Deze optie is nog niet beschikbaar in de gehoste versie van Tandoor!"
#: .\cookbook\views\api.py:1282 #: .\cookbook\views\api.py:1325
msgid "Sync successful!" msgid "Sync successful!"
msgstr "Synchronisatie succesvol!" msgstr "Synchronisatie succesvol!"
#: .\cookbook\views\api.py:1287 #: .\cookbook\views\api.py:1330
msgid "Error synchronizing with Storage" msgid "Error synchronizing with Storage"
msgstr "Er is een fout opgetreden bij het synchroniseren met Opslag" msgstr "Er is een fout opgetreden bij het synchroniseren met Opslag"
@ -2873,6 +2865,10 @@ msgstr "Ontdekken"
msgid "Shopping List" msgid "Shopping List"
msgstr "Boodschappenlijst" msgstr "Boodschappenlijst"
#: .\cookbook\views\lists.py:76
msgid "Invite Links"
msgstr "Uitnodigingslink"
#: .\cookbook\views\lists.py:139 #: .\cookbook\views\lists.py:139
msgid "Supermarkets" msgid "Supermarkets"
msgstr "Supermarkten" msgstr "Supermarkten"
@ -2980,6 +2976,9 @@ msgstr ""
"Links voor het delen van recepten zijn gedeactiveerd. Neem contact op met de " "Links voor het delen van recepten zijn gedeactiveerd. Neem contact op met de "
"paginabeheerder voor aanvullende informatie." "paginabeheerder voor aanvullende informatie."
#~ msgid "Show Links"
#~ msgstr "Toon links"
#~ msgid "A user is required" #~ msgid "A user is required"
#~ msgstr "Een gebruiker is verplicht" #~ msgstr "Een gebruiker is verplicht"

View File

@ -11,7 +11,7 @@ msgid ""
msgstr "" 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: 2022-06-26 12:09+0200\n" "POT-Creation-Date: 2022-07-12 19:20+0200\n"
"PO-Revision-Date: 2021-11-12 20:06+0000\n" "PO-Revision-Date: 2021-11-12 20:06+0000\n"
"Last-Translator: Henrique Silva <hds@mailbox.org>\n" "Last-Translator: Henrique Silva <hds@mailbox.org>\n"
"Language-Team: Portuguese <http://translate.tandoor.dev/projects/tandoor/" "Language-Team: Portuguese <http://translate.tandoor.dev/projects/tandoor/"
@ -529,7 +529,7 @@ msgstr ""
msgid "You have more users than allowed in your space." msgid "You have more users than allowed in your space."
msgstr "" msgstr ""
#: .\cookbook\helper\recipe_search.py:560 #: .\cookbook\helper\recipe_search.py:565
msgid "One of queryset or hash_key must be provided" msgid "One of queryset or hash_key must be provided"
msgstr "" msgstr ""
@ -544,12 +544,12 @@ msgstr "É necessário inserir uma receita ou um título."
msgid "Could not parse template code." msgid "Could not parse template code."
msgstr "" msgstr ""
#: .\cookbook\integration\copymethat.py:42 #: .\cookbook\integration\copymethat.py:41
#: .\cookbook\integration\melarecipes.py:37 #: .\cookbook\integration\melarecipes.py:37
msgid "Favorite" msgid "Favorite"
msgstr "" msgstr ""
#: .\cookbook\integration\copymethat.py:71 #: .\cookbook\integration\copymethat.py:70
#: .\cookbook\integration\recettetek.py:54 #: .\cookbook\integration\recettetek.py:54
#: .\cookbook\integration\recipekeeper.py:63 #: .\cookbook\integration\recipekeeper.py:63
#, fuzzy #, fuzzy
@ -685,127 +685,127 @@ msgstr "Novo"
msgid " is part of a recipe step and cannot be deleted" msgid " is part of a recipe step and cannot be deleted"
msgstr "" msgstr ""
#: .\cookbook\models.py:1160 .\cookbook\templates\search_info.html:28 #: .\cookbook\models.py:1162 .\cookbook\templates\search_info.html:28
msgid "Simple" msgid "Simple"
msgstr "" msgstr ""
#: .\cookbook\models.py:1161 .\cookbook\templates\search_info.html:33 #: .\cookbook\models.py:1163 .\cookbook\templates\search_info.html:33
msgid "Phrase" msgid "Phrase"
msgstr "" msgstr ""
#: .\cookbook\models.py:1162 .\cookbook\templates\search_info.html:38 #: .\cookbook\models.py:1164 .\cookbook\templates\search_info.html:38
msgid "Web" msgid "Web"
msgstr "" msgstr ""
#: .\cookbook\models.py:1163 .\cookbook\templates\search_info.html:47 #: .\cookbook\models.py:1165 .\cookbook\templates\search_info.html:47
msgid "Raw" msgid "Raw"
msgstr "" msgstr ""
#: .\cookbook\models.py:1201 #: .\cookbook\models.py:1203
#, fuzzy #, fuzzy
#| msgid "New Food" #| msgid "New Food"
msgid "Food Alias" msgid "Food Alias"
msgstr "Novo Prato" msgstr "Novo Prato"
#: .\cookbook\models.py:1201 #: .\cookbook\models.py:1203
#, fuzzy #, fuzzy
#| msgid "Units" #| msgid "Units"
msgid "Unit Alias" msgid "Unit Alias"
msgstr "Unidades" msgstr "Unidades"
#: .\cookbook\models.py:1201 #: .\cookbook\models.py:1203
#, fuzzy #, fuzzy
#| msgid "Keywords" #| msgid "Keywords"
msgid "Keyword Alias" msgid "Keyword Alias"
msgstr "Palavras-chave" msgstr "Palavras-chave"
#: .\cookbook\models.py:1225 #: .\cookbook\models.py:1227
#: .\cookbook\templates\include\recipe_open_modal.html:7 #: .\cookbook\templates\include\recipe_open_modal.html:7
#: .\cookbook\views\delete.py:36 .\cookbook\views\edit.py:251 #: .\cookbook\views\delete.py:36 .\cookbook\views\edit.py:251
#: .\cookbook\views\new.py:48 #: .\cookbook\views\new.py:48
msgid "Recipe" msgid "Recipe"
msgstr "Receita" msgstr "Receita"
#: .\cookbook\models.py:1226 #: .\cookbook\models.py:1228
#, fuzzy #, fuzzy
#| msgid "New Food" #| msgid "New Food"
msgid "Food" msgid "Food"
msgstr "Novo Prato" msgstr "Novo Prato"
#: .\cookbook\models.py:1227 .\cookbook\templates\base.html:138 #: .\cookbook\models.py:1229 .\cookbook\templates\base.html:138
msgid "Keyword" msgid "Keyword"
msgstr "Palavra-chave" msgstr "Palavra-chave"
#: .\cookbook\serializer.py:204 #: .\cookbook\serializer.py:207
msgid "Cannot modify Space owner permission." msgid "Cannot modify Space owner permission."
msgstr "" msgstr ""
#: .\cookbook\serializer.py:273 #: .\cookbook\serializer.py:290
msgid "File uploads are not enabled for this Space." msgid "File uploads are not enabled for this Space."
msgstr "" msgstr ""
#: .\cookbook\serializer.py:284 #: .\cookbook\serializer.py:301
msgid "You have reached your file upload limit." msgid "You have reached your file upload limit."
msgstr "" msgstr ""
#: .\cookbook\serializer.py:1051 #: .\cookbook\serializer.py:1081
msgid "Hello" msgid "Hello"
msgstr "" msgstr ""
#: .\cookbook\serializer.py:1051 #: .\cookbook\serializer.py:1081
msgid "You have been invited by " msgid "You have been invited by "
msgstr "" msgstr ""
#: .\cookbook\serializer.py:1052 #: .\cookbook\serializer.py:1082
msgid " to join their Tandoor Recipes space " msgid " to join their Tandoor Recipes space "
msgstr "" msgstr ""
#: .\cookbook\serializer.py:1053 #: .\cookbook\serializer.py:1083
msgid "Click the following link to activate your account: " msgid "Click the following link to activate your account: "
msgstr "" msgstr ""
#: .\cookbook\serializer.py:1054 #: .\cookbook\serializer.py:1084
msgid "" msgid ""
"If the link does not work use the following code to manually join the space: " "If the link does not work use the following code to manually join the space: "
msgstr "" msgstr ""
#: .\cookbook\serializer.py:1055 #: .\cookbook\serializer.py:1085
msgid "The invitation is valid until " msgid "The invitation is valid until "
msgstr "" msgstr ""
#: .\cookbook\serializer.py:1056 #: .\cookbook\serializer.py:1086
msgid "" msgid ""
"Tandoor Recipes is an Open Source recipe manager. Check it out on GitHub " "Tandoor Recipes is an Open Source recipe manager. Check it out on GitHub "
msgstr "" msgstr ""
#: .\cookbook\serializer.py:1059 #: .\cookbook\serializer.py:1089
msgid "Tandoor Recipes Invite" msgid "Tandoor Recipes Invite"
msgstr "" msgstr ""
#: .\cookbook\serializer.py:1179 #: .\cookbook\serializer.py:1209
msgid "Existing shopping list to update" msgid "Existing shopping list to update"
msgstr "" msgstr ""
#: .\cookbook\serializer.py:1181 #: .\cookbook\serializer.py:1211
msgid "" msgid ""
"List of ingredient IDs from the recipe to add, if not provided all " "List of ingredient IDs from the recipe to add, if not provided all "
"ingredients will be added." "ingredients will be added."
msgstr "" msgstr ""
#: .\cookbook\serializer.py:1183 #: .\cookbook\serializer.py:1213
msgid "" msgid ""
"Providing a list_recipe ID and servings of 0 will delete that shopping list." "Providing a list_recipe ID and servings of 0 will delete that shopping list."
msgstr "" msgstr ""
#: .\cookbook\serializer.py:1192 #: .\cookbook\serializer.py:1222
msgid "Amount of food to add to the shopping list" msgid "Amount of food to add to the shopping list"
msgstr "" msgstr ""
#: .\cookbook\serializer.py:1194 #: .\cookbook\serializer.py:1224
msgid "ID of unit to use for the shopping list" msgid "ID of unit to use for the shopping list"
msgstr "" msgstr ""
#: .\cookbook\serializer.py:1196 #: .\cookbook\serializer.py:1226
msgid "When set to true will delete all food from active shopping lists." msgid "When set to true will delete all food from active shopping lists."
msgstr "" msgstr ""
@ -939,7 +939,7 @@ msgid ""
msgstr "" msgstr ""
#: .\cookbook\templates\account\login.html:8 #: .\cookbook\templates\account\login.html:8
#: .\cookbook\templates\base.html:339 .\cookbook\templates\openid\login.html:8 #: .\cookbook\templates\base.html:340 .\cookbook\templates\openid\login.html:8
msgid "Login" msgid "Login"
msgstr "Iniciar sessão" msgstr "Iniciar sessão"
@ -1107,7 +1107,7 @@ msgstr ""
msgid "We are sorry, but the sign up is currently closed." msgid "We are sorry, but the sign up is currently closed."
msgstr "" msgstr ""
#: .\cookbook\templates\api_info.html:5 .\cookbook\templates\base.html:329 #: .\cookbook\templates\api_info.html:5 .\cookbook\templates\base.html:330
#: .\cookbook\templates\rest_framework\api.html:11 #: .\cookbook\templates\rest_framework\api.html:11
msgid "API Documentation" msgid "API Documentation"
msgstr "Documentação API" msgstr "Documentação API"
@ -1212,36 +1212,36 @@ msgstr "Administração"
msgid "Your Spaces" msgid "Your Spaces"
msgstr "Criar" msgstr "Criar"
#: .\cookbook\templates\base.html:319 #: .\cookbook\templates\base.html:320
#: .\cookbook\templates\space_overview.html:6 #: .\cookbook\templates\space_overview.html:6
msgid "Overview" msgid "Overview"
msgstr "" msgstr ""
#: .\cookbook\templates\base.html:323 #: .\cookbook\templates\base.html:324
msgid "Markdown Guide" msgid "Markdown Guide"
msgstr "" msgstr ""
#: .\cookbook\templates\base.html:325 #: .\cookbook\templates\base.html:326
msgid "GitHub" msgid "GitHub"
msgstr "GitHub" msgstr "GitHub"
#: .\cookbook\templates\base.html:327 #: .\cookbook\templates\base.html:328
msgid "Translate Tandoor" msgid "Translate Tandoor"
msgstr "" msgstr ""
#: .\cookbook\templates\base.html:331 #: .\cookbook\templates\base.html:332
msgid "API Browser" msgid "API Browser"
msgstr "Navegador de API" msgstr "Navegador de API"
#: .\cookbook\templates\base.html:334 #: .\cookbook\templates\base.html:335
msgid "Log out" msgid "Log out"
msgstr "" msgstr ""
#: .\cookbook\templates\base.html:356 #: .\cookbook\templates\base.html:357
msgid "You are using the free version of Tandor" msgid "You are using the free version of Tandor"
msgstr "" msgstr ""
#: .\cookbook\templates\base.html:357 #: .\cookbook\templates\base.html:358
msgid "Upgrade Now" msgid "Upgrade Now"
msgstr "" msgstr ""
@ -2241,19 +2241,11 @@ msgstr ""
msgid "Internal Recipes" msgid "Internal Recipes"
msgstr "" msgstr ""
#: .\cookbook\templates\system.html:21 .\cookbook\views\lists.py:76 #: .\cookbook\templates\system.html:20
msgid "Invite Links"
msgstr ""
#: .\cookbook\templates\system.html:22
msgid "Show Links"
msgstr ""
#: .\cookbook\templates\system.html:32
msgid "System Information" msgid "System Information"
msgstr "" msgstr ""
#: .\cookbook\templates\system.html:34 #: .\cookbook\templates\system.html:22
msgid "" msgid ""
"\n" "\n"
" Django Recipes is an open source free software application. It can " " Django Recipes is an open source free software application. It can "
@ -2264,21 +2256,21 @@ msgid ""
" " " "
msgstr "" msgstr ""
#: .\cookbook\templates\system.html:48 #: .\cookbook\templates\system.html:36
msgid "Media Serving" msgid "Media Serving"
msgstr "" msgstr ""
#: .\cookbook\templates\system.html:49 .\cookbook\templates\system.html:64 #: .\cookbook\templates\system.html:37 .\cookbook\templates\system.html:52
#: .\cookbook\templates\system.html:80 #: .\cookbook\templates\system.html:68
msgid "Warning" msgid "Warning"
msgstr "" msgstr ""
#: .\cookbook\templates\system.html:49 .\cookbook\templates\system.html:64 #: .\cookbook\templates\system.html:37 .\cookbook\templates\system.html:52
#: .\cookbook\templates\system.html:80 .\cookbook\templates\system.html:95 #: .\cookbook\templates\system.html:68 .\cookbook\templates\system.html:83
msgid "Ok" msgid "Ok"
msgstr "" msgstr ""
#: .\cookbook\templates\system.html:51 #: .\cookbook\templates\system.html:39
msgid "" msgid ""
"Serving media files directly using gunicorn/python is <b>not recommend</b>!\n" "Serving media files directly using gunicorn/python is <b>not recommend</b>!\n"
" Please follow the steps described\n" " Please follow the steps described\n"
@ -2288,16 +2280,16 @@ msgid ""
" " " "
msgstr "" msgstr ""
#: .\cookbook\templates\system.html:57 .\cookbook\templates\system.html:73 #: .\cookbook\templates\system.html:45 .\cookbook\templates\system.html:61
#: .\cookbook\templates\system.html:88 .\cookbook\templates\system.html:102 #: .\cookbook\templates\system.html:76 .\cookbook\templates\system.html:90
msgid "Everything is fine!" msgid "Everything is fine!"
msgstr "" msgstr ""
#: .\cookbook\templates\system.html:62 #: .\cookbook\templates\system.html:50
msgid "Secret Key" msgid "Secret Key"
msgstr "" msgstr ""
#: .\cookbook\templates\system.html:66 #: .\cookbook\templates\system.html:54
msgid "" msgid ""
"\n" "\n"
" You do not have a <code>SECRET_KEY</code> configured in your " " You do not have a <code>SECRET_KEY</code> configured in your "
@ -2310,11 +2302,11 @@ msgid ""
" " " "
msgstr "" msgstr ""
#: .\cookbook\templates\system.html:78 #: .\cookbook\templates\system.html:66
msgid "Debug Mode" msgid "Debug Mode"
msgstr "" msgstr ""
#: .\cookbook\templates\system.html:82 #: .\cookbook\templates\system.html:70
msgid "" msgid ""
"\n" "\n"
" This application is still running in debug mode. This is most " " This application is still running in debug mode. This is most "
@ -2325,15 +2317,15 @@ msgid ""
" " " "
msgstr "" msgstr ""
#: .\cookbook\templates\system.html:93 #: .\cookbook\templates\system.html:81
msgid "Database" msgid "Database"
msgstr "" msgstr ""
#: .\cookbook\templates\system.html:95 #: .\cookbook\templates\system.html:83
msgid "Info" msgid "Info"
msgstr "" msgstr ""
#: .\cookbook\templates\system.html:97 #: .\cookbook\templates\system.html:85
msgid "" msgid ""
"\n" "\n"
" This application is not running with a Postgres database " " This application is not running with a Postgres database "
@ -2346,245 +2338,245 @@ msgstr ""
msgid "URL Import" msgid "URL Import"
msgstr "" msgstr ""
#: .\cookbook\views\api.py:97 .\cookbook\views\api.py:189 #: .\cookbook\views\api.py:105 .\cookbook\views\api.py:197
msgid "Parameter updated_at incorrectly formatted" msgid "Parameter updated_at incorrectly formatted"
msgstr "" msgstr ""
#: .\cookbook\views\api.py:209 .\cookbook\views\api.py:312 #: .\cookbook\views\api.py:217 .\cookbook\views\api.py:320
msgid "No {self.basename} with id {pk} exists" msgid "No {self.basename} with id {pk} exists"
msgstr "" msgstr ""
#: .\cookbook\views\api.py:213 #: .\cookbook\views\api.py:221
msgid "Cannot merge with the same object!" msgid "Cannot merge with the same object!"
msgstr "" msgstr ""
#: .\cookbook\views\api.py:220 #: .\cookbook\views\api.py:228
msgid "No {self.basename} with id {target} exists" msgid "No {self.basename} with id {target} exists"
msgstr "" msgstr ""
#: .\cookbook\views\api.py:225 #: .\cookbook\views\api.py:233
msgid "Cannot merge with child object!" msgid "Cannot merge with child object!"
msgstr "" msgstr ""
#: .\cookbook\views\api.py:258 #: .\cookbook\views\api.py:266
msgid "{source.name} was merged successfully with {target.name}" msgid "{source.name} was merged successfully with {target.name}"
msgstr "" msgstr ""
#: .\cookbook\views\api.py:263 #: .\cookbook\views\api.py:271
msgid "An error occurred attempting to merge {source.name} with {target.name}" msgid "An error occurred attempting to merge {source.name} with {target.name}"
msgstr "" msgstr ""
#: .\cookbook\views\api.py:321 #: .\cookbook\views\api.py:329
msgid "{child.name} was moved successfully to the root." msgid "{child.name} was moved successfully to the root."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:324 .\cookbook\views\api.py:342 #: .\cookbook\views\api.py:332 .\cookbook\views\api.py:350
msgid "An error occurred attempting to move " msgid "An error occurred attempting to move "
msgstr "" msgstr ""
#: .\cookbook\views\api.py:327 #: .\cookbook\views\api.py:335
msgid "Cannot move an object to itself!" msgid "Cannot move an object to itself!"
msgstr "" msgstr ""
#: .\cookbook\views\api.py:333 #: .\cookbook\views\api.py:341
msgid "No {self.basename} with id {parent} exists" msgid "No {self.basename} with id {parent} exists"
msgstr "" msgstr ""
#: .\cookbook\views\api.py:339 #: .\cookbook\views\api.py:347
msgid "{child.name} was moved successfully to parent {parent.name}" msgid "{child.name} was moved successfully to parent {parent.name}"
msgstr "" msgstr ""
#: .\cookbook\views\api.py:534 #: .\cookbook\views\api.py:542
msgid "{obj.name} was removed from the shopping list." msgid "{obj.name} was removed from the shopping list."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:539 .\cookbook\views\api.py:871 #: .\cookbook\views\api.py:547 .\cookbook\views\api.py:879
#: .\cookbook\views\api.py:884 #: .\cookbook\views\api.py:892
msgid "{obj.name} was added to the shopping list." msgid "{obj.name} was added to the shopping list."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:666 #: .\cookbook\views\api.py:674
msgid "ID of recipe a step is part of. For multiple repeat parameter." msgid "ID of recipe a step is part of. For multiple repeat parameter."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:668 #: .\cookbook\views\api.py:676
msgid "Query string matched (fuzzy) against object name." msgid "Query string matched (fuzzy) against object name."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:712 #: .\cookbook\views\api.py:720
msgid "" msgid ""
"Query string matched (fuzzy) against recipe name. In the future also " "Query string matched (fuzzy) against recipe name. In the future also "
"fulltext search." "fulltext search."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:714 #: .\cookbook\views\api.py:722
msgid "" msgid ""
"ID of keyword a recipe should have. For multiple repeat parameter. " "ID of keyword a recipe should have. For multiple repeat parameter. "
"Equivalent to keywords_or" "Equivalent to keywords_or"
msgstr "" msgstr ""
#: .\cookbook\views\api.py:717 #: .\cookbook\views\api.py:725
msgid "" msgid ""
"Keyword IDs, repeat for multiple. Return recipes with any of the keywords" "Keyword IDs, repeat for multiple. Return recipes with any of the keywords"
msgstr "" msgstr ""
#: .\cookbook\views\api.py:720 #: .\cookbook\views\api.py:728
msgid "" msgid ""
"Keyword IDs, repeat for multiple. Return recipes with all of the keywords." "Keyword IDs, repeat for multiple. Return recipes with all of the keywords."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:723 #: .\cookbook\views\api.py:731
msgid "" msgid ""
"Keyword IDs, repeat for multiple. Exclude recipes with any of the keywords." "Keyword IDs, repeat for multiple. Exclude recipes with any of the keywords."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:726 #: .\cookbook\views\api.py:734
msgid "" msgid ""
"Keyword IDs, repeat for multiple. Exclude recipes with all of the keywords." "Keyword IDs, repeat for multiple. Exclude recipes with all of the keywords."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:728 #: .\cookbook\views\api.py:736
msgid "ID of food a recipe should have. For multiple repeat parameter." msgid "ID of food a recipe should have. For multiple repeat parameter."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:731 #: .\cookbook\views\api.py:739
msgid "Food IDs, repeat for multiple. Return recipes with any of the foods" msgid "Food IDs, repeat for multiple. Return recipes with any of the foods"
msgstr "" msgstr ""
#: .\cookbook\views\api.py:733 #: .\cookbook\views\api.py:741
msgid "Food IDs, repeat for multiple. Return recipes with all of the foods." msgid "Food IDs, repeat for multiple. Return recipes with all of the foods."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:735 #: .\cookbook\views\api.py:743
msgid "Food IDs, repeat for multiple. Exclude recipes with any of the foods." msgid "Food IDs, repeat for multiple. Exclude recipes with any of the foods."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:737 #: .\cookbook\views\api.py:745
msgid "Food IDs, repeat for multiple. Exclude recipes with all of the foods." msgid "Food IDs, repeat for multiple. Exclude recipes with all of the foods."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:738 #: .\cookbook\views\api.py:746
msgid "ID of unit a recipe should have." msgid "ID of unit a recipe should have."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:740 #: .\cookbook\views\api.py:748
msgid "" msgid ""
"Rating a recipe should have or greater. [0 - 5] Negative value filters " "Rating a recipe should have or greater. [0 - 5] Negative value filters "
"rating less than." "rating less than."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:741 #: .\cookbook\views\api.py:749
msgid "ID of book a recipe should be in. For multiple repeat parameter." msgid "ID of book a recipe should be in. For multiple repeat parameter."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:743 #: .\cookbook\views\api.py:751
msgid "Book IDs, repeat for multiple. Return recipes with any of the books" msgid "Book IDs, repeat for multiple. Return recipes with any of the books"
msgstr "" msgstr ""
#: .\cookbook\views\api.py:745 #: .\cookbook\views\api.py:753
msgid "Book IDs, repeat for multiple. Return recipes with all of the books." msgid "Book IDs, repeat for multiple. Return recipes with all of the books."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:747 #: .\cookbook\views\api.py:755
msgid "Book IDs, repeat for multiple. Exclude recipes with any of the books." msgid "Book IDs, repeat for multiple. Exclude recipes with any of the books."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:749 #: .\cookbook\views\api.py:757
msgid "Book IDs, repeat for multiple. Exclude recipes with all of the books." msgid "Book IDs, repeat for multiple. Exclude recipes with all of the books."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:751 #: .\cookbook\views\api.py:759
msgid "If only internal recipes should be returned. [true/<b>false</b>]" msgid "If only internal recipes should be returned. [true/<b>false</b>]"
msgstr "" msgstr ""
#: .\cookbook\views\api.py:753 #: .\cookbook\views\api.py:761
msgid "Returns the results in randomized order. [true/<b>false</b>]" msgid "Returns the results in randomized order. [true/<b>false</b>]"
msgstr "" msgstr ""
#: .\cookbook\views\api.py:755 #: .\cookbook\views\api.py:763
msgid "Returns new results first in search results. [true/<b>false</b>]" msgid "Returns new results first in search results. [true/<b>false</b>]"
msgstr "" msgstr ""
#: .\cookbook\views\api.py:757 #: .\cookbook\views\api.py:765
msgid "" msgid ""
"Filter recipes cooked X times or more. Negative values returns cooked less " "Filter recipes cooked X times or more. Negative values returns cooked less "
"than X times" "than X times"
msgstr "" msgstr ""
#: .\cookbook\views\api.py:759 #: .\cookbook\views\api.py:767
msgid "" msgid ""
"Filter recipes last cooked on or after YYYY-MM-DD. Prepending - filters on " "Filter recipes last cooked on or after YYYY-MM-DD. Prepending - filters on "
"or before date." "or before date."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:761 #: .\cookbook\views\api.py:769
msgid "" msgid ""
"Filter recipes created on or after YYYY-MM-DD. Prepending - filters on or " "Filter recipes created on or after YYYY-MM-DD. Prepending - filters on or "
"before date." "before date."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:763 #: .\cookbook\views\api.py:771
msgid "" msgid ""
"Filter recipes updated on or after YYYY-MM-DD. Prepending - filters on or " "Filter recipes updated on or after YYYY-MM-DD. Prepending - filters on or "
"before date." "before date."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:765 #: .\cookbook\views\api.py:773
msgid "" msgid ""
"Filter recipes lasts viewed on or after YYYY-MM-DD. Prepending - filters on " "Filter recipes lasts viewed on or after YYYY-MM-DD. Prepending - filters on "
"or before date." "or before date."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:767 #: .\cookbook\views\api.py:775
msgid "Filter recipes that can be made with OnHand food. [true/<b>false</b>]" msgid "Filter recipes that can be made with OnHand food. [true/<b>false</b>]"
msgstr "" msgstr ""
#: .\cookbook\views\api.py:929 #: .\cookbook\views\api.py:937
msgid "" msgid ""
"Returns the shopping list entry with a primary key of id. Multiple values " "Returns the shopping list entry with a primary key of id. Multiple values "
"allowed." "allowed."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:934 #: .\cookbook\views\api.py:942
msgid "" msgid ""
"Filter shopping list entries on checked. [true, false, both, <b>recent</b>]" "Filter shopping list entries on checked. [true, false, both, <b>recent</b>]"
"<br> - recent includes unchecked items and recently completed items." "<br> - recent includes unchecked items and recently completed items."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:937 #: .\cookbook\views\api.py:945
msgid "Returns the shopping list entries sorted by supermarket category order." msgid "Returns the shopping list entries sorted by supermarket category order."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:1134 #: .\cookbook\views\api.py:1140
msgid "Nothing to do." msgid "Nothing to do."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:1153 #: .\cookbook\views\api.py:1160
msgid "Invalid Url" msgid "Invalid Url"
msgstr "" msgstr ""
#: .\cookbook\views\api.py:1158 #: .\cookbook\views\api.py:1167
msgid "Connection Refused." msgid "Connection Refused."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:1163 #: .\cookbook\views\api.py:1172
msgid "Bad URL Schema." msgid "Bad URL Schema."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:1170 #: .\cookbook\views\api.py:1195
msgid "No usable data could be found." msgid "No usable data could be found."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:1260 .\cookbook\views\data.py:28 #: .\cookbook\views\api.py:1303 .\cookbook\views\data.py:28
#: .\cookbook\views\edit.py:120 .\cookbook\views\new.py:90 #: .\cookbook\views\edit.py:120 .\cookbook\views\new.py:90
msgid "This feature is not yet available in the hosted version of tandoor!" msgid "This feature is not yet available in the hosted version of tandoor!"
msgstr "" msgstr ""
#: .\cookbook\views\api.py:1282 #: .\cookbook\views\api.py:1325
msgid "Sync successful!" msgid "Sync successful!"
msgstr "" msgstr ""
#: .\cookbook\views\api.py:1287 #: .\cookbook\views\api.py:1330
msgid "Error synchronizing with Storage" msgid "Error synchronizing with Storage"
msgstr "" msgstr ""
@ -2667,6 +2659,10 @@ msgstr ""
msgid "Shopping List" msgid "Shopping List"
msgstr "" msgstr ""
#: .\cookbook\views\lists.py:76
msgid "Invite Links"
msgstr ""
#: .\cookbook\views\lists.py:139 #: .\cookbook\views\lists.py:139
msgid "Supermarkets" msgid "Supermarkets"
msgstr "" msgstr ""

View File

@ -8,7 +8,7 @@ msgid ""
msgstr "" 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: 2022-06-26 12:09+0200\n" "POT-Creation-Date: 2022-07-12 19:20+0200\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n" "Language-Team: LANGUAGE <LL@li.org>\n"
@ -471,7 +471,7 @@ msgstr ""
msgid "You have more users than allowed in your space." msgid "You have more users than allowed in your space."
msgstr "" msgstr ""
#: .\cookbook\helper\recipe_search.py:560 #: .\cookbook\helper\recipe_search.py:565
msgid "One of queryset or hash_key must be provided" msgid "One of queryset or hash_key must be provided"
msgstr "" msgstr ""
@ -484,12 +484,12 @@ msgstr ""
msgid "Could not parse template code." msgid "Could not parse template code."
msgstr "" msgstr ""
#: .\cookbook\integration\copymethat.py:42 #: .\cookbook\integration\copymethat.py:41
#: .\cookbook\integration\melarecipes.py:37 #: .\cookbook\integration\melarecipes.py:37
msgid "Favorite" msgid "Favorite"
msgstr "" msgstr ""
#: .\cookbook\integration\copymethat.py:71 #: .\cookbook\integration\copymethat.py:70
#: .\cookbook\integration\recettetek.py:54 #: .\cookbook\integration\recettetek.py:54
#: .\cookbook\integration\recipekeeper.py:63 #: .\cookbook\integration\recipekeeper.py:63
msgid "Imported from" msgid "Imported from"
@ -620,119 +620,119 @@ msgstr ""
msgid " is part of a recipe step and cannot be deleted" msgid " is part of a recipe step and cannot be deleted"
msgstr "" msgstr ""
#: .\cookbook\models.py:1160 .\cookbook\templates\search_info.html:28 #: .\cookbook\models.py:1162 .\cookbook\templates\search_info.html:28
msgid "Simple" msgid "Simple"
msgstr "" msgstr ""
#: .\cookbook\models.py:1161 .\cookbook\templates\search_info.html:33 #: .\cookbook\models.py:1163 .\cookbook\templates\search_info.html:33
msgid "Phrase" msgid "Phrase"
msgstr "" msgstr ""
#: .\cookbook\models.py:1162 .\cookbook\templates\search_info.html:38 #: .\cookbook\models.py:1164 .\cookbook\templates\search_info.html:38
msgid "Web" msgid "Web"
msgstr "" msgstr ""
#: .\cookbook\models.py:1163 .\cookbook\templates\search_info.html:47 #: .\cookbook\models.py:1165 .\cookbook\templates\search_info.html:47
msgid "Raw" msgid "Raw"
msgstr "" msgstr ""
#: .\cookbook\models.py:1201 #: .\cookbook\models.py:1203
msgid "Food Alias" msgid "Food Alias"
msgstr "" msgstr ""
#: .\cookbook\models.py:1201 #: .\cookbook\models.py:1203
msgid "Unit Alias" msgid "Unit Alias"
msgstr "" msgstr ""
#: .\cookbook\models.py:1201 #: .\cookbook\models.py:1203
msgid "Keyword Alias" msgid "Keyword Alias"
msgstr "" msgstr ""
#: .\cookbook\models.py:1225 #: .\cookbook\models.py:1227
#: .\cookbook\templates\include\recipe_open_modal.html:7 #: .\cookbook\templates\include\recipe_open_modal.html:7
#: .\cookbook\views\delete.py:36 .\cookbook\views\edit.py:251 #: .\cookbook\views\delete.py:36 .\cookbook\views\edit.py:251
#: .\cookbook\views\new.py:48 #: .\cookbook\views\new.py:48
msgid "Recipe" msgid "Recipe"
msgstr "" msgstr ""
#: .\cookbook\models.py:1226 #: .\cookbook\models.py:1228
msgid "Food" msgid "Food"
msgstr "" msgstr ""
#: .\cookbook\models.py:1227 .\cookbook\templates\base.html:138 #: .\cookbook\models.py:1229 .\cookbook\templates\base.html:138
msgid "Keyword" msgid "Keyword"
msgstr "" msgstr ""
#: .\cookbook\serializer.py:204 #: .\cookbook\serializer.py:207
msgid "Cannot modify Space owner permission." msgid "Cannot modify Space owner permission."
msgstr "" msgstr ""
#: .\cookbook\serializer.py:273 #: .\cookbook\serializer.py:290
msgid "File uploads are not enabled for this Space." msgid "File uploads are not enabled for this Space."
msgstr "" msgstr ""
#: .\cookbook\serializer.py:284 #: .\cookbook\serializer.py:301
msgid "You have reached your file upload limit." msgid "You have reached your file upload limit."
msgstr "" msgstr ""
#: .\cookbook\serializer.py:1051 #: .\cookbook\serializer.py:1081
msgid "Hello" msgid "Hello"
msgstr "" msgstr ""
#: .\cookbook\serializer.py:1051 #: .\cookbook\serializer.py:1081
msgid "You have been invited by " msgid "You have been invited by "
msgstr "" msgstr ""
#: .\cookbook\serializer.py:1052 #: .\cookbook\serializer.py:1082
msgid " to join their Tandoor Recipes space " msgid " to join their Tandoor Recipes space "
msgstr "" msgstr ""
#: .\cookbook\serializer.py:1053 #: .\cookbook\serializer.py:1083
msgid "Click the following link to activate your account: " msgid "Click the following link to activate your account: "
msgstr "" msgstr ""
#: .\cookbook\serializer.py:1054 #: .\cookbook\serializer.py:1084
msgid "" msgid ""
"If the link does not work use the following code to manually join the space: " "If the link does not work use the following code to manually join the space: "
msgstr "" msgstr ""
#: .\cookbook\serializer.py:1055 #: .\cookbook\serializer.py:1085
msgid "The invitation is valid until " msgid "The invitation is valid until "
msgstr "" msgstr ""
#: .\cookbook\serializer.py:1056 #: .\cookbook\serializer.py:1086
msgid "" msgid ""
"Tandoor Recipes is an Open Source recipe manager. Check it out on GitHub " "Tandoor Recipes is an Open Source recipe manager. Check it out on GitHub "
msgstr "" msgstr ""
#: .\cookbook\serializer.py:1059 #: .\cookbook\serializer.py:1089
msgid "Tandoor Recipes Invite" msgid "Tandoor Recipes Invite"
msgstr "" msgstr ""
#: .\cookbook\serializer.py:1179 #: .\cookbook\serializer.py:1209
msgid "Existing shopping list to update" msgid "Existing shopping list to update"
msgstr "" msgstr ""
#: .\cookbook\serializer.py:1181 #: .\cookbook\serializer.py:1211
msgid "" msgid ""
"List of ingredient IDs from the recipe to add, if not provided all " "List of ingredient IDs from the recipe to add, if not provided all "
"ingredients will be added." "ingredients will be added."
msgstr "" msgstr ""
#: .\cookbook\serializer.py:1183 #: .\cookbook\serializer.py:1213
msgid "" msgid ""
"Providing a list_recipe ID and servings of 0 will delete that shopping list." "Providing a list_recipe ID and servings of 0 will delete that shopping list."
msgstr "" msgstr ""
#: .\cookbook\serializer.py:1192 #: .\cookbook\serializer.py:1222
msgid "Amount of food to add to the shopping list" msgid "Amount of food to add to the shopping list"
msgstr "" msgstr ""
#: .\cookbook\serializer.py:1194 #: .\cookbook\serializer.py:1224
msgid "ID of unit to use for the shopping list" msgid "ID of unit to use for the shopping list"
msgstr "" msgstr ""
#: .\cookbook\serializer.py:1196 #: .\cookbook\serializer.py:1226
msgid "When set to true will delete all food from active shopping lists." msgid "When set to true will delete all food from active shopping lists."
msgstr "" msgstr ""
@ -864,7 +864,7 @@ msgid ""
msgstr "" msgstr ""
#: .\cookbook\templates\account\login.html:8 #: .\cookbook\templates\account\login.html:8
#: .\cookbook\templates\base.html:339 .\cookbook\templates\openid\login.html:8 #: .\cookbook\templates\base.html:340 .\cookbook\templates\openid\login.html:8
msgid "Login" msgid "Login"
msgstr "" msgstr ""
@ -1028,7 +1028,7 @@ msgstr ""
msgid "We are sorry, but the sign up is currently closed." msgid "We are sorry, but the sign up is currently closed."
msgstr "" msgstr ""
#: .\cookbook\templates\api_info.html:5 .\cookbook\templates\base.html:329 #: .\cookbook\templates\api_info.html:5 .\cookbook\templates\base.html:330
#: .\cookbook\templates\rest_framework\api.html:11 #: .\cookbook\templates\rest_framework\api.html:11
msgid "API Documentation" msgid "API Documentation"
msgstr "" msgstr ""
@ -1121,36 +1121,36 @@ msgstr ""
msgid "Your Spaces" msgid "Your Spaces"
msgstr "" msgstr ""
#: .\cookbook\templates\base.html:319 #: .\cookbook\templates\base.html:320
#: .\cookbook\templates\space_overview.html:6 #: .\cookbook\templates\space_overview.html:6
msgid "Overview" msgid "Overview"
msgstr "" msgstr ""
#: .\cookbook\templates\base.html:323 #: .\cookbook\templates\base.html:324
msgid "Markdown Guide" msgid "Markdown Guide"
msgstr "" msgstr ""
#: .\cookbook\templates\base.html:325 #: .\cookbook\templates\base.html:326
msgid "GitHub" msgid "GitHub"
msgstr "" msgstr ""
#: .\cookbook\templates\base.html:327 #: .\cookbook\templates\base.html:328
msgid "Translate Tandoor" msgid "Translate Tandoor"
msgstr "" msgstr ""
#: .\cookbook\templates\base.html:331 #: .\cookbook\templates\base.html:332
msgid "API Browser" msgid "API Browser"
msgstr "" msgstr ""
#: .\cookbook\templates\base.html:334 #: .\cookbook\templates\base.html:335
msgid "Log out" msgid "Log out"
msgstr "" msgstr ""
#: .\cookbook\templates\base.html:356 #: .\cookbook\templates\base.html:357
msgid "You are using the free version of Tandor" msgid "You are using the free version of Tandor"
msgstr "" msgstr ""
#: .\cookbook\templates\base.html:357 #: .\cookbook\templates\base.html:358
msgid "Upgrade Now" msgid "Upgrade Now"
msgstr "" msgstr ""
@ -2091,19 +2091,11 @@ msgstr ""
msgid "Internal Recipes" msgid "Internal Recipes"
msgstr "" msgstr ""
#: .\cookbook\templates\system.html:21 .\cookbook\views\lists.py:76 #: .\cookbook\templates\system.html:20
msgid "Invite Links"
msgstr ""
#: .\cookbook\templates\system.html:22
msgid "Show Links"
msgstr ""
#: .\cookbook\templates\system.html:32
msgid "System Information" msgid "System Information"
msgstr "" msgstr ""
#: .\cookbook\templates\system.html:34 #: .\cookbook\templates\system.html:22
msgid "" msgid ""
"\n" "\n"
" Django Recipes is an open source free software application. It can " " Django Recipes is an open source free software application. It can "
@ -2114,21 +2106,21 @@ msgid ""
" " " "
msgstr "" msgstr ""
#: .\cookbook\templates\system.html:48 #: .\cookbook\templates\system.html:36
msgid "Media Serving" msgid "Media Serving"
msgstr "" msgstr ""
#: .\cookbook\templates\system.html:49 .\cookbook\templates\system.html:64 #: .\cookbook\templates\system.html:37 .\cookbook\templates\system.html:52
#: .\cookbook\templates\system.html:80 #: .\cookbook\templates\system.html:68
msgid "Warning" msgid "Warning"
msgstr "" msgstr ""
#: .\cookbook\templates\system.html:49 .\cookbook\templates\system.html:64 #: .\cookbook\templates\system.html:37 .\cookbook\templates\system.html:52
#: .\cookbook\templates\system.html:80 .\cookbook\templates\system.html:95 #: .\cookbook\templates\system.html:68 .\cookbook\templates\system.html:83
msgid "Ok" msgid "Ok"
msgstr "" msgstr ""
#: .\cookbook\templates\system.html:51 #: .\cookbook\templates\system.html:39
msgid "" msgid ""
"Serving media files directly using gunicorn/python is <b>not recommend</b>!\n" "Serving media files directly using gunicorn/python is <b>not recommend</b>!\n"
" Please follow the steps described\n" " Please follow the steps described\n"
@ -2138,16 +2130,16 @@ msgid ""
" " " "
msgstr "" msgstr ""
#: .\cookbook\templates\system.html:57 .\cookbook\templates\system.html:73 #: .\cookbook\templates\system.html:45 .\cookbook\templates\system.html:61
#: .\cookbook\templates\system.html:88 .\cookbook\templates\system.html:102 #: .\cookbook\templates\system.html:76 .\cookbook\templates\system.html:90
msgid "Everything is fine!" msgid "Everything is fine!"
msgstr "" msgstr ""
#: .\cookbook\templates\system.html:62 #: .\cookbook\templates\system.html:50
msgid "Secret Key" msgid "Secret Key"
msgstr "" msgstr ""
#: .\cookbook\templates\system.html:66 #: .\cookbook\templates\system.html:54
msgid "" msgid ""
"\n" "\n"
" You do not have a <code>SECRET_KEY</code> configured in your " " You do not have a <code>SECRET_KEY</code> configured in your "
@ -2160,11 +2152,11 @@ msgid ""
" " " "
msgstr "" msgstr ""
#: .\cookbook\templates\system.html:78 #: .\cookbook\templates\system.html:66
msgid "Debug Mode" msgid "Debug Mode"
msgstr "" msgstr ""
#: .\cookbook\templates\system.html:82 #: .\cookbook\templates\system.html:70
msgid "" msgid ""
"\n" "\n"
" This application is still running in debug mode. This is most " " This application is still running in debug mode. This is most "
@ -2175,15 +2167,15 @@ msgid ""
" " " "
msgstr "" msgstr ""
#: .\cookbook\templates\system.html:93 #: .\cookbook\templates\system.html:81
msgid "Database" msgid "Database"
msgstr "" msgstr ""
#: .\cookbook\templates\system.html:95 #: .\cookbook\templates\system.html:83
msgid "Info" msgid "Info"
msgstr "" msgstr ""
#: .\cookbook\templates\system.html:97 #: .\cookbook\templates\system.html:85
msgid "" msgid ""
"\n" "\n"
" This application is not running with a Postgres database " " This application is not running with a Postgres database "
@ -2196,245 +2188,245 @@ msgstr ""
msgid "URL Import" msgid "URL Import"
msgstr "" msgstr ""
#: .\cookbook\views\api.py:97 .\cookbook\views\api.py:189 #: .\cookbook\views\api.py:105 .\cookbook\views\api.py:197
msgid "Parameter updated_at incorrectly formatted" msgid "Parameter updated_at incorrectly formatted"
msgstr "" msgstr ""
#: .\cookbook\views\api.py:209 .\cookbook\views\api.py:312 #: .\cookbook\views\api.py:217 .\cookbook\views\api.py:320
msgid "No {self.basename} with id {pk} exists" msgid "No {self.basename} with id {pk} exists"
msgstr "" msgstr ""
#: .\cookbook\views\api.py:213 #: .\cookbook\views\api.py:221
msgid "Cannot merge with the same object!" msgid "Cannot merge with the same object!"
msgstr "" msgstr ""
#: .\cookbook\views\api.py:220 #: .\cookbook\views\api.py:228
msgid "No {self.basename} with id {target} exists" msgid "No {self.basename} with id {target} exists"
msgstr "" msgstr ""
#: .\cookbook\views\api.py:225 #: .\cookbook\views\api.py:233
msgid "Cannot merge with child object!" msgid "Cannot merge with child object!"
msgstr "" msgstr ""
#: .\cookbook\views\api.py:258 #: .\cookbook\views\api.py:266
msgid "{source.name} was merged successfully with {target.name}" msgid "{source.name} was merged successfully with {target.name}"
msgstr "" msgstr ""
#: .\cookbook\views\api.py:263 #: .\cookbook\views\api.py:271
msgid "An error occurred attempting to merge {source.name} with {target.name}" msgid "An error occurred attempting to merge {source.name} with {target.name}"
msgstr "" msgstr ""
#: .\cookbook\views\api.py:321 #: .\cookbook\views\api.py:329
msgid "{child.name} was moved successfully to the root." msgid "{child.name} was moved successfully to the root."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:324 .\cookbook\views\api.py:342 #: .\cookbook\views\api.py:332 .\cookbook\views\api.py:350
msgid "An error occurred attempting to move " msgid "An error occurred attempting to move "
msgstr "" msgstr ""
#: .\cookbook\views\api.py:327 #: .\cookbook\views\api.py:335
msgid "Cannot move an object to itself!" msgid "Cannot move an object to itself!"
msgstr "" msgstr ""
#: .\cookbook\views\api.py:333 #: .\cookbook\views\api.py:341
msgid "No {self.basename} with id {parent} exists" msgid "No {self.basename} with id {parent} exists"
msgstr "" msgstr ""
#: .\cookbook\views\api.py:339 #: .\cookbook\views\api.py:347
msgid "{child.name} was moved successfully to parent {parent.name}" msgid "{child.name} was moved successfully to parent {parent.name}"
msgstr "" msgstr ""
#: .\cookbook\views\api.py:534 #: .\cookbook\views\api.py:542
msgid "{obj.name} was removed from the shopping list." msgid "{obj.name} was removed from the shopping list."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:539 .\cookbook\views\api.py:871 #: .\cookbook\views\api.py:547 .\cookbook\views\api.py:879
#: .\cookbook\views\api.py:884 #: .\cookbook\views\api.py:892
msgid "{obj.name} was added to the shopping list." msgid "{obj.name} was added to the shopping list."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:666 #: .\cookbook\views\api.py:674
msgid "ID of recipe a step is part of. For multiple repeat parameter." msgid "ID of recipe a step is part of. For multiple repeat parameter."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:668 #: .\cookbook\views\api.py:676
msgid "Query string matched (fuzzy) against object name." msgid "Query string matched (fuzzy) against object name."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:712 #: .\cookbook\views\api.py:720
msgid "" msgid ""
"Query string matched (fuzzy) against recipe name. In the future also " "Query string matched (fuzzy) against recipe name. In the future also "
"fulltext search." "fulltext search."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:714 #: .\cookbook\views\api.py:722
msgid "" msgid ""
"ID of keyword a recipe should have. For multiple repeat parameter. " "ID of keyword a recipe should have. For multiple repeat parameter. "
"Equivalent to keywords_or" "Equivalent to keywords_or"
msgstr "" msgstr ""
#: .\cookbook\views\api.py:717 #: .\cookbook\views\api.py:725
msgid "" msgid ""
"Keyword IDs, repeat for multiple. Return recipes with any of the keywords" "Keyword IDs, repeat for multiple. Return recipes with any of the keywords"
msgstr "" msgstr ""
#: .\cookbook\views\api.py:720 #: .\cookbook\views\api.py:728
msgid "" msgid ""
"Keyword IDs, repeat for multiple. Return recipes with all of the keywords." "Keyword IDs, repeat for multiple. Return recipes with all of the keywords."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:723 #: .\cookbook\views\api.py:731
msgid "" msgid ""
"Keyword IDs, repeat for multiple. Exclude recipes with any of the keywords." "Keyword IDs, repeat for multiple. Exclude recipes with any of the keywords."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:726 #: .\cookbook\views\api.py:734
msgid "" msgid ""
"Keyword IDs, repeat for multiple. Exclude recipes with all of the keywords." "Keyword IDs, repeat for multiple. Exclude recipes with all of the keywords."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:728 #: .\cookbook\views\api.py:736
msgid "ID of food a recipe should have. For multiple repeat parameter." msgid "ID of food a recipe should have. For multiple repeat parameter."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:731 #: .\cookbook\views\api.py:739
msgid "Food IDs, repeat for multiple. Return recipes with any of the foods" msgid "Food IDs, repeat for multiple. Return recipes with any of the foods"
msgstr "" msgstr ""
#: .\cookbook\views\api.py:733 #: .\cookbook\views\api.py:741
msgid "Food IDs, repeat for multiple. Return recipes with all of the foods." msgid "Food IDs, repeat for multiple. Return recipes with all of the foods."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:735 #: .\cookbook\views\api.py:743
msgid "Food IDs, repeat for multiple. Exclude recipes with any of the foods." msgid "Food IDs, repeat for multiple. Exclude recipes with any of the foods."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:737 #: .\cookbook\views\api.py:745
msgid "Food IDs, repeat for multiple. Exclude recipes with all of the foods." msgid "Food IDs, repeat for multiple. Exclude recipes with all of the foods."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:738 #: .\cookbook\views\api.py:746
msgid "ID of unit a recipe should have." msgid "ID of unit a recipe should have."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:740 #: .\cookbook\views\api.py:748
msgid "" msgid ""
"Rating a recipe should have or greater. [0 - 5] Negative value filters " "Rating a recipe should have or greater. [0 - 5] Negative value filters "
"rating less than." "rating less than."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:741 #: .\cookbook\views\api.py:749
msgid "ID of book a recipe should be in. For multiple repeat parameter." msgid "ID of book a recipe should be in. For multiple repeat parameter."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:743 #: .\cookbook\views\api.py:751
msgid "Book IDs, repeat for multiple. Return recipes with any of the books" msgid "Book IDs, repeat for multiple. Return recipes with any of the books"
msgstr "" msgstr ""
#: .\cookbook\views\api.py:745 #: .\cookbook\views\api.py:753
msgid "Book IDs, repeat for multiple. Return recipes with all of the books." msgid "Book IDs, repeat for multiple. Return recipes with all of the books."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:747 #: .\cookbook\views\api.py:755
msgid "Book IDs, repeat for multiple. Exclude recipes with any of the books." msgid "Book IDs, repeat for multiple. Exclude recipes with any of the books."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:749 #: .\cookbook\views\api.py:757
msgid "Book IDs, repeat for multiple. Exclude recipes with all of the books." msgid "Book IDs, repeat for multiple. Exclude recipes with all of the books."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:751 #: .\cookbook\views\api.py:759
msgid "If only internal recipes should be returned. [true/<b>false</b>]" msgid "If only internal recipes should be returned. [true/<b>false</b>]"
msgstr "" msgstr ""
#: .\cookbook\views\api.py:753 #: .\cookbook\views\api.py:761
msgid "Returns the results in randomized order. [true/<b>false</b>]" msgid "Returns the results in randomized order. [true/<b>false</b>]"
msgstr "" msgstr ""
#: .\cookbook\views\api.py:755 #: .\cookbook\views\api.py:763
msgid "Returns new results first in search results. [true/<b>false</b>]" msgid "Returns new results first in search results. [true/<b>false</b>]"
msgstr "" msgstr ""
#: .\cookbook\views\api.py:757 #: .\cookbook\views\api.py:765
msgid "" msgid ""
"Filter recipes cooked X times or more. Negative values returns cooked less " "Filter recipes cooked X times or more. Negative values returns cooked less "
"than X times" "than X times"
msgstr "" msgstr ""
#: .\cookbook\views\api.py:759 #: .\cookbook\views\api.py:767
msgid "" msgid ""
"Filter recipes last cooked on or after YYYY-MM-DD. Prepending - filters on " "Filter recipes last cooked on or after YYYY-MM-DD. Prepending - filters on "
"or before date." "or before date."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:761 #: .\cookbook\views\api.py:769
msgid "" msgid ""
"Filter recipes created on or after YYYY-MM-DD. Prepending - filters on or " "Filter recipes created on or after YYYY-MM-DD. Prepending - filters on or "
"before date." "before date."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:763 #: .\cookbook\views\api.py:771
msgid "" msgid ""
"Filter recipes updated on or after YYYY-MM-DD. Prepending - filters on or " "Filter recipes updated on or after YYYY-MM-DD. Prepending - filters on or "
"before date." "before date."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:765 #: .\cookbook\views\api.py:773
msgid "" msgid ""
"Filter recipes lasts viewed on or after YYYY-MM-DD. Prepending - filters on " "Filter recipes lasts viewed on or after YYYY-MM-DD. Prepending - filters on "
"or before date." "or before date."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:767 #: .\cookbook\views\api.py:775
msgid "Filter recipes that can be made with OnHand food. [true/<b>false</b>]" msgid "Filter recipes that can be made with OnHand food. [true/<b>false</b>]"
msgstr "" msgstr ""
#: .\cookbook\views\api.py:929 #: .\cookbook\views\api.py:937
msgid "" msgid ""
"Returns the shopping list entry with a primary key of id. Multiple values " "Returns the shopping list entry with a primary key of id. Multiple values "
"allowed." "allowed."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:934 #: .\cookbook\views\api.py:942
msgid "" msgid ""
"Filter shopping list entries on checked. [true, false, both, <b>recent</b>]" "Filter shopping list entries on checked. [true, false, both, <b>recent</b>]"
"<br> - recent includes unchecked items and recently completed items." "<br> - recent includes unchecked items and recently completed items."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:937 #: .\cookbook\views\api.py:945
msgid "Returns the shopping list entries sorted by supermarket category order." msgid "Returns the shopping list entries sorted by supermarket category order."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:1134 #: .\cookbook\views\api.py:1140
msgid "Nothing to do." msgid "Nothing to do."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:1153 #: .\cookbook\views\api.py:1160
msgid "Invalid Url" msgid "Invalid Url"
msgstr "" msgstr ""
#: .\cookbook\views\api.py:1158 #: .\cookbook\views\api.py:1167
msgid "Connection Refused." msgid "Connection Refused."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:1163 #: .\cookbook\views\api.py:1172
msgid "Bad URL Schema." msgid "Bad URL Schema."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:1170 #: .\cookbook\views\api.py:1195
msgid "No usable data could be found." msgid "No usable data could be found."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:1260 .\cookbook\views\data.py:28 #: .\cookbook\views\api.py:1303 .\cookbook\views\data.py:28
#: .\cookbook\views\edit.py:120 .\cookbook\views\new.py:90 #: .\cookbook\views\edit.py:120 .\cookbook\views\new.py:90
msgid "This feature is not yet available in the hosted version of tandoor!" msgid "This feature is not yet available in the hosted version of tandoor!"
msgstr "" msgstr ""
#: .\cookbook\views\api.py:1282 #: .\cookbook\views\api.py:1325
msgid "Sync successful!" msgid "Sync successful!"
msgstr "" msgstr ""
#: .\cookbook\views\api.py:1287 #: .\cookbook\views\api.py:1330
msgid "Error synchronizing with Storage" msgid "Error synchronizing with Storage"
msgstr "" msgstr ""
@ -2517,6 +2509,10 @@ msgstr ""
msgid "Shopping List" msgid "Shopping List"
msgstr "" msgstr ""
#: .\cookbook\views\lists.py:76
msgid "Invite Links"
msgstr ""
#: .\cookbook\views\lists.py:139 #: .\cookbook\views\lists.py:139
msgid "Supermarkets" msgid "Supermarkets"
msgstr "" msgstr ""

View File

@ -11,7 +11,7 @@ msgid ""
msgstr "" 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: 2022-06-26 12:09+0200\n" "POT-Creation-Date: 2022-07-12 19:20+0200\n"
"PO-Revision-Date: 2020-06-02 19:28+0000\n" "PO-Revision-Date: 2020-06-02 19:28+0000\n"
"Last-Translator: Emre S, 2020\n" "Last-Translator: Emre S, 2020\n"
"Language-Team: Turkish (https://www.transifex.com/django-recipes/" "Language-Team: Turkish (https://www.transifex.com/django-recipes/"
@ -488,7 +488,7 @@ msgstr ""
msgid "You have more users than allowed in your space." msgid "You have more users than allowed in your space."
msgstr "" msgstr ""
#: .\cookbook\helper\recipe_search.py:560 #: .\cookbook\helper\recipe_search.py:565
msgid "One of queryset or hash_key must be provided" msgid "One of queryset or hash_key must be provided"
msgstr "" msgstr ""
@ -501,12 +501,12 @@ msgstr ""
msgid "Could not parse template code." msgid "Could not parse template code."
msgstr "" msgstr ""
#: .\cookbook\integration\copymethat.py:42 #: .\cookbook\integration\copymethat.py:41
#: .\cookbook\integration\melarecipes.py:37 #: .\cookbook\integration\melarecipes.py:37
msgid "Favorite" msgid "Favorite"
msgstr "" msgstr ""
#: .\cookbook\integration\copymethat.py:71 #: .\cookbook\integration\copymethat.py:70
#: .\cookbook\integration\recettetek.py:54 #: .\cookbook\integration\recettetek.py:54
#: .\cookbook\integration\recipekeeper.py:63 #: .\cookbook\integration\recipekeeper.py:63
msgid "Imported from" msgid "Imported from"
@ -637,119 +637,119 @@ msgstr ""
msgid " is part of a recipe step and cannot be deleted" msgid " is part of a recipe step and cannot be deleted"
msgstr "" msgstr ""
#: .\cookbook\models.py:1160 .\cookbook\templates\search_info.html:28 #: .\cookbook\models.py:1162 .\cookbook\templates\search_info.html:28
msgid "Simple" msgid "Simple"
msgstr "" msgstr ""
#: .\cookbook\models.py:1161 .\cookbook\templates\search_info.html:33 #: .\cookbook\models.py:1163 .\cookbook\templates\search_info.html:33
msgid "Phrase" msgid "Phrase"
msgstr "" msgstr ""
#: .\cookbook\models.py:1162 .\cookbook\templates\search_info.html:38 #: .\cookbook\models.py:1164 .\cookbook\templates\search_info.html:38
msgid "Web" msgid "Web"
msgstr "" msgstr ""
#: .\cookbook\models.py:1163 .\cookbook\templates\search_info.html:47 #: .\cookbook\models.py:1165 .\cookbook\templates\search_info.html:47
msgid "Raw" msgid "Raw"
msgstr "" msgstr ""
#: .\cookbook\models.py:1201 #: .\cookbook\models.py:1203
msgid "Food Alias" msgid "Food Alias"
msgstr "" msgstr ""
#: .\cookbook\models.py:1201 #: .\cookbook\models.py:1203
msgid "Unit Alias" msgid "Unit Alias"
msgstr "" msgstr ""
#: .\cookbook\models.py:1201 #: .\cookbook\models.py:1203
msgid "Keyword Alias" msgid "Keyword Alias"
msgstr "" msgstr ""
#: .\cookbook\models.py:1225 #: .\cookbook\models.py:1227
#: .\cookbook\templates\include\recipe_open_modal.html:7 #: .\cookbook\templates\include\recipe_open_modal.html:7
#: .\cookbook\views\delete.py:36 .\cookbook\views\edit.py:251 #: .\cookbook\views\delete.py:36 .\cookbook\views\edit.py:251
#: .\cookbook\views\new.py:48 #: .\cookbook\views\new.py:48
msgid "Recipe" msgid "Recipe"
msgstr "" msgstr ""
#: .\cookbook\models.py:1226 #: .\cookbook\models.py:1228
msgid "Food" msgid "Food"
msgstr "" msgstr ""
#: .\cookbook\models.py:1227 .\cookbook\templates\base.html:138 #: .\cookbook\models.py:1229 .\cookbook\templates\base.html:138
msgid "Keyword" msgid "Keyword"
msgstr "" msgstr ""
#: .\cookbook\serializer.py:204 #: .\cookbook\serializer.py:207
msgid "Cannot modify Space owner permission." msgid "Cannot modify Space owner permission."
msgstr "" msgstr ""
#: .\cookbook\serializer.py:273 #: .\cookbook\serializer.py:290
msgid "File uploads are not enabled for this Space." msgid "File uploads are not enabled for this Space."
msgstr "" msgstr ""
#: .\cookbook\serializer.py:284 #: .\cookbook\serializer.py:301
msgid "You have reached your file upload limit." msgid "You have reached your file upload limit."
msgstr "" msgstr ""
#: .\cookbook\serializer.py:1051 #: .\cookbook\serializer.py:1081
msgid "Hello" msgid "Hello"
msgstr "" msgstr ""
#: .\cookbook\serializer.py:1051 #: .\cookbook\serializer.py:1081
msgid "You have been invited by " msgid "You have been invited by "
msgstr "" msgstr ""
#: .\cookbook\serializer.py:1052 #: .\cookbook\serializer.py:1082
msgid " to join their Tandoor Recipes space " msgid " to join their Tandoor Recipes space "
msgstr "" msgstr ""
#: .\cookbook\serializer.py:1053 #: .\cookbook\serializer.py:1083
msgid "Click the following link to activate your account: " msgid "Click the following link to activate your account: "
msgstr "" msgstr ""
#: .\cookbook\serializer.py:1054 #: .\cookbook\serializer.py:1084
msgid "" msgid ""
"If the link does not work use the following code to manually join the space: " "If the link does not work use the following code to manually join the space: "
msgstr "" msgstr ""
#: .\cookbook\serializer.py:1055 #: .\cookbook\serializer.py:1085
msgid "The invitation is valid until " msgid "The invitation is valid until "
msgstr "" msgstr ""
#: .\cookbook\serializer.py:1056 #: .\cookbook\serializer.py:1086
msgid "" msgid ""
"Tandoor Recipes is an Open Source recipe manager. Check it out on GitHub " "Tandoor Recipes is an Open Source recipe manager. Check it out on GitHub "
msgstr "" msgstr ""
#: .\cookbook\serializer.py:1059 #: .\cookbook\serializer.py:1089
msgid "Tandoor Recipes Invite" msgid "Tandoor Recipes Invite"
msgstr "" msgstr ""
#: .\cookbook\serializer.py:1179 #: .\cookbook\serializer.py:1209
msgid "Existing shopping list to update" msgid "Existing shopping list to update"
msgstr "" msgstr ""
#: .\cookbook\serializer.py:1181 #: .\cookbook\serializer.py:1211
msgid "" msgid ""
"List of ingredient IDs from the recipe to add, if not provided all " "List of ingredient IDs from the recipe to add, if not provided all "
"ingredients will be added." "ingredients will be added."
msgstr "" msgstr ""
#: .\cookbook\serializer.py:1183 #: .\cookbook\serializer.py:1213
msgid "" msgid ""
"Providing a list_recipe ID and servings of 0 will delete that shopping list." "Providing a list_recipe ID and servings of 0 will delete that shopping list."
msgstr "" msgstr ""
#: .\cookbook\serializer.py:1192 #: .\cookbook\serializer.py:1222
msgid "Amount of food to add to the shopping list" msgid "Amount of food to add to the shopping list"
msgstr "" msgstr ""
#: .\cookbook\serializer.py:1194 #: .\cookbook\serializer.py:1224
msgid "ID of unit to use for the shopping list" msgid "ID of unit to use for the shopping list"
msgstr "" msgstr ""
#: .\cookbook\serializer.py:1196 #: .\cookbook\serializer.py:1226
msgid "When set to true will delete all food from active shopping lists." msgid "When set to true will delete all food from active shopping lists."
msgstr "" msgstr ""
@ -881,7 +881,7 @@ msgid ""
msgstr "" msgstr ""
#: .\cookbook\templates\account\login.html:8 #: .\cookbook\templates\account\login.html:8
#: .\cookbook\templates\base.html:339 .\cookbook\templates\openid\login.html:8 #: .\cookbook\templates\base.html:340 .\cookbook\templates\openid\login.html:8
msgid "Login" msgid "Login"
msgstr "" msgstr ""
@ -1045,7 +1045,7 @@ msgstr ""
msgid "We are sorry, but the sign up is currently closed." msgid "We are sorry, but the sign up is currently closed."
msgstr "" msgstr ""
#: .\cookbook\templates\api_info.html:5 .\cookbook\templates\base.html:329 #: .\cookbook\templates\api_info.html:5 .\cookbook\templates\base.html:330
#: .\cookbook\templates\rest_framework\api.html:11 #: .\cookbook\templates\rest_framework\api.html:11
msgid "API Documentation" msgid "API Documentation"
msgstr "" msgstr ""
@ -1140,36 +1140,36 @@ msgstr ""
msgid "Your Spaces" msgid "Your Spaces"
msgstr "" msgstr ""
#: .\cookbook\templates\base.html:319 #: .\cookbook\templates\base.html:320
#: .\cookbook\templates\space_overview.html:6 #: .\cookbook\templates\space_overview.html:6
msgid "Overview" msgid "Overview"
msgstr "" msgstr ""
#: .\cookbook\templates\base.html:323 #: .\cookbook\templates\base.html:324
msgid "Markdown Guide" msgid "Markdown Guide"
msgstr "" msgstr ""
#: .\cookbook\templates\base.html:325 #: .\cookbook\templates\base.html:326
msgid "GitHub" msgid "GitHub"
msgstr "" msgstr ""
#: .\cookbook\templates\base.html:327 #: .\cookbook\templates\base.html:328
msgid "Translate Tandoor" msgid "Translate Tandoor"
msgstr "" msgstr ""
#: .\cookbook\templates\base.html:331 #: .\cookbook\templates\base.html:332
msgid "API Browser" msgid "API Browser"
msgstr "" msgstr ""
#: .\cookbook\templates\base.html:334 #: .\cookbook\templates\base.html:335
msgid "Log out" msgid "Log out"
msgstr "" msgstr ""
#: .\cookbook\templates\base.html:356 #: .\cookbook\templates\base.html:357
msgid "You are using the free version of Tandor" msgid "You are using the free version of Tandor"
msgstr "" msgstr ""
#: .\cookbook\templates\base.html:357 #: .\cookbook\templates\base.html:358
msgid "Upgrade Now" msgid "Upgrade Now"
msgstr "" msgstr ""
@ -2110,19 +2110,11 @@ msgstr ""
msgid "Internal Recipes" msgid "Internal Recipes"
msgstr "" msgstr ""
#: .\cookbook\templates\system.html:21 .\cookbook\views\lists.py:76 #: .\cookbook\templates\system.html:20
msgid "Invite Links"
msgstr ""
#: .\cookbook\templates\system.html:22
msgid "Show Links"
msgstr ""
#: .\cookbook\templates\system.html:32
msgid "System Information" msgid "System Information"
msgstr "" msgstr ""
#: .\cookbook\templates\system.html:34 #: .\cookbook\templates\system.html:22
msgid "" msgid ""
"\n" "\n"
" Django Recipes is an open source free software application. It can " " Django Recipes is an open source free software application. It can "
@ -2133,21 +2125,21 @@ msgid ""
" " " "
msgstr "" msgstr ""
#: .\cookbook\templates\system.html:48 #: .\cookbook\templates\system.html:36
msgid "Media Serving" msgid "Media Serving"
msgstr "" msgstr ""
#: .\cookbook\templates\system.html:49 .\cookbook\templates\system.html:64 #: .\cookbook\templates\system.html:37 .\cookbook\templates\system.html:52
#: .\cookbook\templates\system.html:80 #: .\cookbook\templates\system.html:68
msgid "Warning" msgid "Warning"
msgstr "" msgstr ""
#: .\cookbook\templates\system.html:49 .\cookbook\templates\system.html:64 #: .\cookbook\templates\system.html:37 .\cookbook\templates\system.html:52
#: .\cookbook\templates\system.html:80 .\cookbook\templates\system.html:95 #: .\cookbook\templates\system.html:68 .\cookbook\templates\system.html:83
msgid "Ok" msgid "Ok"
msgstr "" msgstr ""
#: .\cookbook\templates\system.html:51 #: .\cookbook\templates\system.html:39
msgid "" msgid ""
"Serving media files directly using gunicorn/python is <b>not recommend</b>!\n" "Serving media files directly using gunicorn/python is <b>not recommend</b>!\n"
" Please follow the steps described\n" " Please follow the steps described\n"
@ -2157,16 +2149,16 @@ msgid ""
" " " "
msgstr "" msgstr ""
#: .\cookbook\templates\system.html:57 .\cookbook\templates\system.html:73 #: .\cookbook\templates\system.html:45 .\cookbook\templates\system.html:61
#: .\cookbook\templates\system.html:88 .\cookbook\templates\system.html:102 #: .\cookbook\templates\system.html:76 .\cookbook\templates\system.html:90
msgid "Everything is fine!" msgid "Everything is fine!"
msgstr "" msgstr ""
#: .\cookbook\templates\system.html:62 #: .\cookbook\templates\system.html:50
msgid "Secret Key" msgid "Secret Key"
msgstr "" msgstr ""
#: .\cookbook\templates\system.html:66 #: .\cookbook\templates\system.html:54
msgid "" msgid ""
"\n" "\n"
" You do not have a <code>SECRET_KEY</code> configured in your " " You do not have a <code>SECRET_KEY</code> configured in your "
@ -2179,11 +2171,11 @@ msgid ""
" " " "
msgstr "" msgstr ""
#: .\cookbook\templates\system.html:78 #: .\cookbook\templates\system.html:66
msgid "Debug Mode" msgid "Debug Mode"
msgstr "" msgstr ""
#: .\cookbook\templates\system.html:82 #: .\cookbook\templates\system.html:70
msgid "" msgid ""
"\n" "\n"
" This application is still running in debug mode. This is most " " This application is still running in debug mode. This is most "
@ -2194,15 +2186,15 @@ msgid ""
" " " "
msgstr "" msgstr ""
#: .\cookbook\templates\system.html:93 #: .\cookbook\templates\system.html:81
msgid "Database" msgid "Database"
msgstr "" msgstr ""
#: .\cookbook\templates\system.html:95 #: .\cookbook\templates\system.html:83
msgid "Info" msgid "Info"
msgstr "" msgstr ""
#: .\cookbook\templates\system.html:97 #: .\cookbook\templates\system.html:85
msgid "" msgid ""
"\n" "\n"
" This application is not running with a Postgres database " " This application is not running with a Postgres database "
@ -2215,245 +2207,245 @@ msgstr ""
msgid "URL Import" msgid "URL Import"
msgstr "" msgstr ""
#: .\cookbook\views\api.py:97 .\cookbook\views\api.py:189 #: .\cookbook\views\api.py:105 .\cookbook\views\api.py:197
msgid "Parameter updated_at incorrectly formatted" msgid "Parameter updated_at incorrectly formatted"
msgstr "" msgstr ""
#: .\cookbook\views\api.py:209 .\cookbook\views\api.py:312 #: .\cookbook\views\api.py:217 .\cookbook\views\api.py:320
msgid "No {self.basename} with id {pk} exists" msgid "No {self.basename} with id {pk} exists"
msgstr "" msgstr ""
#: .\cookbook\views\api.py:213 #: .\cookbook\views\api.py:221
msgid "Cannot merge with the same object!" msgid "Cannot merge with the same object!"
msgstr "" msgstr ""
#: .\cookbook\views\api.py:220 #: .\cookbook\views\api.py:228
msgid "No {self.basename} with id {target} exists" msgid "No {self.basename} with id {target} exists"
msgstr "" msgstr ""
#: .\cookbook\views\api.py:225 #: .\cookbook\views\api.py:233
msgid "Cannot merge with child object!" msgid "Cannot merge with child object!"
msgstr "" msgstr ""
#: .\cookbook\views\api.py:258 #: .\cookbook\views\api.py:266
msgid "{source.name} was merged successfully with {target.name}" msgid "{source.name} was merged successfully with {target.name}"
msgstr "" msgstr ""
#: .\cookbook\views\api.py:263 #: .\cookbook\views\api.py:271
msgid "An error occurred attempting to merge {source.name} with {target.name}" msgid "An error occurred attempting to merge {source.name} with {target.name}"
msgstr "" msgstr ""
#: .\cookbook\views\api.py:321 #: .\cookbook\views\api.py:329
msgid "{child.name} was moved successfully to the root." msgid "{child.name} was moved successfully to the root."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:324 .\cookbook\views\api.py:342 #: .\cookbook\views\api.py:332 .\cookbook\views\api.py:350
msgid "An error occurred attempting to move " msgid "An error occurred attempting to move "
msgstr "" msgstr ""
#: .\cookbook\views\api.py:327 #: .\cookbook\views\api.py:335
msgid "Cannot move an object to itself!" msgid "Cannot move an object to itself!"
msgstr "" msgstr ""
#: .\cookbook\views\api.py:333 #: .\cookbook\views\api.py:341
msgid "No {self.basename} with id {parent} exists" msgid "No {self.basename} with id {parent} exists"
msgstr "" msgstr ""
#: .\cookbook\views\api.py:339 #: .\cookbook\views\api.py:347
msgid "{child.name} was moved successfully to parent {parent.name}" msgid "{child.name} was moved successfully to parent {parent.name}"
msgstr "" msgstr ""
#: .\cookbook\views\api.py:534 #: .\cookbook\views\api.py:542
msgid "{obj.name} was removed from the shopping list." msgid "{obj.name} was removed from the shopping list."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:539 .\cookbook\views\api.py:871 #: .\cookbook\views\api.py:547 .\cookbook\views\api.py:879
#: .\cookbook\views\api.py:884 #: .\cookbook\views\api.py:892
msgid "{obj.name} was added to the shopping list." msgid "{obj.name} was added to the shopping list."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:666 #: .\cookbook\views\api.py:674
msgid "ID of recipe a step is part of. For multiple repeat parameter." msgid "ID of recipe a step is part of. For multiple repeat parameter."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:668 #: .\cookbook\views\api.py:676
msgid "Query string matched (fuzzy) against object name." msgid "Query string matched (fuzzy) against object name."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:712 #: .\cookbook\views\api.py:720
msgid "" msgid ""
"Query string matched (fuzzy) against recipe name. In the future also " "Query string matched (fuzzy) against recipe name. In the future also "
"fulltext search." "fulltext search."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:714 #: .\cookbook\views\api.py:722
msgid "" msgid ""
"ID of keyword a recipe should have. For multiple repeat parameter. " "ID of keyword a recipe should have. For multiple repeat parameter. "
"Equivalent to keywords_or" "Equivalent to keywords_or"
msgstr "" msgstr ""
#: .\cookbook\views\api.py:717 #: .\cookbook\views\api.py:725
msgid "" msgid ""
"Keyword IDs, repeat for multiple. Return recipes with any of the keywords" "Keyword IDs, repeat for multiple. Return recipes with any of the keywords"
msgstr "" msgstr ""
#: .\cookbook\views\api.py:720 #: .\cookbook\views\api.py:728
msgid "" msgid ""
"Keyword IDs, repeat for multiple. Return recipes with all of the keywords." "Keyword IDs, repeat for multiple. Return recipes with all of the keywords."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:723 #: .\cookbook\views\api.py:731
msgid "" msgid ""
"Keyword IDs, repeat for multiple. Exclude recipes with any of the keywords." "Keyword IDs, repeat for multiple. Exclude recipes with any of the keywords."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:726 #: .\cookbook\views\api.py:734
msgid "" msgid ""
"Keyword IDs, repeat for multiple. Exclude recipes with all of the keywords." "Keyword IDs, repeat for multiple. Exclude recipes with all of the keywords."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:728 #: .\cookbook\views\api.py:736
msgid "ID of food a recipe should have. For multiple repeat parameter." msgid "ID of food a recipe should have. For multiple repeat parameter."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:731 #: .\cookbook\views\api.py:739
msgid "Food IDs, repeat for multiple. Return recipes with any of the foods" msgid "Food IDs, repeat for multiple. Return recipes with any of the foods"
msgstr "" msgstr ""
#: .\cookbook\views\api.py:733 #: .\cookbook\views\api.py:741
msgid "Food IDs, repeat for multiple. Return recipes with all of the foods." msgid "Food IDs, repeat for multiple. Return recipes with all of the foods."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:735 #: .\cookbook\views\api.py:743
msgid "Food IDs, repeat for multiple. Exclude recipes with any of the foods." msgid "Food IDs, repeat for multiple. Exclude recipes with any of the foods."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:737 #: .\cookbook\views\api.py:745
msgid "Food IDs, repeat for multiple. Exclude recipes with all of the foods." msgid "Food IDs, repeat for multiple. Exclude recipes with all of the foods."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:738 #: .\cookbook\views\api.py:746
msgid "ID of unit a recipe should have." msgid "ID of unit a recipe should have."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:740 #: .\cookbook\views\api.py:748
msgid "" msgid ""
"Rating a recipe should have or greater. [0 - 5] Negative value filters " "Rating a recipe should have or greater. [0 - 5] Negative value filters "
"rating less than." "rating less than."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:741 #: .\cookbook\views\api.py:749
msgid "ID of book a recipe should be in. For multiple repeat parameter." msgid "ID of book a recipe should be in. For multiple repeat parameter."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:743 #: .\cookbook\views\api.py:751
msgid "Book IDs, repeat for multiple. Return recipes with any of the books" msgid "Book IDs, repeat for multiple. Return recipes with any of the books"
msgstr "" msgstr ""
#: .\cookbook\views\api.py:745 #: .\cookbook\views\api.py:753
msgid "Book IDs, repeat for multiple. Return recipes with all of the books." msgid "Book IDs, repeat for multiple. Return recipes with all of the books."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:747 #: .\cookbook\views\api.py:755
msgid "Book IDs, repeat for multiple. Exclude recipes with any of the books." msgid "Book IDs, repeat for multiple. Exclude recipes with any of the books."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:749 #: .\cookbook\views\api.py:757
msgid "Book IDs, repeat for multiple. Exclude recipes with all of the books." msgid "Book IDs, repeat for multiple. Exclude recipes with all of the books."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:751 #: .\cookbook\views\api.py:759
msgid "If only internal recipes should be returned. [true/<b>false</b>]" msgid "If only internal recipes should be returned. [true/<b>false</b>]"
msgstr "" msgstr ""
#: .\cookbook\views\api.py:753 #: .\cookbook\views\api.py:761
msgid "Returns the results in randomized order. [true/<b>false</b>]" msgid "Returns the results in randomized order. [true/<b>false</b>]"
msgstr "" msgstr ""
#: .\cookbook\views\api.py:755 #: .\cookbook\views\api.py:763
msgid "Returns new results first in search results. [true/<b>false</b>]" msgid "Returns new results first in search results. [true/<b>false</b>]"
msgstr "" msgstr ""
#: .\cookbook\views\api.py:757 #: .\cookbook\views\api.py:765
msgid "" msgid ""
"Filter recipes cooked X times or more. Negative values returns cooked less " "Filter recipes cooked X times or more. Negative values returns cooked less "
"than X times" "than X times"
msgstr "" msgstr ""
#: .\cookbook\views\api.py:759 #: .\cookbook\views\api.py:767
msgid "" msgid ""
"Filter recipes last cooked on or after YYYY-MM-DD. Prepending - filters on " "Filter recipes last cooked on or after YYYY-MM-DD. Prepending - filters on "
"or before date." "or before date."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:761 #: .\cookbook\views\api.py:769
msgid "" msgid ""
"Filter recipes created on or after YYYY-MM-DD. Prepending - filters on or " "Filter recipes created on or after YYYY-MM-DD. Prepending - filters on or "
"before date." "before date."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:763 #: .\cookbook\views\api.py:771
msgid "" msgid ""
"Filter recipes updated on or after YYYY-MM-DD. Prepending - filters on or " "Filter recipes updated on or after YYYY-MM-DD. Prepending - filters on or "
"before date." "before date."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:765 #: .\cookbook\views\api.py:773
msgid "" msgid ""
"Filter recipes lasts viewed on or after YYYY-MM-DD. Prepending - filters on " "Filter recipes lasts viewed on or after YYYY-MM-DD. Prepending - filters on "
"or before date." "or before date."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:767 #: .\cookbook\views\api.py:775
msgid "Filter recipes that can be made with OnHand food. [true/<b>false</b>]" msgid "Filter recipes that can be made with OnHand food. [true/<b>false</b>]"
msgstr "" msgstr ""
#: .\cookbook\views\api.py:929 #: .\cookbook\views\api.py:937
msgid "" msgid ""
"Returns the shopping list entry with a primary key of id. Multiple values " "Returns the shopping list entry with a primary key of id. Multiple values "
"allowed." "allowed."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:934 #: .\cookbook\views\api.py:942
msgid "" msgid ""
"Filter shopping list entries on checked. [true, false, both, <b>recent</b>]" "Filter shopping list entries on checked. [true, false, both, <b>recent</b>]"
"<br> - recent includes unchecked items and recently completed items." "<br> - recent includes unchecked items and recently completed items."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:937 #: .\cookbook\views\api.py:945
msgid "Returns the shopping list entries sorted by supermarket category order." msgid "Returns the shopping list entries sorted by supermarket category order."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:1134 #: .\cookbook\views\api.py:1140
msgid "Nothing to do." msgid "Nothing to do."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:1153 #: .\cookbook\views\api.py:1160
msgid "Invalid Url" msgid "Invalid Url"
msgstr "" msgstr ""
#: .\cookbook\views\api.py:1158 #: .\cookbook\views\api.py:1167
msgid "Connection Refused." msgid "Connection Refused."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:1163 #: .\cookbook\views\api.py:1172
msgid "Bad URL Schema." msgid "Bad URL Schema."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:1170 #: .\cookbook\views\api.py:1195
msgid "No usable data could be found." msgid "No usable data could be found."
msgstr "" msgstr ""
#: .\cookbook\views\api.py:1260 .\cookbook\views\data.py:28 #: .\cookbook\views\api.py:1303 .\cookbook\views\data.py:28
#: .\cookbook\views\edit.py:120 .\cookbook\views\new.py:90 #: .\cookbook\views\edit.py:120 .\cookbook\views\new.py:90
msgid "This feature is not yet available in the hosted version of tandoor!" msgid "This feature is not yet available in the hosted version of tandoor!"
msgstr "" msgstr ""
#: .\cookbook\views\api.py:1282 #: .\cookbook\views\api.py:1325
msgid "Sync successful!" msgid "Sync successful!"
msgstr "" msgstr ""
#: .\cookbook\views\api.py:1287 #: .\cookbook\views\api.py:1330
msgid "Error synchronizing with Storage" msgid "Error synchronizing with Storage"
msgstr "" msgstr ""
@ -2536,6 +2528,10 @@ msgstr ""
msgid "Shopping List" msgid "Shopping List"
msgstr "" msgstr ""
#: .\cookbook\views\lists.py:76
msgid "Invite Links"
msgstr ""
#: .\cookbook\views\lists.py:139 #: .\cookbook\views\lists.py:139
msgid "Supermarkets" msgid "Supermarkets"
msgstr "" msgstr ""

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,21 @@
# Generated by Django 4.0.6 on 2022-07-12 18:04
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('cookbook', '0177_recipe_show_ingredient_overview'),
]
operations = [
migrations.RemoveField(
model_name='userpreference',
name='search_style',
),
migrations.RemoveField(
model_name='userpreference',
name='show_recent',
),
]

View File

@ -0,0 +1,25 @@
# Generated by Django 4.0.6 on 2022-07-13 10:53
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('cookbook', '0178_remove_userpreference_search_style_and_more'),
]
operations = [
migrations.AddField(
model_name='recipe',
name='private',
field=models.BooleanField(default=False),
),
migrations.AddField(
model_name='recipe',
name='shared',
field=models.ManyToManyField(blank=True, related_name='recipe_shared_with', to=settings.AUTH_USER_MODEL),
),
]

View File

@ -0,0 +1,18 @@
# Generated by Django 4.0.6 on 2022-07-14 09:20
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('cookbook', '0179_recipe_private_recipe_shared'),
]
operations = [
migrations.AddField(
model_name='invitelink',
name='reusable',
field=models.BooleanField(default=False),
),
]

View File

@ -0,0 +1,19 @@
# Generated by Django 4.0.6 on 2022-07-14 11:14
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('cookbook', '0180_invitelink_reusable'),
]
operations = [
migrations.AddField(
model_name='space',
name='image',
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='space_image', to='cookbook.userfile'),
),
]

View File

@ -0,0 +1,19 @@
# Generated by Django 4.0.6 on 2022-07-14 13:32
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('cookbook', '0181_space_image'),
]
operations = [
migrations.AddField(
model_name='userpreference',
name='image',
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='user_image', to='cookbook.userfile'),
),
]

View File

@ -0,0 +1,19 @@
# Generated by Django 4.0.6 on 2022-08-04 16:46
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('cookbook', '0182_userpreference_image'),
]
operations = [
migrations.AlterField(
model_name='space',
name='image',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='space_image', to='cookbook.userfile'),
),
]

View File

@ -4,6 +4,8 @@ import re
import uuid import uuid
from datetime import date, timedelta from datetime import date, timedelta
import oauth2_provider.models
from PIL import Image
from annoying.fields import AutoOneToOneField from annoying.fields import AutoOneToOneField
from django.contrib import auth from django.contrib import auth
from django.contrib.auth.models import Group, User from django.contrib.auth.models import Group, User
@ -25,7 +27,7 @@ from recipes.settings import (COMMENT_PREF_DEFAULT, FRACTION_PREF_DEFAULT, KJ_PR
SORT_TREE_BY_NAME, STICKY_NAV_PREF_DEFAULT) SORT_TREE_BY_NAME, STICKY_NAV_PREF_DEFAULT)
def get_user_name(self): def get_user_display_name(self):
if not (name := f"{self.first_name} {self.last_name}") == " ": if not (name := f"{self.first_name} {self.last_name}") == " ":
return name return name
else: else:
@ -57,11 +59,18 @@ def get_shopping_share(self):
])) ]))
auth.models.User.add_to_class('get_user_name', get_user_name) auth.models.User.add_to_class('get_user_display_name', get_user_display_name)
auth.models.User.add_to_class('get_shopping_share', get_shopping_share) auth.models.User.add_to_class('get_shopping_share', get_shopping_share)
auth.models.User.add_to_class('get_active_space', get_active_space) auth.models.User.add_to_class('get_active_space', get_active_space)
def oauth_token_get_owner(self):
return self.user
oauth2_provider.models.AccessToken.add_to_class('get_owner', oauth_token_get_owner)
def get_model_name(model): def get_model_name(model):
return ('_'.join(re.findall('[A-Z][^A-Z]*', model.__name__))).lower() return ('_'.join(re.findall('[A-Z][^A-Z]*', model.__name__))).lower()
@ -244,6 +253,7 @@ class FoodInheritField(models.Model, PermissionModelMixin):
class Space(ExportModelOperationsMixin('space'), models.Model): class Space(ExportModelOperationsMixin('space'), models.Model):
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')
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)
@ -355,34 +365,16 @@ class UserPreference(models.Model, PermissionModelMixin):
(BOOKS, _('Books')), (BOOKS, _('Books')),
) )
# Search Style
SMALL = 'SMALL'
LARGE = 'LARGE'
NEW = 'NEW'
SEARCH_STYLE = ((SMALL, _('Small')), (LARGE, _('Large')), (NEW, _('New')))
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, 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( nav_color = models.CharField(choices=COLORS, max_length=128, default=PRIMARY)
choices=COLORS, max_length=128, default=PRIMARY
)
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)
default_page = models.CharField( default_page = models.CharField(choices=PAGES, max_length=64, default=SEARCH)
choices=PAGES, max_length=64, default=SEARCH plan_share = models.ManyToManyField(User, blank=True, related_name='plan_share_default')
) shopping_share = models.ManyToManyField(User, blank=True, related_name='shopping_share')
search_style = models.CharField(
choices=SEARCH_STYLE, max_length=64, default=NEW
)
show_recent = models.BooleanField(default=True)
plan_share = models.ManyToManyField(
User, blank=True, related_name='plan_share_default'
)
shopping_share = models.ManyToManyField(
User, blank=True, related_name='shopping_share'
)
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)
@ -612,7 +604,7 @@ class Food(ExportModelOperationsMixin('food'), TreeModel, PermissionModelMixin):
# remove all inherited fields from food # remove all inherited fields from food
trough = Food.inherit_fields.through trough = Food.inherit_fields.through
trough.objects.all().delete() trough.objects.all().delete()
# food is going to inherit attributes # food is going to inherit attributes
if len(inherit) > 0: if len(inherit) > 0:
# ManyToMany cannot be updated through an UPDATE operation # ManyToMany cannot be updated through an UPDATE operation
@ -749,6 +741,8 @@ class Recipe(ExportModelOperationsMixin('recipe'), models.Model, PermissionModel
internal = models.BooleanField(default=False) internal = models.BooleanField(default=False)
nutrition = models.ForeignKey(NutritionInformation, blank=True, null=True, on_delete=models.CASCADE) nutrition = models.ForeignKey(NutritionInformation, blank=True, null=True, on_delete=models.CASCADE)
show_ingredient_overview = models.BooleanField(default=True) show_ingredient_overview = models.BooleanField(default=True)
private = models.BooleanField(default=False)
shared = models.ManyToManyField(User, blank=True, related_name='recipe_shared_with')
source_url = models.CharField(max_length=1024, default=None, blank=True, null=True) source_url = models.CharField(max_length=1024, default=None, blank=True, null=True)
created_by = models.ForeignKey(User, on_delete=models.PROTECT) created_by = models.ForeignKey(User, on_delete=models.PROTECT)
@ -1017,9 +1011,8 @@ class InviteLink(ExportModelOperationsMixin('invite_link'), models.Model, Permis
email = models.EmailField(blank=True) email = models.EmailField(blank=True)
group = models.ForeignKey(Group, on_delete=models.CASCADE) group = models.ForeignKey(Group, on_delete=models.CASCADE)
valid_until = models.DateField(default=default_valid_until) valid_until = models.DateField(default=default_valid_until)
used_by = models.ForeignKey( used_by = models.ForeignKey(User, null=True, on_delete=models.CASCADE, related_name='used_by')
User, null=True, on_delete=models.CASCADE, related_name='used_by' reusable = models.BooleanField(default=False)
)
created_by = models.ForeignKey(User, on_delete=models.CASCADE) created_by = models.ForeignKey(User, on_delete=models.CASCADE)
created_at = models.DateTimeField(auto_now_add=True) created_at = models.DateTimeField(auto_now_add=True)
@ -1187,6 +1180,13 @@ class UserFile(ExportModelOperationsMixin('user_files'), models.Model, Permissio
objects = ScopedManager(space='space') objects = ScopedManager(space='space')
space = models.ForeignKey(Space, on_delete=models.CASCADE) space = models.ForeignKey(Space, on_delete=models.CASCADE)
def is_image(self):
try:
img = Image.open(self.file.file.file)
return True
except Exception:
return False
def save(self, *args, **kwargs): def save(self, *args, **kwargs):
if hasattr(self.file, 'file') and isinstance(self.file.file, UploadedFile) or isinstance(self.file.file, InMemoryUploadedFile): if hasattr(self.file, 'file') and isinstance(self.file.file, UploadedFile) or isinstance(self.file.file, InMemoryUploadedFile):
self.file.name = f'{uuid.uuid4()}' + pathlib.Path(self.file.name).suffix self.file.name = f'{uuid.uuid4()}' + pathlib.Path(self.file.name).suffix

View File

@ -1,12 +1,12 @@
import traceback import traceback
from datetime import timedelta, datetime import uuid
from datetime import datetime, timedelta
from decimal import Decimal from decimal import Decimal
from gettext import gettext as _ from gettext import gettext as _
from html import escape from html import escape
from smtplib import SMTPException from smtplib import SMTPException
from PIL import Image from django.contrib.auth.models import Group, User, AnonymousUser
from django.contrib.auth.models import User, Group
from django.core.mail import send_mail from django.core.mail import send_mail
from django.db.models import Avg, Q, QuerySet, Sum from django.db.models import Avg, Q, QuerySet, Sum
from django.http import BadHeaderError from django.http import BadHeaderError
@ -14,6 +14,8 @@ from django.urls import reverse
from django.utils import timezone from django.utils import timezone
from django_scopes import scopes_disabled from django_scopes import scopes_disabled
from drf_writable_nested import UniqueFieldsMixin, WritableNestedModelSerializer from drf_writable_nested import UniqueFieldsMixin, WritableNestedModelSerializer
from PIL import Image
from oauth2_provider.models import AccessToken
from rest_framework import serializers from rest_framework import serializers
from rest_framework.exceptions import NotFound, ValidationError from rest_framework.exceptions import NotFound, ValidationError
@ -22,14 +24,14 @@ from cookbook.helper.HelperFunctions import str2bool
from cookbook.helper.permission_helper import above_space_limit from cookbook.helper.permission_helper import above_space_limit
from cookbook.helper.shopping_helper import RecipeShoppingEditor from cookbook.helper.shopping_helper import RecipeShoppingEditor
from cookbook.models import (Automation, BookmarkletImport, Comment, CookLog, CustomFilter, from cookbook.models import (Automation, BookmarkletImport, Comment, CookLog, CustomFilter,
ExportLog, Food, FoodInheritField, ImportLog, Ingredient, Keyword, ExportLog, Food, FoodInheritField, ImportLog, Ingredient, InviteLink,
MealPlan, MealType, NutritionInformation, Recipe, RecipeBook, Keyword, MealPlan, MealType, NutritionInformation, Recipe, RecipeBook,
RecipeBookEntry, RecipeImport, ShareLink, ShoppingList, RecipeBookEntry, RecipeImport, ShareLink, ShoppingList,
ShoppingListEntry, ShoppingListRecipe, Step, Storage, Supermarket, ShoppingListEntry, ShoppingListRecipe, Space, Step, Storage,
SupermarketCategory, SupermarketCategoryRelation, Sync, SyncLog, Unit, Supermarket, SupermarketCategory, SupermarketCategoryRelation, Sync,
UserFile, UserPreference, ViewLog, Space, UserSpace, InviteLink) SyncLog, Unit, UserFile, UserPreference, UserSpace, ViewLog)
from cookbook.templatetags.custom_tags import markdown from cookbook.templatetags.custom_tags import markdown
from recipes.settings import MEDIA_URL, AWS_ENABLED from recipes.settings import AWS_ENABLED, MEDIA_URL
class ExtendedRecipeMixin(serializers.ModelSerializer): class ExtendedRecipeMixin(serializers.ModelSerializer):
@ -124,22 +126,26 @@ class SpaceFilterSerializer(serializers.ListSerializer):
# if query is sliced it came from api request not nested serializer # if query is sliced it came from api request not nested serializer
return super().to_representation(data) return super().to_representation(data)
if self.child.Meta.model == User: if self.child.Meta.model == User:
data = data.filter(userspace__space=self.context['request'].user.get_active_space()).all() if type(self.context['request'].user) == AnonymousUser:
data = []
else:
data = data.filter(userspace__space=self.context['request'].user.get_active_space()).all()
else: else:
data = data.filter(**{'__'.join(data.model.get_space_key()): self.context['request'].space}) data = data.filter(**{'__'.join(data.model.get_space_key()): self.context['request'].space})
return super().to_representation(data) return super().to_representation(data)
class UserNameSerializer(WritableNestedModelSerializer): class UserSerializer(WritableNestedModelSerializer):
username = serializers.SerializerMethodField('get_user_label') display_name = serializers.SerializerMethodField('get_user_label')
def get_user_label(self, obj): def get_user_label(self, obj):
return obj.get_user_name() return obj.get_user_display_name()
class Meta: class Meta:
list_serializer_class = SpaceFilterSerializer list_serializer_class = SpaceFilterSerializer
model = User model = User
fields = ('id', 'username') fields = ('id', 'username', 'first_name', 'last_name', 'display_name')
read_only_fields = ('username',)
class GroupSerializer(UniqueFieldsMixin, WritableNestedModelSerializer): class GroupSerializer(UniqueFieldsMixin, WritableNestedModelSerializer):
@ -170,103 +176,6 @@ class FoodInheritFieldSerializer(UniqueFieldsMixin, WritableNestedModelSerialize
read_only_fields = ['id'] read_only_fields = ['id']
class SpaceSerializer(WritableNestedModelSerializer):
user_count = serializers.SerializerMethodField('get_user_count')
recipe_count = serializers.SerializerMethodField('get_recipe_count')
file_size_mb = serializers.SerializerMethodField('get_file_size_mb')
food_inherit = FoodInheritFieldSerializer(many=True)
def get_user_count(self, obj):
return UserSpace.objects.filter(space=obj).count()
def get_recipe_count(self, obj):
return Recipe.objects.filter(space=obj).count()
def get_file_size_mb(self, obj):
try:
return UserFile.objects.filter(space=obj).aggregate(Sum('file_size_kb'))['file_size_kb__sum'] / 1000
except TypeError:
return 0
def create(self, validated_data):
raise ValidationError('Cannot create using this endpoint')
class Meta:
model = Space
fields = ('id', 'name', 'created_by', 'created_at', 'message', 'max_recipes', 'max_file_storage_mb', 'max_users', 'allow_sharing', 'demo', 'food_inherit', 'show_facet_count', 'user_count', 'recipe_count', 'file_size_mb',)
read_only_fields = ('id', 'created_by', 'created_at', 'max_recipes', 'max_file_storage_mb', 'max_users', 'allow_sharing', 'demo',)
class UserSpaceSerializer(WritableNestedModelSerializer):
user = UserNameSerializer(read_only=True)
groups = GroupSerializer(many=True)
def validate(self, data):
if self.instance.user == self.context['request'].space.created_by: # can't change space owner permission
raise serializers.ValidationError(_('Cannot modify Space owner permission.'))
return super().validate(data)
def create(self, validated_data):
raise ValidationError('Cannot create using this endpoint')
class Meta:
model = UserSpace
fields = ('id', 'user', 'space', 'groups', 'active', 'created_at', 'updated_at',)
read_only_fields = ('id', 'created_at', 'updated_at', 'space')
class SpacedModelSerializer(serializers.ModelSerializer):
def create(self, validated_data):
validated_data['space'] = self.context['request'].space
return super().create(validated_data)
class MealTypeSerializer(SpacedModelSerializer, WritableNestedModelSerializer):
def create(self, validated_data):
validated_data['created_by'] = self.context['request'].user
return super().create(validated_data)
class Meta:
list_serializer_class = SpaceFilterSerializer
model = MealType
fields = ('id', 'name', 'order', 'icon', 'color', 'default', 'created_by')
read_only_fields = ('created_by',)
class UserPreferenceSerializer(WritableNestedModelSerializer):
food_inherit_default = serializers.SerializerMethodField('get_food_inherit_defaults')
plan_share = UserNameSerializer(many=True, allow_null=True, required=False)
shopping_share = UserNameSerializer(many=True, allow_null=True, required=False)
food_children_exist = serializers.SerializerMethodField('get_food_children_exist')
def get_food_inherit_defaults(self, obj):
return FoodInheritFieldSerializer(obj.user.get_active_space().food_inherit.all(), many=True).data
def get_food_children_exist(self, obj):
space = getattr(self.context.get('request', None), 'space', None)
return Food.objects.filter(depth__gt=0, space=space).exists()
def update(self, instance, validated_data):
with scopes_disabled():
return super().update(instance, validated_data)
def create(self, validated_data):
raise ValidationError('Cannot create using this endpoint')
class Meta:
model = UserPreference
fields = (
'user', 'theme', 'nav_color', 'default_unit', 'default_page', 'use_fractions', 'use_kj', 'search_style',
'show_recent', 'plan_share',
'ingredient_decimals', 'comments', 'shopping_auto_sync', 'mealplan_autoadd_shopping',
'food_inherit_default', 'default_delay',
'mealplan_autoinclude_related', 'mealplan_autoexclude_onhand', 'shopping_share', 'shopping_recent_days',
'csv_delim', 'csv_prefix',
'filter_to_supermarket', 'shopping_add_onhand', 'left_handed', 'food_children_exist'
)
class UserFileSerializer(serializers.ModelSerializer): class UserFileSerializer(serializers.ModelSerializer):
file = serializers.FileField(write_only=True) file = serializers.FileField(write_only=True)
file_download = serializers.SerializerMethodField('get_download_link') file_download = serializers.SerializerMethodField('get_download_link')
@ -343,6 +252,106 @@ class UserFileViewSerializer(serializers.ModelSerializer):
read_only_fields = ('id', 'file') read_only_fields = ('id', 'file')
class SpaceSerializer(WritableNestedModelSerializer):
user_count = serializers.SerializerMethodField('get_user_count')
recipe_count = serializers.SerializerMethodField('get_recipe_count')
file_size_mb = serializers.SerializerMethodField('get_file_size_mb')
food_inherit = FoodInheritFieldSerializer(many=True)
image = UserFileViewSerializer(required=False, many=False, allow_null=True)
def get_user_count(self, obj):
return UserSpace.objects.filter(space=obj).count()
def get_recipe_count(self, obj):
return Recipe.objects.filter(space=obj).count()
def get_file_size_mb(self, obj):
try:
return UserFile.objects.filter(space=obj).aggregate(Sum('file_size_kb'))['file_size_kb__sum'] / 1000
except TypeError:
return 0
def create(self, validated_data):
raise ValidationError('Cannot create using this endpoint')
class Meta:
model = Space
fields = ('id', 'name', 'created_by', 'created_at', 'message', 'max_recipes', 'max_file_storage_mb', 'max_users',
'allow_sharing', 'demo', 'food_inherit', 'show_facet_count', 'user_count', 'recipe_count', 'file_size_mb', 'image',)
read_only_fields = ('id', 'created_by', 'created_at', 'max_recipes', 'max_file_storage_mb', 'max_users', 'allow_sharing', 'demo',)
class UserSpaceSerializer(WritableNestedModelSerializer):
user = UserSerializer(read_only=True)
groups = GroupSerializer(many=True)
def validate(self, data):
if self.instance.user == self.context['request'].space.created_by: # can't change space owner permission
raise serializers.ValidationError(_('Cannot modify Space owner permission.'))
return super().validate(data)
def create(self, validated_data):
raise ValidationError('Cannot create using this endpoint')
class Meta:
model = UserSpace
fields = ('id', 'user', 'space', 'groups', 'active', 'created_at', 'updated_at',)
read_only_fields = ('id', 'created_at', 'updated_at', 'space')
class SpacedModelSerializer(serializers.ModelSerializer):
def create(self, validated_data):
validated_data['space'] = self.context['request'].space
return super().create(validated_data)
class MealTypeSerializer(SpacedModelSerializer, WritableNestedModelSerializer):
def create(self, validated_data):
validated_data['created_by'] = self.context['request'].user
return super().create(validated_data)
class Meta:
list_serializer_class = SpaceFilterSerializer
model = MealType
fields = ('id', 'name', 'order', 'icon', 'color', 'default', 'created_by')
read_only_fields = ('created_by',)
class UserPreferenceSerializer(WritableNestedModelSerializer):
food_inherit_default = serializers.SerializerMethodField('get_food_inherit_defaults')
plan_share = UserSerializer(many=True, allow_null=True, required=False)
shopping_share = UserSerializer(many=True, allow_null=True, required=False)
food_children_exist = serializers.SerializerMethodField('get_food_children_exist')
image = UserFileViewSerializer(required=False, allow_null=True, many=False)
def get_food_inherit_defaults(self, obj):
return FoodInheritFieldSerializer(obj.user.get_active_space().food_inherit.all(), many=True).data
def get_food_children_exist(self, obj):
space = getattr(self.context.get('request', None), 'space', None)
return Food.objects.filter(depth__gt=0, space=space).exists()
def update(self, instance, validated_data):
with scopes_disabled():
return super().update(instance, validated_data)
def create(self, validated_data):
raise ValidationError('Cannot create using this endpoint')
class Meta:
model = UserPreference
fields = (
'user', 'image', 'theme', 'nav_color', 'default_unit', 'default_page', 'use_fractions', 'use_kj',
'plan_share', 'sticky_navbar',
'ingredient_decimals', 'comments', 'shopping_auto_sync', 'mealplan_autoadd_shopping',
'food_inherit_default', 'default_delay',
'mealplan_autoinclude_related', 'mealplan_autoexclude_onhand', 'shopping_share', 'shopping_recent_days',
'csv_delim', 'csv_prefix',
'filter_to_supermarket', 'shopping_add_onhand', 'left_handed', 'food_children_exist'
)
class StorageSerializer(SpacedModelSerializer): class StorageSerializer(SpacedModelSerializer):
def create(self, validated_data): def create(self, validated_data):
@ -731,6 +740,7 @@ class RecipeSerializer(RecipeBaseSerializer):
keywords = KeywordSerializer(many=True) keywords = KeywordSerializer(many=True)
rating = serializers.SerializerMethodField('get_recipe_rating') rating = serializers.SerializerMethodField('get_recipe_rating')
last_cooked = serializers.SerializerMethodField('get_recipe_last_cooked') last_cooked = serializers.SerializerMethodField('get_recipe_last_cooked')
shared = UserSerializer(many=True, required=False)
class Meta: class Meta:
model = Recipe model = Recipe
@ -738,6 +748,7 @@ class RecipeSerializer(RecipeBaseSerializer):
'id', 'name', 'description', 'image', 'keywords', 'steps', 'working_time', 'id', 'name', 'description', 'image', 'keywords', 'steps', 'working_time',
'waiting_time', 'created_by', 'created_at', 'updated_at', 'source_url', 'waiting_time', 'created_by', 'created_at', 'updated_at', 'source_url',
'internal', 'show_ingredient_overview', 'nutrition', 'servings', 'file_path', 'servings_text', 'rating', 'last_cooked', 'internal', 'show_ingredient_overview', 'nutrition', 'servings', 'file_path', 'servings_text', 'rating', 'last_cooked',
'private', 'shared',
) )
read_only_fields = ['image', 'created_by', 'created_at'] read_only_fields = ['image', 'created_by', 'created_at']
@ -775,7 +786,7 @@ class CommentSerializer(serializers.ModelSerializer):
class CustomFilterSerializer(SpacedModelSerializer, WritableNestedModelSerializer): class CustomFilterSerializer(SpacedModelSerializer, WritableNestedModelSerializer):
shared = UserNameSerializer(many=True, required=False) shared = UserSerializer(many=True, required=False)
def create(self, validated_data): def create(self, validated_data):
validated_data['created_by'] = self.context['request'].user validated_data['created_by'] = self.context['request'].user
@ -788,7 +799,7 @@ class CustomFilterSerializer(SpacedModelSerializer, WritableNestedModelSerialize
class RecipeBookSerializer(SpacedModelSerializer, WritableNestedModelSerializer): class RecipeBookSerializer(SpacedModelSerializer, WritableNestedModelSerializer):
shared = UserNameSerializer(many=True) shared = UserSerializer(many=True)
filter = CustomFilterSerializer(allow_null=True, required=False) filter = CustomFilterSerializer(allow_null=True, required=False)
def create(self, validated_data): def create(self, validated_data):
@ -832,7 +843,7 @@ class MealPlanSerializer(SpacedModelSerializer, WritableNestedModelSerializer):
meal_type_name = serializers.ReadOnlyField(source='meal_type.name') # TODO deprecate once old meal plan was removed meal_type_name = serializers.ReadOnlyField(source='meal_type.name') # TODO deprecate once old meal plan was removed
note_markdown = serializers.SerializerMethodField('get_note_markdown') note_markdown = serializers.SerializerMethodField('get_note_markdown')
servings = CustomDecimalField() servings = CustomDecimalField()
shared = UserNameSerializer(many=True, required=False, allow_null=True) shared = UserSerializer(many=True, required=False, allow_null=True)
shopping = serializers.SerializerMethodField('in_shopping') shopping = serializers.SerializerMethodField('in_shopping')
def get_note_markdown(self, obj): def get_note_markdown(self, obj):
@ -896,7 +907,7 @@ class ShoppingListEntrySerializer(WritableNestedModelSerializer):
ingredient_note = serializers.ReadOnlyField(source='ingredient.note') ingredient_note = serializers.ReadOnlyField(source='ingredient.note')
recipe_mealplan = ShoppingListRecipeSerializer(source='list_recipe', read_only=True) recipe_mealplan = ShoppingListRecipeSerializer(source='list_recipe', read_only=True)
amount = CustomDecimalField() amount = CustomDecimalField()
created_by = UserNameSerializer(read_only=True) created_by = UserSerializer(read_only=True)
completed_at = serializers.DateTimeField(allow_null=True, required=False) completed_at = serializers.DateTimeField(allow_null=True, required=False)
def get_fields(self, *args, **kwargs): def get_fields(self, *args, **kwargs):
@ -964,7 +975,7 @@ class ShoppingListEntryCheckedSerializer(serializers.ModelSerializer):
class ShoppingListSerializer(WritableNestedModelSerializer): class ShoppingListSerializer(WritableNestedModelSerializer):
recipes = ShoppingListRecipeSerializer(many=True, allow_null=True) recipes = ShoppingListRecipeSerializer(many=True, allow_null=True)
entries = ShoppingListEntrySerializer(many=True, allow_null=True) entries = ShoppingListEntrySerializer(many=True, allow_null=True)
shared = UserNameSerializer(many=True) shared = UserSerializer(many=True)
supermarket = SupermarketSerializer(allow_null=True) supermarket = SupermarketSerializer(allow_null=True)
def create(self, validated_data): def create(self, validated_data):
@ -1077,7 +1088,7 @@ class InviteLinkSerializer(WritableNestedModelSerializer):
if obj.email: if obj.email:
try: try:
if InviteLink.objects.filter(space=self.context['request'].space, created_at__gte=datetime.now() - timedelta(hours=4)).count() < 20: if InviteLink.objects.filter(space=self.context['request'].space, created_at__gte=datetime.now() - timedelta(hours=4)).count() < 20:
message = _('Hello') + '!\n\n' + _('You have been invited by ') + escape(self.context['request'].user.username) message = _('Hello') + '!\n\n' + _('You have been invited by ') + escape(self.context['request'].user.get_user_display_name())
message += _(' to join their Tandoor Recipes space ') + escape(self.context['request'].space.name) + '.\n\n' message += _(' to join their Tandoor Recipes space ') + escape(self.context['request'].space.name) + '.\n\n'
message += _('Click the following link to activate your account: ') + self.context['request'].build_absolute_uri(reverse('view_invite', args=[str(obj.uuid)])) + '\n\n' message += _('Click the following link to activate your account: ') + self.context['request'].build_absolute_uri(reverse('view_invite', args=[str(obj.uuid)])) + '\n\n'
message += _('If the link does not work use the following code to manually join the space: ') + str(obj.uuid) + '\n\n' message += _('If the link does not work use the following code to manually join the space: ') + str(obj.uuid) + '\n\n'
@ -1099,7 +1110,7 @@ class InviteLinkSerializer(WritableNestedModelSerializer):
class Meta: class Meta:
model = InviteLink model = InviteLink
fields = ( fields = (
'id', 'uuid', 'email', 'group', 'valid_until', 'used_by', 'created_by', 'created_at',) 'id', 'uuid', 'email', 'group', 'valid_until', 'used_by', 'reusable', 'created_by', 'created_at',)
read_only_fields = ('id', 'uuid', 'created_by', 'created_at',) read_only_fields = ('id', 'uuid', 'created_by', 'created_at',)
@ -1125,6 +1136,27 @@ class BookmarkletImportSerializer(BookmarkletImportListSerializer):
read_only_fields = ('created_by', 'space') read_only_fields = ('created_by', 'space')
# OAuth / Auth Token related Serializers
class AccessTokenSerializer(serializers.ModelSerializer):
token = serializers.SerializerMethodField('get_token')
def create(self, validated_data):
validated_data['token'] = f'tda_{str(uuid.uuid4()).replace("-","_")}'
validated_data['user'] = self.context['request'].user
return super().create(validated_data)
def get_token(self, obj):
if (timezone.now() - obj.created).seconds < 15:
return obj.token
return f'tda_************_******_***********{obj.token[len(obj.token)-4:]}'
class Meta:
model = AccessToken
fields = ('id', 'token', 'expires', 'scope', 'created', 'updated')
read_only_fields = ('id', 'token',)
# Export/Import Serializers # Export/Import Serializers
class KeywordExportSerializer(KeywordSerializer): class KeywordExportSerializer(KeywordSerializer):
@ -1232,6 +1264,6 @@ class FoodShoppingUpdateSerializer(serializers.ModelSerializer):
# non model serializers # non model serializers
class RecipeFromSourceSerializer(serializers.Serializer): class RecipeFromSourceSerializer(serializers.Serializer):
url = serializers.CharField(max_length=4096, required=False, allow_null=True) url = serializers.CharField(max_length=4096, required=False, allow_null=True, allow_blank=True)
data = serializers.CharField(required=False, allow_null=True, allow_blank=True) data = serializers.CharField(required=False, allow_null=True, allow_blank=True)
bookmarklet = serializers.IntegerField(required=False, allow_null=True, ) bookmarklet = serializers.IntegerField(required=False, allow_null=True, )

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -28,7 +28,7 @@
const xhr = new XMLHttpRequest(); const xhr = new XMLHttpRequest();
xhr.open('POST', url, true); xhr.open('POST', url, true);
xhr.setRequestHeader('Content-Type', 'application/json'); xhr.setRequestHeader('Content-Type', 'application/json');
xhr.setRequestHeader('Authorization', 'Token ' + token); xhr.setRequestHeader('Authorization', 'Bearer ' + token);
// listen for `onload` event // listen for `onload` event
xhr.onload = () => { xhr.onload = () => {

View File

@ -1,43 +0,0 @@
/* frac.js (C) 2012-present SheetJS -- http://sheetjs.com */
/*https://developer.aliyun.com/mirror/npm/package/frac/v/0.3.0 Apache license*/
var frac = function frac(x, D, mixed) {
var n1 = Math.floor(x), d1 = 1;
var n2 = n1+1, d2 = 1;
if(x !== n1) while(d1 <= D && d2 <= D) {
var m = (n1 + n2) / (d1 + d2);
if(x === m) {
if(d1 + d2 <= D) { d1+=d2; n1+=n2; d2=D+1; }
else if(d1 > d2) d2=D+1;
else d1=D+1;
break;
}
else if(x < m) { n2 = n1+n2; d2 = d1+d2; }
else { n1 = n1+n2; d1 = d1+d2; }
}
if(d1 > D) { d1 = d2; n1 = n2; }
if(!mixed) return [0, n1, d1];
var q = Math.floor(n1/d1);
return [q, n1 - q*d1, d1];
};
frac.cont = function cont(x, D, mixed) {
var sgn = x < 0 ? -1 : 1;
var B = x * sgn;
var P_2 = 0, P_1 = 1, P = 0;
var Q_2 = 1, Q_1 = 0, Q = 0;
var A = Math.floor(B);
while(Q_1 < D) {
A = Math.floor(B);
P = A * P_1 + P_2;
Q = A * Q_1 + Q_2;
if((B - A) < 0.00000005) break;
B = 1 / (B - A);
P_2 = P_1; P_1 = P;
Q_2 = Q_1; Q_1 = Q;
}
if(Q > D) { if(Q_1 > D) { Q = Q_2; P = P_2; } else { Q = Q_1; P = P_1; } }
if(!mixed) return [0, sgn * P, Q];
var q = Math.floor(sgn * P/Q);
return [q, sgn*P - q*Q, Q];
};
// eslint-disable-next-line no-undef
if(typeof module !== 'undefined' && typeof DO_NOT_EXPORT_FRAC === 'undefined') module.exports = frac;

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -6113,9 +6113,11 @@ a.close.disabled {
.align-text-top { .align-text-top {
vertical-align: text-top !important vertical-align: text-top !important
} }
/*!
* technically the wrong color but not used anywhere besides nav and this way changing nav color is supported
*/
.bg-primary { .bg-primary {
background-color: #b98766 !important background-color: rgb(221, 191, 134) !important;
} }
a.bg-primary:focus, a.bg-primary:hover, button.bg-primary:focus, button.bg-primary:hover { a.bg-primary:focus, a.bg-primary:hover, button.bg-primary:focus, button.bg-primary:hover {

View File

@ -1,4 +1,3 @@
import django_tables2 as tables import django_tables2 as tables
from django.utils.html import format_html from django.utils.html import format_html
from django.utils.translation import gettext as _ from django.utils.translation import gettext as _
@ -8,60 +7,6 @@ from .models import (CookLog, InviteLink, Recipe, RecipeImport,
Storage, Sync, SyncLog, ViewLog) Storage, Sync, SyncLog, ViewLog)
class ImageUrlColumn(tables.Column):
def render(self, value):
if value.url:
return value.url
return None
class RecipeTableSmall(tables.Table):
id = tables.LinkColumn('edit_recipe', args=[A('id')])
name = tables.LinkColumn('view_recipe', args=[A('id')])
all_tags = tables.Column(
attrs={
'td': {'class': 'd-none d-lg-table-cell'},
'th': {'class': 'd-none d-lg-table-cell'}
}
)
class Meta:
model = Recipe
template_name = 'generic/table_template.html'
fields = ('id', 'name', 'all_tags')
class RecipeTable(tables.Table):
edit = tables.TemplateColumn(
"<a style='color: inherit' href='{% url 'edit_recipe' record.id %}' >" + _('Edit') + "</a>" # noqa: E501
)
name = tables.LinkColumn('view_recipe', args=[A('id')])
all_tags = tables.Column(
attrs={
'td': {'class': 'd-none d-lg-table-cell'},
'th': {'class': 'd-none d-lg-table-cell'}
}
)
image = ImageUrlColumn()
class Meta:
model = Recipe
template_name = 'recipes_table.html'
fields = (
'id', 'name', 'all_tags', 'description', 'image', 'instructions',
'working_time', 'waiting_time', 'internal'
)
# class IngredientTable(tables.Table):
# id = tables.LinkColumn('edit_food', args=[A('id')])
# class Meta:
# model = Keyword
# template_name = 'generic/table_template.html'
# fields = ('id', 'name')
class StorageTable(tables.Table): class StorageTable(tables.Table):
id = tables.LinkColumn('edit_storage', args=[A('id')]) id = tables.LinkColumn('edit_storage', args=[A('id')])
@ -122,7 +67,6 @@ class RecipeImportTable(tables.Table):
fields = ('id', 'name', 'file_path') fields = ('id', 'name', 'file_path')
class InviteLinkTable(tables.Table): class InviteLinkTable(tables.Table):
link = tables.TemplateColumn( link = tables.TemplateColumn(
"<input value='{{ request.scheme }}://{{ request.get_host }}{% url 'view_invite' record.uuid %}' class='form-control' />" "<input value='{{ request.scheme }}://{{ request.get_host }}{% url 'view_invite' record.uuid %}' class='form-control' />"

View File

@ -69,7 +69,7 @@
</head> </head>
<body> <body>
<nav class="navbar navbar-expand-lg navbar-dark bg-{% nav_color request %} bg-header" <nav class="navbar navbar-expand-lg navbar-dark bg-{% nav_color request %}"
id="id_main_nav" id="id_main_nav"
style="{% sticky_nav request %}"> style="{% sticky_nav request %}">
@ -285,7 +285,7 @@
<li class="nav-item dropdown {% if request.resolver_match.url_name in 'view_space,view_settings,view_history,view_system,docs_markdown' %}active{% endif %}"> <li class="nav-item dropdown {% if request.resolver_match.url_name in 'view_space,view_settings,view_history,view_system,docs_markdown' %}active{% endif %}">
<a class="nav-link dropdown-toggle" href="#" id="navbarDropdownMenuLink" data-toggle="dropdown" <a class="nav-link dropdown-toggle" href="#" id="navbarDropdownMenuLink" data-toggle="dropdown"
aria-haspopup="true" aria-expanded="false"><i aria-haspopup="true" aria-expanded="false"><i
class="fas fa-fw fa-user-alt"></i> {{ user.get_user_name }} class="fas fa-fw fa-user-alt"></i> {{ user.get_user_display_name }}
</a> </a>
<div class="dropdown-menu dropdown-menu-right" aria-labelledby="navbarDropdownMenuLink"> <div class="dropdown-menu dropdown-menu-right" aria-labelledby="navbarDropdownMenuLink">
@ -408,6 +408,7 @@
localStorage.setItem('BASE_PATH', "{% base_path request 'base' %}") localStorage.setItem('BASE_PATH', "{% base_path request 'base' %}")
localStorage.setItem('STATIC_URL', "{% base_path request 'static_base' %}") localStorage.setItem('STATIC_URL', "{% base_path request 'static_base' %}")
localStorage.setItem('DEBUG', "{% is_debug %}") localStorage.setItem('DEBUG', "{% is_debug %}")
localStorage.setItem('USER_ID', "{{request.user.pk}}")
window.addEventListener("load", () => { window.addEventListener("load", () => {
if ("serviceWorker" in navigator) { if ("serviceWorker" in navigator) {
navigator.serviceWorker.register("{% url 'service_worker' %}", {scope: "{% base_path request 'base' %}" + '/'}).then(function (reg) { navigator.serviceWorker.register("{% url 'service_worker' %}", {scope: "{% base_path request 'base' %}" + '/'}).then(function (reg) {

View File

@ -1,43 +0,0 @@
{% extends "base.html" %}
{% load django_tables2 %}
{% load crispy_forms_tags %}
{% load static %}
{% load i18n %}
{% block title %}{% trans "Cookbook" %}{% endblock %}
{% block extra_head %}
{{ units_form.media }}
{% endblock %}
{% block content %}
<h2><i class="fas fa-shopping-cart"></i> {% trans 'Edit Ingredients' %}</h2>
{% blocktrans %}
The following form can be used if, accidentally, two (or more) units or ingredients where created that should be
the same.
It merges two units or ingredients and updates all recipes using them.
{% endblocktrans %}
<br/>
<br/>
<h4>{% trans 'Units' %}</h4>
<form action="{% url 'edit_food' %}" method="post"
onsubmit="return confirm('{% trans 'Are you sure that you want to merge these two units?' %}')">
{% csrf_token %}
{{ units_form|crispy }}
<button class="btn btn-danger" type="submit"
><i
class="fas fa-sync-alt"></i> {% trans 'Merge' %}</button>
</form>
<h4>{% trans 'Ingredients' %}</h4>
<form action="{% url 'edit_food' %}" method="post"
onsubmit="return confirm('{% trans 'Are you sure that you want to merge these two ingredients?' %}')">
{% csrf_token %}
{{ food_form|crispy }}
<button class="btn btn-danger" type="submit">
<i class="fas fa-sync-alt"></i> {% trans 'Merge' %}</button>
</form>
{% endblock %}

View File

@ -1,26 +0,0 @@
{% extends "base.html" %}
{% load crispy_forms_filters %}
{% load i18n %}
{% load static %}
{% block title %}{% trans 'Import Recipes' %}{% endblock %}
{% block extra_head %}
{{ form.media }}
{% endblock %}
{% block content %}
<h2>{% trans 'Import' %}</h2>
<div class="row">
<div class="col col-md-12">
<form action="." method="post" enctype="multipart/form-data">
{% csrf_token %}
{{ form|crispy }}
<button class="btn btn-success" type="submit"><i class="fas fa-file-import"></i> {% trans 'Import' %}
</button>
</form>
</div>
</div>
{% endblock %}

View File

@ -1,58 +0,0 @@
{% load i18n %}
<div class="modal" tabindex="-1" role="dialog" id="modal_recipe">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">{% trans 'Recipe' %}</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body" style="text-align: center">
<i class="fas fa-spinner fa-spin fa-8x" id="id_spinner"></i>
<a href="" id="a_recipe_open" target="_blank" onclick="afterClick()" style="font-size: 250%"></a>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">{% trans 'Close' %}</button>
</div>
</div>
</div>
</div>
<script type="application/javascript">
function openRecipe(id) {
var link = $('#a_recipe_open');
link.hide();
$('#id_spinner').show();
var url = "{% url 'api_get_external_file_link' recipe_id=12345 %}".replace(/12345/, id);
link.text("{% trans 'Open Recipe' %}");
$('#modal_recipe').modal('show');
var xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function () {
if (this.readyState === 4 && this.status === 200) {
if (/^((?!chrome|android).)*safari/i.test(navigator.userAgent)) {
link.attr("href", this.responseText);
link.show();
} else {
window.open(this.responseText);
$('#modal_recipe').modal('hide');
}
$('#id_spinner').hide();
}
};
xhttp.open("GET", url, true);
xhttp.send();
}
function afterClick() {
$('#modal_recipe').modal('hide');
return true;
}
</script>

View File

@ -1,18 +0,0 @@
<!--
As there is apparently no good way to pass django named URLs to Vue/Webpack we will pack the urls we need into
this object and load it in all the templates where we load Vue apps
Reason for not using other alternatives
## django-js-reverse
bad performance because the 25kb or so path file needs to be loaded before any other request can be made
or all paths need to be printed in template which is apparently not recommended for CSP reasons (although this here
might do the same)
-->
<script type="application/javascript">
window.DJANGO_URLS = {
'edit_storage'
}
</script>

View File

@ -37,12 +37,6 @@
"short_name": "Shopping", "short_name": "Shopping",
"description": "View your shopping lists", "description": "View your shopping lists",
"url": "./list/shopping-list/" "url": "./list/shopping-list/"
},
{
"name": "Latest Shopping List",
"short_name": "Shopping List",
"description": "View the latest shopping list",
"url": "./shopping/latest/"
} }
] ]
} }

View File

@ -1,85 +0,0 @@
{% extends "base.html" %}
{% load static %}
{% load custom_tags %}
{% load i18n %}
{% block title %}{% trans 'Meal Plan View' %}{% endblock %}
{% block extra_head %}
<link rel="stylesheet" href="{% static 'custom/css/markdown_blockquote.css' %}">
{% endblock %}
{% block content %}
<div class="row">
<div class="col col-12">
<h3>{{ plan.meal_type }} {{ plan.date }} <a href="{% url 'edit_meal_plan' plan.pk %}"
class="d-print-none"><i class="fas fa-pencil-alt"></i></a>
</h3>
<small class="text-muted">{% trans 'Created by' %} {{ plan.created_by.get_user_name }}</small>
{% if plan.shared.all %}
<br/><small class="text-muted">{% trans 'Shared with' %}
{% for x in plan.shared.all %}{{ x.get_user_name }}{% if not forloop.last %}, {% endif %} {% endfor %}</small>
{% endif %}
</div>
</div>
<br/>
<br/>
{% if plan.title %}
<div class="row">
<div class="col col-12">
<h4>{{ plan.title }}</h4>
</div>
</div>
{% endif %}
{% if plan.recipe %}
<div class="row">
<div class="col col-12">
<div class="card">
<div class="card-body">
{% recipe_rating plan.recipe request.user as rating %}
<h5 class="card-title"><a
href="{% url 'view_recipe' plan.recipe.pk %}">{{ plan.recipe }}</a> {{ rating|safe }}
</h5>
{% recipe_last plan.recipe request.user as last_cooked %}
{% if last_cooked %}
{% trans 'Last cooked' %} {{ last_cooked|date }}
{% else %}
{% trans 'Never cooked before.' %}
{% endif %}
{% if plan.recipe.keywords %}
<br/>
<br/>
{% for x in plan.recipe.keywords.all %}
<span class="badge badge-pill badge-light">{{ x }}</span>
{% endfor %}
{% endif %}
</div>
</div>
</div>
</div>
{% endif %}
{% if plan.note %}
<br/>
<div class="row">
<div class="col col-12">
{{ plan.note | markdown | safe }}
</div>
</div>
{% endif %}
{% if same_day_plan %}
<br/>
<h4>{% trans 'Other meals on this day' %}</h4>
<ul class="list-group list-group-flush">
{% for x in same_day_plan %}
<li class="list-group-item"><a href="{% url 'view_plan_entry' x.pk %}">{{ x.get_label }}
({{ x.meal_type }})</a></li>
{% endfor %}
</ul>
{% endif %}
{% endblock %}

View File

@ -0,0 +1,32 @@
{% extends "base.html" %}
{% load render_bundle from webpack_loader %}
{% load static %}
{% load i18n %}
{% load l10n %}
{% block title %}{% trans 'Profile' %}{% endblock %}
{% block content %}
<div id="app" >
<profile-view></profile-view>
</div>
{% endblock %}
{% block script %}
{% if debug %}
<script src="{% url 'js_reverse' %}"></script>
{% else %}
<script src="{% static 'django_js_reverse/reverse.js' %}"></script>
{% endif %}
<script type="application/javascript">
window.CUSTOM_LOCALE = '{{ request.LANGUAGE_CODE }}'
</script>
{% render_bundle 'profile_view' %}
{% endblock %}

View File

@ -64,7 +64,6 @@
window.CUSTOM_LOCALE = '{{ request.LANGUAGE_CODE }}' window.CUSTOM_LOCALE = '{{ request.LANGUAGE_CODE }}'
window.RECIPE_ID = {{recipe.pk}}; window.RECIPE_ID = {{recipe.pk}};
window.USER_SERVINGS = {{ user_servings }};
window.SHARE_UID = '{{ share }}'; window.SHARE_UID = '{{ share }}';
window.USER_PREF = { window.USER_PREF = {
'use_fractions': {% if request.user.userpreference.use_fractions %} true {% else %} false {% endif %}, 'use_fractions': {% if request.user.userpreference.use_fractions %} true {% else %} false {% endif %},

View File

@ -1,153 +0,0 @@
{% load crispy_forms_tags %}
{% load i18n %}
{% load django_tables2 %}
{% load static %}
{% load custom_tags %}
{% block content %}
<div class="row">
<div class="col">
<div class="table-container">
{% block table %}
<table {% render_attrs table.attrs class="table" %}>
{% for row in table.paginated_rows %}
<div class="card" style="margin-top: 1px;">
<div class="row no-gutters">
<div class="col-md-4">
<a href="{% url 'view_recipe' row.cells.id %}">
{% if row.cells.image|length > 1 %}
<img src=" {{ row.cells.image }}" alt="{% trans 'Recipe Image' %}"
class="card-img" style="object-fit:cover;height: 160px">
{% else %}
<img src="{% static 'assets/recipe_no_image.svg' %}"
alt="{% trans 'Recipe Image' %}"
class="card-img d-none d-md-block"
style="object-fit: cover; height: 130px">
{% endif %}
</a>
</div>
<div class="col-md-8">
<div class="card-body" style="padding: 16px">
<div class="d-flex">
<div class="flex-fill">
<h5 class="card-title p-0 m-0">{{ row.cells.name }}
{% recipe_rating row.record request.user as rating %}
{{ rating|safe }}
</h5>
{%if row.record.description|length > 0 %}
<p class="card-subtitle p-0 m-0 text-muted" style="height:3em; overflow:hidden;">
{{ row.cells.description }}
</p>
{% endif %}
<p class="card-text{% if not row.record.keywords %} d-none d-lg-block{% endif %}">
{% for x in row.record.keywords.all %}
<span class="badge badge-pill badge-light">{{ x }}</span>
{% endfor %}
</p>
<p class="card-text">
{% if row.cells.working_time != 0 %}
<span class="badge badge-secondary"><i
class="fas fa-user-clock"></i> {% trans 'Preparation time ca.' %} {{ row.cells.working_time }} min </span>
{% endif %}
{% if row.cells.waiting_time != 0 %}
<span
class="badge badge-secondary"><i
class="far fa-clock"></i> {% trans 'Waiting time ca.' %} {{ row.cells.waiting_time }} min </span>
{% endif %}
{% if not row.record.internal %}
<span class="badge badge-info">{% trans 'External' %} </span>
{% endif %}
{% recipe_last row.record request.user as last_cooked %}
{% if last_cooked %}
<span class="badge badge-primary">{% trans 'Last cooked' %} {{ last_cooked|date }}</span>
{% endif %}
</p>
</div>
<div>
<div class="dropdown">
<a class="btn shadow-none" href="#" role="button"
id="dropdownMenuLink"
data-toggle="dropdown" aria-haspopup="true"
aria-expanded="false">
<i class="fas fa-ellipsis-v text-muted"></i>
</a>
<div class="dropdown-menu dropdown-menu-right"
aria-labelledby="dropdownMenuLink">
<a class="dropdown-item"
href="{% url 'edit_recipe' row.record.pk %}"><i
class="fas fa-pencil-alt fa-fw"></i> {% trans 'Edit' %}
</a>
<button class="dropdown-item"
onclick="openCookLogModal({{ row.record.pk }})"><i
class="fas fa-clipboard-list fa-fw"></i> {% trans 'Log Cooking' %}
</button>
<a class="dropdown-item"
href="{% url 'delete_recipe' row.record.pk %}"><i
class="fas fa-trash fa-fw"></i> {% trans 'Delete' %}
</a>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
{% endfor %}
</table>
{% endblock table %}
</div>
</div>
</div>
{% block pagination %}
{% if table.page and table.paginator.num_pages > 1 %}
<nav aria-label="Table navigation">
<ul class="pagination justify-content-center flex-wrap">
{% if table.page.has_previous %}
{% block pagination.previous %}
<li class="previous page-item">
<a href="{% querystring table.prefixed_page_field=table.page.previous_page_number %}"
class="page-link">
<span aria-hidden="true">&laquo;</span>
{% trans 'previous' %}
</a>
</li>
{% endblock pagination.previous %}
{% endif %}
{% if table.page.has_previous or table.page.has_next %}
{% block pagination.range %}
{% for p in table.page|table_page_range:table.paginator %}
<li class="page-item{% if table.page.number == p %} active{% endif %}">
<a class="page-link"
{% if p != '...' %}href="{% querystring table.prefixed_page_field=p %}"{% endif %}>
{{ p }}
</a>
</li>
{% endfor %}
{% endblock pagination.range %}
{% endif %}
{% if table.page.has_next %}
{% block pagination.next %}
<li class="next page-item">
<a href="{% querystring table.prefixed_page_field=table.page.next_page_number %}"
class="page-link">
{% trans 'next' %}
<span aria-hidden="true">&raquo;</span>
</a>
</li>
{% endblock pagination.next %}
{% endif %}
</ul>
</nav>
{% endif %}
{% endblock pagination %}
{% endblock content %}

View File

@ -6,7 +6,7 @@
{% block title %}{% trans 'Settings' %}{% endblock %} {% block title %}{% trans 'Settings' %}{% endblock %}
{% block extra_head %} {% block extra_head %}
{{ preference_form.media }} {{ search_form.media }}
{% endblock %} {% endblock %}
@ -15,209 +15,53 @@
<nav aria-label="breadcrumb"> <nav aria-label="breadcrumb">
<ol class="breadcrumb"> <ol class="breadcrumb">
<li class="breadcrumb-item"><a href="{% url 'view_settings' %}">{% trans 'Settings' %}</a></li> <li class="breadcrumb-item"><a href="{% url 'view_settings' %}">{% trans 'Settings' %}</a></li>
<li class="breadcrumb-item active" aria-current="page">{% trans 'Search' %}</li>
</ol> </ol>
</nav> </nav>
<!-- Nav tabs --> <div class="tab-pane {% if active_tab == 'search' %} active {% endif %}" id="search" role="tabpanel"
<ul class="nav nav-tabs" id="myTab" role="tablist" style="margin-bottom: 2vh"> aria-labelledby="search-tab">
<li class="nav-item" role="presentation"> <h4>{% trans 'Search Settings' %}</h4>
<a class="nav-link {% if active_tab == 'account' %} active {% endif %}" id="account-tab" data-toggle="tab" {% trans 'There are many options to configure the search depending on your personal preferences.' %}
href="#account" role="tab" {% trans 'Usually you do <b>not need</b> to configure any of them and can just stick with either the default or one of the following presets.' %}
aria-controls="account" {% trans 'If you do want to configure the search you can read about the different options <a href="/docs/search/">here</a>.' %}
aria-selected="{% if active_tab == 'account' %} 'true' {% else %} 'false' {% endif %}">
{% trans 'Account' %}</a>
</li>
<li class="nav-item" role="presentation">
<a class="nav-link {% if active_tab == 'preferences' %} active {% endif %}" id="preferences-tab"
data-toggle="tab" href="#preferences" role="tab"
aria-controls="preferences"
aria-selected="{% if active_tab == 'preferences' %} 'true' {% else %} 'false' {% endif %}">
{% trans 'Preferences' %}</a>
</li>
<li class="nav-item" role="presentation">
<a class="nav-link {% if active_tab == 'api' %} active {% endif %}" id="api-tab" data-toggle="tab"
href="#api" role="tab"
aria-controls="api"
aria-selected="{% if active_tab == 'api' %} 'true' {% else %} 'false' {% endif %}">
{% trans 'API-Settings' %}</a>
</li>
<li class="nav-item" role="presentation">
<a class="nav-link {% if active_tab == 'search' %} active {% endif %}" id="search-tab" data-toggle="tab"
href="#search" role="tab"
aria-controls="search"
aria-selected="{% if active_tab == 'search' %} 'true' {% else %} 'false' {% endif %}">
{% trans 'Search-Settings' %}</a>
</li>
<li class="nav-item" role="presentation">
<a class="nav-link {% if active_tab == 'shopping' %} active {% endif %}" id="shopping-tab" data-toggle="tab"
href="#shopping" role="tab"
aria-controls="search"
aria-selected="{% if active_tab == 'shopping' %} 'true' {% else %} 'false' {% endif %}">
{% trans 'Shopping-Settings' %}</a>
</li>
</ul> <div class="card-deck mt-4">
<div class="card">
<!-- Tab panes --> <div class="card-body">
<div class="tab-content"> <h5 class="card-title">{% trans 'Fuzzy' %}</h5>
<div class="tab-pane {% if active_tab == 'account' %} active {% endif %}" id="account" role="tabpanel" <p class="card-text">{% trans 'Find what you need even if your search or the recipe contains typos. Might return more results than needed to make sure you find what you are looking for.' %}</p>
aria-labelledby="account-tab"> <p class="card-text"><small class="text-muted">{% trans 'This is the default behavior' %}</small>
<h4>{% trans 'Name Settings' %}</h4> </p>
<form action="." method="post"> <button class="btn btn-primary card-link"
{% csrf_token %} onclick="applyPreset('fuzzy')">{% trans 'Apply' %}</button>
{{ user_name_form|crispy }} </div>
<button class="btn btn-success" type="submit" name="user_name_form"><i </div>
class="fas fa-save"></i> {% trans 'Save' %}</button> <div class="card">
</form> <div class="card-body">
<h5 class="card-title">{% trans 'Precise' %}</h5>
<h4>{% trans 'Account Settings' %}</h4> <p class="card-text">{% trans 'Allows fine control over search results but might not return results if too many spelling mistakes are made.' %}</p>
<p class="card-text"><small class="text-muted">{% trans 'Perfect for large Databases' %}</small></p>
<a href="{% url 'account_email' %}" class="btn btn-primary">{% trans 'Emails' %}</a> <button class="btn btn-primary card-link"
<a href="{% url 'account_change_password' %}" class="btn btn-primary">{% trans 'Password' %}</a> onclick="applyPreset('precise')">{% trans 'Apply' %}</button>
</div>
<a href="{% url 'socialaccount_connections' %}" class="btn btn-primary">{% trans 'Social' %}</a> </div>
<br/>
<br/>
<br/>
<br/>
</div> </div>
<div class="tab-pane {% if active_tab == 'preferences' %} active {% endif %}" id="preferences" role="tabpanel"
aria-labelledby="preferences-tab">
<div class="row">
<div class="col col-md-12">
<h4><i class="fas fa-language fa-fw"></i> {% trans 'Language' %}</h4>
</div>
</div>
<div class="row">
<div class="col-md-12">
<form action="{% url 'set_language' %}" method="post">{% csrf_token %}
<input class="form-control" name="next" type="hidden" value="{{ redirect_to }}">
<select name="language" class="form-control">
{% get_current_language as LANGUAGE_CODE %}
{% get_available_languages as LANGUAGES %}
{% get_language_info_list for LANGUAGES as languages %}
{% for language in languages %}
<option value="{{ language.code }}"{% if language.code == LANGUAGE_CODE %}
selected{% endif %}>
{{ language.name_local }} ({{ language.code }})
</option>
{% endfor %}
</select>
<br/>
<button class="btn btn-success" type="submit"><i class="fas fa-save"></i> {% trans 'Save' %}
</button>
</form>
</div>
</div>
<div class="row">
<div class="col col-md-12">
<h4><i class="fas fa-palette fa-fw"></i> {% trans 'Style' %}</h4>
</div>
</div>
<div class="row">
<div class="col col-md-12">
<form action="." method="post">
{% csrf_token %}
{{ preference_form|crispy }}
<button class="btn btn-success" type="submit" name="preference_form"><i
class="fas fa-save"></i> {% trans 'Save' %}</button>
</form>
</div>
</div>
</div>
<div class="tab-pane {% if active_tab == 'api' %} active {% endif %}" id="api" role="tabpanel"
aria-labelledby="api-tab">
<div class="row">
<div class="col col-md-12">
<h4><i class="fas fa-terminal fa-fw"></i> {% trans 'API Token' %}</h4>
{% trans 'You can use both basic authentication and token based authentication to access the REST API.' %}
<br/>
<br/>
</div>
</div>
<div class="row">
<div class="col col-md-12">
<div class="input-group mb-3">
<input class="form-control" value="{{ api_token }}" id="id_token">
<div class="input-group-append">
<button class="input-group-btn btn btn-primary" onclick="copyToken()"><i
class="far fa-copy"></i></button>
</div>
</div>
<br/>
{% trans 'Use the token as an Authorization header prefixed by the word token as shown in the following examples:' %}
<br/>
<code>Authorization: Token {{ api_token }}</code> {% trans 'or' %}<br/>
<code>curl -X GET http://your.domain.com/api/recipes/ -H 'Authorization:
Token {{ api_token }}'</code>
</div>
</div>
</div>
<div class="tab-pane {% if active_tab == 'search' %} active {% endif %}" id="search" role="tabpanel"
aria-labelledby="search-tab">
<h4>{% trans 'Search Settings' %}</h4>
{% trans 'There are many options to configure the search depending on your personal preferences.' %}
{% trans 'Usually you do <b>not need</b> to configure any of them and can just stick with either the default or one of the following presets.' %}
{% trans 'If you do want to configure the search you can read about the different options <a href="/docs/search/">here</a>.' %}
<div class="card-deck mt-4">
<div class="card">
<div class="card-body">
<h5 class="card-title">{% trans 'Fuzzy' %}</h5>
<p class="card-text">{% trans 'Find what you need even if your search or the recipe contains typos. Might return more results than needed to make sure you find what you are looking for.' %}</p>
<p class="card-text"><small class="text-muted">{% trans 'This is the default behavior' %}</small></p>
<button class="btn btn-primary card-link" onclick="applyPreset('fuzzy')">{% trans 'Apply' %}</button>
</div>
</div>
<div class="card">
<div class="card-body">
<h5 class="card-title">{% trans 'Precise' %}</h5>
<p class="card-text">{% trans 'Allows fine control over search results but might not return results if too many spelling mistakes are made.' %}</p>
<p class="card-text"><small class="text-muted">{% trans 'Perfect for large Databases' %}</small></p>
<button class="btn btn-primary card-link" onclick="applyPreset('precise')">{% trans 'Apply' %}</button>
</div>
</div>
</div>
<hr/>
<form action="./#search" method="post" id="id_search_form">
{% csrf_token %}
{{ search_form|crispy }}
<button class="btn btn-success" type="submit" name="search_form" id="search_form_button"><i
class="fas fa-save"></i> {% trans 'Save' %}</button>
</form>
</div>
<div class="tab-pane {% if active_tab == 'shopping' %} active {% endif %}" id="shopping" role="tabpanel"
aria-labelledby="shopping-tab">
<h4>{% trans 'Shopping Settings' %}</h4>
<form action="./#shopping" method="post" id="id_shopping_form">
{% csrf_token %}
{{ shopping_form|crispy }}
<button class="btn btn-success" type="submit" name="shopping_form" id="shopping_form_button"><i
class="fas fa-save"></i> {% trans 'Save' %}</button>
</form>
</div>
<hr/>
<form action="./#search" method="post" id="id_search_form">
{% csrf_token %}
{{ search_form|crispy }}
<button class="btn btn-success" type="submit" name="search_form" id="search_form_button"><i
class="fas fa-save"></i> {% trans 'Save' %}</button>
</form>
</div> </div>
<script type="application/javascript"> <script type="application/javascript">
$(function() { $(function () {
$('#id_search-trigram_threshold').get(0).type = 'range'; $('#id_search-trigram_threshold').get(0).type = 'range';
}); });
@ -225,44 +69,5 @@
$('#id_search-preset').val(preset); $('#id_search-preset').val(preset);
$('#search_form_button').click(); $('#search_form_button').click();
} }
function copyToken() {
let token = $('#id_token');
token.select();
document.execCommand("copy");
}
// Javascript to enable link to tab
var hash = location.hash.replace(/^#/, ''); // ^ means starting, meaning only match the first hash
if (hash) {
$('.nav-tabs a[href="#' + hash + '"]').tab('show');
}
// Change hash for page-reload
$('.nav-tabs a').on('shown.bs.tab', function(e) {
window.location.hash = e.target.hash;
})
{% comment %}
// listen for events
$(document).ready(function() {
hideShow()
// call hideShow when the user clicks on the mealplan_autoadd checkbox
$("#id_shopping-mealplan_autoadd_shopping").click(function(event) {
hideShow();
});
})
function hideShow() {
if(document.getElementById('id_shopping-mealplan_autoadd_shopping').checked == true) {
$('#div_id_shopping-mealplan_autoexclude_onhand').show();
$('#div_id_shopping-mealplan_autoinclude_related').show();
}
else {
$('#div_id_shopping-mealplan_autoexclude_onhand').hide();
$('#div_id_shopping-mealplan_autoinclude_related').hide();
}
}
{% endcomment %}
</script> </script>
{% endblock %} {% endblock %}

View File

@ -1,18 +1,25 @@
{% extends "base.html" %} {% load render_bundle from webpack_loader %} {% load static %} {% load i18n %} {% block title %} {{ title }} {% endblock %} {% block content_fluid %} {% extends "base.html" %}
{% load render_bundle from webpack_loader %}
{% load static %}
{% load i18n %}
{% block title %} {{ title }} {% endblock %}
<div id="app"> {% block content_fluid %}
<shopping-list-view></shopping-list-view>
</div> <div id="app">
<shopping-list-view></shopping-list-view>
</div>
{% endblock %} {% block script %} {% if debug %} {% endblock %} {% block script %} {% if debug %}
<script src="{% url 'js_reverse' %}"></script> <script src="{% url 'js_reverse' %}"></script>
{% else %} {% else %}
<script src="{% static 'django_js_reverse/reverse.js' %}"></script> <script src="{% static 'django_js_reverse/reverse.js' %}"></script>
{% endif %} {% endif %}
<script type="application/javascript"> <script type="application/javascript">
window.IMAGE_PLACEHOLDER = "{% static 'assets/recipe_no_image.svg' %}" window.IMAGE_PLACEHOLDER = "{% static 'assets/recipe_no_image.svg' %}"
window.CUSTOM_LOCALE = '{{ request.LANGUAGE_CODE }}' window.CUSTOM_LOCALE = '{{ request.LANGUAGE_CODE }}'
window.SHOPPING_MIN_AUTOSYNC_INTERVAL = {{ SHOPPING_MIN_AUTOSYNC_INTERVAL }}
</script> </script>
{% render_bundle 'shopping_list_view' %} {% endblock %} {% render_bundle 'shopping_list_view' %} {% endblock %}

View File

@ -32,15 +32,23 @@
{% for us in request.user.userspace_set.all %} {% for us in request.user.userspace_set.all %}
<div class="card"> <div class="card">
{% if us.space.image and us.space.image.is_image %}
<img style="height: 15vh; object-fit: cover" src="{{ us.space.image.file.url }}"
class="card-img-top" alt="Image">
{% else %}
<img style="height: 15vh; object-fit: cover" src="{% static 'assets/recipe_no_image.svg' %}"
class="card-img-top" alt="Image">
{% endif %}
<div class="card-body"> <div class="card-body">
<h5 class="card-title"><a <h5 class="card-title"><a
href="{% url 'view_switch_space' us.space.id %}">{{ us.space.name }}</a> href="{% url 'view_switch_space' us.space.id %}">{{ us.space.name }}</a>
</h5> </h5>
{# {% if us.active %}#} {# {% if us.active %}#}
{# <i class="far fa-dot-circle fa-fw"></i>#} {# <i class="far fa-dot-circle fa-fw"></i>#}
{# {% else %}#} {# {% else %}#}
{# <i class="far fa-circle fa-fw"></i>#} {# <i class="far fa-circle fa-fw"></i>#}
{# {% endif %}#} {# {% endif %}#}
<p class="card-text"><small <p class="card-text"><small
class="text-muted">{% trans 'Owner' %}: {{ us.space.created_by }}</small> class="text-muted">{% trans 'Owner' %}: {{ us.space.created_by }}</small>
{% if us.space.created_by != us.user %} {% if us.space.created_by != us.user %}

View File

@ -1,53 +0,0 @@
{% extends "base.html" %}
{% load i18n %}
{% block title %}{% trans 'Stats' %}{% endblock %}
{% block content %}
<div class="row">
<div class="col col-12">
<h3>{% trans 'Statistics' %} </h3>
</div>
</div>
<div class="row">
<div class="col-md-6">
<div class="card">
<div class="card-header">
{% trans 'Number of objects' %}
</div>
<ul class="list-group list-group-flush">
<li class="list-group-item">{% trans 'Recipes' %} : <span
class="badge badge-pill badge-info">{{ counts.recipes }}</span></li>
<li class="list-group-item">{% trans 'Keywords' %} : <span
class="badge badge-pill badge-info">{{ counts.keywords }}</span></li>
<li class="list-group-item">{% trans 'Units' %} : <span
class="badge badge-pill badge-info">{{ counts.units }}</span></li>
<li class="list-group-item">{% trans 'Ingredients' %} : <span
class="badge badge-pill badge-info">{{ counts.ingredients }}</span></li>
<li class="list-group-item">{% trans 'Recipe Imports' %} : <span
class="badge badge-pill badge-info">{{ counts.recipe_import }}</span></li>
</ul>
</div>
</div>
<div class="col-md-6">
<div class="card">
<div class="card-header">
{% trans 'Objects stats' %}
</div>
<ul class="list-group list-group-flush">
<li class="list-group-item">{% trans 'Recipes without Keywords' %} : <span
class="badge badge-pill badge-info">{{ counts.recipes_no_keyword }}</span></li>
<li class="list-group-item">{% trans 'External Recipes' %} : <span
class="badge badge-pill badge-info">{{ counts.recipes_external }}</span></li>
<li class="list-group-item">{% trans 'Internal Recipes' %} : <span
class="badge badge-pill badge-info">{{ counts.recipes_internal }}</span></li>
<li class="list-group-item">{% trans 'Comments' %} : <span
class="badge badge-pill badge-info">{{ counts.comments }}</span></li>
</ul>
</div>
</div>
</div>
{% endblock %}

View File

@ -11,19 +11,7 @@
{% block content %} {% block content %}
<h1>{% trans 'System' %}</h1> <h1>{% trans 'System' %}</h1>
<br/>
<br/>
<br/>
<div class="row">
<div class="col-md-6">
<h3>{% trans 'Invite Links' %}</h3>
<a href="{% url 'list_invite_link' %}" class="btn btn-success">{% trans 'Show Links' %}</a>
</div>
</div>
<br/> <br/>
<br/> <br/>
<br/> <br/>

View File

@ -0,0 +1,46 @@
{% extends "base.html" %}
{% load render_bundle from webpack_loader %}
{% load static %}
{% load i18n %}
{% load l10n %}
{% load custom_tags %}
{% block title %}{% trans 'Settings' %}{% endblock %}
{% block content %}
<div id="app">
<settings-view></settings-view>
</div>
{% endblock %}
{% block script %}
{% if debug %}
<script src="{% url 'js_reverse' %}"></script>
{% else %}
<script src="{% static 'django_js_reverse/reverse.js' %}"></script>
{% endif %}
<script type="application/javascript">
window.CUSTOM_LOCALE = '{{ request.LANGUAGE_CODE }}'
window.USER_ID = {{ request.user.pk }}
window.SHOPPING_MIN_AUTOSYNC_INTERVAL = {{ SHOPPING_MIN_AUTOSYNC_INTERVAL }}
<!--TODO build custom API endpoint for this -->
{% get_available_languages as LANGUAGES %}
{% get_language_info_list for LANGUAGES as languages %}
window.AVAILABLE_LANGUAGES = [
{% for language in languages %}
['{{ language.name_local }}', '{{ language.code }}'],
{% endfor %}
]
</script>
{% render_bundle 'settings_view' %}
{% endblock %}

View File

@ -151,7 +151,7 @@ def bookmarklet(request):
localStorage.setItem('redirectURL', '" + server + reverse('data_import_url') + "'); \ localStorage.setItem('redirectURL', '" + server + reverse('data_import_url') + "'); \
localStorage.setItem('token', '" + api_token.__str__() + "'); \ localStorage.setItem('token', '" + api_token.__str__() + "'); \
document.body.appendChild(document.createElement(\'script\')).src=\'" \ document.body.appendChild(document.createElement(\'script\')).src=\'" \
+ server + prefix + static('js/bookmarklet.js') + "? \ + server + prefix + static('js/bookmarklet_v3.js') + "? \
r=\'+Math.floor(Math.random()*999999999);}})();'>Test</a>" r=\'+Math.floor(Math.random()*999999999);}})();'>Test</a>"
return re.sub(r"[\n\t]*", "", bookmark) return re.sub(r"[\n\t]*", "", bookmark)

View File

@ -0,0 +1,115 @@
import json
import pytest
from django.contrib import auth
from django.urls import reverse
from django.utils import timezone
from django_scopes import scopes_disabled
from oauth2_provider.models import AccessToken
from cookbook.models import ViewLog
LIST_URL = 'api:accesstoken-list'
DETAIL_URL = 'api:accesstoken-detail'
@pytest.fixture()
def obj_1(u1_s1):
return AccessToken.objects.create(user=auth.get_user(u1_s1), scope='test', expires=timezone.now() + timezone.timedelta(days=365 * 5), token='test1')
@pytest.fixture()
def obj_2(u1_s1):
return AccessToken.objects.create(user=auth.get_user(u1_s1), scope='test', expires=timezone.now() + timezone.timedelta(days=365 * 5), token='test2')
@pytest.mark.parametrize("arg", [
['a_u', 403],
['g1_s1', 200],
['u1_s1', 200],
['a1_s1', 200],
])
def test_list_permission(arg, request):
c = request.getfixturevalue(arg[0])
assert c.get(reverse(LIST_URL)).status_code == arg[1]
def test_list_space(obj_1, obj_2, u1_s1, u1_s2, space_2):
assert len(json.loads(u1_s1.get(reverse(LIST_URL)).content)) == 2
assert len(json.loads(u1_s2.get(reverse(LIST_URL)).content)) == 0
obj_1.user = auth.get_user(u1_s2)
obj_1.save()
assert len(json.loads(u1_s1.get(reverse(LIST_URL)).content)) == 1
assert len(json.loads(u1_s2.get(reverse(LIST_URL)).content)) == 1
def test_token_visibility(u1_s1, obj_1):
# tokens should only be returned on the first API request (first 15 seconds)
at = json.loads(u1_s1.get(reverse(DETAIL_URL, args=[obj_1.id])).content)
assert at['token'] == obj_1.token
with scopes_disabled():
obj_1.created = timezone.now() - timezone.timedelta(seconds=16)
obj_1.save()
at = json.loads(u1_s1.get(reverse(DETAIL_URL, args=[obj_1.id])).content)
assert at['token'] != obj_1.token
@pytest.mark.parametrize("arg", [
['a_u', 403],
['g1_s1', 404],
['u1_s1', 200],
['a1_s1', 404],
['g1_s2', 404],
['u1_s2', 404],
['a1_s2', 404],
])
def test_update(arg, request, obj_1):
c = request.getfixturevalue(arg[0])
r = c.patch(
reverse(
DETAIL_URL,
args={obj_1.id}
),
{'scope': 'lorem ipsum'},
content_type='application/json'
)
assert r.status_code == arg[1]
@pytest.mark.parametrize("arg", [
['a_u', 403],
['g1_s1', 201],
['u1_s1', 201],
['a1_s1', 201],
])
def test_add(arg, request, u1_s2, u2_s1, recipe_1_s1):
c = request.getfixturevalue(arg[0])
r = c.post(
reverse(LIST_URL),
{'scope': 'test', 'expires': timezone.now() + timezone.timedelta(days=365 * 5)},
content_type='application/json'
)
response = json.loads(r.content)
assert r.status_code == arg[1]
if r.status_code == 201:
assert response['scope'] == 'test'
def test_delete(u1_s1, u1_s2, obj_1):
r = u1_s2.delete(
reverse(
DETAIL_URL,
args={obj_1.id}
)
)
assert r.status_code == 404
r = u1_s1.delete(
reverse(
DETAIL_URL,
args={obj_1.id}
)
)
assert r.status_code == 204

View File

@ -1,6 +1,7 @@
import json import json
import pytest import pytest
from django.contrib import auth
from django.urls import reverse from django.urls import reverse
from django_scopes import scopes_disabled from django_scopes import scopes_disabled
@ -30,6 +31,7 @@ def test_list_space(recipe_1_s1, u1_s1, u1_s2, space_2):
assert len(json.loads(u1_s1.get(reverse(LIST_URL)).content)['results']) == 1 assert len(json.loads(u1_s1.get(reverse(LIST_URL)).content)['results']) == 1
assert len(json.loads(u1_s2.get(reverse(LIST_URL)).content)['results']) == 0 assert len(json.loads(u1_s2.get(reverse(LIST_URL)).content)['results']) == 0
# test for space filter
with scopes_disabled(): with scopes_disabled():
recipe_1_s1.space = space_2 recipe_1_s1.space = space_2
recipe_1_s1.save() recipe_1_s1.save()
@ -37,8 +39,23 @@ def test_list_space(recipe_1_s1, u1_s1, u1_s2, space_2):
assert len(json.loads(u1_s1.get(reverse(LIST_URL)).content)['results']) == 0 assert len(json.loads(u1_s1.get(reverse(LIST_URL)).content)['results']) == 0
assert len(json.loads(u1_s2.get(reverse(LIST_URL)).content)['results']) == 1 assert len(json.loads(u1_s2.get(reverse(LIST_URL)).content)['results']) == 1
# test for private recipe filter
with scopes_disabled():
recipe_1_s1.created_by = auth.get_user(u1_s1)
recipe_1_s1.private = True
recipe_1_s1.save()
def test_share_permission(recipe_1_s1, u1_s1, u1_s2, a_u): assert len(json.loads(u1_s1.get(reverse(LIST_URL)).content)['results']) == 0
assert len(json.loads(u1_s2.get(reverse(LIST_URL)).content)['results']) == 0
with scopes_disabled():
recipe_1_s1.created_by = auth.get_user(u1_s2)
recipe_1_s1.save()
assert len(json.loads(u1_s2.get(reverse(LIST_URL)).content)['results']) == 1
def test_share_permission(recipe_1_s1, u1_s1, u1_s2, u2_s1, a_u):
assert u1_s1.get(reverse(DETAIL_URL, args=[recipe_1_s1.pk])).status_code == 200 assert u1_s1.get(reverse(DETAIL_URL, args=[recipe_1_s1.pk])).status_code == 200
assert u1_s2.get(reverse(DETAIL_URL, args=[recipe_1_s1.pk])).status_code == 404 assert u1_s2.get(reverse(DETAIL_URL, args=[recipe_1_s1.pk])).status_code == 404
@ -52,6 +69,15 @@ def test_share_permission(recipe_1_s1, u1_s1, u1_s2, a_u):
assert u1_s1.get(reverse(DETAIL_URL, args=[recipe_1_s1.pk]) + f'?share={share.uuid}').status_code == 200 assert u1_s1.get(reverse(DETAIL_URL, args=[recipe_1_s1.pk]) + f'?share={share.uuid}').status_code == 200
assert u1_s2.get(reverse(DETAIL_URL, args=[recipe_1_s1.pk]) + f'?share={share.uuid}').status_code == 404 # TODO fix in https://github.com/TandoorRecipes/recipes/issues/1238 assert u1_s2.get(reverse(DETAIL_URL, args=[recipe_1_s1.pk]) + f'?share={share.uuid}').status_code == 404 # TODO fix in https://github.com/TandoorRecipes/recipes/issues/1238
recipe_1_s1.created_by = auth.get_user(u1_s1)
recipe_1_s1.private = True
recipe_1_s1.save()
assert a_u.get(reverse(DETAIL_URL, args=[recipe_1_s1.pk]) + f'?share={share.uuid}').status_code == 200
assert u1_s1.get(reverse(DETAIL_URL, args=[recipe_1_s1.pk]) + f'?share={share.uuid}').status_code == 200
assert u2_s1.get(reverse(DETAIL_URL, args=[recipe_1_s1.pk]) + f'?share={share.uuid}').status_code == 200
assert u2_s1.get(reverse(DETAIL_URL, args=[recipe_1_s1.pk])).status_code == 403
@pytest.mark.parametrize("arg", [ @pytest.mark.parametrize("arg", [
['a_u', 403], ['a_u', 403],
@ -80,6 +106,38 @@ def test_update(arg, request, recipe_1_s1):
validate_recipe(j, json.loads(r.content)) validate_recipe(j, json.loads(r.content))
def test_update_share(u1_s1, u2_s1, u1_s2, recipe_1_s1):
with scopes_disabled():
r = u1_s1.patch(
reverse(
DETAIL_URL,
args={recipe_1_s1.id}
),
{'shared': [{'id': auth.get_user(u1_s2).pk, 'username': auth.get_user(u1_s2).username}, {'id': auth.get_user(u2_s1).pk, 'username': auth.get_user(u2_s1).username}]},
content_type='application/json'
)
response = json.loads(r.content)
assert r.status_code == 200
assert len(response['shared']) == 1
assert response['shared'][0]['id'] == auth.get_user(u2_s1).pk
def test_update_private_recipe(u1_s1, u2_s1, recipe_1_s1):
r = u1_s1.patch(reverse(DETAIL_URL, args={recipe_1_s1.id}), {'name': 'test1'}, content_type='application/json')
assert r.status_code == 200
with scopes_disabled():
recipe_1_s1.private = True
recipe_1_s1.created_by = auth.get_user(u1_s1)
recipe_1_s1.save()
r = u1_s1.patch(reverse(DETAIL_URL, args={recipe_1_s1.id}), {'name': 'test2'}, content_type='application/json')
assert r.status_code == 200
r = u2_s1.patch(reverse(DETAIL_URL, args={recipe_1_s1.id}), {'name': 'test3'}, content_type='application/json')
assert r.status_code == 403
@pytest.mark.parametrize("arg", [ @pytest.mark.parametrize("arg", [
['a_u', 403], ['a_u', 403],
['g1_s1', 201], ['g1_s1', 201],
@ -107,22 +165,22 @@ def test_add(arg, request, u1_s2):
x += 1 x += 1
def test_delete(u1_s1, u1_s2, recipe_1_s1): def test_delete(u1_s1, u1_s2, u2_s1, recipe_1_s1, recipe_2_s1):
with scopes_disabled(): with scopes_disabled():
r = u1_s2.delete( r = u1_s2.delete(reverse(DETAIL_URL, args={recipe_1_s1.id}))
reverse(
DETAIL_URL,
args={recipe_1_s1.id}
)
)
assert r.status_code == 404 assert r.status_code == 404
r = u1_s1.delete( r = u1_s1.delete(reverse(DETAIL_URL, args={recipe_1_s1.id}))
reverse(
DETAIL_URL,
args={recipe_1_s1.id}
)
)
assert r.status_code == 204 assert r.status_code == 204
assert not Recipe.objects.filter(pk=recipe_1_s1.id).exists() assert not Recipe.objects.filter(pk=recipe_1_s1.id).exists()
recipe_2_s1.created_by = auth.get_user(u1_s1)
recipe_2_s1.private = True
recipe_2_s1.save()
r = u2_s1.delete(reverse(DETAIL_URL, args={recipe_2_s1.id}))
assert r.status_code == 403
r = u1_s1.delete(reverse(DETAIL_URL, args={recipe_2_s1.id}))
assert r.status_code == 204

View File

@ -7,22 +7,11 @@ from django.urls import reverse
from cookbook.models import UserSpace from cookbook.models import UserSpace
LIST_URL = 'api:username-list' LIST_URL = 'api:user-list'
DETAIL_URL = 'api:username-detail' DETAIL_URL = 'api:user-detail'
def test_forbidden_methods(u1_s1): def test_forbidden_methods(u1_s1):
r = u1_s1.post(
reverse(LIST_URL))
assert r.status_code == 405
r = u1_s1.put(
reverse(
DETAIL_URL,
args=[auth.get_user(u1_s1).pk])
)
assert r.status_code == 405
r = u1_s1.delete( r = u1_s1.delete(
reverse( reverse(
DETAIL_URL, DETAIL_URL,
@ -69,3 +58,56 @@ def test_list_space(u1_s1, u2_s1, u1_s2, space_2):
assert len(json.loads(u1_s1.get(reverse(LIST_URL)).content)) == 1 assert len(json.loads(u1_s1.get(reverse(LIST_URL)).content)) == 1
assert len(json.loads(u1_s2.get(reverse(LIST_URL)).content)) == 2 assert len(json.loads(u1_s2.get(reverse(LIST_URL)).content)) == 2
@pytest.mark.parametrize("arg", [
['a_u', 403],
['g1_s1', 403],
['u1_s1', 200],
['a1_s1', 403],
['g1_s2', 404],
['u1_s2', 404],
['a1_s2', 404],
])
def test_user_retrieve(arg, request, u1_s1):
c = request.getfixturevalue(arg[0])
r = c.get(reverse(DETAIL_URL, args={auth.get_user(u1_s1).id}), )
print(r.content, auth.get_user(u1_s1).username)
assert r.status_code == arg[1]
def test_user_update(u1_s1, u2_s1,u1_s2):
# can update own user
r = u1_s1.patch(
reverse(
DETAIL_URL,
args={auth.get_user(u1_s1).id}
),
{'first_name': 'test'},
content_type='application/json'
)
response = json.loads(r.content)
assert r.status_code == 200
assert response['first_name'] == 'test'
# can't update another user
r = u1_s1.patch(
reverse(
DETAIL_URL,
args={auth.get_user(u2_s1).id}
),
{'first_name': 'test'},
content_type='application/json'
)
assert r.status_code == 403
r = u1_s1.patch(
reverse(
DETAIL_URL,
args={auth.get_user(u1_s2).id}
),
{'first_name': 'test'},
content_type='application/json'
)
assert r.status_code == 404

View File

@ -47,10 +47,11 @@ router.register(r'sync', api.SyncViewSet)
router.register(r'sync-log', api.SyncLogViewSet) router.register(r'sync-log', api.SyncLogViewSet)
router.register(r'unit', api.UnitViewSet) router.register(r'unit', api.UnitViewSet)
router.register(r'user-file', api.UserFileViewSet) router.register(r'user-file', api.UserFileViewSet)
router.register(r'user-name', api.UserNameViewSet, basename='username') router.register(r'user', api.UserViewSet)
router.register(r'user-preference', api.UserPreferenceViewSet) router.register(r'user-preference', api.UserPreferenceViewSet)
router.register(r'user-space', api.UserSpaceViewSet) router.register(r'user-space', api.UserSpaceViewSet)
router.register(r'view-log', api.ViewLogViewSet) router.register(r'view-log', api.ViewLogViewSet)
router.register(r'access-token', api.AccessTokenViewSet)
urlpatterns = [ urlpatterns = [
path('', views.index, name='index'), path('', views.index, name='index'),
@ -59,23 +60,22 @@ urlpatterns = [
path('space-overview', views.space_overview, name='view_space_overview'), path('space-overview', views.space_overview, name='view_space_overview'),
path('space-manage/<int:space_id>', views.space_manage, name='view_space_manage'), path('space-manage/<int:space_id>', views.space_manage, name='view_space_manage'),
path('switch-space/<int:space_id>', views.switch_space, name='view_switch_space'), path('switch-space/<int:space_id>', views.switch_space, name='view_switch_space'),
path('profile/<int:user_id>', views.view_profile, name='view_profile'),
path('no-perm', views.no_perm, name='view_no_perm'), path('no-perm', views.no_perm, name='view_no_perm'),
path('invite/<slug:token>', views.invite_link, name='view_invite'), path('invite/<slug:token>', views.invite_link, name='view_invite'),
path('system/', views.system, name='view_system'), path('system/', views.system, name='view_system'),
path('search/', views.search, name='view_search'), path('search/', views.search, name='view_search'),
path('search/v2/', views.search_v2, name='view_search_v2'),
path('books/', views.books, name='view_books'), path('books/', views.books, name='view_books'),
path('plan/', views.meal_plan, name='view_plan'), path('plan/', views.meal_plan, name='view_plan'),
path('plan/entry/<int:pk>', views.meal_plan_entry, name='view_plan_entry'),
path('shopping/latest/', lists.shopping_list, name='view_shopping_latest'),
path('shopping/', lists.shopping_list, name='view_shopping'), path('shopping/', lists.shopping_list, name='view_shopping'),
path('settings/', views.user_settings, name='view_settings'), path('settings/', views.user_settings, name='view_settings'),
path('settings-shopping/', views.shopping_settings, name='view_shopping_settings'),
path('history/', views.history, name='view_history'), path('history/', views.history, name='view_history'),
path('supermarket/', views.supermarket, name='view_supermarket'), path('supermarket/', views.supermarket, name='view_supermarket'),
path('ingredient-editor/', views.ingredient_editor, name='view_ingredient_editor'), path('ingredient-editor/', views.ingredient_editor, name='view_ingredient_editor'),
path('abuse/<slug:token>', views.report_share_abuse, name='view_report_share_abuse'), path('abuse/<slug:token>', views.report_share_abuse, name='view_report_share_abuse'),
path('import/', import_export.import_recipe, name='view_import'), path('api/import/', api.import_files, name='view_import'),
path('import-response/<int:pk>/', import_export.import_response, name='view_import_response'), path('import-response/<int:pk>/', import_export.import_response, name='view_import_response'),
path('export/', import_export.export_recipe, name='view_export'), path('export/', import_export.export_recipe, name='view_export'),
path('export-response/<int:pk>/', import_export.export_response, name='view_export_response'), path('export-response/<int:pk>/', import_export.export_response, name='view_export_response'),
@ -103,7 +103,6 @@ urlpatterns = [
path('data/batch/edit', data.batch_edit, name='data_batch_edit'), path('data/batch/edit', data.batch_edit, name='data_batch_edit'),
path('data/batch/import', data.batch_import, name='data_batch_import'), path('data/batch/import', data.batch_import, name='data_batch_import'),
path('data/sync/wait', data.sync_wait, name='data_sync_wait'), path('data/sync/wait', data.sync_wait, name='data_sync_wait'),
path('data/statistics', data.statistics, name='data_stats'),
path('data/import/url', data.import_url, name='data_import_url'), path('data/import/url', data.import_url, name='data_import_url'),
path('api/get_external_file_link/<int:recipe_id>/', api.get_external_file_link, name='api_get_external_file_link'), path('api/get_external_file_link/<int:recipe_id>/', api.get_external_file_link, name='api_get_external_file_link'),

View File

@ -2,9 +2,12 @@ import io
import json import json
import mimetypes import mimetypes
import re import re
import threading
import traceback import traceback
import uuid import uuid
from collections import OrderedDict from collections import OrderedDict
from json import JSONDecodeError
from urllib.parse import unquote
from zipfile import ZipFile from zipfile import ZipFile
import requests import requests
@ -13,23 +16,25 @@ from PIL import UnidentifiedImageError
from annoying.decorators import ajax_request from annoying.decorators import ajax_request
from annoying.functions import get_object_or_None from annoying.functions import get_object_or_None
from django.contrib import messages from django.contrib import messages
from django.contrib.auth.models import User, Group from django.contrib.auth.models import Group, User
from django.contrib.postgres.search import TrigramSimilarity from django.contrib.postgres.search import TrigramSimilarity
from django.core.exceptions import FieldError, ValidationError from django.core.exceptions import FieldError, ValidationError
from django.core.files import File from django.core.files import File
from django.db.models import (Case, Count, Exists, OuterRef, ProtectedError, Q, from django.db.models import Case, Count, Exists, OuterRef, ProtectedError, Q, Subquery, Value, When
Subquery, Value, When)
from django.db.models.fields.related import ForeignObjectRel from django.db.models.fields.related import ForeignObjectRel
from django.db.models.functions import Coalesce, Lower from django.db.models.functions import Coalesce, Lower
from django.http import FileResponse, HttpResponse, JsonResponse from django.http import FileResponse, HttpResponse, JsonResponse
from django.shortcuts import get_object_or_404, redirect from django.shortcuts import get_object_or_404, redirect
from django.urls import reverse from django.urls import reverse
from django.utils import timezone
from django.utils.translation import gettext as _ from django.utils.translation import gettext as _
from django_scopes import scopes_disabled from django_scopes import scopes_disabled
from icalendar import Calendar, Event from icalendar import Calendar, Event
from oauth2_provider.models import AccessToken
from recipe_scrapers import scrape_me
from recipe_scrapers._exceptions import NoSchemaFoundInWildMode
from requests.exceptions import MissingSchema from requests.exceptions import MissingSchema
from rest_framework import decorators, status, viewsets from rest_framework import decorators, status, viewsets
from rest_framework.authtoken.models import Token
from rest_framework.authtoken.views import ObtainAuthToken from rest_framework.authtoken.views import ObtainAuthToken
from rest_framework.decorators import api_view, permission_classes from rest_framework.decorators import api_view, permission_classes
from rest_framework.exceptions import APIException, PermissionDenied from rest_framework.exceptions import APIException, PermissionDenied
@ -41,43 +46,49 @@ from rest_framework.throttling import AnonRateThrottle
from rest_framework.viewsets import ViewSetMixin from rest_framework.viewsets import ViewSetMixin
from treebeard.exceptions import InvalidMoveToDescendant, InvalidPosition, PathOverflow from treebeard.exceptions import InvalidMoveToDescendant, InvalidPosition, PathOverflow
from cookbook.forms import ImportForm
from cookbook.helper import recipe_url_import as helper
from cookbook.helper.HelperFunctions import str2bool from cookbook.helper.HelperFunctions import str2bool
from cookbook.helper.image_processing import handle_image from cookbook.helper.image_processing import handle_image
from cookbook.helper.ingredient_parser import IngredientParser from cookbook.helper.ingredient_parser import IngredientParser
from cookbook.helper.permission_helper import (CustomIsAdmin, CustomIsGuest, CustomIsOwner, from cookbook.helper.permission_helper import (CustomIsAdmin, CustomIsOwner,
CustomIsShare, CustomIsShared, CustomIsUser, CustomIsOwnerReadOnly, CustomIsShared,
group_required, CustomIsSpaceOwner, switch_user_active_space, is_space_owner, CustomIsOwnerReadOnly) CustomIsSpaceOwner, CustomIsUser, group_required,
from cookbook.helper.recipe_html_import import get_recipe_from_source is_space_owner, switch_user_active_space, above_space_limit, CustomRecipePermission, CustomUserPermission, CustomTokenHasReadWriteScope, CustomTokenHasScope)
from cookbook.helper.recipe_search import RecipeFacet, RecipeSearch, old_search from cookbook.helper.recipe_search import RecipeFacet, RecipeSearch
from cookbook.helper.recipe_url_import import get_from_youtube_scraper from cookbook.helper.recipe_url_import import get_from_youtube_scraper, get_images_from_soup
from cookbook.helper.scrapers.scrapers import text_scraper
from cookbook.helper.shopping_helper import RecipeShoppingEditor, shopping_helper from cookbook.helper.shopping_helper import RecipeShoppingEditor, shopping_helper
from cookbook.models import (Automation, BookmarkletImport, CookLog, CustomFilter, ExportLog, Food, from cookbook.models import (Automation, BookmarkletImport, CookLog, CustomFilter, ExportLog, Food,
FoodInheritField, ImportLog, Ingredient, Keyword, MealPlan, MealType, FoodInheritField, ImportLog, Ingredient, InviteLink, Keyword, MealPlan,
Recipe, RecipeBook, RecipeBookEntry, ShareLink, ShoppingList, MealType, Recipe, RecipeBook, RecipeBookEntry, ShareLink, ShoppingList,
ShoppingListEntry, ShoppingListRecipe, Step, Storage, Supermarket, ShoppingListEntry, ShoppingListRecipe, Space, Step, Storage,
SupermarketCategory, SupermarketCategoryRelation, Sync, SyncLog, Unit, Supermarket, SupermarketCategory, SupermarketCategoryRelation, Sync,
UserFile, UserPreference, ViewLog, Space, UserSpace, InviteLink) SyncLog, Unit, UserFile, UserPreference, UserSpace, ViewLog)
from cookbook.provider.dropbox import Dropbox from cookbook.provider.dropbox import Dropbox
from cookbook.provider.local import Local from cookbook.provider.local import Local
from cookbook.provider.nextcloud import Nextcloud from cookbook.provider.nextcloud import Nextcloud
from cookbook.schemas import FilterSchema, QueryParam, QueryParamAutoSchema, TreeSchema from cookbook.schemas import FilterSchema, QueryParam, QueryParamAutoSchema, TreeSchema
from cookbook.serializer import (AutomationSerializer, BookmarkletImportSerializer, from cookbook.serializer import (AutomationSerializer, BookmarkletImportListSerializer,
CookLogSerializer, CustomFilterSerializer, ExportLogSerializer, BookmarkletImportSerializer, CookLogSerializer,
CustomFilterSerializer, ExportLogSerializer,
FoodInheritFieldSerializer, FoodSerializer, FoodInheritFieldSerializer, FoodSerializer,
FoodShoppingUpdateSerializer, ImportLogSerializer, FoodShoppingUpdateSerializer, GroupSerializer, ImportLogSerializer,
IngredientSerializer, KeywordSerializer, MealPlanSerializer, IngredientSerializer, IngredientSimpleSerializer,
InviteLinkSerializer, KeywordSerializer, MealPlanSerializer,
MealTypeSerializer, RecipeBookEntrySerializer, MealTypeSerializer, RecipeBookEntrySerializer,
RecipeBookSerializer, RecipeImageSerializer, RecipeBookSerializer, RecipeFromSourceSerializer,
RecipeOverviewSerializer, RecipeSerializer, RecipeImageSerializer, RecipeOverviewSerializer, RecipeSerializer,
RecipeShoppingUpdateSerializer, RecipeSimpleSerializer, RecipeShoppingUpdateSerializer, RecipeSimpleSerializer,
ShoppingListAutoSyncSerializer, ShoppingListEntrySerializer, ShoppingListAutoSyncSerializer, ShoppingListEntrySerializer,
ShoppingListRecipeSerializer, ShoppingListSerializer, ShoppingListRecipeSerializer, ShoppingListSerializer,
StepSerializer, StorageSerializer, SpaceSerializer, StepSerializer, StorageSerializer,
SupermarketCategoryRelationSerializer, SupermarketCategoryRelationSerializer,
SupermarketCategorySerializer, SupermarketSerializer, SupermarketCategorySerializer, SupermarketSerializer,
SyncLogSerializer, SyncSerializer, UnitSerializer, SyncLogSerializer, SyncSerializer, UnitSerializer,
UserFileSerializer, UserNameSerializer, UserPreferenceSerializer, UserFileSerializer, UserSerializer, UserPreferenceSerializer,
ViewLogSerializer, IngredientSimpleSerializer, BookmarkletImportListSerializer, RecipeFromSourceSerializer, SpaceSerializer, UserSpaceSerializer, GroupSerializer, InviteLinkSerializer) UserSpaceSerializer, ViewLogSerializer, AccessTokenSerializer)
from cookbook.views.import_export import get_integration
from recipes import settings from recipes import settings
@ -344,7 +355,7 @@ class TreeMixin(MergeMixin, FuzzyFilterMixin, ExtendedRecipeMixin):
return Response(content, status=status.HTTP_400_BAD_REQUEST) return Response(content, status=status.HTTP_400_BAD_REQUEST)
class UserNameViewSet(viewsets.ReadOnlyModelViewSet): class UserViewSet(viewsets.ModelViewSet):
""" """
list: list:
optional parameters optional parameters
@ -352,9 +363,9 @@ class UserNameViewSet(viewsets.ReadOnlyModelViewSet):
- **filter_list**: array of user id's to get names for - **filter_list**: array of user id's to get names for
""" """
queryset = User.objects queryset = User.objects
serializer_class = UserNameSerializer serializer_class = UserSerializer
permission_classes = [CustomIsGuest] permission_classes = [CustomUserPermission & CustomTokenHasReadWriteScope]
http_method_names = ['get'] http_method_names = ['get', 'patch']
def get_queryset(self): def get_queryset(self):
queryset = self.queryset.filter(userspace__space=self.request.space) queryset = self.queryset.filter(userspace__space=self.request.space)
@ -371,14 +382,14 @@ class UserNameViewSet(viewsets.ReadOnlyModelViewSet):
class GroupViewSet(viewsets.ModelViewSet): class GroupViewSet(viewsets.ModelViewSet):
queryset = Group.objects.all() queryset = Group.objects.all()
serializer_class = GroupSerializer serializer_class = GroupSerializer
permission_classes = [CustomIsAdmin] permission_classes = [CustomIsAdmin & CustomTokenHasReadWriteScope]
http_method_names = ['get', ] http_method_names = ['get', ]
class SpaceViewSet(viewsets.ModelViewSet): class SpaceViewSet(viewsets.ModelViewSet):
queryset = Space.objects queryset = Space.objects
serializer_class = SpaceSerializer serializer_class = SpaceSerializer
permission_classes = [CustomIsOwner & CustomIsAdmin] permission_classes = [CustomIsOwner & CustomIsAdmin & CustomTokenHasReadWriteScope]
http_method_names = ['get', 'patch'] http_method_names = ['get', 'patch']
def get_queryset(self): def get_queryset(self):
@ -388,7 +399,7 @@ class SpaceViewSet(viewsets.ModelViewSet):
class UserSpaceViewSet(viewsets.ModelViewSet): class UserSpaceViewSet(viewsets.ModelViewSet):
queryset = UserSpace.objects queryset = UserSpace.objects
serializer_class = UserSpaceSerializer serializer_class = UserSpaceSerializer
permission_classes = [CustomIsSpaceOwner | CustomIsOwnerReadOnly] permission_classes = [(CustomIsSpaceOwner | CustomIsOwnerReadOnly) & CustomTokenHasReadWriteScope]
http_method_names = ['get', 'patch', 'delete'] http_method_names = ['get', 'patch', 'delete']
def destroy(self, request, *args, **kwargs): def destroy(self, request, *args, **kwargs):
@ -406,7 +417,7 @@ class UserSpaceViewSet(viewsets.ModelViewSet):
class UserPreferenceViewSet(viewsets.ModelViewSet): class UserPreferenceViewSet(viewsets.ModelViewSet):
queryset = UserPreference.objects queryset = UserPreference.objects
serializer_class = UserPreferenceSerializer serializer_class = UserPreferenceSerializer
permission_classes = [CustomIsOwner, ] permission_classes = [CustomIsOwner & CustomTokenHasReadWriteScope]
http_method_names = ['get', 'patch', ] http_method_names = ['get', 'patch', ]
def get_queryset(self): def get_queryset(self):
@ -418,7 +429,7 @@ class StorageViewSet(viewsets.ModelViewSet):
# TODO handle delete protect error and adjust test # TODO handle delete protect error and adjust test
queryset = Storage.objects queryset = Storage.objects
serializer_class = StorageSerializer serializer_class = StorageSerializer
permission_classes = [CustomIsAdmin, ] permission_classes = [CustomIsAdmin & CustomTokenHasReadWriteScope]
def get_queryset(self): def get_queryset(self):
return self.queryset.filter(space=self.request.space) return self.queryset.filter(space=self.request.space)
@ -427,7 +438,7 @@ class StorageViewSet(viewsets.ModelViewSet):
class SyncViewSet(viewsets.ModelViewSet): class SyncViewSet(viewsets.ModelViewSet):
queryset = Sync.objects queryset = Sync.objects
serializer_class = SyncSerializer serializer_class = SyncSerializer
permission_classes = [CustomIsAdmin, ] permission_classes = [CustomIsAdmin & CustomTokenHasReadWriteScope]
def get_queryset(self): def get_queryset(self):
return self.queryset.filter(space=self.request.space) return self.queryset.filter(space=self.request.space)
@ -436,7 +447,7 @@ class SyncViewSet(viewsets.ModelViewSet):
class SyncLogViewSet(viewsets.ReadOnlyModelViewSet): class SyncLogViewSet(viewsets.ReadOnlyModelViewSet):
queryset = SyncLog.objects queryset = SyncLog.objects
serializer_class = SyncLogSerializer serializer_class = SyncLogSerializer
permission_classes = [CustomIsAdmin, ] permission_classes = [CustomIsAdmin & CustomTokenHasReadWriteScope]
pagination_class = DefaultPagination pagination_class = DefaultPagination
def get_queryset(self): def get_queryset(self):
@ -446,7 +457,7 @@ class SyncLogViewSet(viewsets.ReadOnlyModelViewSet):
class SupermarketViewSet(viewsets.ModelViewSet, StandardFilterMixin): class SupermarketViewSet(viewsets.ModelViewSet, StandardFilterMixin):
queryset = Supermarket.objects queryset = Supermarket.objects
serializer_class = SupermarketSerializer serializer_class = SupermarketSerializer
permission_classes = [CustomIsUser] permission_classes = [CustomIsUser & CustomTokenHasReadWriteScope]
def get_queryset(self): def get_queryset(self):
self.queryset = self.queryset.filter(space=self.request.space) self.queryset = self.queryset.filter(space=self.request.space)
@ -457,7 +468,7 @@ class SupermarketCategoryViewSet(viewsets.ModelViewSet, FuzzyFilterMixin):
queryset = SupermarketCategory.objects queryset = SupermarketCategory.objects
model = SupermarketCategory model = SupermarketCategory
serializer_class = SupermarketCategorySerializer serializer_class = SupermarketCategorySerializer
permission_classes = [CustomIsUser] permission_classes = [CustomIsUser & CustomTokenHasReadWriteScope]
def get_queryset(self): def get_queryset(self):
self.queryset = self.queryset.filter(space=self.request.space).order_by(Lower('name').asc()) self.queryset = self.queryset.filter(space=self.request.space).order_by(Lower('name').asc())
@ -467,7 +478,7 @@ class SupermarketCategoryViewSet(viewsets.ModelViewSet, FuzzyFilterMixin):
class SupermarketCategoryRelationViewSet(viewsets.ModelViewSet, StandardFilterMixin): class SupermarketCategoryRelationViewSet(viewsets.ModelViewSet, StandardFilterMixin):
queryset = SupermarketCategoryRelation.objects queryset = SupermarketCategoryRelation.objects
serializer_class = SupermarketCategoryRelationSerializer serializer_class = SupermarketCategoryRelationSerializer
permission_classes = [CustomIsUser] permission_classes = [CustomIsUser & CustomTokenHasReadWriteScope]
pagination_class = DefaultPagination pagination_class = DefaultPagination
def get_queryset(self): def get_queryset(self):
@ -479,7 +490,7 @@ class KeywordViewSet(viewsets.ModelViewSet, TreeMixin):
queryset = Keyword.objects queryset = Keyword.objects
model = Keyword model = Keyword
serializer_class = KeywordSerializer serializer_class = KeywordSerializer
permission_classes = [CustomIsUser] permission_classes = [CustomIsUser & CustomTokenHasReadWriteScope]
pagination_class = DefaultPagination pagination_class = DefaultPagination
@ -487,14 +498,14 @@ class UnitViewSet(viewsets.ModelViewSet, MergeMixin, FuzzyFilterMixin):
queryset = Unit.objects queryset = Unit.objects
model = Unit model = Unit
serializer_class = UnitSerializer serializer_class = UnitSerializer
permission_classes = [CustomIsUser] permission_classes = [CustomIsUser & CustomTokenHasReadWriteScope]
pagination_class = DefaultPagination pagination_class = DefaultPagination
class FoodInheritFieldViewSet(viewsets.ReadOnlyModelViewSet): class FoodInheritFieldViewSet(viewsets.ReadOnlyModelViewSet):
queryset = FoodInheritField.objects queryset = FoodInheritField.objects
serializer_class = FoodInheritFieldSerializer serializer_class = FoodInheritFieldSerializer
permission_classes = [CustomIsUser] permission_classes = [CustomIsUser & CustomTokenHasReadWriteScope]
def get_queryset(self): def get_queryset(self):
# exclude fields not yet implemented # exclude fields not yet implemented
@ -506,7 +517,7 @@ class FoodViewSet(viewsets.ModelViewSet, TreeMixin):
queryset = Food.objects queryset = Food.objects
model = Food model = Food
serializer_class = FoodSerializer serializer_class = FoodSerializer
permission_classes = [CustomIsUser] permission_classes = [CustomIsUser & CustomTokenHasReadWriteScope]
pagination_class = DefaultPagination pagination_class = DefaultPagination
def get_queryset(self): def get_queryset(self):
@ -517,9 +528,10 @@ class FoodViewSet(viewsets.ModelViewSet, TreeMixin):
shopping_status = ShoppingListEntry.objects.filter(space=self.request.space, food=OuterRef('id'), shopping_status = ShoppingListEntry.objects.filter(space=self.request.space, food=OuterRef('id'),
checked=False).values('id') checked=False).values('id')
# onhand_status = self.queryset.annotate(onhand_status=Exists(onhand_users_set__in=[shared_users])) # onhand_status = self.queryset.annotate(onhand_status=Exists(onhand_users_set__in=[shared_users]))
return self.queryset.annotate(shopping_status=Exists(shopping_status)).prefetch_related('onhand_users', return self.queryset\
'inherit_fields').select_related( .annotate(shopping_status=Exists(shopping_status))\
'recipe', 'supermarket_category') .prefetch_related('onhand_users', 'inherit_fields', 'child_inherit_fields', 'substitute')\
.select_related('recipe', 'supermarket_category')
@decorators.action(detail=True, methods=['PUT'], serializer_class=FoodShoppingUpdateSerializer, ) @decorators.action(detail=True, methods=['PUT'], serializer_class=FoodShoppingUpdateSerializer, )
# TODO DRF only allows one action in a decorator action without overriding get_operation_id_base() this should be PUT and DELETE probably # TODO DRF only allows one action in a decorator action without overriding get_operation_id_base() this should be PUT and DELETE probably
@ -554,7 +566,7 @@ class FoodViewSet(viewsets.ModelViewSet, TreeMixin):
class RecipeBookViewSet(viewsets.ModelViewSet, StandardFilterMixin): class RecipeBookViewSet(viewsets.ModelViewSet, StandardFilterMixin):
queryset = RecipeBook.objects queryset = RecipeBook.objects
serializer_class = RecipeBookSerializer serializer_class = RecipeBookSerializer
permission_classes = [CustomIsOwner | CustomIsShared] permission_classes = [(CustomIsOwner | CustomIsShared) & CustomTokenHasReadWriteScope]
def get_queryset(self): def get_queryset(self):
self.queryset = self.queryset.filter(Q(created_by=self.request.user) | Q(shared=self.request.user)).filter( self.queryset = self.queryset.filter(Q(created_by=self.request.user) | Q(shared=self.request.user)).filter(
@ -573,7 +585,7 @@ class RecipeBookEntryViewSet(viewsets.ModelViewSet, viewsets.GenericViewSet):
""" """
queryset = RecipeBookEntry.objects queryset = RecipeBookEntry.objects
serializer_class = RecipeBookEntrySerializer serializer_class = RecipeBookEntrySerializer
permission_classes = [CustomIsOwner | CustomIsShared] permission_classes = [(CustomIsOwner | CustomIsShared) & CustomTokenHasReadWriteScope]
def get_queryset(self): def get_queryset(self):
queryset = self.queryset.filter( queryset = self.queryset.filter(
@ -601,7 +613,7 @@ class MealPlanViewSet(viewsets.ModelViewSet):
""" """
queryset = MealPlan.objects queryset = MealPlan.objects
serializer_class = MealPlanSerializer serializer_class = MealPlanSerializer
permission_classes = [CustomIsOwner | CustomIsShared] permission_classes = [(CustomIsOwner | CustomIsShared) & CustomTokenHasReadWriteScope]
def get_queryset(self): def get_queryset(self):
queryset = self.queryset.filter( queryset = self.queryset.filter(
@ -626,7 +638,7 @@ class MealTypeViewSet(viewsets.ModelViewSet):
""" """
queryset = MealType.objects queryset = MealType.objects
serializer_class = MealTypeSerializer serializer_class = MealTypeSerializer
permission_classes = [CustomIsOwner] permission_classes = [CustomIsOwner & CustomTokenHasReadWriteScope]
def get_queryset(self): def get_queryset(self):
queryset = self.queryset.order_by('order', 'id').filter(created_by=self.request.user).filter( queryset = self.queryset.order_by('order', 'id').filter(created_by=self.request.user).filter(
@ -637,7 +649,7 @@ class MealTypeViewSet(viewsets.ModelViewSet):
class IngredientViewSet(viewsets.ModelViewSet): class IngredientViewSet(viewsets.ModelViewSet):
queryset = Ingredient.objects queryset = Ingredient.objects
serializer_class = IngredientSerializer serializer_class = IngredientSerializer
permission_classes = [CustomIsUser] permission_classes = [CustomIsUser & CustomTokenHasReadWriteScope]
pagination_class = DefaultPagination pagination_class = DefaultPagination
def get_serializer_class(self): def get_serializer_class(self):
@ -661,7 +673,7 @@ class IngredientViewSet(viewsets.ModelViewSet):
class StepViewSet(viewsets.ModelViewSet): class StepViewSet(viewsets.ModelViewSet):
queryset = Step.objects queryset = Step.objects
serializer_class = StepSerializer serializer_class = StepSerializer
permission_classes = [CustomIsUser] permission_classes = [CustomIsUser & CustomTokenHasReadWriteScope]
pagination_class = DefaultPagination pagination_class = DefaultPagination
query_params = [ query_params = [
QueryParam(name='recipe', description=_('ID of recipe a step is part of. For multiple repeat parameter.'), QueryParam(name='recipe', description=_('ID of recipe a step is part of. For multiple repeat parameter.'),
@ -705,7 +717,7 @@ class RecipeViewSet(viewsets.ModelViewSet):
queryset = Recipe.objects queryset = Recipe.objects
serializer_class = RecipeSerializer serializer_class = RecipeSerializer
# TODO split read and write permission for meal plan guest # TODO split read and write permission for meal plan guest
permission_classes = [CustomIsShare | CustomIsGuest] permission_classes = [CustomRecipePermission & CustomTokenHasReadWriteScope]
pagination_class = RecipePagination pagination_class = RecipePagination
query_params = [ query_params = [
@ -772,13 +784,14 @@ class RecipeViewSet(viewsets.ModelViewSet):
def get_queryset(self): def get_queryset(self):
share = self.request.query_params.get('share', None) share = self.request.query_params.get('share', None)
if self.detail: if self.detail: # if detail request and not list, private condition is verified by permission class
if not share: if not share: # filter for space only if not shared
self.queryset = self.queryset.filter(space=self.request.space) self.queryset = self.queryset.filter(space=self.request.space)
return super().get_queryset() return super().get_queryset()
if not (share and self.detail): self.queryset = self.queryset.filter(space=self.request.space).filter(
self.queryset = self.queryset.filter(space=self.request.space) Q(private=False) | (Q(private=True) & (Q(created_by=self.request.user) | Q(shared=self.request.user)))
)
params = {x: self.request.GET.get(x) if len({**self.request.GET}[x]) == 1 else self.request.GET.getlist(x) for x params = {x: self.request.GET.get(x) if len({**self.request.GET}[x]) == 1 else self.request.GET.getlist(x) for x
in list(self.request.GET)} in list(self.request.GET)}
@ -790,12 +803,9 @@ class RecipeViewSet(viewsets.ModelViewSet):
if self.request.GET.get('debug', False): if self.request.GET.get('debug', False):
return JsonResponse({ return JsonResponse({
'new': str(self.get_queryset().query), 'new': str(self.get_queryset().query),
'old': str(old_search(request).query)
}) })
return super().list(request, *args, **kwargs) return super().list(request, *args, **kwargs)
# TODO write extensive tests for permissions
def get_serializer_class(self): def get_serializer_class(self):
if self.action == 'list': if self.action == 'list':
return RecipeOverviewSerializer return RecipeOverviewSerializer
@ -908,7 +918,7 @@ class RecipeViewSet(viewsets.ModelViewSet):
class ShoppingListRecipeViewSet(viewsets.ModelViewSet): class ShoppingListRecipeViewSet(viewsets.ModelViewSet):
queryset = ShoppingListRecipe.objects queryset = ShoppingListRecipe.objects
serializer_class = ShoppingListRecipeSerializer serializer_class = ShoppingListRecipeSerializer
permission_classes = [CustomIsOwner | CustomIsShared] permission_classes = [(CustomIsOwner | CustomIsShared) & CustomTokenHasReadWriteScope]
def get_queryset(self): def get_queryset(self):
self.queryset = self.queryset.filter( self.queryset = self.queryset.filter(
@ -924,7 +934,7 @@ class ShoppingListRecipeViewSet(viewsets.ModelViewSet):
class ShoppingListEntryViewSet(viewsets.ModelViewSet): class ShoppingListEntryViewSet(viewsets.ModelViewSet):
queryset = ShoppingListEntry.objects queryset = ShoppingListEntry.objects
serializer_class = ShoppingListEntrySerializer serializer_class = ShoppingListEntrySerializer
permission_classes = [CustomIsOwner | CustomIsShared] permission_classes = [(CustomIsOwner | CustomIsShared) & CustomTokenHasReadWriteScope]
query_params = [ query_params = [
QueryParam(name='id', QueryParam(name='id',
description=_('Returns the shopping list entry with a primary key of id. Multiple values allowed.'), description=_('Returns the shopping list entry with a primary key of id. Multiple values allowed.'),
@ -963,7 +973,7 @@ class ShoppingListEntryViewSet(viewsets.ModelViewSet):
class ShoppingListViewSet(viewsets.ModelViewSet): class ShoppingListViewSet(viewsets.ModelViewSet):
queryset = ShoppingList.objects queryset = ShoppingList.objects
serializer_class = ShoppingListSerializer serializer_class = ShoppingListSerializer
permission_classes = [CustomIsOwner | CustomIsShared] permission_classes = [(CustomIsOwner | CustomIsShared) & CustomTokenHasReadWriteScope]
def get_queryset(self): def get_queryset(self):
return self.queryset.filter( return self.queryset.filter(
@ -985,7 +995,7 @@ class ShoppingListViewSet(viewsets.ModelViewSet):
class ViewLogViewSet(viewsets.ModelViewSet): class ViewLogViewSet(viewsets.ModelViewSet):
queryset = ViewLog.objects queryset = ViewLog.objects
serializer_class = ViewLogSerializer serializer_class = ViewLogSerializer
permission_classes = [CustomIsOwner] permission_classes = [CustomIsOwner & CustomTokenHasReadWriteScope]
pagination_class = DefaultPagination pagination_class = DefaultPagination
def get_queryset(self): def get_queryset(self):
@ -996,7 +1006,7 @@ class ViewLogViewSet(viewsets.ModelViewSet):
class CookLogViewSet(viewsets.ModelViewSet): class CookLogViewSet(viewsets.ModelViewSet):
queryset = CookLog.objects queryset = CookLog.objects
serializer_class = CookLogSerializer serializer_class = CookLogSerializer
permission_classes = [CustomIsOwner] permission_classes = [CustomIsOwner & CustomTokenHasReadWriteScope]
pagination_class = DefaultPagination pagination_class = DefaultPagination
def get_queryset(self): def get_queryset(self):
@ -1006,7 +1016,7 @@ class CookLogViewSet(viewsets.ModelViewSet):
class ImportLogViewSet(viewsets.ModelViewSet): class ImportLogViewSet(viewsets.ModelViewSet):
queryset = ImportLog.objects queryset = ImportLog.objects
serializer_class = ImportLogSerializer serializer_class = ImportLogSerializer
permission_classes = [CustomIsUser] permission_classes = [CustomIsUser & CustomTokenHasReadWriteScope]
pagination_class = DefaultPagination pagination_class = DefaultPagination
def get_queryset(self): def get_queryset(self):
@ -1016,7 +1026,7 @@ class ImportLogViewSet(viewsets.ModelViewSet):
class ExportLogViewSet(viewsets.ModelViewSet): class ExportLogViewSet(viewsets.ModelViewSet):
queryset = ExportLog.objects queryset = ExportLog.objects
serializer_class = ExportLogSerializer serializer_class = ExportLogSerializer
permission_classes = [CustomIsUser] permission_classes = [CustomIsUser & CustomTokenHasReadWriteScope]
pagination_class = DefaultPagination pagination_class = DefaultPagination
def get_queryset(self): def get_queryset(self):
@ -1026,7 +1036,8 @@ class ExportLogViewSet(viewsets.ModelViewSet):
class BookmarkletImportViewSet(viewsets.ModelViewSet): class BookmarkletImportViewSet(viewsets.ModelViewSet):
queryset = BookmarkletImport.objects queryset = BookmarkletImport.objects
serializer_class = BookmarkletImportSerializer serializer_class = BookmarkletImportSerializer
permission_classes = [CustomIsUser] permission_classes = [CustomIsUser & CustomTokenHasScope]
required_scopes = ['bookmarklet']
def get_serializer_class(self): def get_serializer_class(self):
if self.action == 'list': if self.action == 'list':
@ -1040,7 +1051,7 @@ class BookmarkletImportViewSet(viewsets.ModelViewSet):
class UserFileViewSet(viewsets.ModelViewSet, StandardFilterMixin): class UserFileViewSet(viewsets.ModelViewSet, StandardFilterMixin):
queryset = UserFile.objects queryset = UserFile.objects
serializer_class = UserFileSerializer serializer_class = UserFileSerializer
permission_classes = [CustomIsUser] permission_classes = [CustomIsUser & CustomTokenHasReadWriteScope]
parser_classes = [MultiPartParser] parser_classes = [MultiPartParser]
def get_queryset(self): def get_queryset(self):
@ -1051,7 +1062,7 @@ class UserFileViewSet(viewsets.ModelViewSet, StandardFilterMixin):
class AutomationViewSet(viewsets.ModelViewSet, StandardFilterMixin): class AutomationViewSet(viewsets.ModelViewSet, StandardFilterMixin):
queryset = Automation.objects queryset = Automation.objects
serializer_class = AutomationSerializer serializer_class = AutomationSerializer
permission_classes = [CustomIsUser] permission_classes = [CustomIsUser & CustomTokenHasReadWriteScope]
def get_queryset(self): def get_queryset(self):
self.queryset = self.queryset.filter(space=self.request.space).all() self.queryset = self.queryset.filter(space=self.request.space).all()
@ -1061,7 +1072,7 @@ class AutomationViewSet(viewsets.ModelViewSet, StandardFilterMixin):
class InviteLinkViewSet(viewsets.ModelViewSet, StandardFilterMixin): class InviteLinkViewSet(viewsets.ModelViewSet, StandardFilterMixin):
queryset = InviteLink.objects queryset = InviteLink.objects
serializer_class = InviteLinkSerializer serializer_class = InviteLinkSerializer
permission_classes = [CustomIsSpaceOwner & CustomIsAdmin] permission_classes = [CustomIsSpaceOwner & CustomIsAdmin & CustomTokenHasReadWriteScope]
def get_queryset(self): def get_queryset(self):
if is_space_owner(self.request.user, self.request.space): if is_space_owner(self.request.user, self.request.space):
@ -1074,7 +1085,7 @@ class InviteLinkViewSet(viewsets.ModelViewSet, StandardFilterMixin):
class CustomFilterViewSet(viewsets.ModelViewSet, StandardFilterMixin): class CustomFilterViewSet(viewsets.ModelViewSet, StandardFilterMixin):
queryset = CustomFilter.objects queryset = CustomFilter.objects
serializer_class = CustomFilterSerializer serializer_class = CustomFilterSerializer
permission_classes = [CustomIsOwner] permission_classes = [CustomIsOwner & CustomTokenHasReadWriteScope]
def get_queryset(self): def get_queryset(self):
self.queryset = self.queryset.filter(Q(created_by=self.request.user) | Q(shared=self.request.user)).filter( self.queryset = self.queryset.filter(Q(created_by=self.request.user) | Q(shared=self.request.user)).filter(
@ -1082,6 +1093,15 @@ class CustomFilterViewSet(viewsets.ModelViewSet, StandardFilterMixin):
return super().get_queryset() return super().get_queryset()
class AccessTokenViewSet(viewsets.ModelViewSet):
queryset = AccessToken.objects
serializer_class = AccessTokenSerializer
permission_classes = [CustomIsOwner & CustomTokenHasReadWriteScope]
def get_queryset(self):
return self.queryset.filter(user=self.request.user)
# -------------- DRF custom views -------------------- # -------------- DRF custom views --------------------
class AuthTokenThrottle(AnonRateThrottle): class AuthTokenThrottle(AnonRateThrottle):
@ -1096,16 +1116,22 @@ class CustomAuthToken(ObtainAuthToken):
context={'request': request}) context={'request': request})
serializer.is_valid(raise_exception=True) serializer.is_valid(raise_exception=True)
user = serializer.validated_data['user'] user = serializer.validated_data['user']
token, created = Token.objects.get_or_create(user=user) if token := AccessToken.objects.filter(scope__contains='read').filter(scope__contains='write').first():
access_token = token
else:
access_token = AccessToken.objects.create(user=request.user, token=f'tda_{str(uuid.uuid4()).replace("-", "_")}', expires=(timezone.now() + timezone.timedelta(days=365 * 5)), scope='read write app')
return Response({ return Response({
'token': token.key, 'id': access_token.id,
'token': access_token.token,
'scope': access_token.scope,
'expires': access_token.expires,
'user_id': user.pk, 'user_id': user.pk,
}) })
@api_view(['POST']) @api_view(['POST'])
# @schema(AutoSchema()) #TODO add proper schema # @schema(AutoSchema()) #TODO add proper schema
@permission_classes([CustomIsUser]) @permission_classes([CustomIsUser & CustomTokenHasReadWriteScope])
# TODO add rate limiting # TODO add rate limiting
def recipe_from_source(request): def recipe_from_source(request):
""" """
@ -1114,76 +1140,86 @@ def recipe_from_source(request):
- url: url to use for importing recipe - url: url to use for importing recipe
- data: if no url is given recipe is imported from provided source data - data: if no url is given recipe is imported from provided source data
- (optional) bookmarklet: id of bookmarklet import to use, overrides URL and data attributes - (optional) bookmarklet: id of bookmarklet import to use, overrides URL and data attributes
:return: JsonResponse containing the parsed json, original html,json and images :return: JsonResponse containing the parsed json and images
""" """
scrape = None
serializer = RecipeFromSourceSerializer(data=request.data) serializer = RecipeFromSourceSerializer(data=request.data)
if serializer.is_valid(): if serializer.is_valid():
try:
if bookmarklet := BookmarkletImport.objects.filter(pk=serializer.validated_data['bookmarklet']).first():
serializer.validated_data['url'] = bookmarklet.url
serializer.validated_data['data'] = bookmarklet.html
bookmarklet.delete()
except KeyError:
pass
# headers to use for request to external sites if (b_pk := serializer.validated_data.get('bookmarklet', None)) and (bookmarklet := BookmarkletImport.objects.filter(pk=b_pk).first()):
external_request_headers = {"User-Agent": "Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.0.7) Gecko/2009021910 Firefox/3.0.7"} serializer.validated_data['url'] = bookmarklet.url
serializer.validated_data['data'] = bookmarklet.html
bookmarklet.delete()
if not 'url' in serializer.validated_data and not 'data' in serializer.validated_data: url = serializer.validated_data.get('url', None)
data = unquote(serializer.validated_data.get('data', None))
if not url and not data:
return Response({ return Response({
'error': True, 'error': True,
'msg': _('Nothing to do.') 'msg': _('Nothing to do.')
}, status=status.HTTP_400_BAD_REQUEST) }, status=status.HTTP_400_BAD_REQUEST)
# in manual mode request complete page to return it later elif url and not data:
if 'url' in serializer.validated_data: if re.match('^(https?://)?(www\.youtube\.com|youtu\.be)/.+$', url):
if re.match('^(https?://)?(www\.youtube\.com|youtu\.be)/.+$', serializer.validated_data['url']): if validators.url(url, public=True):
if validators.url(serializer.validated_data['url'], public=True):
return Response({ return Response({
'recipe_json': get_from_youtube_scraper(serializer.validated_data['url'], request), 'recipe_json': get_from_youtube_scraper(url, request),
'recipe_tree': '', # 'recipe_tree': '',
'recipe_html': '', # 'recipe_html': '',
'recipe_images': [], 'recipe_images': [],
}, status=status.HTTP_200_OK) }, status=status.HTTP_200_OK)
try: else:
if validators.url(serializer.validated_data['url'], public=True): try:
serializer.validated_data['data'] = requests.get(serializer.validated_data['url'], headers=external_request_headers).content if validators.url(url, public=True):
else: scrape = scrape_me(url_path=url, wild_mode=True)
else:
return Response({
'error': True,
'msg': _('Invalid Url')
}, status=status.HTTP_400_BAD_REQUEST)
except NoSchemaFoundInWildMode:
pass
except requests.exceptions.ConnectionError:
return Response({ return Response({
'error': True, 'error': True,
'msg': _('Invalid Url') 'msg': _('Connection Refused.')
}, status=status.HTTP_400_BAD_REQUEST) }, status=status.HTTP_400_BAD_REQUEST)
except requests.exceptions.ConnectionError: except requests.exceptions.MissingSchema:
return Response({ return Response({
'error': True, 'error': True,
'msg': _('Connection Refused.') 'msg': _('Bad URL Schema.')
}, status=status.HTTP_400_BAD_REQUEST) }, status=status.HTTP_400_BAD_REQUEST)
except requests.exceptions.MissingSchema: else:
return Response({ try:
'error': True, json.loads(data)
'msg': _('Bad URL Schema.') data = "<script type='application/ld+json'>" + data + "</script>"
}, status=status.HTTP_400_BAD_REQUEST) except JSONDecodeError:
pass
scrape = text_scraper(text=data, url=url)
if not url and (found_url := scrape.schema.data.get('url', None)):
scrape = text_scraper(text=data, url=found_url)
recipe_json, recipe_tree, recipe_html, recipe_images = get_recipe_from_source(serializer.validated_data['data'], serializer.validated_data['url'], request) if scrape:
if len(recipe_tree) == 0 and len(recipe_json) == 0: return Response({
'recipe_json': helper.get_from_scraper(scrape, request),
# 'recipe_tree': recipe_tree,
# 'recipe_html': recipe_html,
'recipe_images': list(dict.fromkeys(get_images_from_soup(scrape.soup, url))),
}, status=status.HTTP_200_OK)
else:
return Response({ return Response({
'error': True, 'error': True,
'msg': _('No usable data could be found.') 'msg': _('No usable data could be found.')
}, status=status.HTTP_400_BAD_REQUEST) }, status=status.HTTP_400_BAD_REQUEST)
else:
return Response({
'recipe_json': recipe_json,
'recipe_tree': recipe_tree,
'recipe_html': recipe_html,
'recipe_images': list(dict.fromkeys(recipe_images)),
}, status=status.HTTP_200_OK)
else: else:
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
@api_view(['GET']) @api_view(['GET'])
# @schema(AutoSchema()) #TODO add proper schema # @schema(AutoSchema()) #TODO add proper schema
@permission_classes([CustomIsAdmin]) @permission_classes([CustomIsAdmin & CustomTokenHasReadWriteScope])
# TODO add rate limiting # TODO add rate limiting
def reset_food_inheritance(request): def reset_food_inheritance(request):
""" """
@ -1199,7 +1235,7 @@ def reset_food_inheritance(request):
@api_view(['GET']) @api_view(['GET'])
# @schema(AutoSchema()) #TODO add proper schema # @schema(AutoSchema()) #TODO add proper schema
@permission_classes([CustomIsAdmin]) @permission_classes([CustomIsAdmin & CustomTokenHasReadWriteScope])
# TODO add rate limiting # TODO add rate limiting
def switch_active_space(request, space_id): def switch_active_space(request, space_id):
""" """
@ -1219,7 +1255,7 @@ def switch_active_space(request, space_id):
@api_view(['GET']) @api_view(['GET'])
# @schema(AutoSchema()) #TODO add proper schema # @schema(AutoSchema()) #TODO add proper schema
@permission_classes([CustomIsUser]) @permission_classes([CustomIsUser & CustomTokenHasReadWriteScope])
def download_file(request, file_id): def download_file(request, file_id):
""" """
function to download a user file securely (wrapping as zip to prevent any context based XSS problems) function to download a user file securely (wrapping as zip to prevent any context based XSS problems)
@ -1242,6 +1278,35 @@ def download_file(request, file_id):
return Response({}, status=status.HTTP_400_BAD_REQUEST) return Response({}, status=status.HTTP_400_BAD_REQUEST)
@api_view(['POST'])
# @schema(AutoSchema()) #TODO add proper schema
@permission_classes([CustomIsUser & CustomTokenHasReadWriteScope])
def import_files(request):
"""
function to handle files passed by application importer
"""
limit, msg = above_space_limit(request.space)
if limit:
return Response({'error': msg}, status=status.HTTP_400_BAD_REQUEST)
form = ImportForm(request.POST, request.FILES)
if form.is_valid() and request.FILES != {}:
try:
integration = get_integration(request, form.cleaned_data['type'])
il = ImportLog.objects.create(type=form.cleaned_data['type'], created_by=request.user, space=request.space)
files = []
for f in request.FILES.getlist('files'):
files.append({'file': io.BytesIO(f.read()), 'name': f.name})
t = threading.Thread(target=integration.do_import, args=[files, il, form.cleaned_data['duplicates']])
t.setDaemon(True)
t.start()
return Response({'import_id': il.pk}, status=status.HTTP_200_OK)
except NotImplementedError:
return Response({'error': True, 'msg': _('Importing is not implemented for this provider')}, status=status.HTTP_400_BAD_REQUEST)
def get_recipe_provider(recipe): def get_recipe_provider(recipe):
if recipe.storage.method == Storage.DROPBOX: if recipe.storage.method == Storage.DROPBOX:
return Dropbox return Dropbox

View File

@ -1,12 +1,15 @@
import uuid
from datetime import datetime from datetime import datetime
from django.contrib import messages from django.contrib import messages
from django.http import HttpResponseRedirect from django.http import HttpResponseRedirect
from django.shortcuts import redirect, render from django.shortcuts import redirect, render
from django.urls import reverse from django.urls import reverse
from django.utils import timezone
from django.utils.translation import gettext as _ from django.utils.translation import gettext as _
from django.utils.translation import ngettext from django.utils.translation import ngettext
from django_tables2 import RequestConfig from django_tables2 import RequestConfig
from oauth2_provider.models import AccessToken
from rest_framework.authtoken.models import Token from rest_framework.authtoken.models import Token
from cookbook.forms import BatchEditForm, SyncForm from cookbook.forms import BatchEditForm, SyncForm
@ -115,34 +118,12 @@ def import_url(request):
messages.add_message(request, messages.WARNING, msg) messages.add_message(request, messages.WARNING, msg)
return HttpResponseRedirect(reverse('index')) return HttpResponseRedirect(reverse('index'))
if (api_token := Token.objects.filter(user=request.user).first()) is None: if (api_token := AccessToken.objects.filter(user=request.user, scope='bookmarklet').first()) is None:
api_token = Token.objects.create(user=request.user) api_token = AccessToken.objects.create(user=request.user, scope='bookmarklet', expires=(timezone.now() + timezone.timedelta(days=365*10)), token=f'tda_{str(uuid.uuid4()).replace("-","_")}')
bookmarklet_import_id = -1 bookmarklet_import_id = -1
if 'id' in request.GET: if 'id' in request.GET:
if bookmarklet_import := BookmarkletImport.objects.filter(id=request.GET['id']).first(): if bookmarklet_import := BookmarkletImport.objects.filter(id=request.GET['id']).first():
bookmarklet_import_id = bookmarklet_import.pk bookmarklet_import_id = bookmarklet_import.pk
return render(request, 'url_import.html', {'api_token': api_token, 'bookmarklet_import_id': bookmarklet_import_id}) return render(request, 'url_import.html', {'api_token': api_token, 'bookmarklet_import_id': bookmarklet_import_id})
class Object(object):
pass
@group_required('user')
def statistics(request):
counts = Object()
counts.recipes = Recipe.objects.filter(space=request.space).count()
counts.keywords = Keyword.objects.filter(space=request.space).count()
counts.recipe_import = RecipeImport.objects.filter(space=request.space).count()
counts.units = Unit.objects.filter(space=request.space).count()
counts.ingredients = Food.objects.filter(space=request.space).count()
counts.comments = Comment.objects.filter(recipe__space=request.space).count()
counts.recipes_internal = Recipe.objects.filter(internal=True, space=request.space).count()
counts.recipes_external = counts.recipes - counts.recipes_internal
counts.recipes_no_keyword = Recipe.objects.filter(keywords=None, space=request.space).count()
return render(request, 'stats.html', {'counts': counts})

View File

@ -82,42 +82,6 @@ def get_integration(request, export_type):
return Cookmate(request, export_type) return Cookmate(request, export_type)
@group_required('user')
def import_recipe(request):
limit, msg = above_space_limit(request.space)
if limit:
messages.add_message(request, messages.WARNING, msg)
return HttpResponseRedirect(reverse('index'))
if request.method == "POST":
form = ImportForm(request.POST, request.FILES)
if form.is_valid() and request.FILES != {}:
try:
integration = get_integration(request, form.cleaned_data['type'])
il = ImportLog.objects.create(type=form.cleaned_data['type'], created_by=request.user, space=request.space)
files = []
for f in request.FILES.getlist('files'):
files.append({'file': BytesIO(f.read()), 'name': f.name})
t = threading.Thread(target=integration.do_import, args=[files, il, form.cleaned_data['duplicates']])
t.setDaemon(True)
t.start()
return JsonResponse({'import_id': il.pk})
except NotImplementedError:
return JsonResponse(
{
'error': True,
'msg': _('Importing is not implemented for this provider')
},
status=400
)
else:
form = ImportForm()
return render(request, 'import.html', {'form': form})
@group_required('user') @group_required('user')
def export_recipe(request): def export_recipe(request):
if request.method == "POST": if request.method == "POST":

View File

@ -1,5 +1,6 @@
import os import os
import re import re
import uuid
from datetime import datetime from datetime import datetime
from uuid import UUID from uuid import UUID
@ -11,28 +12,21 @@ from django.contrib.auth.forms import PasswordChangeForm
from django.contrib.auth.models import Group from django.contrib.auth.models import Group
from django.contrib.auth.password_validation import validate_password from django.contrib.auth.password_validation import validate_password
from django.core.exceptions import ValidationError from django.core.exceptions import ValidationError
from django.db.models import Avg, Q
from django.db.models.functions import Lower
from django.http import HttpResponseRedirect from django.http import HttpResponseRedirect
from django.shortcuts import get_object_or_404, redirect, render from django.shortcuts import get_object_or_404, redirect, render
from django.urls import reverse, reverse_lazy from django.urls import reverse, reverse_lazy
from django.utils import timezone from django.utils import timezone
from django.utils.translation import gettext as _ from django.utils.translation import gettext as _
from django_scopes import scopes_disabled from django_scopes import scopes_disabled
from django_tables2 import RequestConfig from oauth2_provider.models import AccessToken
from rest_framework.authtoken.models import Token
from cookbook.filters import RecipeFilter
from cookbook.forms import (CommentForm, Recipe, SearchPreferenceForm, ShoppingPreferenceForm, from cookbook.forms import (CommentForm, Recipe, SearchPreferenceForm, ShoppingPreferenceForm,
SpaceCreateForm, SpaceJoinForm, SpacePreferenceForm, User, SpaceCreateForm, SpaceJoinForm, User,
UserCreateForm, UserNameForm, UserPreference, UserPreferenceForm) UserCreateForm, UserNameForm, UserPreference, UserPreferenceForm)
from cookbook.helper.permission_helper import group_required, has_group_permission, share_link_valid, switch_user_active_space from cookbook.helper.permission_helper import group_required, has_group_permission, share_link_valid, switch_user_active_space
from cookbook.models import (Comment, CookLog, Food, InviteLink, Keyword, from cookbook.models import (Comment, CookLog, InviteLink, SearchFields, SearchPreference, ShareLink,
MealPlan, RecipeImport, SearchFields, SearchPreference, ShareLink, Space, ViewLog, UserSpace)
Space, Unit, ViewLog, UserSpace) from cookbook.tables import (CookLogTable, ViewLogTable)
from cookbook.tables import (CookLogTable, InviteLinkTable, RecipeTable, RecipeTableSmall,
ViewLogTable)
from cookbook.views.data import Object
from recipes.version import BUILD_REF, VERSION_NUMBER from recipes.version import BUILD_REF, VERSION_NUMBER
@ -58,34 +52,7 @@ def index(request):
# TODO need to deprecate # TODO need to deprecate
def search(request): def search(request):
if has_group_permission(request.user, ('guest',)): if has_group_permission(request.user, ('guest',)):
if request.user.userpreference.search_style == UserPreference.NEW: return render(request, 'search.html', {})
return search_v2(request)
f = RecipeFilter(request.GET,
queryset=Recipe.objects.filter(space=request.space).all().order_by(
Lower('name').asc()),
space=request.space)
if request.user.userpreference.search_style == UserPreference.LARGE:
table = RecipeTable(f.qs)
else:
table = RecipeTableSmall(f.qs)
RequestConfig(request, paginate={'per_page': 25}).configure(table)
if request.GET == {} and request.user.userpreference.show_recent:
qs = Recipe.objects.filter(viewlog__created_by=request.user).filter(
space=request.space).order_by('-viewlog__created_at').all()
recent_list = []
for r in qs:
if r not in recent_list:
recent_list.append(r)
if len(recent_list) >= 5:
break
last_viewed = RecipeTable(recent_list)
else:
last_viewed = None
return render(request, 'index.html', {'recipes': table, 'filter': f, 'last_viewed': last_viewed})
else: else:
if request.user.is_authenticated: if request.user.is_authenticated:
return HttpResponseRedirect(reverse('view_no_group')) return HttpResponseRedirect(reverse('view_no_group'))
@ -93,11 +60,6 @@ def search(request):
return HttpResponseRedirect(reverse('account_login') + '?next=' + request.path) return HttpResponseRedirect(reverse('account_login') + '?next=' + request.path)
@group_required('guest')
def search_v2(request):
return render(request, 'search.html', {})
def no_groups(request): def no_groups(request):
return render(request, 'no_groups_info.html') return render(request, 'no_groups_info.html')
@ -127,14 +89,14 @@ def space_overview(request):
if join_form.is_valid(): if join_form.is_valid():
return HttpResponseRedirect(reverse('view_invite', args=[join_form.cleaned_data['token']])) return HttpResponseRedirect(reverse('view_invite', args=[join_form.cleaned_data['token']]))
else: else:
if settings.SOCIAL_DEFAULT_ACCESS: if settings.SOCIAL_DEFAULT_ACCESS and len(request.user.userspace_set.all()) == 0:
user_space = UserSpace.objects.create(space=Space.objects.first(), user=request.user, active=True) user_space = UserSpace.objects.create(space=Space.objects.first(), user=request.user, active=False)
user_space.groups.add(Group.objects.filter(name=settings.SOCIAL_DEFAULT_GROUP).get()) user_space.groups.add(Group.objects.filter(name=settings.SOCIAL_DEFAULT_GROUP).get())
return HttpResponseRedirect(reverse('index')) return HttpResponseRedirect(reverse('index'))
if 'signup_token' in request.session: if 'signup_token' in request.session:
return HttpResponseRedirect(reverse('view_invite', args=[request.session.pop('signup_token', '')])) return HttpResponseRedirect(reverse('view_invite', args=[request.session.pop('signup_token', '')]))
create_form = SpaceCreateForm(initial={'name': f'{request.user.username}\'s Space'}) create_form = SpaceCreateForm(initial={'name': f'{request.user.get_user_display_name()}\'s Space'})
join_form = SpaceJoinForm() join_form = SpaceJoinForm()
return render(request, 'space_overview.html', {'create_form': create_form, 'join_form': join_form}) return render(request, 'space_overview.html', {'create_form': create_form, 'join_form': join_form})
@ -190,18 +152,6 @@ def recipe_view(request, pk, share=None):
comment_form = CommentForm() comment_form = CommentForm()
user_servings = None
if request.user.is_authenticated:
user_servings = CookLog.objects.filter(
recipe=recipe,
created_by=request.user,
servings__gt=0,
space=request.space,
).all().aggregate(Avg('servings'))['servings__avg']
if not user_servings:
user_servings = 0
if request.user.is_authenticated: if request.user.is_authenticated:
if not ViewLog.objects.filter(recipe=recipe, created_by=request.user, if not ViewLog.objects.filter(recipe=recipe, created_by=request.user,
created_at__gt=(timezone.now() - timezone.timedelta(minutes=5)), created_at__gt=(timezone.now() - timezone.timedelta(minutes=5)),
@ -209,8 +159,7 @@ def recipe_view(request, pk, share=None):
ViewLog.objects.create(recipe=recipe, created_by=request.user, space=request.space) ViewLog.objects.create(recipe=recipe, created_by=request.user, space=request.space)
return render(request, 'recipe_view.html', return render(request, 'recipe_view.html',
{'recipe': recipe, 'comments': comments, 'comment_form': comment_form, 'share': share, {'recipe': recipe, 'comments': comments, 'comment_form': comment_form, 'share': share, })
'user_servings': user_servings})
@group_required('user') @group_required('user')
@ -228,6 +177,20 @@ def supermarket(request):
return render(request, 'supermarket.html', {}) return render(request, 'supermarket.html', {})
@group_required('user')
def view_profile(request, user_id):
return render(request, 'profile.html', {})
@group_required('guest')
def user_settings(request):
if request.space.demo:
messages.add_message(request, messages.ERROR, _('This feature is not available in the demo version!'))
return redirect('index')
return render(request, 'user_settings.html', {})
@group_required('user') @group_required('user')
def ingredient_editor(request): def ingredient_editor(request):
template_vars = {'food_id': -1, 'unit_id': -1} template_vars = {'food_id': -1, 'unit_id': -1}
@ -241,74 +204,17 @@ def ingredient_editor(request):
return render(request, 'ingredient_editor.html', template_vars) return render(request, 'ingredient_editor.html', template_vars)
@group_required('user')
def meal_plan_entry(request, pk):
plan = MealPlan.objects.filter(space=request.space).get(pk=pk)
if plan.created_by != request.user and plan.shared != request.user:
messages.add_message(request, messages.ERROR, _('You do not have the required permissions to view this page!'))
return HttpResponseRedirect(reverse_lazy('index'))
same_day_plan = MealPlan.objects \
.filter(date=plan.date, space=request.space) \
.exclude(pk=plan.pk) \
.filter(Q(created_by=request.user) | Q(shared=request.user)) \
.order_by('meal_type').all()
return render(request, 'meal_plan_entry.html', {'plan': plan, 'same_day_plan': same_day_plan})
@group_required('guest') @group_required('guest')
def user_settings(request): def shopping_settings(request):
if request.space.demo: if request.space.demo:
messages.add_message(request, messages.ERROR, _('This feature is not available in the demo version!')) messages.add_message(request, messages.ERROR, _('This feature is not available in the demo version!'))
return redirect('index') return redirect('index')
up = request.user.userpreference
sp = request.user.searchpreference sp = request.user.searchpreference
search_error = False search_error = False
active_tab = 'account'
user_name_form = UserNameForm(instance=request.user)
if request.method == "POST": if request.method == "POST":
if 'preference_form' in request.POST: if 'search_form' in request.POST:
active_tab = 'preferences'
form = UserPreferenceForm(request.POST, prefix='preference', space=request.space)
if form.is_valid():
if not up:
up = UserPreference(user=request.user)
up.theme = form.cleaned_data['theme']
up.nav_color = form.cleaned_data['nav_color']
up.default_unit = form.cleaned_data['default_unit']
up.default_page = form.cleaned_data['default_page']
up.show_recent = form.cleaned_data['show_recent']
up.search_style = form.cleaned_data['search_style']
up.plan_share.set(form.cleaned_data['plan_share'])
up.ingredient_decimals = form.cleaned_data['ingredient_decimals'] # noqa: E501
up.comments = form.cleaned_data['comments']
up.use_fractions = form.cleaned_data['use_fractions']
up.use_kj = form.cleaned_data['use_kj']
up.sticky_navbar = form.cleaned_data['sticky_navbar']
up.left_handed = form.cleaned_data['left_handed']
up.save()
elif 'user_name_form' in request.POST:
user_name_form = UserNameForm(request.POST, prefix='name')
if user_name_form.is_valid():
request.user.first_name = user_name_form.cleaned_data['first_name']
request.user.last_name = user_name_form.cleaned_data['last_name']
request.user.save()
elif 'password_form' in request.POST:
password_form = PasswordChangeForm(request.user, request.POST)
if password_form.is_valid():
user = password_form.save()
update_session_auth_hash(request, user)
elif 'search_form' in request.POST:
active_tab = 'search' active_tab = 'search'
search_form = SearchPreferenceForm(request.POST, prefix='search') search_form = SearchPreferenceForm(request.POST, prefix='search')
if search_form.is_valid(): if search_form.is_valid():
@ -357,39 +263,13 @@ def user_settings(request):
sp.lookup = True sp.lookup = True
sp.unaccent.set(SearchFields.objects.all()) sp.unaccent.set(SearchFields.objects.all())
# full text on food is very slow, add search_vector field and index it (including Admin functions and postsave signal to rebuild index) # full text on food is very slow, add search_vector field and index it (including Admin functions and postsave signal to rebuild index)
sp.icontains.set([SearchFields.objects.get(name__in=['Name', 'Ingredients'])]) sp.icontains.set([SearchFields.objects.get(name='Name')])
sp.istartswith.set([SearchFields.objects.get(name='Name')]) sp.istartswith.set([SearchFields.objects.get(name='Name')])
sp.trigram.clear() sp.trigram.clear()
sp.fulltext.set(SearchFields.objects.filter(name__in=['Ingredients'])) sp.fulltext.set(SearchFields.objects.filter(name__in=['Ingredients']))
sp.trigram_threshold = 0.2 sp.trigram_threshold = 0.2
sp.save() sp.save()
elif 'shopping_form' in request.POST:
shopping_form = ShoppingPreferenceForm(request.POST, prefix='shopping')
if shopping_form.is_valid():
if not up:
up = UserPreference(user=request.user)
up.shopping_share.set(shopping_form.cleaned_data['shopping_share'])
up.mealplan_autoadd_shopping = shopping_form.cleaned_data['mealplan_autoadd_shopping']
up.mealplan_autoexclude_onhand = shopping_form.cleaned_data['mealplan_autoexclude_onhand']
up.mealplan_autoinclude_related = shopping_form.cleaned_data['mealplan_autoinclude_related']
up.shopping_auto_sync = shopping_form.cleaned_data['shopping_auto_sync']
up.filter_to_supermarket = shopping_form.cleaned_data['filter_to_supermarket']
up.default_delay = shopping_form.cleaned_data['default_delay']
up.shopping_recent_days = shopping_form.cleaned_data['shopping_recent_days']
up.shopping_add_onhand = shopping_form.cleaned_data['shopping_add_onhand']
up.csv_delim = shopping_form.cleaned_data['csv_delim']
up.csv_prefix = shopping_form.cleaned_data['csv_prefix']
if up.shopping_auto_sync < settings.SHOPPING_MIN_AUTOSYNC_INTERVAL:
up.shopping_auto_sync = settings.SHOPPING_MIN_AUTOSYNC_INTERVAL
up.save()
if up:
preference_form = UserPreferenceForm(instance=up, space=request.space)
shopping_form = ShoppingPreferenceForm(instance=up)
else:
preference_form = UserPreferenceForm(space=request.space)
shopping_form = ShoppingPreferenceForm(space=request.space)
fields_searched = len(sp.icontains.all()) + len(sp.istartswith.all()) + len(sp.trigram.all()) + len( fields_searched = len(sp.icontains.all()) + len(sp.istartswith.all()) + len(sp.trigram.all()) + len(
sp.fulltext.all()) sp.fulltext.all())
@ -398,9 +278,6 @@ def user_settings(request):
elif not search_error: elif not search_error:
search_form = SearchPreferenceForm() search_form = SearchPreferenceForm()
if (api_token := Token.objects.filter(user=request.user).first()) is None:
api_token = Token.objects.create(user=request.user)
# these fields require postgresql - just disable them if postgresql isn't available # these fields require postgresql - just disable them if postgresql isn't available
if not settings.DATABASES['default']['ENGINE'] in ['django.db.backends.postgresql_psycopg2', if not settings.DATABASES['default']['ENGINE'] in ['django.db.backends.postgresql_psycopg2',
'django.db.backends.postgresql']: 'django.db.backends.postgresql']:
@ -410,12 +287,7 @@ def user_settings(request):
search_form.fields['fulltext'].disabled = True search_form.fields['fulltext'].disabled = True
return render(request, 'settings.html', { return render(request, 'settings.html', {
'preference_form': preference_form,
'user_name_form': user_name_form,
'api_token': api_token,
'search_form': search_form, 'search_form': search_form,
'shopping_form': shopping_form,
'active_tab': active_tab
}) })
@ -496,8 +368,9 @@ def invite_link(request, token):
if link := InviteLink.objects.filter(valid_until__gte=datetime.today(), used_by=None, uuid=token).first(): if link := InviteLink.objects.filter(valid_until__gte=datetime.today(), used_by=None, uuid=token).first():
if request.user.is_authenticated and not request.user.userspace_set.filter(space=link.space).exists(): if request.user.is_authenticated and not request.user.userspace_set.filter(space=link.space).exists():
link.used_by = request.user if not link.reusable:
link.save() link.used_by = request.user
link.save()
user_space = UserSpace.objects.create(user=request.user, space=link.space, active=False) user_space = UserSpace.objects.create(user=request.user, space=link.space, active=False)
@ -519,6 +392,9 @@ def invite_link(request, token):
@group_required('admin') @group_required('admin')
def space_manage(request, space_id): def space_manage(request, space_id):
if request.space.demo:
messages.add_message(request, messages.ERROR, _('This feature is not available in the demo version!'))
return redirect('index')
space = get_object_or_404(Space, id=space_id) space = get_object_or_404(Space, id=space_id)
switch_user_active_space(request.user, space) switch_user_active_space(request.user, space)
return render(request, 'space_manage.html', {}) return render(request, 'space_manage.html', {})

View File

@ -0,0 +1,44 @@
# How to migrate from sqlite3 database to postgresql
This migration was written while using the unraid template (docker) for TandoorRecipes, version 1.3.0.
While some commands are unraid specific, it should in general work for any setup.
1. Make a backup of your `/mnt/user/appdata/recipes` dir.
2. Without changing any settings, get a shell into the TandoorRecipes docker through the Web-UI or by running `docker exec -it TandoorRecipes /bin/sh`
```cmd
cd /opt/recipes
./venv/bin/python manage.py export -a > /data/dump.json
```
3. Create a Postgresql database (With a new user & database for recipes)
I used the `postgresql14` template.
```cmd
psql -U postgres
postgres=# create database tandoor;
postgres=# create user tandoor with encrypted password 'yoursupersecretpassworddontusethisone';
postgres=# grant all privileges on database tandoor to tandoor;
```
4. Now its time to change some enviourment variables in TandoorRecipes template:
```env
DB_ENGINE=django.db.backends.postgresql # Database Engine, previous value: `django.db.backends.sqlite3`
POSTGRES_HOST=<Your unraid host ip> # PostgreSQL Host
POSTGRES_PORT=5432 # PostgreSQL Host
POSTGRES_USER=tandoor # PostgreSQL User
POSTGRES_PASSWORD=yoursupersecretpassworddyoudidntcopy # PostgreSQL Password
POSTGRES_DB=tandoor # Database, previous value: `/data/recipes.db`
```
5. Save it, and start the container once.
It will perform all database migrations once for the postgresql database.
6. Get a shell into the docker through the WEB-UI or by running `docker exec -it TandoorRecipes /bin/sh`
```cmd
cd /opt/recipes
./venv/bin/python manage.py import /data/dump.json
```
7. Enjoy your new fuzzy search options and SLIGHTLY performance increase!

View File

@ -8,7 +8,7 @@ msgid ""
msgstr "" 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: 2022-06-26 12:09+0200\n" "POT-Creation-Date: 2022-07-12 19:20+0200\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n" "Language-Team: LANGUAGE <LL@li.org>\n"

View File

@ -8,7 +8,7 @@ msgid ""
msgstr "" 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: 2022-06-26 12:09+0200\n" "POT-Creation-Date: 2022-07-12 19:20+0200\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n" "Language-Team: LANGUAGE <LL@li.org>\n"

View File

@ -8,7 +8,7 @@ msgid ""
msgstr "" 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: 2022-06-26 12:09+0200\n" "POT-Creation-Date: 2022-07-12 19:20+0200\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n" "Language-Team: LANGUAGE <LL@li.org>\n"

View File

@ -8,7 +8,7 @@ msgid ""
msgstr "" 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: 2022-06-26 12:09+0200\n" "POT-Creation-Date: 2022-07-12 19:20+0200\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n" "Language-Team: LANGUAGE <LL@li.org>\n"

View File

@ -8,7 +8,7 @@ msgid ""
msgstr "" 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: 2022-06-26 12:09+0200\n" "POT-Creation-Date: 2022-07-12 19:20+0200\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n" "Language-Team: LANGUAGE <LL@li.org>\n"

View File

@ -8,7 +8,7 @@ msgid ""
msgstr "" 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: 2022-06-26 12:09+0200\n" "POT-Creation-Date: 2022-07-12 19:20+0200\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n" "Language-Team: LANGUAGE <LL@li.org>\n"

View File

@ -8,7 +8,7 @@ msgid ""
msgstr "" 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: 2022-06-26 12:09+0200\n" "POT-Creation-Date: 2022-07-12 19:20+0200\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n" "Language-Team: LANGUAGE <LL@li.org>\n"

View File

@ -8,7 +8,7 @@ msgid ""
msgstr "" 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: 2022-06-26 12:09+0200\n" "POT-Creation-Date: 2022-07-12 19:20+0200\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n" "Language-Team: LANGUAGE <LL@li.org>\n"

View File

@ -8,7 +8,7 @@ msgid ""
msgstr "" 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: 2022-06-26 12:09+0200\n" "POT-Creation-Date: 2022-07-12 19:20+0200\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n" "Language-Team: LANGUAGE <LL@li.org>\n"

View File

@ -8,7 +8,7 @@ msgid ""
msgstr "" 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: 2022-06-26 12:09+0200\n" "POT-Creation-Date: 2022-07-12 19:20+0200\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n" "Language-Team: LANGUAGE <LL@li.org>\n"

View File

@ -8,7 +8,7 @@ msgid ""
msgstr "" 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: 2022-06-26 12:09+0200\n" "POT-Creation-Date: 2022-07-12 19:20+0200\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n" "Language-Team: LANGUAGE <LL@li.org>\n"

View File

@ -8,7 +8,7 @@ msgid ""
msgstr "" 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: 2022-06-26 12:09+0200\n" "POT-Creation-Date: 2022-07-12 19:20+0200\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n" "Language-Team: LANGUAGE <LL@li.org>\n"

View File

@ -8,7 +8,7 @@ msgid ""
msgstr "" 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: 2022-06-26 12:09+0200\n" "POT-Creation-Date: 2022-07-12 19:20+0200\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n" "Language-Team: LANGUAGE <LL@li.org>\n"

View File

@ -52,6 +52,9 @@ SHOPPING_MIN_AUTOSYNC_INTERVAL = int(os.getenv('SHOPPING_MIN_AUTOSYNC_INTERVAL',
ALLOWED_HOSTS = os.getenv('ALLOWED_HOSTS').split(',') if os.getenv('ALLOWED_HOSTS') else ['*'] ALLOWED_HOSTS = os.getenv('ALLOWED_HOSTS').split(',') if os.getenv('ALLOWED_HOSTS') else ['*']
if os.getenv('CSRF_TRUSTED_ORIGINS'):
CSRF_TRUSTED_ORIGINS = os.getenv('CSRF_TRUSTED_ORIGINS').split(',')
CORS_ORIGIN_ALLOW_ALL = True CORS_ORIGIN_ALLOW_ALL = True
LOGIN_REDIRECT_URL = "index" LOGIN_REDIRECT_URL = "index"
@ -96,10 +99,10 @@ INSTALLED_APPS = [
'django.contrib.sites', 'django.contrib.sites',
'django.contrib.staticfiles', 'django.contrib.staticfiles',
'django.contrib.postgres', 'django.contrib.postgres',
'oauth2_provider',
'django_prometheus', 'django_prometheus',
'django_tables2', 'django_tables2',
'corsheaders', 'corsheaders',
'django_filters',
'crispy_forms', 'crispy_forms',
'rest_framework', 'rest_framework',
'rest_framework.authtoken', 'rest_framework.authtoken',
@ -233,10 +236,17 @@ AUTH_PASSWORD_VALIDATORS = [
SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https') SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')
OAUTH2_PROVIDER = {
'SCOPES': {'read': 'Read scope', 'write': 'Write scope', 'bookmarklet': 'only access to bookmarklet'}
}
READ_SCOPE = 'read'
WRITE_SCOPE = 'write'
REST_FRAMEWORK = { REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': ( 'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework.authentication.SessionAuthentication', 'rest_framework.authentication.SessionAuthentication',
'rest_framework.authentication.TokenAuthentication', 'oauth2_provider.contrib.rest_framework.OAuth2Authentication',
'rest_framework.authentication.BasicAuthentication', 'rest_framework.authentication.BasicAuthentication',
), ),
'DEFAULT_PERMISSION_CLASSES': [ 'DEFAULT_PERMISSION_CLASSES': [
@ -409,6 +419,8 @@ if os.getenv('S3_ACCESS_KEY', ''):
if os.getenv('S3_ENDPOINT_URL', ''): if os.getenv('S3_ENDPOINT_URL', ''):
AWS_S3_ENDPOINT_URL = os.getenv('S3_ENDPOINT_URL', '') AWS_S3_ENDPOINT_URL = os.getenv('S3_ENDPOINT_URL', '')
if os.getenv('S3_CUSTOM_DOMAIN', ''):
AWS_S3_CUSTOM_DOMAIN = os.getenv('S3_CUSTOM_DOMAIN', '')
MEDIA_URL = os.getenv('MEDIA_URL', '/media/') MEDIA_URL = os.getenv('MEDIA_URL', '/media/')
MEDIA_ROOT = os.path.join(BASE_DIR, "mediafiles") MEDIA_ROOT = os.path.join(BASE_DIR, "mediafiles")

View File

@ -1,13 +1,13 @@
Django==4.0.6 Django==4.0.7
cryptography==37.0.2 cryptography==37.0.2
django-annoying==0.10.6 django-annoying==0.10.6
django-autocomplete-light==3.9.4 django-autocomplete-light==3.9.4
django-cleanup==6.0.0 django-cleanup==6.0.0
django-crispy-forms==1.14.0 django-crispy-forms==1.14.0
django-filter==22.1
django-tables2==2.4.1 django-tables2==2.4.1
djangorestframework==3.13.1 djangorestframework==3.13.1
drf-writable-nested==0.6.3 drf-writable-nested==0.6.4
django-oauth-toolkit==2.1.0
bleach==5.0.1 bleach==5.0.1
bleach-allowlist==1.0.3 bleach-allowlist==1.0.3
gunicorn==20.1.0 gunicorn==20.1.0
@ -20,7 +20,7 @@ requests==2.28.1
six==1.16.0 six==1.16.0
webdavclient3==3.14.6 webdavclient3==3.14.6
whitenoise==6.2.0 whitenoise==6.2.0
icalendar==4.0.9 icalendar==4.1.0
pyyaml==6.0 pyyaml==6.0
uritemplate==4.1.1 uritemplate==4.1.1
beautifulsoup4==4.11.1 beautifulsoup4==4.11.1
@ -29,7 +29,7 @@ Jinja2==3.1.2
django-webpack-loader==1.5.0 django-webpack-loader==1.5.0
git+https://github.com/ierror/django-js-reverse@7cab78c4531780ab4b32033d5104ccd5be1a246a git+https://github.com/ierror/django-js-reverse@7cab78c4531780ab4b32033d5104ccd5be1a246a
django-allauth==0.51.0 django-allauth==0.51.0
recipe-scrapers==14.6.0 recipe-scrapers==14.11.0
django-scopes==1.2.0.post1 django-scopes==1.2.0.post1
pytest==7.1.2 pytest==7.1.2
pytest-django==4.5.2 pytest-django==4.5.2
@ -39,7 +39,7 @@ django-storages==1.12.3
boto3==1.24.21 boto3==1.24.21
django-prometheus==2.2.0 django-prometheus==2.2.0
django-hCaptcha==0.2.0 django-hCaptcha==0.2.0
python-ldap==3.4.0 python-ldap==3.4.2
django-auth-ldap==4.1.0 django-auth-ldap==4.1.0
pytest-factoryboy==2.5.0 pytest-factoryboy==2.5.0
pyppeteer==1.0.2 pyppeteer==1.0.2

View File

@ -69,7 +69,9 @@
v-if="recipe.imported !== undefined && recipe.imported" v-if="recipe.imported !== undefined && recipe.imported"
target="_blank">{{ target="_blank">{{
recipe.recipe_name recipe.recipe_name
}}</a> <a target="_blank" :href="`${resolveDjangoUrl('view_search') }?query=${recipe.recipe_name}`" v-else>{{ recipe.recipe_name }}</a> }}</a> <a target="_blank"
:href="`${resolveDjangoUrl('view_search') }?query=${recipe.recipe_name}`"
v-else>{{ recipe.recipe_name }}</a>
<b-badge class="float-right text-white">{{ index + 1 }}</b-badge> <b-badge class="float-right text-white">{{ index + 1 }}</b-badge>
</h5> </h5>
<p class="mb-0"> <p class="mb-0">
@ -212,16 +214,19 @@ export default {
} }
if (out.info !== '') { if (out.info !== '') {
let items = out.info.split(/:(.*)/s)[1] let items = out.info.split(/:(.*)/s)[1]
items = items.split(",") if (items !== undefined) {
out.duplicates_total = items.length items = items.split(",")
out.recipes.forEach((recipe) => { out.duplicates_total = items.length
recipe.imported = true out.recipes.forEach((recipe) => {
items.forEach((item) => { recipe.imported = true
if (recipe.recipe_name === item.trim()) { items.forEach((item) => {
recipe.imported = false if (recipe.recipe_name === item.trim()) {
} recipe.imported = false
}
})
}) })
}) }
} else { } else {
if (out.imported_total > 0) { if (out.imported_total > 0) {
out.recipes.forEach((recipe) => { out.recipes.forEach((recipe) => {

View File

@ -461,8 +461,8 @@ export default {
recent_urls: [], recent_urls: [],
source_data: '', source_data: '',
recipe_json: undefined, recipe_json: undefined,
recipe_html: undefined, // recipe_html: undefined,
recipe_tree: undefined, // recipe_tree: undefined,
recipe_images: [], recipe_images: [],
imported_recipes: [], imported_recipes: [],
failed_imports: [], failed_imports: [],
@ -593,9 +593,9 @@ export default {
} }
// reset all variables // reset all variables
this.recipe_html = undefined // this.recipe_html = undefined
this.recipe_json = undefined this.recipe_json = undefined
this.recipe_tree = undefined // this.recipe_tree = undefined
this.recipe_images = [] this.recipe_images = []
// load recipe // load recipe
@ -621,8 +621,8 @@ export default {
return x return x
}) })
this.recipe_tree = response.data['recipe_tree']; // this.recipe_tree = response.data['recipe_tree'];
this.recipe_html = response.data['recipe_html']; // this.recipe_html = response.data['recipe_html'];
this.recipe_images = response.data['recipe_images'] !== undefined ? response.data['recipe_images'] : []; this.recipe_images = response.data['recipe_images'] !== undefined ? response.data['recipe_images'] : [];
if (!silent) { if (!silent) {
@ -695,7 +695,7 @@ export default {
`localStorage.setItem("importURL", "${localStorage.getItem('BASE_PATH')}${this.resolveDjangoUrl('api:bookmarkletimport-list')}");` + `localStorage.setItem("importURL", "${localStorage.getItem('BASE_PATH')}${this.resolveDjangoUrl('api:bookmarkletimport-list')}");` +
`localStorage.setItem("redirectURL", "${localStorage.getItem('BASE_PATH')}${this.resolveDjangoUrl('data_import_url')}");` + `localStorage.setItem("redirectURL", "${localStorage.getItem('BASE_PATH')}${this.resolveDjangoUrl('data_import_url')}");` +
`localStorage.setItem("token", "${window.API_TOKEN}");` + `localStorage.setItem("token", "${window.API_TOKEN}");` +
`document.body.appendChild(document.createElement("script")).src="${localStorage.getItem('BASE_PATH')}${resolveDjangoStatic('/js/bookmarklet.js')}?r="+Math.floor(Math.random()*999999999)}` + `document.body.appendChild(document.createElement("script")).src="${localStorage.getItem('BASE_PATH')}${resolveDjangoStatic('/js/bookmarklet_v3.js')}?r="+Math.floor(Math.random()*999999999)}` +
`})()` `})()`
}, },
}, },

View File

@ -0,0 +1,45 @@
<template>
<div id="app" class="col-12 col-xl-8 col-lg-10 offset-xl-2 offset-lg-1 offset">
<div class="col-12 col-xl-8 col-lg-10 offset-xl-2 offset-lg-1">
</div>
</div>
</template>
<script>
import Vue from "vue"
import { BootstrapVue } from "bootstrap-vue"
import "bootstrap-vue/dist/bootstrap-vue.css"
import { ApiApiFactory } from "@/utils/openapi/api"
import CookbookSlider from "@/components/CookbookSlider"
import LoadingSpinner from "@/components/LoadingSpinner"
import { StandardToasts, ApiMixin } from "@/utils/utils"
Vue.use(BootstrapVue)
export default {
name: "ProfileView",
mixins: [ApiMixin],
components: { },
data() {
return {
}
},
computed: {
},
mounted() {
this.$i18n.locale = window.CUSTOM_LOCALE
},
methods: {
},
}
</script>
<style>
</style>

View File

@ -0,0 +1,18 @@
import Vue from 'vue'
import App from './ProfileView.vue'
import i18n from '@/i18n'
Vue.config.productionTip = false
// TODO move this and other default stuff to centralized JS file (verify nothing breaks)
let publicPath = localStorage.STATIC_URL + 'vue/'
if (process.env.NODE_ENV === 'development') {
publicPath = 'http://localhost:8080/'
}
export default __webpack_public_path__ = publicPath // eslint-disable-line
new Vue({
i18n,
render: h => h(App),
}).$mount('#app')

View File

@ -188,10 +188,31 @@
</b-form-checkbox> </b-form-checkbox>
<br/> <br/>
<label for="id_name"> {{ $t("Imported_From") }}</label> <label> {{ $t("Imported_From") }}</label>
<b-form-input v-model="recipe.source_url"> <b-form-input v-model="recipe.source_url">
</b-form-input> </b-form-input>
<br/>
<label> {{ $t("Private_Recipe") }}</label>
<b-form-checkbox v-model="recipe.private">
{{ $t('Private_Recipe_Help') }}
</b-form-checkbox>
<br/>
<label> {{ $t("Share") }}</label>
<generic-multiselect
@change="recipe.shared = $event.val"
parent_variable="recipe.shared"
:initial_selection="recipe.shared"
:label="'display_name'"
:model="Models.USER_NAME"
style="flex-grow: 1; flex-shrink: 1; flex-basis: 0"
v-bind:placeholder="$t('Share')"
:limit="25"
></generic-multiselect>
</b-collapse> </b-collapse>
</div> </div>
</div> </div>
@ -577,9 +598,9 @@
<div class="col-md-12"> <div class="col-md-12">
<label :for="'id_instruction_' + step.id">{{ $t("Instructions") }}</label> <label :for="'id_instruction_' + step.id">{{ $t("Instructions") }}</label>
<mavon-editor v-model="step.instruction" :autofocus="false" <mavon-editor v-model="step.instruction" :autofocus="false"
style="height: 40vh; z-index: auto" :id="'id_instruction_' + step.id" style="z-index: auto" :id="'id_instruction_' + step.id"
:language="'en'" :language="'en'"
:toolbars="md_editor_toolbars"/> :toolbars="md_editor_toolbars" :defaultOpen="'edit'"/>
<!-- TODO markdown DOCS link and markdown editor --> <!-- TODO markdown DOCS link and markdown editor -->
</div> </div>
@ -723,6 +744,7 @@ import GenericModalForm from "@/components/Modals/GenericModalForm"
import mavonEditor from 'mavon-editor' import mavonEditor from 'mavon-editor'
import 'mavon-editor/dist/css/index.css' import 'mavon-editor/dist/css/index.css'
import _debounce from "lodash/debounce"; import _debounce from "lodash/debounce";
import GenericMultiselect from "@/components/GenericMultiselect";
// use // use
Vue.use(mavonEditor) Vue.use(mavonEditor)
@ -731,7 +753,7 @@ Vue.use(BootstrapVue)
export default { export default {
name: "RecipeEditView", name: "RecipeEditView",
mixins: [ResolveUrlMixin, ApiMixin], mixins: [ResolveUrlMixin, ApiMixin],
components: {Multiselect, LoadingSpinner, draggable, GenericModalForm}, components: {Multiselect, LoadingSpinner, draggable, GenericModalForm, GenericMultiselect},
data() { data() {
return { return {
recipe_id: window.RECIPE_ID, recipe_id: window.RECIPE_ID,
@ -771,9 +793,9 @@ export default {
imagelink: false, imagelink: false,
code: true, code: true,
table: false, table: false,
fullscreen: true, fullscreen: false,
readmodel: true, readmodel: false,
htmlcode: true, htmlcode: false,
help: true, help: true,
undo: true, undo: true,
redo: true, redo: true,

View File

@ -269,10 +269,6 @@ export default {
}, },
loadRecipe: function (recipe_id) { loadRecipe: function (recipe_id) {
apiLoadRecipe(recipe_id).then((recipe) => { apiLoadRecipe(recipe_id).then((recipe) => {
if (window.USER_SERVINGS !== 0) {
recipe.servings = window.USER_SERVINGS
}
let total_time = 0 let total_time = 0
for (let step of recipe.steps) { for (let step of recipe.steps) {
for (let ingredient of step.ingredients) { for (let ingredient of step.ingredients) {

View File

@ -0,0 +1,119 @@
<template>
<div id="app">
<div class="row">
<div class="col-12">
<nav aria-label="breadcrumb">
<ol class="breadcrumb">
<li class="breadcrumb-item"><a :href="resolveDjangoUrl('view_settings')">{{
$t('Settings')
}}</a></li>
<li class="breadcrumb-item" v-if="visible_settings === 'cosmetic'"
@click="visible_settings = 'cosmetic'">{{ $t('Cosmetic') }}
</li>
<li class="breadcrumb-item" v-if="visible_settings === 'account'"
@click="visible_settings = 'account'"> {{ $t('Account') }}
</li>
<li class="breadcrumb-item" v-if="visible_settings === 'search'"
@click="visible_settings = 'search'">{{ $t('Search') }}
</li>
<li class="breadcrumb-item" v-if="visible_settings === 'shopping'"
@click="visible_settings = 'shopping'">{{ $t('Shopping_list') }}
</li>
<li class="breadcrumb-item" v-if="visible_settings === 'meal_plan'"
@click="visible_settings = 'meal_plan'">
{{ $t('Meal_Plan') }}
</li>
<li class="breadcrumb-item" v-if="visible_settings === 'api'" @click="visible_settings = 'api'">
{{ $t('API') }}
</li>
</ol>
</nav>
</div>
</div>
<div class="row">
<div class="col-md-3 col-12">
<b-nav vertical>
<b-nav-item :active="visible_settings === 'cosmetic'" @click="visible_settings = 'cosmetic'"><i
class="fas fa-fw fa-eye"></i> {{ $t('Cosmetic') }}
</b-nav-item>
<b-nav-item :active="visible_settings === 'account'" @click="visible_settings = 'account'"><i
class="fas fa-fw fa-user"></i> {{ $t('Account') }}
</b-nav-item>
<b-nav-item :active="visible_settings === 'search'" @click="visible_settings = 'search'"><i
class="fas fa-fw fa-search"></i> {{ $t('Search') }}
</b-nav-item>
<b-nav-item :active="visible_settings === 'shopping'" @click="visible_settings = 'shopping'"><i
class="fas fa-fw fa-shopping-cart"></i> {{ $t('Shopping_list') }}
</b-nav-item>
<b-nav-item :active="visible_settings === 'meal_plan'" @click="visible_settings = 'meal_plan'"><i
class="fas fa-fw fa-calendar"></i> {{ $t('Meal_Plan') }}
</b-nav-item>
<b-nav-item :active="visible_settings === 'api'" @click="visible_settings = 'api'"><i
class="fas fa-fw fa-code"></i> {{ $t('API') }}
</b-nav-item>
</b-nav>
</div>
<div class="col-md-9 col-12">
<cosmetic-settings-component v-if="visible_settings === 'cosmetic'"
:user_id="user_id"></cosmetic-settings-component>
<account-settings-component v-if="visible_settings === 'account'"
:user_id="user_id"></account-settings-component>
<search-settings-component v-if="visible_settings === 'search'"
:user_id="user_id"></search-settings-component>
<shopping-settings-component v-if="visible_settings === 'shopping'"
:user_id="user_id"></shopping-settings-component>
<meal-plan-settings-component v-if="visible_settings === 'meal_plan'"
:user_id="user_id"></meal-plan-settings-component>
<a-p-i-settings-component v-if="visible_settings === 'api'"
:user_id="user_id"></a-p-i-settings-component>
</div>
</div>
</div>
</template>
<script>
import Vue from "vue"
import {BootstrapVue} from "bootstrap-vue"
import "bootstrap-vue/dist/bootstrap-vue.css"
import CosmeticSettingsComponent from "@/components/Settings/CosmeticSettingsComponent";
import AccountSettingsComponent from "@/components/Settings/AccountSettingsComponent";
import SearchSettingsComponent from "@/components/Settings/SearchSettingsComponent";
import ShoppingSettingsComponent from "@/components/Settings/ShoppingSettingsComponent";
import MealPlanSettingsComponent from "@/components/Settings/MealPlanSettingsComponent";
import APISettingsComponent from "@/components/Settings/APISettingsComponent";
import {ResolveUrlMixin} from "@/utils/utils";
Vue.use(BootstrapVue)
export default {
name: "ProfileView",
mixins: [ResolveUrlMixin],
components: {
CosmeticSettingsComponent,
AccountSettingsComponent,
SearchSettingsComponent,
ShoppingSettingsComponent,
MealPlanSettingsComponent,
APISettingsComponent
},
data() {
return {
visible_settings: 'cosmetic',
user_id: window.USER_ID,
}
},
mounted() {
this.$i18n.locale = window.CUSTOM_LOCALE
},
methods: {},
}
</script>
<style>
</style>

View File

@ -0,0 +1,17 @@
import Vue from 'vue'
import App from './SettingsView.vue'
import i18n from '@/i18n'
Vue.config.productionTip = false
// TODO move this and other default stuff to centralized JS file (verify nothing breaks)
let publicPath = localStorage.STATIC_URL + 'vue/'
if (process.env.NODE_ENV === 'development') {
publicPath = 'http://localhost:8080/'
}
export default __webpack_public_path__ = publicPath // eslint-disable-line
new Vue({
i18n,
render: h => h(App),
}).$mount('#app')

View File

@ -33,7 +33,8 @@
<template #title> <template #title>
<b-spinner v-if="loading" type="border" small class="d-inline-block"></b-spinner> <b-spinner v-if="loading" type="border" small class="d-inline-block"></b-spinner>
<i v-if="!loading" class="fas fa-shopping-cart fa-fw d-inline-block d-md-none"></i> <i v-if="!loading" class="fas fa-shopping-cart fa-fw d-inline-block d-md-none"></i>
<span class="d-none d-md-inline-block">{{ $t('Shopping_list') }}</span> <span
class="d-none d-md-inline-block">{{ $t('Shopping_list') + ` (${items.filter(x => x.checked === false).length})` }}</span>
</template> </template>
<div class="container p-0 p-md-3" id="shoppinglist"> <div class="container p-0 p-md-3" id="shoppinglist">
<div class="row"> <div class="row">
@ -177,7 +178,7 @@
<b-tab :title="$t('Recipes')"> <b-tab :title="$t('Recipes')">
<template #title> <template #title>
<i class="fas fa-book fa-fw d-block d-md-none"></i> <i class="fas fa-book fa-fw d-block d-md-none"></i>
<span class="d-none d-md-block">{{ $t('Recipes') }}</span> <span class="d-none d-md-block">{{ $t('Recipes') + ` (${Recipes.length})` }}</span>
</template> </template>
<div class="container p-0"> <div class="container p-0">
<div class="row"> <div class="row">
@ -234,7 +235,9 @@
</thead> </thead>
<tr v-for="r in Recipes" :key="r.list_recipe"> <tr v-for="r in Recipes" :key="r.list_recipe">
<td>{{ r.recipe_mealplan.name }}</td> <td>{{ r.recipe_mealplan.name }}</td>
<td><a :href="resolveDjangoUrl('view_recipe', r.recipe_mealplan.recipe)">{{ r.recipe_mealplan.recipe_name }}</a></td> <td><a :href="resolveDjangoUrl('view_recipe', r.recipe_mealplan.recipe)">{{
r.recipe_mealplan.recipe_name
}}</a></td>
<td class="block-inline"> <td class="block-inline">
<b-form-input min="1" type="number" :debounce="300" <b-form-input min="1" type="number" :debounce="300"
:value="r.recipe_mealplan.servings" :value="r.recipe_mealplan.servings"
@ -258,7 +261,7 @@
<b-tab> <b-tab>
<template #title> <template #title>
<i class="fas fa-store-alt fa-fw d-block d-md-none"></i> <i class="fas fa-store-alt fa-fw d-block d-md-none"></i>
<span class="d-none d-md-block">{{ $t('Supermarkets') }}</span> <span class="d-none d-md-block">{{ $t('Supermarkets') + ` (${supermarkets.length})` }}</span>
</template> </template>
<div class="container p-0"> <div class="container p-0">
<div class="row"> <div class="row">
@ -460,183 +463,7 @@
</template> </template>
<div class="row justify-content-center"> <div class="row justify-content-center">
<div class="col-12 col-md-8"> <div class="col-12 col-md-8">
<b-card class="no-body"> <shopping-settings-component @updated="settings = $event" :user_id="user_id"></shopping-settings-component>
<div class="row">
<div class="col col-md-6">{{ $t("mealplan_autoadd_shopping") }}</div>
<div class="col col-md-6 text-right">
<input type="checkbox" class="form-control settings-checkbox"
v-model="settings.mealplan_autoadd_shopping" @change="saveSettings"/>
</div>
</div>
<div class="row sm mb-3">
<div class="col">
<em class="small text-muted">{{ $t("mealplan_autoadd_shopping_desc") }}</em>
</div>
</div>
<div v-if="settings.mealplan_autoadd_shopping">
<div class="row">
<div class="col col-md-6">{{ $t("mealplan_autoexclude_onhand") }}</div>
<div class="col col-md-6 text-right">
<input type="checkbox" class="form-control settings-checkbox"
v-model="settings.mealplan_autoexclude_onhand" @change="saveSettings"/>
</div>
</div>
<div class="row sm mb-3">
<div class="col">
<em class="small text-muted">{{ $t("mealplan_autoexclude_onhand_desc") }}</em>
</div>
</div>
</div>
<div v-if="settings.mealplan_autoadd_shopping">
<div class="row">
<div class="col col-md-6">{{ $t("mealplan_autoinclude_related") }}</div>
<div class="col col-md-6 text-right">
<input type="checkbox" class="form-control settings-checkbox"
v-model="settings.mealplan_autoinclude_related" @change="saveSettings"/>
</div>
</div>
<div class="row sm mb-3">
<div class="col">
<em class="small text-muted">
{{ $t("mealplan_autoinclude_related_desc") }}
</em>
</div>
</div>
</div>
<div class="row">
<div class="col col-md-6">{{ $t("shopping_share") }}</div>
<div class="col col-md-6 text-right">
<generic-multiselect
size="sm"
@change="
settings.shopping_share = $event.val
saveSettings()
"
:model="Models.USER"
:initial_selection="settings.shopping_share"
label="username"
:multiple="true"
style="flex-grow: 1; flex-shrink: 1; flex-basis: 0"
:placeholder="$t('User')"
/>
</div>
</div>
<div class="row sm mb-3">
<div class="col">
<em class="small text-muted">{{ $t("shopping_share_desc") }}</em>
</div>
</div>
<div class="row">
<div class="col col-md-6">{{ $t("shopping_auto_sync") }}</div>
<div class="col col-md-6 text-right">
<input type="number" class="form-control" v-model="settings.shopping_auto_sync"
@change="saveSettings"/>
</div>
</div>
<div class="row sm mb-3">
<div class="col">
<em class="small text-muted">
{{ $t("shopping_auto_sync_desc") }}
</em>
</div>
</div>
<div class="row">
<div class="col col-md-6">{{ $t("shopping_add_onhand") }}</div>
<div class="col col-md-6 text-right">
<input type="checkbox" class="form-control settings-checkbox"
v-model="settings.shopping_add_onhand" @change="saveSettings"/>
</div>
</div>
<div class="row sm mb-3">
<div class="col">
<em class="small text-muted">
{{ $t("shopping_add_onhand_desc") }}
</em>
</div>
</div>
<div class="row">
<div class="col col-md-6">{{ $t("shopping_recent_days") }}</div>
<div class="col col-md-6 text-right">
<input type="number" class="form-control" v-model="settings.shopping_recent_days"
@change="saveSettings"/>
</div>
</div>
<div class="row sm mb-3">
<div class="col">
<em class="small text-muted">
{{ $t("shopping_recent_days_desc") }}
</em>
</div>
</div>
<div class="row">
<div class="col col-md-6">{{ $t("filter_to_supermarket") }}</div>
<div class="col col-md-6 text-right">
<input type="checkbox" class="form-control settings-checkbox"
v-model="settings.filter_to_supermarket" @change="saveSettings"/>
</div>
</div>
<div class="row sm mb-3">
<div class="col">
<em class="small text-muted">
{{ $t("filter_to_supermarket_desc") }}
</em>
</div>
</div>
<div class="row">
<div class="col col-md-6">{{ $t("default_delay") }}</div>
<div class="col col-md-6 text-right">
<input type="number" class="form-control" min="1" v-model="settings.default_delay"
@change="saveSettings"/>
</div>
</div>
<div class="row sm mb-3">
<div class="col">
<em class="small text-muted">
{{ $t("default_delay_desc") }}
</em>
</div>
</div>
<div class="row">
<div class="col col-md-6">{{ $t("csv_delim_label") }}</div>
<div class="col col-md-6 text-right">
<input class="form-control" v-model="settings.csv_delim" @change="saveSettings"/>
</div>
</div>
<div class="row sm mb-3">
<div class="col">
<em class="small text-muted">
{{ $t("csv_delim_help") }}
</em>
</div>
</div>
<div class="row">
<div class="col col-md-6">{{ $t("csv_prefix_label") }}</div>
<div class="col col-md-6 text-right">
<input class="form-control" v-model="settings.csv_prefix" @change="saveSettings"/>
</div>
</div>
<div class="row sm mb-3">
<div class="col">
<em class="small text-muted">
{{ $t("csv_prefix_help") }}
</em>
</div>
</div>
<div class="row">
<div class="col col-md-6">{{ $t("left_handed") }}</div>
<div class="col col-md-6">
<input type="checkbox" class="form-control settings-checkbox"
v-model="settings.left_handed" @change="saveSettings"/>
</div>
</div>
<div class="row sm mb-3">
<div class="col">
<em class="small text-muted">
{{ $t("left_handed_help") }}
</em>
</div>
</div>
</b-card>
</div> </div>
</div> </div>
</b-tab> </b-tab>
@ -783,6 +610,7 @@ import ShoppingModal from "@/components/Modals/ShoppingModal"
import {ApiMixin, getUserPreference, StandardToasts, makeToast, ResolveUrlMixin} from "@/utils/utils" import {ApiMixin, getUserPreference, StandardToasts, makeToast, ResolveUrlMixin} from "@/utils/utils"
import {ApiApiFactory} from "@/utils/openapi/api" import {ApiApiFactory} from "@/utils/openapi/api"
import ShoppingSettingsComponent from "@/components/Settings/ShoppingSettingsComponent";
Vue.use(BootstrapVue) Vue.use(BootstrapVue)
Vue.use(VueCookies) Vue.use(VueCookies)
@ -790,7 +618,7 @@ let SETTINGS_COOKIE_NAME = "shopping_settings"
export default { export default {
name: "ShoppingListView", name: "ShoppingListView",
mixins: [ApiMixin,ResolveUrlMixin], mixins: [ApiMixin, ResolveUrlMixin],
components: { components: {
ContextMenu, ContextMenu,
ContextMenuItem, ContextMenuItem,
@ -801,7 +629,8 @@ export default {
DownloadCSV, DownloadCSV,
CopyToClipboard, CopyToClipboard,
ShoppingModal, ShoppingModal,
draggable draggable,
ShoppingSettingsComponent
}, },
data() { data() {
@ -837,6 +666,7 @@ export default {
shopping_add_onhand: true, shopping_add_onhand: true,
left_handed: false, left_handed: false,
}, },
user_id: parseInt(localStorage.getItem('USER_ID')),
editing_supermarket_categories: [], editing_supermarket_categories: [],
editing_supermarket: null, editing_supermarket: null,
new_supermarket: {entrymode: false, value: undefined, editmode: undefined}, new_supermarket: {entrymode: false, value: undefined, editmode: undefined},
@ -868,7 +698,7 @@ export default {
case "category": case "category":
return item?.food?.supermarket_category?.name ?? x return item?.food?.supermarket_category?.name ?? x
case "created_by": case "created_by":
return item?.created_by?.username ?? x return item?.created_by?.display_name ?? x
case "recipe": case "recipe":
return item?.recipe_mealplan?.recipe_name ?? x return item?.recipe_mealplan?.recipe_name ?? x
} }
@ -1315,16 +1145,6 @@ export default {
this.$refs.menu.open(e, value) this.$refs.menu.open(e, value)
}, },
saveSettings: function () {
let api = ApiApiFactory()
api.partialUpdateUserPreference(this.settings.user, this.settings)
.then((result) => {
StandardToasts.makeStandardToast(this, StandardToasts.SUCCESS_UPDATE)
})
.catch((err) => {
StandardToasts.makeStandardToast(this, StandardToasts.FAIL_UPDATE, err)
})
},
saveThis: function (thisItem, toast = true) { saveThis: function (thisItem, toast = true) {
let api = new ApiApiFactory() let api = new ApiApiFactory()
if (!thisItem?.id) { if (!thisItem?.id) {

View File

@ -141,6 +141,13 @@
<label>{{ $t('Message') }}</label> <label>{{ $t('Message') }}</label>
<b-form-textarea v-model="space.message"></b-form-textarea> <b-form-textarea v-model="space.message"></b-form-textarea>
<label>{{ $t('Image') }}</label>
<generic-multiselect :initial_single_selection="space.image"
:model="Models.USERFILE"
:multiple="false"
@change="space.image = $event.val;"></generic-multiselect>
<br/>
<b-form-checkbox v-model="space.show_facet_count"> Facet Count</b-form-checkbox> <b-form-checkbox v-model="space.show_facet_count"> Facet Count</b-form-checkbox>
<span class="text-muted small">{{ $t('facet_count_info') }}</span><br/> <span class="text-muted small">{{ $t('facet_count_info') }}</span><br/>

View File

@ -5,7 +5,7 @@
{{ book_copy.icon }}&nbsp;{{ book_copy.name }} {{ book_copy.icon }}&nbsp;{{ book_copy.name }}
<span class="float-right text-primary" @click="editOrSave"><i class="fa" v-bind:class="{ 'fa-pen': !editing, 'fa-save': editing }" aria-hidden="true"></i></span> <span class="float-right text-primary" @click="editOrSave"><i class="fa" v-bind:class="{ 'fa-pen': !editing, 'fa-save': editing }" aria-hidden="true"></i></span>
</h5> </h5>
<b-badge class="font-weight-normal mr-1" v-for="u in book_copy.shared" v-bind:key="u.id" variant="primary" pill>{{ u.username }}</b-badge> <b-badge class="font-weight-normal mr-1" v-for="u in book_copy.shared" v-bind:key="u.id" variant="primary" pill>{{ u.display_name }}</b-badge>
</b-card-header> </b-card-header>
<b-card-body class="p-4"> <b-card-body class="p-4">
<div class="form-group" v-if="editing"> <div class="form-group" v-if="editing">
@ -25,7 +25,7 @@
@change="book_copy.shared = $event.val" @change="book_copy.shared = $event.val"
parent_variable="book.shared" parent_variable="book.shared"
:initial_selection="book.shared" :initial_selection="book.shared"
:label="'username'" :label="'display_name'"
:model="Models.USER_NAME" :model="Models.USER_NAME"
style="flex-grow: 1; flex-shrink: 1; flex-basis: 0" style="flex-grow: 1; flex-shrink: 1; flex-basis: 0"
v-bind:placeholder="$t('Share')" v-bind:placeholder="$t('Share')"

Some files were not shown because too many files have changed in this diff Show More