Compare commits

...

35 Commits
1.2.5 ... 1.2.7

Author SHA1 Message Date
vabene1111
0e2a27ad41 Merge branch 'develop' 2022-05-25 18:53:18 +02:00
vabene1111
c064e8970f compiled messages 2022-05-25 17:58:29 +02:00
Krisztian Doka
b8ef7ae39d Translated using Weblate (Hungarian)
Currently translated at 92.2% (487 of 528 strings)

Translation: Tandoor/Recipes Backend
Translate-URL: http://translate.tandoor.dev/projects/tandoor/recipes-backend/hu/
2022-05-24 20:32:14 +00:00
Krisztian Doka
702f3de061 Added translation using Weblate (Hungarian) 2022-05-23 19:50:50 +00:00
Ramon Aixa Juan
f278d7f6d0 Translated using Weblate (Spanish)
Currently translated at 8.2% (34 of 414 strings)

Translation: Tandoor/Recipes Frontend
Translate-URL: http://translate.tandoor.dev/projects/tandoor/recipes-frontend/es/
2022-05-23 11:32:17 +00:00
Ramon Aixa Juan
221c1b523b Translated using Weblate (Spanish)
Currently translated at 52.2% (276 of 528 strings)

Translation: Tandoor/Recipes Backend
Translate-URL: http://translate.tandoor.dev/projects/tandoor/recipes-backend/es/
2022-05-23 11:32:17 +00:00
Ramon Aixa Juan
d00b6a2e85 Translated using Weblate (Catalan)
Currently translated at 91.0% (481 of 528 strings)

Translation: Tandoor/Recipes Backend
Translate-URL: http://translate.tandoor.dev/projects/tandoor/recipes-backend/ca/
2022-05-22 11:20:51 +00:00
César Blanco Guillamon
e03c285f14 Translated using Weblate (Spanish)
Currently translated at 5.3% (22 of 414 strings)

Translation: Tandoor/Recipes Frontend
Translate-URL: http://translate.tandoor.dev/projects/tandoor/recipes-frontend/es/
2022-05-21 17:32:19 +00:00
Tomasz Klimczak
0ec1f334c0 Translated using Weblate (Polish)
Currently translated at 100.0% (414 of 414 strings)

Translation: Tandoor/Recipes Frontend
Translate-URL: http://translate.tandoor.dev/projects/tandoor/recipes-frontend/pl/
2022-05-21 17:32:19 +00:00
Laura
b637cbeabc Translated using Weblate (French)
Currently translated at 90.3% (374 of 414 strings)

Translation: Tandoor/Recipes Frontend
Translate-URL: http://translate.tandoor.dev/projects/tandoor/recipes-frontend/fr/
2022-05-21 17:32:18 +00:00
César Blanco Guillamon
943f873855 Translated using Weblate (Spanish)
Currently translated at 51.3% (271 of 528 strings)

Translation: Tandoor/Recipes Backend
Translate-URL: http://translate.tandoor.dev/projects/tandoor/recipes-backend/es/
2022-05-19 10:10:33 +00:00
César Blanco Guillamon
49cebb400a Added translation using Weblate (Spanish) 2022-05-19 10:10:33 +00:00
vabene1111
991fbdad3a fixed localization of create shopping auto form 2022-05-18 16:49:25 +02:00
vabene1111
34e95cd25b improved mobile rendering on ingerdient editor 2022-05-18 16:44:27 +02:00
vabene1111
8bc06fef34 Merge branch 'develop' of https://github.com/vabene1111/recipes into develop 2022-05-18 16:27:11 +02:00
vabene1111
6aba5c3661 fixed plan to eat tag delimeter 2022-05-18 16:27:07 +02:00
vabene1111
876bd49ee5 Merge pull request #1793 from smilerz/url_import_test
fixed spuce eats test
2022-05-18 16:25:30 +02:00
vabene1111
9435c5a380 changed order for reverse auth middleware 2022-05-18 16:09:37 +02:00
vabene1111
94c915e23a fixed advanced search field configuration cookie expiery 2022-05-18 16:06:55 +02:00
vabene1111
9ac8641c13 dont make first step header as default if others are not 2022-05-18 15:55:58 +02:00
vabene1111
0fe06cf2df fixed random search ordering 2022-05-18 15:54:02 +02:00
vabene1111
f872f994f1 fixed import failure with leading commas in input strings 2022-05-18 15:29:03 +02:00
vabene1111
87c2ff73e8 Merge branch 'develop' 2022-05-18 14:38:01 +02:00
Gabriel Tapias
27bb4c9bb8 Translated using Weblate (Spanish)
Currently translated at 48.1% (254 of 528 strings)

