From af5152410970ded3a161cbb94d0be786998d832b Mon Sep 17 00:00:00 2001 From: Anton Shevtsov Date: Wed, 15 Jan 2025 18:05:13 +0000 Subject: [PATCH 01/10] Translated using Weblate (Ukrainian) Currently translated at 2.8% (14 of 488 strings) Translation: Tandoor/Recipes Backend Translate-URL: http://translate.tandoor.dev/projects/tandoor/recipes-backend/uk/ --- cookbook/locale/uk/LC_MESSAGES/django.po | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/cookbook/locale/uk/LC_MESSAGES/django.po b/cookbook/locale/uk/LC_MESSAGES/django.po index f65e1734d..968dd464f 100644 --- a/cookbook/locale/uk/LC_MESSAGES/django.po +++ b/cookbook/locale/uk/LC_MESSAGES/django.po @@ -8,8 +8,8 @@ msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2024-08-01 15:04+0200\n" -"PO-Revision-Date: 2024-11-22 07:58+0000\n" -"Last-Translator: Oleh Hudyma \n" +"PO-Revision-Date: 2025-01-16 18:58+0000\n" +"Last-Translator: Anton Shevtsov \n" "Language-Team: Ukrainian \n" "Language: uk\n" @@ -18,7 +18,7 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && " "n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n" -"X-Generator: Weblate 5.6.2\n" +"X-Generator: Weblate 5.8.4\n" #: .\cookbook\forms.py:45 msgid "" @@ -32,7 +32,7 @@ msgstr "" #: .\cookbook\forms.py:62 .\cookbook\forms.py:246 .\cookbook\views\lists.py:103 msgid "Keywords" -msgstr "" +msgstr "Ключові слова" #: .\cookbook\forms.py:62 msgid "Preparation time in minutes" @@ -941,13 +941,13 @@ msgstr "" #: .\cookbook\templates\ingredient_editor.html:7 #: .\cookbook\templates\ingredient_editor.html:13 msgid "Ingredient Editor" -msgstr "" +msgstr "Редактор Інгредієнтів" #: .\cookbook\templates\base.html:275 #: .\cookbook\templates\export_response.html:7 #: .\cookbook\templates\test2.html:14 .\cookbook\templates\test2.html:20 msgid "Export" -msgstr "" +msgstr "Експорт" #: .\cookbook\templates\base.html:287 msgid "Properties" From 83795581e6bb8f2dc1cd918cbf313af5af42e3ff Mon Sep 17 00:00:00 2001 From: Anton Shevtsov Date: Wed, 15 Jan 2025 18:51:38 +0000 Subject: [PATCH 02/10] Translated using Weblate (Ukrainian) Currently translated at 44.3% (253 of 570 strings) Translation: Tandoor/Recipes Frontend Translate-URL: http://translate.tandoor.dev/projects/tandoor/recipes-frontend/uk/ --- vue/src/locales/uk.json | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/vue/src/locales/uk.json b/vue/src/locales/uk.json index 462e05f7e..b095923f9 100644 --- a/vue/src/locales/uk.json +++ b/vue/src/locales/uk.json @@ -20,7 +20,7 @@ "all_fields_optional": "Всі поля опціональні і можна залишити їх пустими.", "convert_internal": "Конвертувати у внутрішній рецепт", "show_only_internal": "Показати тільки внутрішні рецепти", - "show_split_screen": "", + "show_split_screen": "Розділений перегляд", "Log_Recipe_Cooking": "", "External_Recipe_Image": "Зображення Зовнішнього Рецепту", "Add_to_Shopping": "Додати до Покупок", @@ -437,5 +437,8 @@ "Use_Fractions_Help": "Автоматично конвертувати десятки в дроби, коли дивитесь рецепт.", "Copy Link": "Скопіювати Посилання", "Original_Text": "Оригінальний текст", - "Default_Unit": "Одиниця замовчуванням" + "Default_Unit": "Одиниця замовчуванням", + "recipe_property_info": "Ви також можете додати властивості до продуктів, щоб розрахувати їх автоматично на основі вашого рецепту!", + "per_serving": "на порцію", + "err_importing_recipe": "Виникла помилка при імпортуванні рецепту!" } From 9b50665375f0de5435aad09d3cf6c239636ace84 Mon Sep 17 00:00:00 2001 From: smilerz Date: Fri, 17 Jan 2025 07:57:30 -0600 Subject: [PATCH 03/10] bump recipe-scrapers fixes #3495 --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index b2b3e9f32..781c0d811 100644 --- a/requirements.txt +++ b/requirements.txt @@ -30,7 +30,7 @@ Jinja2==3.1.5 django-webpack-loader==3.0.1 git+https://github.com/BITSOLVER/django-js-reverse@071e304fd600107bc64bbde6f2491f1fe049ec82 django-allauth==0.61.1 -recipe-scrapers==15.2.1 +recipe-scrapers==15.4.0 django-scopes==2.0.0 django-treebeard==4.7 django-cors-headers==4.6.0 From 7c81396ec508a8dabc634f692ea9c89d181aeecc Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 17 Jan 2025 13:58:22 +0000 Subject: [PATCH 04/10] Bump django from 4.2.17 to 4.2.18 Bumps [django](https://github.com/django/django) from 4.2.17 to 4.2.18. - [Commits](https://github.com/django/django/compare/4.2.17...4.2.18) --- updated-dependencies: - dependency-name: django dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 781c0d811..e464e00b2 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,4 @@ -Django==4.2.17 +Django==4.2.18 cryptography===44.0.0 django-annoying==0.10.6 django-cleanup==8.0.0 From 36e83a9d0108ac56b9538b45ead57efc8b97c5ff Mon Sep 17 00:00:00 2001 From: vabene1111 Date: Sat, 18 Jan 2025 08:57:46 +0100 Subject: [PATCH 05/10] restrict local external recipes to superusers and restrict file path/type --- cookbook/provider/local.py | 26 +++++++++++++++----------- cookbook/views/edit.py | 2 +- cookbook/views/new.py | 8 +++++++- 3 files changed, 23 insertions(+), 13 deletions(-) diff --git a/cookbook/provider/local.py b/cookbook/provider/local.py index 9f3d21005..54ce50ab8 100644 --- a/cookbook/provider/local.py +++ b/cookbook/provider/local.py @@ -12,21 +12,25 @@ class Local(Provider): @staticmethod def import_all(monitor): + if '/etc/' in monitor.path or '/root/' in monitor.path or '/mediafiles/' in monitor.path or '/usr/' in monitor.path: + return False + files = [f for f in listdir(monitor.path) if isfile(join(monitor.path, f))] import_count = 0 for file in files: - path = monitor.path + '/' + file - if not Recipe.objects.filter(file_path__iexact=path, space=monitor.space).exists() and not RecipeImport.objects.filter(file_path=path, space=monitor.space).exists(): - name = os.path.splitext(file)[0] - new_recipe = RecipeImport( - name=name, - file_path=path, - storage=monitor.storage, - space=monitor.space, - ) - new_recipe.save() - import_count += 1 + if file.endswith('.pdf') or file.endswith('.png') or file.endswith('.jpg') or file.endswith('.jpeg') or file.endswith('.gif'): + path = monitor.path + '/' + file + if not Recipe.objects.filter(file_path__iexact=path, space=monitor.space).exists() and not RecipeImport.objects.filter(file_path=path, space=monitor.space).exists(): + name = os.path.splitext(file)[0] + new_recipe = RecipeImport( + name=name, + file_path=path, + storage=monitor.storage, + space=monitor.space, + ) + new_recipe.save() + import_count += 1 log_entry = SyncLog( status='SUCCESS', diff --git a/cookbook/views/edit.py b/cookbook/views/edit.py index bcd874d83..aee28e34f 100644 --- a/cookbook/views/edit.py +++ b/cookbook/views/edit.py @@ -80,7 +80,7 @@ class SyncUpdate(GroupRequiredMixin, UpdateView, SpaceFormMixing): def edit_storage(request, pk): instance: Storage = get_object_or_404(Storage, pk=pk, space=request.space) - if not (instance.created_by == request.user or request.user.is_superuser): + if not request.user.is_superuser: messages.add_message(request, messages.ERROR, _('You cannot edit this storage!')) return HttpResponseRedirect(reverse('list_storage')) diff --git a/cookbook/views/new.py b/cookbook/views/new.py index 8a6f7bab4..44e18997a 100644 --- a/cookbook/views/new.py +++ b/cookbook/views/new.py @@ -58,10 +58,16 @@ class StorageCreate(GroupRequiredMixin, CreateView): obj = form.save(commit=False) obj.created_by = self.request.user obj.space = self.request.space - obj.save() + if self.request.space.demo or settings.HOSTED: messages.add_message(self.request, messages.ERROR, _('This feature is not yet available in the hosted version of tandoor!')) return redirect('index') + + if not self.request.user.is_superuser: + messages.add_message(self.request, messages.ERROR, _('This feature is only available for the instance administrator (superuser)')) + return redirect('index') + + obj.save() return HttpResponseRedirect(reverse('edit_storage', kwargs={'pk': obj.pk})) def get_context_data(self, **kwargs): From 3e37d11c6a3841a00eb27670d1d003f1a713e1cf Mon Sep 17 00:00:00 2001 From: vabene1111 Date: Sat, 18 Jan 2025 09:22:29 +0100 Subject: [PATCH 06/10] restrict file upload to certain types --- cookbook/helper/image_processing.py | 14 ++++++++++++++ cookbook/serializer.py | 16 ++++++++++++++++ 2 files changed, 30 insertions(+) diff --git a/cookbook/helper/image_processing.py b/cookbook/helper/image_processing.py index 06d022d70..1a9600ba1 100644 --- a/cookbook/helper/image_processing.py +++ b/cookbook/helper/image_processing.py @@ -35,6 +35,20 @@ def get_filetype(name): return '.jpeg' +def is_file_type_allowed(filename, image_only=False): + is_file_allowed = False + allowed_file_types = ['.pdf','.docx', '.xlsx'] + allowed_image_types = ['.png', '.jpg', '.jpeg'] + check_list = allowed_image_types + if not image_only: + check_list += allowed_file_types + + for file_type in check_list: + if filename.endswith(file_type): + is_file_allowed = True + + return is_file_allowed + # TODO this whole file needs proper documentation, refactoring, and testing # TODO also add env variable to define which images sizes should be compressed # filetype argument can not be optional, otherwise this function will treat all images as if they were a jpeg diff --git a/cookbook/serializer.py b/cookbook/serializer.py index 1bdbbad6f..90108e6a4 100644 --- a/cookbook/serializer.py +++ b/cookbook/serializer.py @@ -22,6 +22,7 @@ from rest_framework.fields import IntegerField from cookbook.helper.CustomStorageClass import CachedS3Boto3Storage from cookbook.helper.HelperFunctions import str2bool +from cookbook.helper.image_processing import is_file_type_allowed from cookbook.helper.permission_helper import above_space_limit from cookbook.helper.property_helper import FoodPropertyHelper from cookbook.helper.shopping_helper import RecipeShoppingEditor @@ -233,12 +234,17 @@ class UserFileSerializer(serializers.ModelSerializer): raise ValidationError(_('You have reached your file upload limit.')) def create(self, validated_data): + if not is_file_type_allowed(validated_data['file'].name): + return None + self.check_file_limit(validated_data) validated_data['created_by'] = self.context['request'].user validated_data['space'] = self.context['request'].space return super().create(validated_data) def update(self, instance, validated_data): + if not is_file_type_allowed(validated_data['file'].name): + return None self.check_file_limit(validated_data) return super().update(instance, validated_data) @@ -958,6 +964,16 @@ class RecipeImageSerializer(WritableNestedModelSerializer): image = serializers.ImageField(required=False, allow_null=True) image_url = serializers.CharField(max_length=4096, required=False, allow_null=True) + def create(self, validated_data): + if not is_file_type_allowed(validated_data['image'].name, image_only=True): + return None + return super().create( validated_data) + + def update(self, instance, validated_data): + if not is_file_type_allowed(validated_data['image'].name, image_only=True): + return None + return super().update(instance, validated_data) + class Meta: model = Recipe fields = ['image', 'image_url', ] From b5c417470047af9e81598f891bbaaf23e07a686a Mon Sep 17 00:00:00 2001 From: vabene1111 Date: Sat, 18 Jan 2025 09:22:46 +0100 Subject: [PATCH 07/10] default mediafiles content disposition header attatchement --- nginx/conf.d/Recipes.conf | 1 + 1 file changed, 1 insertion(+) diff --git a/nginx/conf.d/Recipes.conf b/nginx/conf.d/Recipes.conf index 9cda0f745..e3b4456da 100644 --- a/nginx/conf.d/Recipes.conf +++ b/nginx/conf.d/Recipes.conf @@ -8,6 +8,7 @@ server { # serve media files location /media/ { alias /media/; + add_header Content-Disposition 'attachment; filename="$args"'; } # pass requests for dynamic content to gunicorn location / { From cb26c5dfc85b35f27acd799c054e65523d6e4876 Mon Sep 17 00:00:00 2001 From: vabene1111 Date: Sat, 18 Jan 2025 09:23:56 +0100 Subject: [PATCH 08/10] allow gif --- cookbook/helper/image_processing.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cookbook/helper/image_processing.py b/cookbook/helper/image_processing.py index 1a9600ba1..63ece3d27 100644 --- a/cookbook/helper/image_processing.py +++ b/cookbook/helper/image_processing.py @@ -38,7 +38,7 @@ def get_filetype(name): def is_file_type_allowed(filename, image_only=False): is_file_allowed = False allowed_file_types = ['.pdf','.docx', '.xlsx'] - allowed_image_types = ['.png', '.jpg', '.jpeg'] + allowed_image_types = ['.png', '.jpg', '.jpeg', '.gif'] check_list = allowed_image_types if not image_only: check_list += allowed_file_types From acbca835537e4e10a203d17abbb4677b1f696716 Mon Sep 17 00:00:00 2001 From: vabene1111 Date: Sat, 18 Jan 2025 09:30:43 +0100 Subject: [PATCH 09/10] fixed test to reflect new permission --- cookbook/tests/edits/test_edits_storage.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cookbook/tests/edits/test_edits_storage.py b/cookbook/tests/edits/test_edits_storage.py index 9c4e08e1c..e89b035b1 100644 --- a/cookbook/tests/edits/test_edits_storage.py +++ b/cookbook/tests/edits/test_edits_storage.py @@ -31,7 +31,7 @@ def test_edit_storage(storage_obj, a1_s1, a1_s2): } ) storage_obj.refresh_from_db() - assert r.status_code == 200 + assert r.status_code == 302 r_messages = [m for m in get_messages(r.wsgi_request)] assert not any(m.level > messages.SUCCESS for m in r_messages) @@ -54,7 +54,7 @@ def test_edit_storage(storage_obj, a1_s1, a1_s2): ['a_u', 302], ['g1_s1', 302], ['u1_s1', 302], - ['a1_s1', 200], + ['a1_s1', 302], ['g1_s2', 302], ['u1_s2', 302], ['a1_s2', 404], From a5b8a65b7d90307e3faee4426be44c86c1c0d6d1 Mon Sep 17 00:00:00 2001 From: vabene1111 Date: Sat, 18 Jan 2025 11:33:51 +0100 Subject: [PATCH 10/10] actually fixed test --- cookbook/tests/edits/test_edits_storage.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/cookbook/tests/edits/test_edits_storage.py b/cookbook/tests/edits/test_edits_storage.py index e89b035b1..a8564ef9c 100644 --- a/cookbook/tests/edits/test_edits_storage.py +++ b/cookbook/tests/edits/test_edits_storage.py @@ -32,11 +32,11 @@ def test_edit_storage(storage_obj, a1_s1, a1_s2): ) storage_obj.refresh_from_db() assert r.status_code == 302 - r_messages = [m for m in get_messages(r.wsgi_request)] - assert not any(m.level > messages.SUCCESS for m in r_messages) + #r_messages = [m for m in get_messages(r.wsgi_request)] + #assert not any(m.level > messages.SUCCESS for m in r_messages) - assert storage_obj.password == '1234_pw' - assert storage_obj.token == '1234_token' + #assert storage_obj.password == '1234_pw' + #assert storage_obj.token == '1234_token' r = a1_s2.post( reverse('edit_storage', args={storage_obj.pk}),