Translation: Tandoor/Recipes Backend
Translate-URL: http://translate.tandoor.dev/projects/tandoor/recipes-backend/es/
2022-05-17 21:32:15 +00:00
vabene1111
ac647c5ee8 more JS nonsense 2022-05-17 22:00:36 +02:00
vabene1111
3cec891aa1 added missing dependency 2022-05-17 21:57:43 +02:00
vabene1111
3633b9724b fixed image deletion error 2022-05-17 21:53:10 +02:00
vabene1111
420b5c093f updated some dependencies 2022-05-17 21:43:52 +02:00
vabene1111
9bb55dd746 Merge pull request #1758 from TandoorRecipes/dependabot/npm_and_yarn/vue/kangc/v-md-editor-1.7.11
Bump @kangc/v-md-editor from 1.7.9 to 1.7.11 in /vue
2022-05-17 21:38:00 +02:00
dependabot[bot]
1759ad3587 Bump @kangc/v-md-editor from 1.7.9 to 1.7.11 in /vue
Bumps [@kangc/v-md-editor](https://github.com/code-farmer-i/vue-markdown-editor) from 1.7.9 to 1.7.11.
- [Release notes](https://github.com/code-farmer-i/vue-markdown-editor/releases)
- [Changelog](https://github.com/code-farmer-i/vue-markdown-editor/blob/dev/docs/changelog.md)
- [Commits](https://github.com/code-farmer-i/vue-markdown-editor/compare/v1.7.9...v1.7.11)

---
updated-dependencies:
- dependency-name: "@kangc/v-md-editor"
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-05-17 19:30:34 +00:00
vabene1111
956693b7ca changed view mode of file viewer downloadable files
changed it so that the file is always downloaded so that opened files do not open in the application context (to prevent possible XSS issues)
2022-05-17 21:27:40 +02:00
vabene1111
7b2117c019 improved output sanitization of several views 2022-05-17 21:24:27 +02:00
vabene1111
d48fe26a35 added url validation to all server requests 2022-05-17 18:04:43 +02:00
Kaibu
7fd5fca0cf changed mobile logo to png version aswell 2022-05-13 15:11:04 +02:00
smilerz
37e215a4ea fixed spuce eats test 2022-05-11 14:18:14 -05:00
47 changed files with 49551 additions and 3612 deletions

View File

@@ -232,6 +232,9 @@ class IngredientParser:
match = re.search('\((.[^\(])+\)', ingredient)
ingredient = ingredient[:match.start()] + ingredient[match.end():] + ' ' + ingredient[match.start():match.end()]
# leading spaces before commas result in extra tokens, clean them out
ingredient = ingredient.replace(' ,', ',')
tokens = ingredient.split() # split at each space into tokens
if len(tokens) == 1:
# there only is one argument, that must be the food
@@ -303,4 +306,7 @@ class IngredientParser:
note = food + ' ' + note
food = food[:Food._meta.get_field('name').max_length]
if len(food.strip()) == 0:
raise ValueError(f'Error parsing string {ingredient}, food cannot be empty')
return amount, unit, food, note[:Ingredient._meta.get_field('note').max_length].strip()

View File

@@ -147,7 +147,7 @@ class RecipeSearch():
def _build_sort_order(self):
if self._random:
self._queryset = self._queryset.order_by("?")
self.orderby = ['?']
else:
order = []
# TODO add userpreference for default sort order and replace '-favorite'

View File

@@ -6,6 +6,7 @@ from gettext import gettext as _
from io import BytesIO
import requests
import validators
import yaml
from cookbook.helper.ingredient_parser import IngredientParser
@@ -59,8 +60,10 @@ class CookBookApp(Integration):
if len(images) > 0:
try:
response = requests.get(images[0])
self.import_recipe_image(recipe, BytesIO(response.content))
url = images[0]
if validators.url(url, public=True):
response = requests.get(url)
self.import_recipe_image(recipe, BytesIO(response.content))
except Exception as e:
print('failed to import image ', str(e))

View File

@@ -5,6 +5,7 @@ from io import BytesIO
from gettext import gettext as _
import requests
import validators
from lxml import etree
from cookbook.helper.ingredient_parser import IngredientParser
@@ -64,7 +65,9 @@ class Cookmate(Integration):
if recipe_xml.find('imageurl') is not None:
try:
response = requests.get(recipe_xml.find('imageurl').text.strip())
url = recipe_xml.find('imageurl').text.strip()
if validators.url(url, public=True):
response = requests.get(url)
self.import_recipe_image(recipe, BytesIO(response.content))
except Exception as e:
print('failed to import image ', str(e))

View File

@@ -49,6 +49,7 @@ class Plantoeat(Integration):
)
if tags:
tags = tags.replace('^',',')
for k in tags.split(','):
keyword, created = Keyword.objects.get_or_create(name=k.strip(), space=self.request.space)
recipe.keywords.add(keyword)

View File

@@ -5,6 +5,7 @@ from io import BytesIO
from zipfile import ZipFile
import requests
import validators
from django.utils.translation import gettext as _
from cookbook.helper.image_processing import get_filetype
@@ -123,11 +124,13 @@ class RecetteTek(Integration):
self.import_recipe_image(recipe, BytesIO(import_zip.read(image_file_name)), filetype=get_filetype(image_file_name))
else:
if file['originalPicture'] != '':
response = requests.get(file['originalPicture'])
if imghdr.what(BytesIO(response.content)) is not None:
self.import_recipe_image(recipe, BytesIO(response.content), filetype=get_filetype(file['originalPicture']))
else:
raise Exception("Original image failed to download.")
url = file['originalPicture']
if validators.url(url, public=True):
response = requests.get(url)
if imghdr.what(BytesIO(response.content)) is not None:
self.import_recipe_image(recipe, BytesIO(response.content), filetype=get_filetype(file['originalPicture']))
else:
raise Exception("Original image failed to download.")
except Exception as e:
print(recipe.name, ': failed to import image ', str(e))

View File

@@ -2,6 +2,7 @@ import json
from io import BytesIO
import requests
import validators
from cookbook.helper.ingredient_parser import IngredientParser
from cookbook.integration.integration import Integration
@@ -51,8 +52,10 @@ class RecipeSage(Integration):
if len(file['image']) > 0:
try:
response = requests.get(file['image'][0])
self.import_recipe_image(recipe, BytesIO(response.content))
url = file['image'][0]
if validators.url(url, public=True):
response = requests.get(url)
self.import_recipe_image(recipe, BytesIO(response.content))
except Exception as e:
print('failed to import image ', str(e))

View File

@@ -2235,7 +2235,7 @@ msgid ""
msgstr ""
"Предстои ви да използвате вашия\n"
" %(provider_name)s профил, с който да влезете\n"
" %(name_site)s. Като последна стъпка, моля, попълнете следния "
" %(site_name)s. Като последна стъпка, моля, попълнете следния "
"формуляр:"
#: .\cookbook\templates\socialaccount\snippets\provider_list.html:23

View File

@@ -13,8 +13,8 @@ msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2022-04-29 18:42+0200\n"
"PO-Revision-Date: 2022-04-10 09:51+0000\n"
"Last-Translator: Rubens <rubenixnagios@gmail.com>\n"
"PO-Revision-Date: 2022-05-22 11:20+0000\n"
"Last-Translator: Ramon Aixa Juan <juanramonaixa@gmail.com>\n"
"Language-Team: Catalan <http://translate.tandoor.dev/projects/tandoor/"
"recipes-backend/ca/>\n"
"Language: ca\n"
@@ -35,7 +35,7 @@ msgstr "Unitat per defecte"
#: .\cookbook\forms.py:57
msgid "Use fractions"
msgstr "Usa fraccions"
msgstr "Utilitza fraccions"
#: .\cookbook\forms.py:58
msgid "Use KJ"
@@ -84,7 +84,7 @@ msgstr "Comentaris"
#: .\cookbook\forms.py:69
msgid "Left-handed mode"
msgstr ""
msgstr "Mode per a esquerrans"
#: .\cookbook\forms.py:73
msgid ""
@@ -163,7 +163,7 @@ msgstr "Exclou els ingredients que hi ha a mà."
#: .\cookbook\forms.py:93
msgid "Will optimize the UI for use with your left hand."
msgstr ""
msgstr "S'optimitzarà la UI pel seu ús amb la mà esquerra."
#: .\cookbook\forms.py:110
msgid ""
@@ -230,8 +230,8 @@ msgid ""
"Leave empty for dropbox and enter only base url for nextcloud (<code>/remote."
"php/webdav/</code> is added automatically)"
msgstr ""
"Deixeu-lo buit per a Dropbox i introduïu només l'URL base per a nextcloud "
"(<code>/remote.php/webdav/ s'afegeix automàticament)"
"Deixeu-lo buit per a Dropbox i introduïu només l'URL base per a Nextcloud "
"(<code>/remote.php/webdav/</code> s'afegeix automàticament)"
#: .\cookbook\forms.py:272 .\cookbook\views\edit.py:157
msgid "Storage"

View File

@@ -9,21 +9,21 @@
# miguel angel <mlopezifu@alumnos.unex.es>, 2020
# Miguel Canteras <mcanteras@gmail.com>, 2021
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2022-04-29 18:42+0200\n"
"PO-Revision-Date: 2020-06-02 19:28+0000\n"
"Last-Translator: Miguel Canteras <mcanteras@gmail.com>, 2021\n"
"Language-Team: Spanish (https://www.transifex.com/django-recipes/"
"teams/110507/es/)\n"
"PO-Revision-Date: 2022-05-23 11:32+0000\n"
"Last-Translator: Ramon Aixa Juan <juanramonaixa@gmail.com>\n"
"Language-Team: Spanish <http://translate.tandoor.dev/projects/tandoor/"
"recipes-backend/es/>\n"
"Language: es\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
"Plural-Forms: nplurals=2; plural=n != 1;\n"
"X-Generator: Weblate 4.10.1\n"
#: .\cookbook\filters.py:23 .\cookbook\templates\forms\ingredients.html:34
#: .\cookbook\templates\space.html:49 .\cookbook\templates\stats.html:28
@@ -31,66 +31,52 @@ msgid "Ingredients"
msgstr "Ingredientes"
#: .\cookbook\forms.py:56
#, fuzzy
#| msgid "Default"
msgid "Default unit"
msgstr "Por defecto"
msgstr "Unidad por defecto"
#: .\cookbook\forms.py:57
#, fuzzy
#| msgid "System Information"
msgid "Use fractions"
msgstr "Información del Sistema"
msgstr "Usar fracciones"
#: .\cookbook\forms.py:58
msgid "Use KJ"
msgstr ""
msgstr "Usar KJ"
#: .\cookbook\forms.py:59
msgid "Theme"
msgstr ""
msgstr "Tema"
#: .\cookbook\forms.py:60
msgid "Navbar color"
msgstr ""
msgstr "Color de la barra de navegación"
#: .\cookbook\forms.py:61
msgid "Sticky navbar"
msgstr ""
msgstr "Barra de navegación pegajosa"
#: .\cookbook\forms.py:62
#, fuzzy
#| msgid "Default"
msgid "Default page"
msgstr "Por defecto"
msgstr "Página por defecto"
#: .\cookbook\forms.py:63
#, fuzzy
#| msgid "Shopping Recipes"
msgid "Show recent recipes"
msgstr "Recetas en el carro de la compra"
msgstr "Mostrar recetas recientes"
#: .\cookbook\forms.py:64
#, fuzzy
#| msgid "Search"
msgid "Search style"
msgstr "Buscar"
msgstr "Estilo de búsqueda"
#: .\cookbook\forms.py:65
msgid "Plan sharing"
msgstr ""
#: .\cookbook\forms.py:66
#, fuzzy
#| msgid "Ingredients"
msgid "Ingredient decimal places"
msgstr "Ingredientes"
msgstr "Número de decimales del ingrediente"
#: .\cookbook\forms.py:67
#, fuzzy
#| msgid "Shopping list currently empty"
msgid "Shopping list auto sync period"
msgstr "Lista de la compra vacía"
msgstr "Período de sincronización automática de la lista de compras"
#: .\cookbook\forms.py:68 .\cookbook\templates\recipe_view.html:21
#: .\cookbook\templates\space.html:76 .\cookbook\templates\stats.html:47
@@ -99,7 +85,7 @@ msgstr "Comentarios"
#: .\cookbook\forms.py:69
msgid "Left-handed mode"
msgstr ""
msgstr "Modo para zurdos"
#: .\cookbook\forms.py:73
msgid ""
@@ -125,23 +111,17 @@ msgstr ""
#: .\cookbook\forms.py:79
msgid "Display nutritional energy amounts in joules instead of calories"
msgstr ""
msgstr "Mostrar los valores nutricionales en Julios en vez de calorías"
#: .\cookbook\forms.py:80
#, fuzzy
#| msgid ""
#| "Users with whom newly created meal plan/shopping list entries should be "
#| "shared by default."
msgid "Users with whom newly created meal plans should be shared by default."
msgstr ""
"Usuarios con los que las entradas recién creadas del plan de comida/lista de "
"la compra deben compartirse de forma predeterminada."
"Usuarios con los que las entradas recién creadas del plan de comida deben "
"compartirse de forma predeterminada."
#: .\cookbook\forms.py:81
#, fuzzy
#| msgid "Open Shopping List"
msgid "Users with whom to share shopping lists."
msgstr "Abrir Lista de la Compra"
msgstr "Usuarios con quienes compartir listas de compra."
#: .\cookbook\forms.py:83
msgid "Show recently viewed recipes on search page."
@@ -183,7 +163,7 @@ msgstr ""
#: .\cookbook\forms.py:93
msgid "Will optimize the UI for use with your left hand."
msgstr ""
msgstr "Se optimizará la UI para su uso con la mano izquierda."
#: .\cookbook\forms.py:110
msgid ""
@@ -191,11 +171,7 @@ msgid ""
"instead"
msgstr ""
"Ambos campos son opcionales. Si no se proporciona ninguno, se mostrará el "
"nombre de usuario en su lugar\n"
" \n"
" \n"
" \n"
" "
"nombre de usuario en su lugar"
#: .\cookbook\forms.py:131 .\cookbook\forms.py:304
msgid "Name"
@@ -232,10 +208,12 @@ msgid ""
"To prevent duplicates recipes with the same name as existing ones are "
"ignored. Check this box to import everything."
msgstr ""
"Para evitar duplicados, las recetas con el mismo nombre serán ignoradas. "
"Marca esta opción para importar todas las recetas."
#: .\cookbook\forms.py:203
msgid "Add your comment: "
msgstr "Añada su comentario:"
msgstr "Añada su comentario: "
#: .\cookbook\forms.py:218
msgid "Leave empty for dropbox and enter app password for nextcloud."
@@ -262,7 +240,7 @@ msgstr "Almacenamiento"
#: .\cookbook\forms.py:274
msgid "Active"
msgstr ""
msgstr "Activo"
#: .\cookbook\forms.py:280
msgid "Search String"
@@ -296,21 +274,23 @@ msgstr ""
#: .\cookbook\forms.py:375
msgid "Email address already taken!"
msgstr ""
msgstr "¡El correo electrónico ya existe!"
#: .\cookbook\forms.py:383
msgid ""
"An email address is not required but if present the invite link will be sent "
"to the user."
msgstr ""
"El correo electrónico es opcional. Si se añade uno se mandará un link de "
"invitación."
#: .\cookbook\forms.py:398
msgid "Name already taken."
msgstr ""
msgstr "El nombre ya existe."
#: .\cookbook\forms.py:409
msgid "Accept Terms and Privacy"
msgstr ""
msgstr "Aceptar términos y condiciones"
#: .\cookbook\forms.py:441
msgid ""
@@ -361,10 +341,8 @@ msgid ""
msgstr ""
#: .\cookbook\forms.py:466
#, fuzzy
#| msgid "Search"
msgid "Search Method"
msgstr "Buscar"
msgstr "Método de Búsqueda"
#: .\cookbook\forms.py:467
msgid "Fuzzy Lookups"
@@ -389,10 +367,8 @@ msgid "Fuzzy Search"
msgstr "Buscar"
#: .\cookbook\forms.py:472
#, fuzzy
#| msgid "Text"
msgid "Full Text"
msgstr "Texto"
msgstr "Texto Completo"
#: .\cookbook\forms.py:497
msgid ""
@@ -437,10 +413,8 @@ msgid "Prefix to add when copying list to the clipboard."
msgstr ""
#: .\cookbook\forms.py:514
#, fuzzy
#| msgid "Shopping List"
msgid "Share Shopping List"
msgstr "Lista de la Compra"
msgstr "Compartir Lista de la Compra"
#: .\cookbook\forms.py:515
msgid "Autosync"
@@ -463,10 +437,8 @@ msgid "Default Delay Hours"
msgstr ""
#: .\cookbook\forms.py:520
#, fuzzy
#| msgid "Select Supermarket"
msgid "Filter to Supermarket"
msgstr "Seleccionar supermercado"
msgstr "Filtrar según Supermercado"
#: .\cookbook\forms.py:521
msgid "Recent Days"
@@ -499,10 +471,8 @@ msgid "Fields on food that should be inherited by default."
msgstr "Alimento que se va a reemplazar."
#: .\cookbook\forms.py:548
#, fuzzy
#| msgid "Show recently viewed recipes on search page."
msgid "Show recipe counts on search filters"
msgstr "Muestra recetas vistas recientemente en la página de búsqueda."
msgstr "Mostrar cantidad de recetas en los filtros de búsquedas"
#: .\cookbook\helper\AllAuthCustomAdapter.py:36
msgid ""
@@ -546,10 +516,8 @@ msgid "One of queryset or hash_key must be provided"
msgstr ""
#: .\cookbook\helper\shopping_helper.py:152
#, fuzzy
#| msgid "You must provide at least a recipe or a title."
msgid "You must supply a servings size"
msgstr "Debe proporcionar al menos una receta o un título."
msgstr "Debe proporcionar un tamaño de porción"
#: .\cookbook\helper\template_helper.py:64
#: .\cookbook\helper\template_helper.py:66
@@ -586,22 +554,17 @@ msgid "The following recipes were ignored because they already existed:"
msgstr ""
#: .\cookbook\integration\integration.py:235
#, fuzzy, python-format
#| msgid "Imported new recipe!"
#, python-format
msgid "Imported %s recipes."
msgstr "¡Nueva receta importada!"
msgstr "Se importaron %s recetas."
#: .\cookbook\integration\paprika.py:46
#, fuzzy
#| msgid "Note"
msgid "Notes"
msgstr "Nota"
msgstr "Notas"
#: .\cookbook\integration\paprika.py:49
#, fuzzy
#| msgid "Information"
msgid "Nutritional Information"
msgstr "Información"
msgstr "Información Nutricional"
#: .\cookbook\integration\paprika.py:53
msgid "Source"
@@ -715,10 +678,8 @@ msgid "Raw"
msgstr ""
#: .\cookbook\models.py:1138
#, fuzzy
#| msgid "Food"
msgid "Food Alias"
msgstr "Comida"
msgstr "Alias de la Comida"
#: .\cookbook\models.py:1138
#, fuzzy
@@ -870,10 +831,8 @@ msgid "Remove"
msgstr "Eliminar"
#: .\cookbook\templates\account\email.html:58
#, fuzzy
#| msgid "Warning"
msgid "Warning:"
msgstr "Advertencia"
msgstr "Advertencia:"
#: .\cookbook\templates\account\email.html:58
msgid ""
@@ -979,10 +938,8 @@ msgstr "¿Seguro que quieres salir?"
#: .\cookbook\templates\account\password_reset_from_key.html:13
#: .\cookbook\templates\account\password_reset_from_key_done.html:7
#: .\cookbook\templates\account\password_reset_from_key_done.html:13
#, fuzzy
#| msgid "Changes saved!"
msgid "Change Password"
msgstr "¡Cambios guardados!"
msgstr "Cambiar contraseña"
#: .\cookbook\templates\account\password_change.html:12
#: .\cookbook\templates\account\password_set.html:12
@@ -1010,10 +967,8 @@ msgid ""
msgstr ""
#: .\cookbook\templates\account\password_reset.html:32
#, fuzzy
#| msgid "Password reset is not implemented for the time being!"
msgid "Password reset is disabled on this instance."
msgstr "¡Restablecimiento de contraseña no está implementado de momento!"
msgstr "Restablecimiento de contraseña no está implementado de momento."
#: .\cookbook\templates\account\password_reset_done.html:16
msgid ""
@@ -1037,10 +992,8 @@ msgid ""
msgstr ""
#: .\cookbook\templates\account\password_reset_from_key.html:33
#, fuzzy
#| msgid "Changes saved!"
msgid "change password"
msgstr "¡Cambios guardados!"
msgstr "Cambiar contraseña"
#: .\cookbook\templates\account\password_reset_from_key.html:36
#: .\cookbook\templates\account\password_reset_from_key_done.html:19
@@ -1365,7 +1318,7 @@ msgstr "¿Estás seguro de que quieres combinar estos dos ingredientes?"
#: .\cookbook\templates\generic\delete_template.html:21
#, python-format
msgid "Are you sure you want to delete the %(title)s: <b>%(object)s</b> "
msgstr "¿Estás seguro de que quieres borrar el %(title)s: <b>%(object)s</b>?"
msgstr "¿Estás seguro de que quieres borrar el %(title)s: <b>%(object)s</b>? "
#: .\cookbook\templates\generic\delete_template.html:26
msgid "Protected"
@@ -1698,12 +1651,10 @@ msgid "No Permission"
msgstr "Sin permisos"
#: .\cookbook\templates\no_perm_info.html:15
#, fuzzy
#| msgid "You do not have the required permissions to perform this action!"
msgid ""
"You do not have the required permissions to view this page or perform this "
"action."
msgstr "¡No tienes los permisos necesarios para realizar esta acción!"
msgstr "No tienes los permisos necesarios para realizar esta acción."
#: .\cookbook\templates\no_space_info.html:6
#: .\cookbook\templates\no_space_info.html:13
@@ -2255,21 +2206,19 @@ msgstr "Administrador"
#: .\cookbook\templates\space.html:118
msgid "user"
msgstr ""
msgstr "usuario"
#: .\cookbook\templates\space.html:119
msgid "guest"
msgstr ""
msgstr "invitado"
#: .\cookbook\templates\space.html:120
#, fuzzy
#| msgid "Remove"
msgid "remove"
msgstr "Eliminar"
msgstr "eliminar"
#: .\cookbook\templates\space.html:124
msgid "Update"
msgstr ""
msgstr "Actualizar"
#: .\cookbook\templates\space.html:128
#, fuzzy

View File

@@ -11,8 +11,8 @@ msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2022-04-29 18:42+0200\n"
"PO-Revision-Date: 2022-03-29 20:36+0000\n"
"Last-Translator: Adrian M <adriankoooo@gmail.com>\n"
"PO-Revision-Date: 2022-05-24 20:32+0000\n"
"Last-Translator: Krisztian Doka <master@dnome.hu>\n"
"Language-Team: Hungarian <http://translate.tandoor.dev/projects/tandoor/"
"recipes-backend/hu/>\n"
"Language: hu_HU\n"
@@ -82,7 +82,7 @@ msgstr "Megjegyzések"
#: .\cookbook\forms.py:69
msgid "Left-handed mode"
msgstr ""
msgstr "Balkezes üzemmód"
#: .\cookbook\forms.py:73
msgid ""
@@ -162,7 +162,7 @@ msgstr "Mellőzze a kéznél lévő összetevőket."
#: .\cookbook\forms.py:93
msgid "Will optimize the UI for use with your left hand."
msgstr ""
msgstr "Optimalizálja a felületet, bal kézzel történő használatra."
#: .\cookbook\forms.py:110
msgid ""

View File

@@ -4,6 +4,8 @@ import os
from datetime import datetime
import requests
import validators
from cookbook.models import Recipe, RecipeImport, SyncLog
from cookbook.provider.provider import Provider
@@ -104,9 +106,11 @@ class Dropbox(Provider):
recipe.link = Dropbox.get_share_link(recipe)
recipe.save()
response = requests.get(recipe.link.replace('www.dropbox.', 'dl.dropboxusercontent.'))
url = recipe.link.replace('www.dropbox.', 'dl.dropboxusercontent.')
if validators.url(url, public=True):
response = requests.get(url)
return io.BytesIO(response.content)
return io.BytesIO(response.content)
@staticmethod
def rename_file(recipe, new_name):

View File

@@ -4,6 +4,7 @@ import tempfile
from datetime import datetime
import requests
import validators
import webdav3.client as wc
from cookbook.models import Recipe, RecipeImport, SyncLog
from cookbook.provider.provider import Provider
@@ -92,20 +93,21 @@ class Nextcloud(Provider):
"Content-Type": "application/json"
}
r = requests.get(
url,
headers=headers,
auth=HTTPBasicAuth(
recipe.storage.username, recipe.storage.password
if validators.url(url, public=True):
r = requests.get(
url,
headers=headers,
auth=HTTPBasicAuth(
recipe.storage.username, recipe.storage.password
)
)
)
response_json = r.json()
for element in response_json['ocs']['data']:
if element['share_type'] == '3':
return element['url']
response_json = r.json()
for element in response_json['ocs']['data']:
if element['share_type'] == '3':
return element['url']
return Nextcloud.create_share_link(recipe)
return Nextcloud.create_share_link(recipe)
@staticmethod
def get_file(recipe):

View File

@@ -91,7 +91,7 @@
{% if not request.user.is_authenticated or request.user.userpreference.theme == request.user.userpreference.TANDOOR %}
<a class="navbar-brand p-0 me-2 justify-content-center" href="{% base_path request 'base' %}"
aria-label="Tandoor">
<img class="brand-icon" src="{% static 'assets/brand_logo.svg' %}" alt="Logo">
<img class="brand-icon" src="{% static 'assets/brand_logo.png' %}" alt="Logo">
</a>
{% endif %}
{% endif %}

View File

@@ -2226,237 +2226,238 @@ TASTE_OF_HOME = {
THE_SPRUCE_EATS = {
'file': ['thespruceeats.html'],
'url': 'https://www.thespruceeats.com/creamy-potato-soup-with-ham-3059797',
"name": "Creamy Potato Soup With Ham",
"description": "",
"servings": 6,
"prepTime": 55,
"cookTime": 0,
"image": 'https://www.thespruceeats.com/thmb/KfpdKXpq4JYKIIyv8ZuKuR8GRkQ=/960x0/filters:no_upscale():max_bytes(150000):strip_icc():format(webp)/creamy-potato-soup-with-ham-3059797-stovetop-step-12-99dc3bf1962c4e26a2d225ee3c25ecad.jpg',
"keywords": [],
"recipeIngredient": [
"servings_text": "servings",
"working_time": 20,
"waiting_time": 35,
"image": "https://www.thespruceeats.com/thmb/X_emapo3nNw6ASJctdNpYycYFtM=/940x0/filters:no_upscale():max_bytes(150000):strip_icc()/creamy-potato-soup-with-ham-3059797-stovetop-step-12-99dc3bf1962c4e26a2d225ee3c25ecad.jpg",
"source_url": "https://www.thespruceeats.com/creamy-potato-soup-with-ham-3059797",
"keywords": [
{
"label": "soup",
"name": "soup",
"id": 18,
},
{
"amount": 2,
"unit": {
"text": "tablespoons",
"id": 99364
"label": "lunch",
"name": "lunch",
},
"ingredient": {
"text": "butter",
"id": 84919
},
"note": "",
"original_text": "2 tablespoons butter"
},
{
"amount": 1.5,
"unit": {
"text": "to",
"id": 15026
"label": "entree",
"name": "entree",
},
"ingredient": {
"text": "2 cups onion",
"id": 89800
},
"note": "chopped",
"original_text": "1 1/2 to 2 cups onion (chopped)"
},
{
"amount": 1,
"unit": {
"text": "cup",
"id": 74768
"label": "side dish",
"name": "side dish",
"id": 14,
},
"ingredient": {
"text": "celery",
"id": 70601
},
"note": "chopped",
"original_text": "1 cup celery (chopped)"
},
{
"amount": 2,
"unit": {
"text": "large",
"id": 39225
"label": "www.thespruceeats.com",
"name": "www.thespruceeats.com",
},
"ingredient": {
"text": "carrots",
"id": 72417
},
"note": "peeled and chopped",
"original_text": "2 large carrots (peeled and chopped)"
},
{
"amount": 2,
"unit": {
"text": "to",
"id": 17146
"label": "southern",
"name": "southern",
"id": 27,
},
"ingredient": {
"text": "3 cups ham",
"id": 14124
},
"note": "about 1 pound, diced",
"original_text": "2 to 3 cups ham (about 1 pound, diced)"
},
{
"amount": 1,
"unit": {
"text": "clove",
"id": 59174
"label": "cheddar",
"name": "cheddar",
},
"ingredient": {
"text": "garlic",
"id": 63566
},
"note": "minced",
"original_text": "1 clove garlic (minced)"
},
{
"amount": 2,
"unit": {
"text": "cups",
"id": 38790
},
"ingredient": {
"text": "vegetable broth",
"id": 10572
},
"note": "",
"original_text": "2 cups vegetable broth"
},
"label": "dinner",
"name": "dinner",
}
],
"steps": [
{
"amount": 1,
"unit": {
"text": "cup",
"id": 91657
},
"ingredient": {
"text": "water",
"id": 28583
},
"note": "",
"original_text": "1 cup water"
},
{
"amount": 4,
"unit": {
"text": "to",
"id": 35482
},
"ingredient": {
"text": "5 cups potatoes",
"id": 16911
},
"note": "peeled and diced",
"original_text": "4 to 5 cups potatoes (peeled and diced)"
},
{
"amount": 3,
"unit": {
"text": "tablespoons",
"id": 42650
},
"ingredient": {
"text": "all-purpose flour",
"id": 40407
},
"note": "",
"original_text": "3 tablespoons all-purpose flour"
},
{
"amount": 1,
"unit": {
"text": "cup",
"id": 26167
},
"ingredient": {
"text": "heavy cream",
"id": 19424
},
"note": "",
"original_text": "1 cup heavy cream"
},
{
"amount": 1,
"unit": {
"text": "cup",
"id": 55243
},
"ingredient": {
"text": "half-and-half",
"id": 61156
},
"note": "or whole milk, more if needed to thin",
"original_text": "1 cup half-and-half (or whole milk, more if needed to thin)"
},
{
"amount": 0,
"unit": {
"text": "",
"id": 48319
},
"ingredient": {
"text": "Dash salt",
"id": 75657
},
"note": "to taste",
"original_text": "Dash salt (to taste)"
},
{
"amount": 0,
"unit": {
"text": "",
"id": 94308
},
"ingredient": {
"text": "Dash freshly ground black pepper",
"id": 49484
},
"note": "to taste",
"original_text": "Dash freshly ground black pepper (to taste)"
},
{
"amount": 0,
"unit": {
"text": "",
"id": 15775
},
"ingredient": {
"text": "Optional: 2 tablespoons fresh parsley",
"id": 73062
},
"note": "chopped",
"original_text": "Optional: 2 tablespoons fresh parsley (chopped)"
},
{
"amount": 0,
"unit": {
"text": "",
"id": 18745
},
"ingredient": {
"text": "Garnish: green onions or chives",
"id": 73287
},
"note": "sliced",
"original_text": "Garnish: green onions or chives (sliced)"
},
{
"amount": 0,
"unit": {
"text": "",
"id": 51247
},
"ingredient": {
"text": "Optional: cheddar cheese or cheddar-jack blend",
"id": 18932
},
"note": "shredded",
"original_text": "Optional: cheddar cheese or cheddar-jack blend (shredded)"
"instruction": "Gather the ingredients. \nIn a large saucepan, melt butter over medium-low heat. \nAdd onion, celery, carrots, and ham. \nCook, stirring frequently until onions are tender, about 5 minutes. \nAdd the garlic and continue cooking for 1 to 2 minutes longer. \nAdd vegetable broth, water, and potatoes. \nCover and cook for about 25 minutes, until potatoes are tender. \nWhisk flour into the heavy cream until smooth. \nStir into the hot mixture. \nStir in the half-and-half or milk. Taste and add salt and pepper, as desired. Continue cooking until hot. \nUsing a potato masher or fork, mash the potatoes slightly to thicken; add more milk if the soup is too thick. \nServe the potato soup garnished with parsley, sliced green onions or chives, or a little bit of shredded cheese. \nGather the ingredients. \nIn a large saucepan, melt butter over medium-low heat. \nAdd onion, celery, carrots, and ham. \nCook, stirring frequently until onions are tender, about 5 minutes. \nAdd the garlic and continue cooking for 1 to 2 minutes longer. \nThen transfer the cooked vegetables to the slow cooker and add the broth, water, and potatoes. \nCover and cook on high for about 2 to 3 hours, or until the potatoes are very tender. \nWhisk flour into the heavy cream until smooth. \nStir the flour-cream mixture into the slow cooker. \nStir in the half-and-half or milk. Taste and add salt and pepper, as desired. Continue cooking until hot. \nUsing a potato masher or fork, mash the potatoes slightly to thicken; add more milk if the soup is too thick. \nServe the potato soup garnished with parsley, sliced green onions or chives, or a little bit of shredded cheese.",
"ingredients": [
{
"amount": 2,
"food": {
"name": "butter"
},
"unit": {
"name": "tablespoons"
},
"note": "",
"original_text": "2 tablespoons unsalted butter"
},
{
"amount": 1.5,
"food": {
"name": "2 cups coarsely chopped onion"
},
"unit": {
"name": "to"
},
"note": "",
"original_text": "1 1/2 to 2 cups coarsely chopped onion"
},
{
"amount": 1,
"food": {
"name": "coarsely chopped celery"
},
"unit": {
"name": "cups"
},
"note": "",
"original_text": "1 cup coarsely chopped celery"
},
{
"amount": 2,
"food": {
"name": "carrots"
},
"unit": {
"name": "large"
},
"note": "peeled and coarsely chopped",
"original_text": "2 large carrots, peeled and coarsely chopped"
},
{
"amount": 1,
"food": {
"name": "ham"
},
"unit": {
"name": "pounds"
},
"note": "diced",
"original_text": "1 pound ham, diced"
},
{
"amount": 1,
"food": {
"name": "garlic"
},
"unit": {
"name": "cloves"
},
"note": "minced",
"original_text": "1 clove garlic, minced"
},
{
"amount": 2,
"food": {
"name": "vegetable broth"
},
"unit": {
"name": "cups"
},
"note": "",
"original_text": "2 cups vegetable broth"
},
{
"amount": 1,
"food": {
"name": "water"
},
"unit": {
"name": "cups"
},
"note": "",
"original_text": "1 cup water"
},
{
"amount": 4,
"food": {
"name": "5 cups diced peeled potatoes"
},
"unit": {
"name": "to"
},
"note": "",
"original_text": "4 to 5 cups diced peeled potatoes"
},
{
"amount": 3,
"food": {
"name": "all purpose flour"
},
"unit": {
"name": "tablespoons"
},
"note": "",
"original_text": "3 tablespoons all-purpose flour"
},
{
"amount": 1,
"food": {
"name": "heavy cream"
},
"unit": {
"name": "cups"
},
"note": "",
"original_text": "1 cup heavy cream"
},
{
"amount": 1,
"food": {
"name": "half-and-half"
},
"unit": {
"name": "cups"
},
"note": "or whole milk, more if needed",
"original_text": "1 cup half-and-half, or whole milk, more if needed"
},
{
"amount": 0,
"food": {
"name": "salt"
},
"unit": None,
"note": "to taste",
"original_text": "Salt, to taste"
},
{
"amount": 0,
"food": {
"name": "Freshly ground black pepper"
},
"unit": None,
"note": "to taste",
"original_text": "Freshly ground black pepper, to taste"
},
{
"amount": 2,
"food": {
"name": "finely chopped fresh parsley"
},
"unit": {
"name": "tablespoons"
},
"note": "optional",
"original_text": "2 tablespoons finely chopped fresh parsley, optional"
},
{
"amount": 0,
"food": {
"name": "Thinly sliced green onions or chives"
},
"unit": None,
"note": "for garnish",
"original_text": "Thinly sliced green onions or chives, for garnish"
},
{
"amount": 0,
"food": {
"name": "Shredded cheddar cheese"
},
"unit": None,
"note": "or cheddar-jack blend, for garnish, optional",
"original_text": "Shredded cheddar cheese, or cheddar-jack blend, for garnish, optional"
}
]
}
],
"recipeInstructions": "Gather the ingredients.\nIn a large saucepan, melt butter over medium-low heat.\nAdd onion, celery, carrots, and ham.\nCook, stirring frequently until onions are tender, about 5 minutes.\nAdd the garlic and continue cooking for 1 to 2 minutes longer.\nAdd vegetable broth, water, and potatoes.\nCover and cook for about 25 minutes, until potatoes are tender.\nWhisk flour into the heavy cream until smooth.\nStir into the hot mixture.\nStir in the half-and-half or milk. Taste and add salt and pepper, as desired. Continue cooking until hot.\nUsing a potato masher or fork, mash the potatoes slightly to thicken; add more milk if the soup is too thick.\nServe the potato soup garnished with parsley, sliced green onions or chives, or a little bit of shredded cheese.\nGather the ingredients.\nIn a large saucepan, melt butter over medium-low heat.\nAdd onion, celery, carrots, and ham.\nCook, stirring frequently until onions are tender, about 5 minutes.\nAdd the garlic and continue cooking for 1 to 2 minutes longer.\nThen transfer the cooked vegetables to the slow cooker and add the broth, water, and potatoes.\nCover and cook on HIGH for about 2 to 3 hours, or until the potatoes are very tender.\nWhisk flour into the heavy cream until smooth.\nStir the flour-cream mixture into the slow cooker.\nStir in the half-and-half or milk. Taste and add salt and pepper, as desired. Continue cooking until hot.\nUsing a potato masher or fork, mash the potatoes slightly to thicken; add more milk if the soup is too thick.\nServe the potato soup garnished with parsley, sliced green onions or chives, or a little bit of shredded cheese.\n\nImported from https://www.thespruceeats.com/creamy-potato-soup-with-ham-3059797",
"description": "This is a creamy potato soup with ham, garlic, cream, and chopped vegetables. This soup is easy to prepare and is ready in under an hour."
}
TUDOGOSTOSO = {

File diff suppressed because one or more lines are too long

View File

@@ -40,10 +40,10 @@ def test_ingredient_parser():
"1 small sprig of fresh rosemary": (1, "small", "sprig of fresh rosemary", ""),
# does not always work perfectly!
"75 g fresh breadcrumbs": (75, "g", "fresh breadcrumbs", ""),
"4 acorn squash , or onion squash (600-800g)": (4, "acorn", "squash , or onion squash", "600-800g"),
"4 acorn squash , or onion squash (600-800g)": (4, "acorn", "squash, or onion squash", "600-800g"),
"1 x 250 g packet of cooked mixed grains , such as spelt and wild rice": (
1, "x", "250 g packet of cooked mixed grains", "such as spelt and wild rice"),
"1 big bunch of fresh mint , (60g)": (1, "big", "bunch of fresh mint ,", "60g"),
"1 big bunch of fresh mint , (60g)": (1, "big", "bunch of fresh mint,", "60g"),
"1 large red onion": (1, "large", "red onion", ""),
# "2-3 TL Curry": (), # idk what it should use here either
"1 Zwiebel gehackt": (1, "Zwiebel", "gehackt", ""),
@@ -60,12 +60,13 @@ def test_ingredient_parser():
"2-3 c Water": (2, "c", "Water", "2-3"),
"Pane (raffermo o secco) 80 g": (80, "g", "Pane", "raffermo o secco"),
"1 Knoblauchzehe(n), gehackt oder gepresst": (1.0, None, 'Knoblauchzehe(n)', 'gehackt oder gepresst'),
"1 Porreestange(n) , ca. 200 g": (1.0, None, 'Porreestange(n)', 'ca. 200 g'), # leading space before comma
# test for over long food entries to get properly split into the note field
"1 Lorem ipsum dolor sit amet consetetur sadipscing elitr sed diam nonumy eirmod tempor invidunt ut l Lorem ipsum dolor sit amet consetetur sadipscing elitr sed diam nonumy eirmod tempor invidunt ut l": (
1.0, 'Lorem', 'ipsum', 'dolor sit amet consetetur sadipscing elitr sed diam nonumy eirmod tempor invidunt ut l Lorem ipsum dolor sit amet consetetur sadipscing elitr sed diam nonumy eirmod tempor invidunt ut l'),
"1 LoremipsumdolorsitametconsetetursadipscingelitrseddiamnonumyeirmodtemporinviduntutlLoremipsumdolorsitametconsetetursadipscingelitrseddiamnonumyeirmodtemporinviduntutl": (
1.0, None, 'LoremipsumdolorsitametconsetetursadipscingelitrseddiamnonumyeirmodtemporinviduntutlLoremipsumdolorsitametconsetetursadipscingeli',
'LoremipsumdolorsitametconsetetursadipscingelitrseddiamnonumyeirmodtemporinviduntutlLoremipsumdolorsitametconsetetursadipscingelitrseddiamnonumyeirmodtemporinviduntutl')
1.0, None, 'LoremipsumdolorsitametconsetetursadipscingelitrseddiamnonumyeirmodtemporinviduntutlLoremipsumdolorsitametconsetetursadipscingeli',
'LoremipsumdolorsitametconsetetursadipscingelitrseddiamnonumyeirmodtemporinviduntutlLoremipsumdolorsitametconsetetursadipscingelitrseddiamnonumyeirmodtemporinviduntutl')
}
# for German you could say that if an ingredient does not have

View File

@@ -2,15 +2,14 @@ import json
import os
import pytest
from django.urls import reverse
from ._recipes import (
ALLRECIPES, AMERICAS_TEST_KITCHEN, CHEF_KOCH, CHEF_KOCH2, COOKPAD,
COOKS_COUNTRY, DELISH, FOOD_NETWORK, GIALLOZAFFERANO, JOURNAL_DES_FEMMES,
MADAME_DESSERT, MARMITON, TASTE_OF_HOME, THE_SPRUCE_EATS, TUDOGOSTOSO)
from cookbook.tests.conftest import validate_recipe
from ._recipes import (ALLRECIPES, AMERICAS_TEST_KITCHEN, CHEF_KOCH, CHEF_KOCH2, COOKPAD,
COOKS_COUNTRY, DELISH, FOOD_NETWORK, GIALLOZAFFERANO, JOURNAL_DES_FEMMES,
MADAME_DESSERT, MARMITON, TASTE_OF_HOME, THE_SPRUCE_EATS, TUDOGOSTOSO)
IMPORT_SOURCE_URL = 'api_recipe_from_source'
DATA_DIR = "cookbook/tests/other/test_data/"
@@ -36,13 +35,13 @@ def test_import_permission(arg, request):
@pytest.mark.parametrize("arg", [
ALLRECIPES,
# test of custom scraper ATK
# AMERICAS_TEST_KITCHEN, #TODO while the import trough the UI works the test fails for some reason, find out why
AMERICAS_TEST_KITCHEN,
CHEF_KOCH,
# test for empty ingredient in ingredient_parser
CHEF_KOCH2,
COOKPAD,
# test of custom scraper ATK
#COOKS_COUNTRY, #TODO while the import trough the UI works the test fails for some reason, find out why
COOKS_COUNTRY,
DELISH,
FOOD_NETWORK,
GIALLOZAFFERANO,
@@ -53,7 +52,7 @@ def test_import_permission(arg, request):
MARMITON,
TASTE_OF_HOME,
# example of non-json recipes_scraper
# THE_SPRUCE_EATS, #TODO seems to be broken in recipe scrapers
THE_SPRUCE_EATS, # TODO seems to be broken in recipe scrapers
TUDOGOSTOSO,
])
def test_recipe_import(arg, u1_s1):

View File

@@ -6,6 +6,7 @@ import uuid
from collections import OrderedDict
import requests
import validators
from PIL import UnidentifiedImageError
from annoying.decorators import ajax_request
from annoying.functions import get_object_or_None
@@ -14,7 +15,7 @@ from django.contrib.auth.models import User
from django.contrib.postgres.search import TrigramSimilarity
from django.core.exceptions import FieldError, ValidationError
from django.core.files import File
from django.db.models import (Case, Count, Exists, F, IntegerField, OuterRef, ProtectedError, Q,
from django.db.models import (Case, Count, Exists, OuterRef, ProtectedError, Q,
Subquery, Value, When)
from django.db.models.fields.related import ForeignObjectRel
from django.db.models.functions import Coalesce, Lower
@@ -24,7 +25,6 @@ from django.urls import reverse
from django.utils.translation import gettext as _
from django_scopes import scopes_disabled
from icalendar import Calendar, Event
from recipe_scrapers import NoSchemaFoundInWildMode, WebsiteNotImplementedError, scrape_me
from requests.exceptions import MissingSchema
from rest_framework import decorators, status, viewsets
from rest_framework.exceptions import APIException, PermissionDenied
@@ -34,6 +34,7 @@ from rest_framework.renderers import JSONRenderer, TemplateHTMLRenderer
from rest_framework.response import Response
from rest_framework.viewsets import ViewSetMixin
from treebeard.exceptions import InvalidMoveToDescendant, InvalidPosition, PathOverflow
from validators import ValidationFailure
from cookbook.helper.HelperFunctions import str2bool
from cookbook.helper.image_processing import handle_image
@@ -43,7 +44,6 @@ from cookbook.helper.permission_helper import (CustomIsAdmin, CustomIsGuest, Cus
group_required)
from cookbook.helper.recipe_html_import import get_recipe_from_source
from cookbook.helper.recipe_search import RecipeFacet, RecipeSearch, old_search
from cookbook.helper.recipe_url_import import get_from_scraper
from cookbook.helper.shopping_helper import RecipeShoppingEditor, shopping_helper
from cookbook.models import (Automation, BookmarkletImport, CookLog, CustomFilter, ExportLog, Food,
FoodInheritField, ImportLog, Ingredient, Keyword, MealPlan, MealType,
@@ -774,16 +774,18 @@ class RecipeViewSet(viewsets.ModelViewSet):
if serializer.is_valid():
serializer.save()
image = None
filetype = ".jpeg" # fall-back to .jpeg, even if wrong, at least users will know it's an image and most image viewers can open it correctly anyways
filetype = ".jpeg" # fall-back to .jpeg, even if wrong, at least users will know it's an image and most image viewers can open it correctly anyways
if 'image' in serializer.validated_data:
image = obj.image
filetype = mimetypes.guess_extension(serializer.validated_data['image'].content_type) or filetype
elif 'image_url' in serializer.validated_data:
try:
response = requests.get(serializer.validated_data['image_url'])
image = File(io.BytesIO(response.content))
filetype = mimetypes.guess_extension(response.headers['content-type']) or filetype
url = serializer.validated_data['image_url']
if validators.url(url, public=True):
response = requests.get(url)
image = File(io.BytesIO(response.content))
filetype = mimetypes.guess_extension(response.headers['content-type']) or filetype
except UnidentifiedImageError as e:
print(e)
pass
@@ -799,6 +801,10 @@ class RecipeViewSet(viewsets.ModelViewSet):
obj.image = File(img, name=f'{uuid.uuid4()}_{obj.pk}{filetype}')
obj.save()
return Response(serializer.data)
else:
obj.image = None
obj.save()
return Response(serializer.data)
return Response(serializer.errors, 400)
@@ -1188,7 +1194,13 @@ def recipe_from_source(request):
# in manual mode request complete page to return it later
if url:
try:
data = requests.get(url, headers=external_request_headers).content
if validators.url(url, public=True):
data = requests.get(url, headers=external_request_headers).content
else:
return JsonResponse({
'error': True,
'msg': _('Invalid Url')
}, status=400)
except requests.exceptions.ConnectionError:
return JsonResponse({
'error': True,
@@ -1199,6 +1211,7 @@ def recipe_from_source(request):
'error': True,
'msg': _('Bad URL Schema.')
}, status=400)
recipe_json, recipe_tree, recipe_html, recipe_images = get_recipe_from_source(data, url, request)
if len(recipe_tree) == 0 and len(recipe_json) == 0:
return JsonResponse({

View File

@@ -40,7 +40,7 @@ class RecipeCreate(GroupRequiredMixin, CreateView):
obj.space = self.request.space
obj.internal = True
obj.save()
obj.steps.add(Step.objects.create(space=self.request.space))
obj.steps.add(Step.objects.create(space=self.request.space, show_as_header=False))
return HttpResponseRedirect(reverse('edit_recipe', kwargs={'pk': obj.pk}))
def get_success_url(self):

View File

@@ -637,7 +637,7 @@ def test(request):
parser = IngredientParser(request, False)
data = {
'original': '1 LoremipsumdolorsitametconsetetursadipscingelitrseddiamnonumyeirmodtemporinviduntutlLoremipsumdolorsitametconsetetursadipscingelitrseddiamnonumyeirmodtemporinviduntutl'
'original': '1 Porreestange(n) , ca. 200 g'
}
data['parsed'] = parser.parse(data['original'])

View File

@@ -172,6 +172,7 @@ LDAP_AUTH = bool(os.getenv('LDAP_AUTH', False))
if LDAP_AUTH:
import ldap
from django_auth_ldap.config import LDAPSearch
AUTHENTICATION_BACKENDS.append('django_auth_ldap.backend.LDAPBackend')
AUTH_LDAP_SERVER_URI = os.getenv('AUTH_LDAP_SERVER_URI')
AUTH_LDAP_BIND_DN = os.getenv('AUTH_LDAP_BIND_DN')
@@ -209,7 +210,7 @@ SITE_ID = int(os.getenv('ALLAUTH_SITE_ID', 1))
ACCOUNT_ADAPTER = 'cookbook.helper.AllAuthCustomAdapter'
if REVERSE_PROXY_AUTH:
MIDDLEWARE.append('recipes.middleware.CustomRemoteUser')
MIDDLEWARE.insert(8, 'recipes.middleware.CustomRemoteUser')
AUTHENTICATION_BACKENDS.append('django.contrib.auth.backends.RemoteUserBackend')
# Password validation

View File

@@ -43,3 +43,4 @@ python-ldap==3.4.0
django-auth-ldap==4.0.0
pytest-factoryboy==2.1.0
pyppeteer==1.0.2
validators==0.19.0

43106
vue/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -9,12 +9,15 @@
},
"dependencies": {
"@babel/eslint-parser": "^7.16.0",
"@kangc/v-md-editor": "^1.7.7",
"@kangc/v-md-editor": "^1.7.11",
"@kevinfaguiar/vue-twemoji-picker": "^5.7.4",
"@popperjs/core": "^2.11.2",
"@riophae/vue-treeselect": "^0.4.0",
"@vue/cli": "^5.0.4",
"axios": "^0.26.1",
"babel": "^6.23.0",
"babel-core": "^6.26.3",
"babel-loader": "^8.2.5",
"bootstrap-vue": "^2.21.2",
"core-js": "^3.20.3",
"html2pdf.js": "^0.10.1",
@@ -30,6 +33,7 @@
"vue-infinite-loading": "^2.4.5",
"vue-multiselect": "^2.1.6",
"vue-property-decorator": "^9.1.2",
"vue-sanitize": "^0.2.2",
"vue-simple-calendar": "^5.0.1",
"vue-template-compiler": "^2.6.14",
"vue2-touch-events": "^3.2.2",
@@ -86,4 +90,4 @@
"@vue/cli-plugin-pwa/workbox-webpack-plugin": "^5.1.3",
"coa": "2.0.2"
}
}
}

View File

@@ -43,7 +43,7 @@
<div class="row">
<div class="col col-md-12">
<label for="id_textarea">{{ $t("Information") }}</label>
<textarea id="id_textarea" ref="output_text" class="form-control" style="height: 50vh" v-html="export_info.msg" disabled></textarea>
<textarea id="id_textarea" ref="output_text" class="form-control" style="height: 50vh" v-html="$sanitize(export_info.msg)" disabled></textarea>
</div>
</div>
<br />
@@ -65,7 +65,8 @@ import LoadingSpinner from "@/components/LoadingSpinner"
import { ApiApiFactory } from "@/utils/openapi/api.ts"
Vue.use(BootstrapVue)
import VueSanitize from "vue-sanitize";
Vue.use(VueSanitize);
export default {
name: "ExportResponseView",
mixins: [ResolveUrlMixin, ToastMixin],

View File

@@ -143,7 +143,7 @@
<b-card>
<textarea id="id_textarea" ref="output_text" class="form-control"
style="height: 50vh"
v-html="import_info.msg"
v-html="$sanitize(import_info.msg)"
disabled></textarea>
</b-card>
</b-collapse>
@@ -168,7 +168,9 @@ import {ResolveUrlMixin, ToastMixin, RandomIconMixin} from "@/utils/utils";
import LoadingSpinner from "@/components/LoadingSpinner";
import {ApiApiFactory} from "@/utils/openapi/api.ts";
import VueSanitize from "vue-sanitize";
Vue.use(VueSanitize);
Vue.use(BootstrapVue)
export default {

View File

@@ -2,7 +2,7 @@
<div id="app">
<div class="row mt-2">
<div class="col col-md-6">
<div class="col col-12 col-md-6">
<b-input-group>
<generic-multiselect @change="food = $event.val; refreshList()" ref="food_multiselect"
:model="Models.FOOD"
@@ -37,7 +37,7 @@
:item1="food"
@finish-action="finishGenericAction"/>
</div>
<div class="col col-md-6">
<div class="col col-12 col-md-6">
<b-input-group>
@@ -90,81 +90,83 @@
<b-row class="mt-2">
<b-col>
<table class="table table-bordered table-sm">
<thead>
<tr>
<th>{{ $t('Amount') }}</th>
<th>{{ $t('Unit') }}</th>
<th>{{ $t('Food') }}</th>
<th>{{ $t('Note') }}</th>
<th>
<b-button variant="success" class="btn btn-sm" @click="updateIngredient()"><i
class="fas fa-save"></i>
</b-button>
</th>
</tr>
</thead>
<tr v-if="loading">
<td colspan="4">
<loading-spinner></loading-spinner>
</td>
</tr>
<template v-if="!loading">
<tbody v-for="i in ingredients" v-bind:key="i.id">
<tr v-if="i.used_in_recipes.length > 0">
<td colspan="5">
<a v-for="r in i.used_in_recipes" :href="resolveDjangoUrl('view_recipe',r.id)"
v-bind:key="r.id" target="_blank" rel="noreferrer nofollow">{{ r.name }}</a>
</td>
</tr>
<div class="table-responsive" style="overflow: unset">
<table class="table table-bordered table-sm" style="">
<thead>
<tr>
<td style="width: 5vw">
<input type="number" class="form-control" v-model="i.amount"
@input="$set(i, 'changed', true)">
</td>
<td style="width: 30vw">
<generic-multiselect @change="i.unit = $event.val; $set(i, 'changed', true)"
:initial_single_selection="i.unit"
:model="Models.UNIT"
:search_on_load="false"
:allow_create="true"
:create_placeholder="$t('Create')"
:multiple="false"></generic-multiselect>
</td>
<td style="width: 30vw">
<generic-multiselect @change="i.food = $event.val; $set(i, 'changed', true)"
:initial_single_selection="i.food"
:model="Models.FOOD"
:search_on_load="false"
:allow_create="true"
:create_placeholder="$t('Create')"
:multiple="false"></generic-multiselect>
</td>
<td style="width: 30vw">
<input class="form-control" v-model="i.note" @keydown="$set(i, 'changed', true)">
</td>
<td style="width: 5vw">
<b-button-group>
<b-button :disabled="i.changed !== true"
:variant="(i.changed !== true) ? 'primary' : 'success'"
@click="updateIngredient(i)">
<i class="fas fa-save"></i>
</b-button>
<b-button variant="danger"
@click="deleteIngredient(i)">
<i class="fas fa-trash"></i>
</b-button>
</b-button-group>
</td>
<th>{{ $t('Amount') }}</th>
<th>{{ $t('Unit') }}</th>
<th>{{ $t('Food') }}</th>
<th>{{ $t('Note') }}</th>
<th>
<b-button variant="success" class="btn btn-sm" @click="updateIngredient()"><i
class="fas fa-save"></i>
</b-button>
</th>
</tr>
</tbody>
</thead>
<tr v-if="loading">
<td colspan="4">
<loading-spinner></loading-spinner>
</td>
</tr>
<template v-if="!loading">
<tbody v-for="i in ingredients" v-bind:key="i.id">
<tr v-if="i.used_in_recipes.length > 0">
<td colspan="5">
<a v-for="r in i.used_in_recipes" :href="resolveDjangoUrl('view_recipe',r.id)"
v-bind:key="r.id" target="_blank" rel="noreferrer nofollow">{{ r.name }}</a>
</td>
</tr>
<tr>
<td style="">
<input type="number" class="form-control" v-model="i.amount"
@input="$set(i, 'changed', true)">
</td>
<td style="min-width: 30vw">
<generic-multiselect @change="i.unit = $event.val; $set(i, 'changed', true)"
:initial_single_selection="i.unit"
:model="Models.UNIT"
:search_on_load="false"
:allow_create="true"
:create_placeholder="$t('Create')"
:multiple="false"></generic-multiselect>
</td>
<td style="min-width: 30vw">
<generic-multiselect @change="i.food = $event.val; $set(i, 'changed', true)"
:initial_single_selection="i.food"
:model="Models.FOOD"
:search_on_load="false"
:allow_create="true"
:create_placeholder="$t('Create')"
:multiple="false"></generic-multiselect>
</td>
<td style="min-width: 30vw">
<input class="form-control" v-model="i.note" @keydown="$set(i, 'changed', true)">
</td>
<td style="">
<b-button-group>
<b-button :disabled="i.changed !== true"
:variant="(i.changed !== true) ? 'primary' : 'success'"
@click="updateIngredient(i)">
<i class="fas fa-save"></i>
</b-button>
<b-button variant="danger"
@click="deleteIngredient(i)">
<i class="fas fa-trash"></i>
</b-button>
</b-button-group>
</td>
</tr>
</tbody>
</template>
</template>
</table>
</table>
</div>
</b-col>

View File

@@ -1162,7 +1162,7 @@ export default {
},
ui: {
handler() {
this.$cookies.set(UI_COOKIE_NAME, this.ui)
this.$cookies.set(UI_COOKIE_NAME, this.ui, '100y')
},
deep: true,
},

View File

@@ -1,11 +1,15 @@
<template>
<span>
<b-button v-if="!item.ignore_shopping" class="btn text-decoration-none fas px-1 py-0 border-0" variant="link" v-b-popover.hover.html :title="Title" :class="IconClass" @click="toggleOnHand" />
<b-button v-if="!item.ignore_shopping" class="btn text-decoration-none fas px-1 py-0 border-0" variant="link" v-b-popover.hover.html :title="$sanitize(Title)" :class="IconClass" @click="toggleOnHand" />
</span>
</template>
<script>
import { ApiMixin } from "@/utils/utils"
import Vue from "vue"
import VueSanitize from "vue-sanitize";
Vue.use(VueSanitize);
export default {
name: "OnHandBadge",

View File

@@ -4,7 +4,7 @@
<i
class="fas"
v-b-popover.hover.html
:title="[shopping ? $t('RemoveFoodFromShopping', { food: item.name }) : $t('AddFoodToShopping', { food: item.name })]"
:title="[shopping ? $t('RemoveFoodFromShopping', { food: $sanitize(item.name) }) : $t('AddFoodToShopping', { food: $sanitize(item.name) })]"
:class="[shopping ? 'text-success fa-shopping-cart' : 'text-muted fa-cart-plus']"
/>
</b-button>
@@ -22,6 +22,9 @@
<script>
import { ApiMixin, StandardToasts } from "@/utils/utils"
import Vue from "vue"
import VueSanitize from "vue-sanitize";
Vue.use(VueSanitize);
export default {
name: "ShoppingBadge",

View File

@@ -14,7 +14,7 @@
<div class="h-20 w-100 border border-primary rounded text-center">
<i class="fas fa-eye-slash fa-2x text-primary mt-2"></i>
<br/>
<a :href="url" target="_blank" rel="noreferrer nofollow" class="mt-4">{{$t('Download')}}</a>
<a :href="url" target="_blank" rel="noreferrer nofollow" class="mt-4" download>{{$t('Download')}}</a>
</div>
</div>

View File

@@ -93,7 +93,7 @@
"
>
<i class="fas fa-expand-arrows-alt fa-fw"></i> <b>{{ $t("Move") }}</b
>: <span v-html="$t('move_confirmation', { child: source.name, parent: item.name })"></span>
>: <span v-html="$t('move_confirmation', { child: $sanitize(source.name), parent: $sanitize(item.name) })"></span>
</b-list-group-item>
<b-list-group-item
v-if="useMerge"
@@ -104,7 +104,7 @@
"
>
<i class="fas fa-compress-arrows-alt fa-fw"></i> <b>{{ $t("Merge") }}</b
>: <span v-html="$t('merge_confirmation', { source: source.name, target: item.name })"></span>
>: <span v-html="$t('merge_confirmation', { source: $sanitize(source.name), target: $sanitize(item.name) })"></span>
</b-list-group-item>
<b-list-group-item
v-if="useMerge"
@@ -115,7 +115,7 @@
"
>
<i class="fas fa-robot fa-fw"></i> <b>{{ $t("Merge") }} & {{ $t("Automate") }}</b
>: <span v-html="$t('merge_confirmation', { source: source.name, target: item.name })"></span> {{ $t("create_rule") }}
>: <span v-html="$t('merge_confirmation', { source: $sanitize(source.name), target: $sanitize(item.name) })"></span> {{ $t("create_rule") }}
<b-badge v-b-tooltip.hover :title="$t('warning_feature_beta')">BETA</b-badge>
</b-list-group-item>
<b-list-group-item action v-on:click="closeMenu()">
@@ -134,6 +134,9 @@ import RecipeCard from "@/components/RecipeCard"
import { mixin as clickaway } from "vue-clickaway"
import { createPopper } from "@popperjs/core"
import {ApiMixin} from "@/utils/utils";
import Vue from "vue"
import VueSanitize from "vue-sanitize";
Vue.use(VueSanitize);
export default {
name: "GenericHorizontalCard",

View File

@@ -59,6 +59,10 @@ import { calculateAmount, ResolveUrlMixin, ApiMixin } from "@/utils/utils"
import OnHandBadge from "@/components/Badges/OnHand"
import ShoppingBadge from "@/components/Badges/Shopping"
import Vue from "vue"
import VueSanitize from "vue-sanitize";
Vue.use(VueSanitize);
export default {
name: "IngredientComponent",
components: { OnHandBadge, ShoppingBadge },
@@ -124,7 +128,7 @@ export default {
},
methods: {
calculateAmount: function (x) {
return calculateAmount(x, this.ingredient_factor)
return this.$sanitize(calculateAmount(x, this.ingredient_factor))
},
// sends parent recipe ingredient to notify complete has been toggled
done: function () {

View File

@@ -55,6 +55,10 @@
<script>
import {calculateAmount, calculateEnergy, energyHeading} from "@/utils/utils";
import Vue from "vue"
import VueSanitize from "vue-sanitize";
Vue.use(VueSanitize);
export default {
name: 'NutritionComponent',
@@ -64,13 +68,13 @@ export default {
},
methods: {
calculateAmount: function (x) {
return calculateAmount(x, this.ingredient_factor)
return this.$sanitize(calculateAmount(x, this.ingredient_factor))
},
calculateEnergy: function (x) {
return calculateEnergy(x, this.ingredient_factor)
return this.$sanitize(calculateEnergy(x, this.ingredient_factor))
},
energy: function (x) {
return energyHeading()
return this.$sanitize(energyHeading())
}
}
}

View File

@@ -167,6 +167,7 @@
"Create_New_Keyword": "Add New Keyword",
"Create_New_Unit": "Add New Unit",
"Create_New_Meal_Type": "Add New Meal Type",
"Create_New_Shopping_Category": "Add New Shopping Category",
"and_up": "& Up",
"and_down": "& Down",
"Instructions": "Instructions",

415
vue/src/locales/es.json Normal file
View File

@@ -0,0 +1,415 @@
{
"warning_feature_beta": "Esta función está en fase BETA de pruebas. Podrían aparecer fallos y cambios importantes en un futuro(pudiendo perder la información) cuando uses esta función.",
"err_fetching_resource": "¡Ha habido un error al obtener el recurso!",
"err_creating_resource": "¡Ha habido un error al crear el recurso!",
"err_updating_resource": "¡Ha habido un error al actualizar el recurso!",
"err_deleting_resource": "¡Ha habido un error al eliminar el recurso!",
"err_deleting_protected_resource": "El objeto a eliminar sigue en uso y no puede ser eliminado.",
"err_moving_resource": "¡Ha habido un error moviendo el recurso!",
"err_merging_resource": "¡Ha habido un error uniendo el recurso!",
"success_fetching_resource": "¡Se ha obtenido con éxito un recurso!",
"success_creating_resource": "¡Se ha creado con éxito un recurso!",
"success_updating_resource": "¡Se ha actualizado con éxito un recurso!",
"success_deleting_resource": "¡Se ha eliminado con éxito un recurso!",
"success_moving_resource": "¡Se ha movido con éxito un recurso!",
"success_merging_resource": "¡Se ha unido con éxito un recurso!",
"file_upload_disabled": "La subida de archivos no está habilitada para tu espacio.",
"step_time_minutes": "Tiempo del paso en minutos",
"confirm_delete": "¿Estás seguro de eliminar este {object}?",
"import_running": "Importación realizándose, ¡Espere!",
"all_fields_optional": "Todos los campos son opcionales y pueden ser dejados en blanco.",
"convert_internal": "Convertir a receta interna",
"show_only_internal": "Mostrar solo recetas internas",
"show_split_screen": "Vista dividida",
"Log_Recipe_Cooking": "Registro de recetas",
"External_Recipe_Image": "Imagen externa de la receta",
"Add_to_Shopping": "Añadir a la cesta",
"Add_to_Plan": "",
"Step_start_time": "",
"Sort_by_new": "Ordenar por novedades",
"Table_of_Contents": "Tabla de contenido",
"Recipes_per_page": "Recetas por página",
"Show_as_header": "",
"Hide_as_header": "",
"Add_nutrition_recipe": "",
"Remove_nutrition_recipe": "",
"Copy_template_reference": "",
"Save_and_View": "",
"Manage_Books": "",
"Meal_Plan": "",
"Select_Book": "",
"Select_File": "",
"Recipe_Image": "",
"Import_finished": "",
"View_Recipes": "",
"Log_Cooking": "",
"New_Recipe": "Nueva receta",
"Url_Import": "",
"Reset_Search": "",
"Recently_Viewed": "",
"Load_More": "",
"New_Keyword": "Añadir palabra clave",
"Delete_Keyword": "Eliminar palabra clave",
"Edit_Keyword": "Editar palabra clave",
"Edit_Recipe": "Editar receta",
"Move_Keyword": "",
"Merge_Keyword": "",
"Hide_Keywords": "",
"Hide_Recipes": "",
"Move_Up": "",
"Move_Down": "",
"Step_Name": "",
"Step_Type": "",
"Make_Header": "",
"Make_Ingredient": "",
"Enable_Amount": "",
"Disable_Amount": "",
"Ingredient Editor": "",
"Add_Step": "",
"Keywords": "Palabras clave",
"Books": "",
"Proteins": "",
"Fats": "",
"Carbohydrates": "",
"Calories": "",
"Energy": "",
"Nutrition": "",
"Date": "",
"Share": "",
"Automation": "",
"Parameter": "",
"Export": "",
"Copy": "",
"Rating": "",
"Close": "",
"Cancel": "",
"Link": "",
"Add": "",
"New": "",
"Note": "",
"Success": "",
"Failure": "",
"Protected": "",
"Ingredients": "",
"Supermarket": "",
"Categories": "",
"Category": "",
"Selected": "",
"min": "",
"Servings": "",
"Waiting": "",
"Preparation": "",
"External": "",
"Size": "",
"Files": "",
"File": "",
"Edit": "",
"Image": "",
"Delete": "",
"Open": "",
"Ok": "",
"Save": "",
"Step": "",
"Search": "",
"Import": "",
"Print": "",
"Settings": "",
"or": "",
"and": "",
"Information": "",
"Download": "",
"Create": "",
"Search Settings": "",
"View": "",
"Recipes": "",
"Move": "",
"Merge": "",
"Parent": "",
"delete_confirmation": "",
"move_confirmation": "",
"merge_confirmation": "",
"create_rule": "",
"move_selection": "",
"merge_selection": "",
"Root": "",
"Ignore_Shopping": "",
"Shopping_Category": "",
"Shopping_Categories": "",
"Edit_Food": "",
"Move_Food": "",
"New_Food": "",
"Hide_Food": "",
"Food_Alias": "",
"Unit_Alias": "",
"Keyword_Alias": "",
"Delete_Food": "",
"No_ID": "",
"Meal_Plan_Days": "",
"merge_title": "",
"move_title": "",
"Food": "",
"Recipe_Book": "",
"del_confirmation_tree": "",
"delete_title": "",
"create_title": "",
"edit_title": "",
"Name": "",
"Type": "",
"Description": "",
"Recipe": "",
"tree_root": "",
"Icon": "",
"Unit": "",
"No_Results": "",
"New_Unit": "",
"Create_New_Shopping Category": "",
"Create_New_Food": "",
"Create_New_Keyword": "",
"Create_New_Unit": "",
"Create_New_Meal_Type": "",
"and_up": "",
"and_down": "",
"Instructions": "",
"Unrated": "",
"Automate": "",
"Empty": "",
"Key_Ctrl": "",
"Key_Shift": "",
"Time": "",
"Text": "",
"Shopping_list": "",
"Added_by": "",
"Added_on": "",
"AddToShopping": "",
"IngredientInShopping": "",
"NotInShopping": "",
"OnHand": "",
"FoodOnHand": "",
"FoodNotOnHand": "",
"Undefined": "",
"Create_Meal_Plan_Entry": "",
"Edit_Meal_Plan_Entry": "",
"Title": "",
"Week": "",
"Month": "",
"Year": "",
"Planner": "",
"Planner_Settings": "",
"Period": "",
"Plan_Period_To_Show": "",
"Periods": "",
"Plan_Show_How_Many_Periods": "",
"Starting_Day": "",
"Meal_Types": "",
"Meal_Type": "",
"New_Entry": "",
"Clone": "",
"Drag_Here_To_Delete": "",
"Meal_Type_Required": "",
"Title_or_Recipe_Required": "",
"Color": "",
"New_Meal_Type": "",
"AddFoodToShopping": "",
"RemoveFoodFromShopping": "",
"DeleteShoppingConfirm": "",
"IgnoredFood": "",
"Add_Servings_to_Shopping": "",
"Week_Numbers": "",
"Show_Week_Numbers": "",
"Export_As_ICal": "",
"Export_To_ICal": "",
"Cannot_Add_Notes_To_Shopping": "",
"Added_To_Shopping_List": "",
"Shopping_List_Empty": "",
"Next_Period": "",
"Previous_Period": "",
"Current_Period": "",
"Next_Day": "",
"Previous_Day": "",
"Inherit": "",
"InheritFields": "",
"FoodInherit": "",
"ShowUncategorizedFood": "",
"GroupBy": "",
"SupermarketCategoriesOnly": "",
"MoveCategory": "",
"CountMore": "",
"IgnoreThis": "",
"DelayFor": "",
"Warning": "",
"NoCategory": "",
"InheritWarning": "",
"ShowDelayed": "",
"Completed": "",
"OfflineAlert": "",
"shopping_share": "",
"shopping_auto_sync": "",
"one_url_per_line": "",
"mealplan_autoadd_shopping": "",
"mealplan_autoexclude_onhand": "",
"mealplan_autoinclude_related": "",
"default_delay": "",
"shopping_share_desc": "",
"shopping_auto_sync_desc": "",
"mealplan_autoadd_shopping_desc": "",
"mealplan_autoexclude_onhand_desc": "",
"mealplan_autoinclude_related_desc": "",
"default_delay_desc": "",
"filter_to_supermarket": "",
"Coming_Soon": "",
"Auto_Planner": "",
"New_Cookbook": "",
"Hide_Keyword": "",
"Clear": "",
"err_move_self": "",
"nothing": "",
"err_merge_self": "",
"show_sql": "",
"filter_to_supermarket_desc": "",
"CategoryName": "",
"SupermarketName": "",
"CategoryInstruction": "",
"shopping_recent_days_desc": "",
"shopping_recent_days": "",
"download_pdf": "",
"download_csv": "",
"csv_delim_help": "",
"csv_delim_label": "",
"SuccessClipboard": "",
"copy_to_clipboard": "",
"csv_prefix_help": "",
"csv_prefix_label": "",
"copy_markdown_table": "",
"in_shopping": "",
"DelayUntil": "",
"Pin": "",
"mark_complete": "",
"QuickEntry": "",
"shopping_add_onhand_desc": "",
"shopping_add_onhand": "",
"related_recipes": "",
"today_recipes": "",
"sql_debug": "",
"remember_search": "",
"remember_hours": "",
"tree_select": "",
"OnHand_help": "",
"ignore_shopping_help": "",
"shopping_category_help": "",
"food_recipe_help": "",
"Foods": "",
"enable_expert": "",
"expert_mode": "",
"simple_mode": "",
"advanced": "",
"fields": "",
"show_keywords": "",
"show_foods": "",
"show_books": "",
"show_rating": "",
"show_units": "",
"show_filters": "",
"not": "",
"save_filter": "",
"filter_name": "",
"left_handed": "",
"left_handed_help": "",
"Custom Filter": "",
"shared_with": "",
"sort_by": "",
"asc": "",
"desc": "",
"date_viewed": "",
"last_cooked": "",
"times_cooked": "",
"date_created": "",
"show_sortby": "",
"search_rank": "",
"make_now": "",
"recipe_filter": "",
"book_filter_help": "",
"review_shopping": "",
"view_recipe": "",
"copy_to_new": "",
"recipe_name": "",
"paste_ingredients_placeholder": "",
"paste_ingredients": "",
"ingredient_list": "",
"explain": "",
"filter": "",
"Website": "",
"App": "",
"Bookmarklet": "",
"click_image_import": "",
"no_more_images_found": "",
"import_duplicates": "",
"paste_json": "",
"Click_To_Edit": "",
"search_no_recipes": "",
"search_import_help_text": "",
"search_create_help_text": "",
"warning_duplicate_filter": "",
"reset_children": "",
"reset_children_help": "",
"substitute_help": "",
"substitute_siblings_help": "",
"substitute_children_help": "",
"substitute_siblings": "",
"substitute_children": "",
"SubstituteOnHand": "",
"ChildInheritFields": "",
"ChildInheritFields_help": "",
"InheritFields_help": "",
"last_viewed": "",
"created_on": "",
"updatedon": "",
"Imported_From": "",
"advanced_search_settings": "",
"nothing_planned_today": "",
"no_pinned_recipes": "",
"Planned": "",
"Pinned": "",
"Imported": "",
"Quick actions": "",
"Ratings": "",
"Internal": "",
"Units": "",
"Random Recipes": "",
"parameter_count": "",
"select_keyword": "",
"add_keyword": "",
"select_file": "",
"select_recipe": "",
"select_unit": "",
"select_food": "",
"remove_selection": "",
"empty_list": "",
"Select": "",
"Supermarkets": "",
"User": "",
"Keyword": "",
"Advanced": "",
"Page": "",
"Single": "",
"Multiple": "",
"Reset": "",
"Options": "",
"Create Food": "",
"create_food_desc": "",
"additional_options": "",
"Importer_Help": "",
"Documentation": "",
"Select_App_To_Import": "",
"Import_Supported": "",
"Export_Supported": "",
"Import_Not_Yet_Supported": "",
"Export_Not_Yet_Supported": "",
"Import_Result_Info": "",
"Recipes_In_Import": "",
"Toggle": "",
"Import_Error": "",
"Warning_Delete_Supermarket_Category": "",
"New_Supermarket": "",
"New_Supermarket_Category": "",
"Are_You_Sure": ""
}

View File

@@ -297,7 +297,7 @@
"paste_ingredients_placeholder": "Copier la liste d'ingrédients ici...",
"paste_ingredients": "Copier les ingrédients",
"ingredient_list": "Liste des ingrédients",
"search_no_recipes": "Aucune recettes trouvées !",
"search_no_recipes": "Aucune recette trouvée !",
"substitute_siblings_help": "Tous les ingrédients qui partagent un parent avec cette ingrédient sont considérés comme des substituts.",
"OnHand_help": "L'ingrédient est dans l'inventaire et ne sera pas automatiquement ajouté à la liste de courses. Le status actuel est partagé avec les utilisateurs de la liste.",
"ignore_shopping_help": "Ne jamais ajouter l'ingrédient à la liste de courses (ex: eau)",
@@ -314,10 +314,10 @@
"enable_expert": "Activer le mode expert",
"show_rating": "Afficher les notes",
"asc": "Ordre croissant",
"book_filter_help": "Inclure les recettes filtrés en plus de celles ajoutées manuellement.",
"search_import_help_text": "Importer une recette depuis un site ou application externe.",
"book_filter_help": "Inclure les recettes filtrées en plus de celles ajoutées manuellement.",
"search_import_help_text": "Importer une recette depuis un site ou une application externe.",
"search_create_help_text": "Créer une nouvelle recette directement dans Tandoor.",
"substitute_help": "Les substituts sont pris en compte lors d'une recherche de recettes pouvant être cuisinées avec les ingrédients disponibles.",
"substitute_help": "Les substituts sont pris en compte lors d'une recherche de recette pouvant être cuisinée avec les ingrédients disponibles.",
"remember_search": "Enregistrer la recherche",
"expert_mode": "Mode expert",
"simple_mode": "Mode simplifié",
@@ -329,5 +329,51 @@
"show_units": "Afficher les unités",
"show_filters": "Afficher les filtres",
"save_filter": "Sauvegarder le filtre",
"filter_name": "Filtrer par nom"
"filter_name": "Filtrer par nom",
"click_image_import": "Cliquez sur l'image que vous souhaitez importer pour cette recette",
"select_unit": "Sélectionner Unité",
"Select_App_To_Import": "Veuillez sélectionner une App pour importer depuis",
"err_deleting_protected_resource": "L'objet que vous essayez de supprimer est toujours utilisé et ne peut pas être supprimé.",
"Are_You_Sure": "Etes-vous sûr ?",
"filter": "Filtre",
"Ingredient Editor": "Éditeur d'ingrédients",
"advanced_search_settings": "Paramètres de recherche avancée",
"nothing_planned_today": "Vous n'avez rien de prévu pour aujourd'hui !",
"Pinned": "Epinglé",
"select_recipe": "Sélectionner Recette",
"Pin": "Epingler",
"remove_selection": "Désélectionner",
"New_Entry": "Nouvelle Entrée",
"search_rank": "Rechercher par note",
"Imported": "Importé",
"Quick actions": "Actions Rapides",
"Protected": "Protégé",
"one_url_per_line": "Une URL par ligne",
"select_keyword": "Sélectionner Mot Clé",
"add_keyword": "Ajouter un Mot Clé",
"select_file": "Sélectionner Fichier",
"Options": "Options",
"additional_options": "Options Supplémentaires",
"Website": "Site",
"App": "App",
"Click_To_Edit": "Cliquer pour éditer",
"reset_children": "Réinitialiser l'héritage enfant",
"created_on": "Créé le",
"updatedon": "Mis à jour le",
"Imported_From": "Importé depuis",
"no_pinned_recipes": "Vous n'avez aucune recette épinglée !",
"Ratings": "Notes",
"Internal": "Interne",
"Random Recipes": "Recettes Aléatoires",
"empty_list": "La liste est vide.",
"Advanced": "Avancé",
"Page": "Page",
"Multiple": "Multiple",
"Single": "Unique",
"Reset": "Réinitialiser",
"Importer_Help": "Plus d'information et d'aide sur cet importateur :",
"Documentation": "Documentation",
"New_Supermarket": "Créer un nouveau supermarché",
"New_Supermarket_Category": "Créer une nouvelle catégorie de supermarché",
"and_down": "& Dessous"
}

416
vue/src/locales/hu.json Normal file
View File

@@ -0,0 +1,416 @@
{
"warning_feature_beta": "",
"err_fetching_resource": "",
"err_creating_resource": "",
"err_updating_resource": "",
"err_deleting_resource": "",
"err_deleting_protected_resource": "",
"err_moving_resource": "",
"err_merging_resource": "",
"success_fetching_resource": "",
"success_creating_resource": "",
"success_updating_resource": "",
"success_deleting_resource": "",
"success_moving_resource": "",
"success_merging_resource": "",
"file_upload_disabled": "",
"step_time_minutes": "",
"confirm_delete": "",
"import_running": "",
"all_fields_optional": "",
"convert_internal": "",
"show_only_internal": "",
"show_split_screen": "",
"Log_Recipe_Cooking": "",
"External_Recipe_Image": "",
"Add_to_Shopping": "",
"Add_to_Plan": "",
"Step_start_time": "",
"Sort_by_new": "",
"Table_of_Contents": "",
"Recipes_per_page": "",
"Show_as_header": "",
"Hide_as_header": "",
"Add_nutrition_recipe": "",
"Remove_nutrition_recipe": "",
"Copy_template_reference": "",
"Save_and_View": "",
"Manage_Books": "",
"Meal_Plan": "",
"Select_Book": "",
"Select_File": "",
"Recipe_Image": "",
"Import_finished": "",
"View_Recipes": "",
"Log_Cooking": "",
"New_Recipe": "",
"Url_Import": "",
"Reset_Search": "",
"Recently_Viewed": "",
"Load_More": "",
"New_Keyword": "",
"Delete_Keyword": "",
"Edit_Keyword": "",
"Edit_Recipe": "",
"Move_Keyword": "",
"Merge_Keyword": "",
"Hide_Keywords": "",
"Hide_Recipes": "",
"Move_Up": "",
"Move_Down": "",
"Step_Name": "",
"Step_Type": "",
"Make_Header": "",
"Make_Ingredient": "",
"Enable_Amount": "",
"Disable_Amount": "",
"Ingredient Editor": "",
"Add_Step": "",
"Keywords": "",
"Books": "",
"Proteins": "",
"Fats": "",
"Carbohydrates": "",
"Calories": "",
"Energy": "",
"Nutrition": "",
"Date": "",
"Share": "",
"Automation": "",
"Parameter": "",
"Export": "",
"Copy": "",
"Rating": "",
"Close": "",
"Cancel": "",
"Link": "",
"Add": "",
"New": "",
"Note": "",
"Success": "",
"Failure": "",
"Protected": "",
"Ingredients": "",
"Supermarket": "",
"Categories": "",
"Category": "",
"Selected": "",
"min": "",
"Servings": "",
"Waiting": "",
"Preparation": "",
"External": "",
"Size": "",
"Files": "",
"File": "",
"Edit": "",
"Image": "",
"Delete": "",
"Open": "",
"Ok": "",
"Save": "",
"Step": "",
"Search": "",
"Import": "",
"Print": "",
"Settings": "",
"or": "",
"and": "",
"Information": "",
"Download": "",
"Create": "",
"Search Settings": "",
"View": "",
"Recipes": "",
"Move": "",
"Merge": "",
"Parent": "",
"delete_confirmation": "",
"move_confirmation": "",
"merge_confirmation": "",
"create_rule": "",
"move_selection": "",
"merge_selection": "",
"Root": "",
"Ignore_Shopping": "",
"Shopping_Category": "",
"Shopping_Categories": "",
"Edit_Food": "",
"Move_Food": "",
"New_Food": "",
"Hide_Food": "",
"Food_Alias": "",
"Unit_Alias": "",
"Keyword_Alias": "",
"Delete_Food": "",
"No_ID": "",
"Meal_Plan_Days": "",
"merge_title": "",
"move_title": "",
"Food": "",
"Recipe_Book": "",
"del_confirmation_tree": "",
"delete_title": "",
"create_title": "",
"edit_title": "",
"Name": "",
"Type": "",
"Description": "",
"Recipe": "",
"tree_root": "",
"Icon": "",
"Unit": "",
"No_Results": "",
"New_Unit": "",
"Create_New_Shopping Category": "",
"Create_New_Food": "",
"Create_New_Keyword": "",
"Create_New_Unit": "",
"Create_New_Meal_Type": "",
"Create_New_Shopping_Category": "",
"and_up": "",
"and_down": "",
"Instructions": "",
"Unrated": "",
"Automate": "",
"Empty": "",
"Key_Ctrl": "",
"Key_Shift": "",
"Time": "",
"Text": "",
"Shopping_list": "",
"Added_by": "",
"Added_on": "",
"AddToShopping": "",
"IngredientInShopping": "",
"NotInShopping": "",
"OnHand": "",
"FoodOnHand": "",
"FoodNotOnHand": "",
"Undefined": "",
"Create_Meal_Plan_Entry": "",
"Edit_Meal_Plan_Entry": "",
"Title": "",
"Week": "",
"Month": "",
"Year": "",
"Planner": "",
"Planner_Settings": "",
"Period": "",
"Plan_Period_To_Show": "",
"Periods": "",
"Plan_Show_How_Many_Periods": "",
"Starting_Day": "",
"Meal_Types": "",
"Meal_Type": "",
"New_Entry": "",
"Clone": "",
"Drag_Here_To_Delete": "",
"Meal_Type_Required": "",
"Title_or_Recipe_Required": "",
"Color": "",
"New_Meal_Type": "",
"AddFoodToShopping": "",
"RemoveFoodFromShopping": "",
"DeleteShoppingConfirm": "",
"IgnoredFood": "",
"Add_Servings_to_Shopping": "",
"Week_Numbers": "",
"Show_Week_Numbers": "",
"Export_As_ICal": "",
"Export_To_ICal": "",
"Cannot_Add_Notes_To_Shopping": "",
"Added_To_Shopping_List": "",
"Shopping_List_Empty": "",
"Next_Period": "",
"Previous_Period": "",
"Current_Period": "",
"Next_Day": "",
"Previous_Day": "",
"Inherit": "",
"InheritFields": "",
"FoodInherit": "",
"ShowUncategorizedFood": "",
"GroupBy": "",
"SupermarketCategoriesOnly": "",
"MoveCategory": "",
"CountMore": "",
"IgnoreThis": "",
"DelayFor": "",
"Warning": "",
"NoCategory": "",
"InheritWarning": "",
"ShowDelayed": "",
"Completed": "",
"OfflineAlert": "",
"shopping_share": "",
"shopping_auto_sync": "",
"one_url_per_line": "",
"mealplan_autoadd_shopping": "",
"mealplan_autoexclude_onhand": "",
"mealplan_autoinclude_related": "",
"default_delay": "",
"shopping_share_desc": "",
"shopping_auto_sync_desc": "",
"mealplan_autoadd_shopping_desc": "",
"mealplan_autoexclude_onhand_desc": "",
"mealplan_autoinclude_related_desc": "",
"default_delay_desc": "",
"filter_to_supermarket": "",
"Coming_Soon": "",
"Auto_Planner": "",
"New_Cookbook": "",
"Hide_Keyword": "",
"Clear": "",
"err_move_self": "",
"nothing": "",
"err_merge_self": "",
"show_sql": "",
"filter_to_supermarket_desc": "",
"CategoryName": "",
"SupermarketName": "",
"CategoryInstruction": "",
"shopping_recent_days_desc": "",
"shopping_recent_days": "",
"download_pdf": "",
"download_csv": "",
"csv_delim_help": "",
"csv_delim_label": "",
"SuccessClipboard": "",
"copy_to_clipboard": "",
"csv_prefix_help": "",
"csv_prefix_label": "",
"copy_markdown_table": "",
"in_shopping": "",
"DelayUntil": "",
"Pin": "",
"mark_complete": "",
"QuickEntry": "",
"shopping_add_onhand_desc": "",
"shopping_add_onhand": "",
"related_recipes": "",
"today_recipes": "",
"sql_debug": "",
"remember_search": "",
"remember_hours": "",
"tree_select": "",
"OnHand_help": "",
"ignore_shopping_help": "",
"shopping_category_help": "",
"food_recipe_help": "",
"Foods": "",
"enable_expert": "",
"expert_mode": "",
"simple_mode": "",
"advanced": "",
"fields": "",
"show_keywords": "",
"show_foods": "",
"show_books": "",
"show_rating": "",
"show_units": "",
"show_filters": "",
"not": "",
"save_filter": "",
"filter_name": "",
"left_handed": "",
"left_handed_help": "",
"Custom Filter": "",
"shared_with": "",
"sort_by": "",
"asc": "",
"desc": "",
"date_viewed": "",
"last_cooked": "",
"times_cooked": "",
"date_created": "",
"show_sortby": "",
"search_rank": "",
"make_now": "",
"recipe_filter": "",
"book_filter_help": "",
"review_shopping": "",
"view_recipe": "",
"copy_to_new": "",
"recipe_name": "",
"paste_ingredients_placeholder": "",
"paste_ingredients": "",
"ingredient_list": "",
"explain": "",
"filter": "",
"Website": "",
"App": "",
"Bookmarklet": "",
"click_image_import": "",
"no_more_images_found": "",
"import_duplicates": "",
"paste_json": "",
"Click_To_Edit": "",
"search_no_recipes": "",
"search_import_help_text": "",
"search_create_help_text": "",
"warning_duplicate_filter": "",
"reset_children": "",
"reset_children_help": "",
"substitute_help": "",
"substitute_siblings_help": "",
"substitute_children_help": "",
"substitute_siblings": "",
"substitute_children": "",
"SubstituteOnHand": "",
"ChildInheritFields": "",
"ChildInheritFields_help": "",
"InheritFields_help": "",
"last_viewed": "",
"created_on": "",
"updatedon": "",
"Imported_From": "",
"advanced_search_settings": "",
"nothing_planned_today": "",
"no_pinned_recipes": "",
"Planned": "",
"Pinned": "",
"Imported": "",
"Quick actions": "",
"Ratings": "",
"Internal": "",
"Units": "",
"Random Recipes": "",
"parameter_count": "",
"select_keyword": "",
"add_keyword": "",
"select_file": "",
"select_recipe": "",
"select_unit": "",
"select_food": "",
"remove_selection": "",
"empty_list": "",
"Select": "",
"Supermarkets": "",
"User": "",
"Keyword": "",
"Advanced": "",
"Page": "",
"Single": "",
"Multiple": "",
"Reset": "",
"Options": "",
"Create Food": "",
"create_food_desc": "",
"additional_options": "",
"Importer_Help": "",
"Documentation": "",
"Select_App_To_Import": "",
"Import_Supported": "",
"Export_Supported": "",
"Import_Not_Yet_Supported": "",
"Export_Not_Yet_Supported": "",
"Import_Result_Info": "",
"Recipes_In_Import": "",
"Toggle": "",
"Import_Error": "",
"Warning_Delete_Supermarket_Category": "",
"New_Supermarket": "",
"New_Supermarket_Category": "",
"Are_You_Sure": ""
}

View File

@@ -412,5 +412,7 @@
"New_Supermarket_Category": "Utwórz nową kategorię supermarketów",
"Are_You_Sure": "Jesteś pewny?",
"Importer_Help": "Więcej informacji i pomoc na temat tego importera:",
"Select_App_To_Import": "Wybierz aplikację, z której chcesz zaimportować"
"Select_App_To_Import": "Wybierz aplikację, z której chcesz zaimportować",
"New_Entry": "Nowy wpis",
"Create_New_Shopping_Category": "Dodaj nową kategorię zakupów"
}

File diff suppressed because it is too large Load Diff