diff --git a/.env.template b/.env.template
index 6fba0dd78..36c2357d5 100644
--- a/.env.template
+++ b/.env.template
@@ -41,10 +41,17 @@ SHOPPING_MIN_AUTOSYNC_INTERVAL=5
# Default for user setting sticky navbar
# STICKY_NAV_PREF_DEFAULT=1
-# If staticfiles are stored at a different location uncomment and change accordingly
+# If base URL is something other than just / (you are serving a subfolder in your proxy for instance http://recipe_app/recipes/)
+# SCRIPT_NAME=/recipes
+
+# If staticfiles are stored at a different location uncomment and change accordingly, MUST END IN /
+# this is not required if you are just using a subfolder
+# This can either be a relative path from the applications base path or the url of an external host
# STATIC_URL=/static/
-# If mediafiles are stored at a different location uncomment and change accordingly
+# If mediafiles are stored at a different location uncomment and change accordingly, MUST END IN /
+# this is not required if you are just using a subfolder
+# This can either be a relative path from the applications base path or the url of an external host
# MEDIA_URL=/media/
# Serve mediafiles directly using gunicorn. Basically everyone recommends not doing this. Please use any of the examples
@@ -79,8 +86,6 @@ GUNICORN_MEDIA=0
# when unset: 0 (false)
REVERSE_PROXY_AUTH=0
-# If base URL is something other than just / (you are serving a subfolder in your proxy for instance http://recipe_app/recipes/)
-# SCRIPT_NAME=/recipes
# Default settings for spaces, apply per space and can be changed in the admin view
# SPACE_DEFAULT_MAX_RECIPES=0 # 0=unlimited recipes
# SPACE_DEFAULT_MAX_USERS=0 # 0=unlimited users per space
diff --git a/.github/workflows/docker-publish-release.yml b/.github/workflows/docker-publish-release.yml
index 0d0b60b1b..0f52da91d 100644
--- a/.github/workflows/docker-publish-release.yml
+++ b/.github/workflows/docker-publish-release.yml
@@ -50,4 +50,4 @@ jobs:
DISCORD_WEBHOOK: ${{ secrets.DISCORD_RELEASE_WEBHOOK }}
uses: Ilshidur/action-discord@0.3.2
with:
- args: '🚀 A new Version of tandoor has been released 🥳 \n https://github.com/vabene1111/recipes/releases/tag/{{ steps.get_version.outputs.VERSION }}'
\ No newline at end of file
+ args: '🚀 A new Version of tandoor has been released 🥳 \n https://github.com/vabene1111/recipes/releases/tag/{{GITHUB_REF/refs\/tags\//}}'
\ No newline at end of file
diff --git a/Dockerfile b/Dockerfile
index f060aa76e..b69c037ac 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,7 +1,7 @@
FROM python:3.9-alpine3.12
#Install all dependencies.
-RUN apk add --no-cache postgresql-libs gettext zlib libjpeg libxml2-dev libxslt-dev py-cryptography
+RUN apk add --no-cache postgresql-libs gettext zlib libjpeg libwebp libxml2-dev libxslt-dev py-cryptography
#Print all logs without buffering it.
ENV PYTHONUNBUFFERED 1
@@ -15,7 +15,7 @@ WORKDIR /opt/recipes
COPY requirements.txt ./
-RUN apk add --no-cache --virtual .build-deps gcc musl-dev postgresql-dev zlib-dev jpeg-dev libressl-dev libffi-dev cargo openssl-dev openldap-dev && \
+RUN apk add --no-cache --virtual .build-deps gcc musl-dev postgresql-dev zlib-dev jpeg-dev libwebp-dev libressl-dev libffi-dev cargo openssl-dev openldap-dev && \
python -m venv venv && \
/opt/recipes/venv/bin/python -m pip install --upgrade pip && \
venv/bin/pip install wheel==0.36.2 && \
@@ -25,4 +25,4 @@ RUN apk add --no-cache --virtual .build-deps gcc musl-dev postgresql-dev zlib-de
#Copy project and execute it.
COPY . ./
RUN chmod +x boot.sh
-ENTRYPOINT ["/opt/recipes/boot.sh"]
\ No newline at end of file
+ENTRYPOINT ["/opt/recipes/boot.sh"]
diff --git a/README.md b/README.md
index c7d2dfab8..cd6442de9 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,6 @@

-# Your Feedback
+## Core Features
+- 🥗 **Manage your recipes** - Manage your ever growing recipe collection
+- 📆 **Plan** - multiple meals for each day
+- 🛒 **Shopping lists** - via the meal plan or straight from recipes
+- 📚 **Cookbooks** - collect recipes into books
+- 👪 **Share and collaborate** on recipes with friends and family
-Share some information on how you use Tandoor to help me improve the application [Google Survey](https://forms.gle/qNfLK2tWTeWHe9Qd7)
+## Made by and for power users
-## Features
-
-- 📦 **Sync** files with Dropbox and Nextcloud (more can easily be added)
-- 🔍 Powerful **search** with Djangos [TrigramSimilarity](https://docs.djangoproject.com/en/3.0/ref/contrib/postgres/search/#trigram-similarity)
+- 🔍 Powerful & customizable **search** with fulltext support and [TrigramSimilarity](https://docs.djangoproject.com/en/3.0/ref/contrib/postgres/search/#trigram-similarity)
- 🏷️ Create and search for **tags**, assign them in batch to all files matching certain filters
-- 📄 **Create recipes** locally within a nice, standardized web interface
-- ⬇️ **Import recipes** from thousands of websites supporting [ld+json or microdata](https://schema.org/Recipe)
-- 📱 Optimized for use on **mobile** devices like phones and tablets
-- 🛒 Generate **shopping** lists from recipes
-- 📆 Create a **Plan** on what to eat when
-- 👪 **Share** recipes with friends and comment on them to suggest or remember changes you made
-- ➗ automatically convert decimal units to **fractions** for those who like this
-- 🐳 Easy setup with **Docker** and included examples for Kubernetes, Unraid and Synology
+- ↔️ Quickly merge and rename ingredients, tags and units
+- 📥️ **Import recipes** from thousands of websites supporting [ld+json or microdata](https://schema.org/Recipe)
+- ➗ Support for **fractions** or decimals
+- 🐳 Easy setup with **Docker** and included examples for **Kubernetes**, **Unraid** and **Synology**
- 🎨 Customize your interface with **themes**
-- ✉️ Export and import recipes from other users
+- 📦 **Sync** files with Dropbox and Nextcloud
+
+## All the must haves
+
+- 📱Optimized for use on **mobile** devices
- 🌍 localized in many languages thanks to the awesome community
-- ➕ Many more like recipe scaling, image compression, cookbooks, printing views, ...
+- 📥️ **Import your collection** from many other [recipe managers](https://docs.tandoor.dev/features/import_export/)
+- ➕ Many more like recipe scaling, image compression, printing views and supermarkets
This application is meant for people with a collection of recipes they want to share with family and friends or simply
store them in a nicely organized way. A basic permission system exists but this application is not meant to be run as
a public page.
+
+## Docs
+
Documentation can be found [here](https://docs.tandoor.dev/).
-While this application has been around for a while and is actively used by many (including myself), it is still considered
-**beta** software that has a lot of rough edges and unpolished parts.
+## Contributing
+
+You can help out with the ongoing development by looking for potential bugs in our code base, or by contributing new features. We are always welcoming new pull requests containing bug fixes, refactors and new features. We have a list of tasks and bugs on our issue tracker on Github. Please comment on issues if you want to contribute with, to avoid duplicating effort.
+
+## Your Feedback
+
+Share some information on how you use Tandoor to help me improve the application [Google Survey](https://forms.gle/qNfLK2tWTeWHe9Qd7)
+
+## Get in touch
+
+
You can follow our Twitter account to get updates on new features or releases
+
+
+
## License
-Beginning with version 0.10.0 the code in this repository is licensed under the [GNU AGPL v3](https://www.gnu.org/licenses/agpl-3.0.de.html) license with an
+Beginning with version 0.10.0 the code in this repository is licensed under the [GNU AGPL v3](https://www.gnu.org/licenses/agpl-3.0.de.html) license with a
[common clause](https://commonsclause.com/) selling exception. See [LICENSE.md](https://github.com/vabene1111/recipes/blob/develop/LICENSE.md) for details.
> NOTE: There appears to be a whole range of legal issues with licensing anything else then the standard completely open licenses.
@@ -68,8 +96,8 @@ Beginning with version 0.10.0 the code in this repository is licensed under the
**This software and *all* its features are and will always be free for everyone to use and enjoy.**
The reason for the selling exception is that a significant amount of time was spend over multiple years to develop this software.
-A payed hosted version which will be identical in features and code base to the software offered in this repository will
+A paid hosted version which will be identical in features and code base to the software offered in this repository will
likely be released in the future (including all features needed to sell a hosted version as they might also be useful for personal use).
-This will not only benefit me personally but also everyone who self-hosts this software as any profits made trough selling the hosted option
+This will not only benefit me personally but also everyone who self-hosts this software as any profits made through selling the hosted option
allow me to spend more time developing and improving the software for everyone. Selling exceptions are [approved by Richard Stallman](http://www.gnu.org/philosophy/selling-exceptions.en.html) and the
common clause license is very permissive (see the [FAQ](https://commonsclause.com/)).
diff --git a/cookbook/locale/de/LC_MESSAGES/django.mo b/cookbook/locale/de/LC_MESSAGES/django.mo
index e0bb29365..8dee8bac0 100644
Binary files a/cookbook/locale/de/LC_MESSAGES/django.mo and b/cookbook/locale/de/LC_MESSAGES/django.mo differ
diff --git a/cookbook/locale/fr/LC_MESSAGES/django.mo b/cookbook/locale/fr/LC_MESSAGES/django.mo
index 7456bf6c5..963e5c42e 100644
Binary files a/cookbook/locale/fr/LC_MESSAGES/django.mo and b/cookbook/locale/fr/LC_MESSAGES/django.mo differ
diff --git a/cookbook/locale/fr/LC_MESSAGES/django.po b/cookbook/locale/fr/LC_MESSAGES/django.po
index 1d9725850..7493a86e9 100644
--- a/cookbook/locale/fr/LC_MESSAGES/django.po
+++ b/cookbook/locale/fr/LC_MESSAGES/django.po
@@ -14,10 +14,10 @@ msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-09-13 22:40+0200\n"
-"PO-Revision-Date: 2021-09-07 16:06+0000\n"
-"Last-Translator: Afaren \n"
-"Language-Team: French \n"
+"PO-Revision-Date: 2021-10-26 10:06+0000\n"
+"Last-Translator: tarek EL SOL \n"
+"Language-Team: French \n"
"Language: fr\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
@@ -305,7 +305,7 @@ msgstr ""
#: .\cookbook\forms.py:500
msgid "Partial Match"
-msgstr ""
+msgstr "correspondance partielle"
#: .\cookbook\forms.py:501
msgid "Starts Wtih"
diff --git a/cookbook/locale/hy/LC_MESSAGES/django.mo b/cookbook/locale/hy/LC_MESSAGES/django.mo
index b9b8a32f9..d66c7a0df 100644
Binary files a/cookbook/locale/hy/LC_MESSAGES/django.mo and b/cookbook/locale/hy/LC_MESSAGES/django.mo differ
diff --git a/cookbook/locale/nl/LC_MESSAGES/django.mo b/cookbook/locale/nl/LC_MESSAGES/django.mo
index df897bf55..19ec76218 100644
Binary files a/cookbook/locale/nl/LC_MESSAGES/django.mo and b/cookbook/locale/nl/LC_MESSAGES/django.mo differ
diff --git a/cookbook/locale/nl/LC_MESSAGES/django.po b/cookbook/locale/nl/LC_MESSAGES/django.po
index 984fb994d..81c7e42b0 100644
--- a/cookbook/locale/nl/LC_MESSAGES/django.po
+++ b/cookbook/locale/nl/LC_MESSAGES/django.po
@@ -13,7 +13,7 @@ msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-09-13 22:40+0200\n"
-"PO-Revision-Date: 2021-10-02 12:25+0000\n"
+"PO-Revision-Date: 2021-10-26 10:06+0000\n"
"Last-Translator: Jesse \n"
"Language-Team: Dutch \n"
@@ -58,7 +58,7 @@ msgid ""
"Users with whom newly created meal plan/shopping list entries should be "
"shared by default."
msgstr ""
-"Gebruikers waarmee nieuwe maaltijdplannen/boodschappenlijstjes standaard "
+"Gebruikers waarmee een nieuw maaltijdplan/boodschappenlijst standaard "
"gedeeld moeten worden."
#: .\cookbook\forms.py:59
@@ -71,7 +71,7 @@ msgstr "Aantal decimalen om ingrediënten op af te ronden."
#: .\cookbook\forms.py:61
msgid "If you want to be able to create and see comments underneath recipes."
-msgstr "Als je opmerkingen bij recepten wil kunnen maken en zien."
+msgstr "Als je opmerkingen onder recepten wil kunnen maken en zien."
#: .\cookbook\forms.py:63
msgid ""
@@ -842,7 +842,7 @@ msgstr "Winkelen"
#: .\cookbook\templates\base.html:113
msgid "Keyword"
-msgstr "Etiket"
+msgstr "Etiketten"
#: .\cookbook\templates\base.html:137
#: .\cookbook\templates\forms\ingredients.html:24
@@ -874,7 +874,7 @@ msgstr "Recept importeren"
#: .\cookbook\templates\shopping_list.html:188
#: .\cookbook\templates\shopping_list.html:210
msgid "Create"
-msgstr "Maak"
+msgstr "Nieuw recept"
#: .\cookbook\templates\base.html:207 .\cookbook\templates\space.html:7
#: .\cookbook\templates\space.html:19
@@ -1028,11 +1028,11 @@ msgid ""
" "
msgstr ""
"\n"
-" Het volgende formulier kan worden gebruikt wanneer per ongeluk twee "
-"(of meer) eenheden of ingrediënten zijn gemaakt die eigenlijk hetzelfde "
-"zijn.\n"
-" Het voegt de twee eenheden of ingrediënten samen en past alle bijbehorende "
-"recepten aan.\n"
+" Het volgende formulier kan worden gebruikt wanneer per ongeluk twee ("
+"of meer) eenheden of ingrediënten zijn gemaakt die eigenlijk\n"
+" hetzelfde zijn\n"
+" Het voegt de twee eenheden of ingrediënten samen en past alle "
+"bijbehorende recepten aan.\n"
" "
#: .\cookbook\templates\forms\ingredients.html:26
@@ -1068,7 +1068,7 @@ msgstr "Origineel bestand verwijderen"
#: .\cookbook\templates\generic\list_template.html:6
#: .\cookbook\templates\generic\list_template.html:21
msgid "List"
-msgstr "Lijst"
+msgstr " "
#: .\cookbook\templates\generic\list_template.html:34
msgid "Filter"
@@ -1201,13 +1201,14 @@ msgstr ""
"\n"
" Markdown is een lichtgewicht opmaak taal die gebruikt kan worden om "
"tekst eenvoudig op te maken.\n"
-" Deze site gebruikt de Python Markdown bibliotheek\n"
-" om je tekst in mooi uitziende HTML om te zetten. De volledige documentatie "
-"kan \n"
-" hiergevonden worden.\n"
-" Onvolledige, maar waarschijnlijk voldoende, informatie staat hieronder.\n"
+" Deze site gebruikt de Python Markdown bibliotheek\n"
+" om je tekst in mooi uitziende HTML om te zetten. De volledige "
+"documentatie kan \n"
+" hiergevonden worden.\n"
+" Onvolledige, maar waarschijnlijk voldoende, informatie staat "
+"hieronder.\n"
" "
#: .\cookbook\templates\markdown_info.html:25
@@ -1216,7 +1217,7 @@ msgstr "Koppen"
#: .\cookbook\templates\markdown_info.html:54
msgid "Formatting"
-msgstr "Formattering"
+msgstr "Opmaak"
#: .\cookbook\templates\markdown_info.html:56
#: .\cookbook\templates\markdown_info.html:72
@@ -1299,7 +1300,7 @@ msgstr ""
#: .\cookbook\templates\markdown_info.html:132
#: .\cookbook\templates\markdown_info.html:145
msgid "This will become an image"
-msgstr "Dit wordt een plaatje"
+msgstr "Dit wordt een afbeelding"
#: .\cookbook\templates\markdown_info.html:152
msgid "Tables"
@@ -1372,7 +1373,7 @@ msgstr "Maak alleen een notitie"
#: .\cookbook\templates\shopping_list.html:29
#: .\cookbook\templates\shopping_list.html:714
msgid "Shopping List"
-msgstr "Boodschappenlijstje"
+msgstr "Boodschappenlijst"
#: .\cookbook\templates\meal_plan.html:172
msgid "Shopping list currently empty"
@@ -1380,7 +1381,7 @@ msgstr "Boodschappenlijst is momenteel leeg"
#: .\cookbook\templates\meal_plan.html:175
msgid "Open Shopping List"
-msgstr "Open boodschappenlijstje"
+msgstr "Open boodschappenlijst"
#: .\cookbook\templates\meal_plan.html:189
msgid "Plan"
@@ -1405,15 +1406,15 @@ msgstr ""
#: .\cookbook\templates\meal_plan.html:217
#: .\cookbook\templates\meal_plan.html:294
msgid "Edit plan types"
-msgstr "Bewerk plan soorten"
+msgstr "Bewerk maaltijdplan types"
#: .\cookbook\templates\meal_plan.html:219
msgid "Show help"
-msgstr "Toon help"
+msgstr "Help"
#: .\cookbook\templates\meal_plan.html:220
msgid "Week iCal export"
-msgstr "Week iCal export"
+msgstr "Exporteer week als iCal"
#: .\cookbook\templates\meal_plan.html:256
#: .\cookbook\templates\url_import.html:542
@@ -1433,7 +1434,7 @@ msgstr "Gedeeld met"
#: .\cookbook\templates\meal_plan.html:280
msgid "Add to Shopping"
-msgstr "Voeg toe aan Boodschappen"
+msgstr "Voeg toe aan boodschappenlijst"
#: .\cookbook\templates\meal_plan.html:323
msgid "New meal type"
@@ -1556,7 +1557,7 @@ msgid ""
"Recipes, foods, shopping lists and more are organized in spaces of one or "
"more people."
msgstr ""
-"Recepten, ingrediënten, boodschappenlijstjes en meer zijn georganiseerd in "
+"Recepten, ingrediënten, boodschappenlijsten en meer zijn georganiseerd in "
"ruimtes van één of meer personen."
#: .\cookbook\templates\no_space_info.html:18
@@ -1995,7 +1996,7 @@ msgstr "Maak Superuser acount"
#: .\cookbook\templates\shopping_list.html:79
msgid "Shopping Recipes"
-msgstr "Boodschappen recepten"
+msgstr "Recepten op boodschappenlijst"
#: .\cookbook\templates\shopping_list.html:83
msgid "No recipes selected"
@@ -2007,7 +2008,7 @@ msgstr "Invoermodus"
#: .\cookbook\templates\shopping_list.html:158
msgid "Add Entry"
-msgstr "Zet op lijst"
+msgstr "Voeg toe aan boodschappenlijst"
#: .\cookbook\templates\shopping_list.html:174
msgid "Amount"
@@ -2044,7 +2045,7 @@ msgstr "Afgerond"
#: .\cookbook\templates\shopping_list.html:296
msgid "You are offline, shopping list might not syncronize."
-msgstr "Je bent offline, boodschappenlijst synchroniseert mogelijk niet."
+msgstr "Je bent offline, de boodschappenlijst synchroniseert mogelijk niet."
#: .\cookbook\templates\shopping_list.html:361
msgid "Copy/Export"
@@ -2721,7 +2722,7 @@ msgstr "Supermarkten"
#: .\cookbook\views\lists.py:179
msgid "Shopping Categories"
-msgstr "Boodschappen categorieën"
+msgstr "Boodschappencategorieën"
#: .\cookbook\views\new.py:122
msgid "Imported new recipe!"
diff --git a/cookbook/locale/pl/LC_MESSAGES/django.mo b/cookbook/locale/pl/LC_MESSAGES/django.mo
index e90dadc64..5d8a8c772 100644
Binary files a/cookbook/locale/pl/LC_MESSAGES/django.mo and b/cookbook/locale/pl/LC_MESSAGES/django.mo differ
diff --git a/cookbook/locale/ru/LC_MESSAGES/django.mo b/cookbook/locale/ru/LC_MESSAGES/django.mo
new file mode 100644
index 000000000..6e90aacaa
Binary files /dev/null and b/cookbook/locale/ru/LC_MESSAGES/django.mo differ
diff --git a/cookbook/serializer.py b/cookbook/serializer.py
index 4ca666d10..451fdb80d 100644
--- a/cookbook/serializer.py
+++ b/cookbook/serializer.py
@@ -2,22 +2,21 @@ import random
from datetime import timedelta
from decimal import Decimal
from gettext import gettext as _
+
from django.contrib.auth.models import User
from django.db.models import Avg, QuerySet, Sum
from django.urls import reverse
from django.utils import timezone
-from drf_writable_nested import (UniqueFieldsMixin,
- WritableNestedModelSerializer)
+from drf_writable_nested import UniqueFieldsMixin, WritableNestedModelSerializer
from rest_framework import serializers
-from rest_framework.exceptions import ValidationError, NotFound
+from rest_framework.exceptions import NotFound, ValidationError
-from cookbook.models import (Comment, CookLog, Food, Ingredient, Keyword,
- MealPlan, MealType, NutritionInformation, Recipe,
- RecipeBook, RecipeBookEntry, RecipeImport,
- ShareLink, ShoppingList, ShoppingListEntry,
- ShoppingListRecipe, Step, Storage, Sync, SyncLog,
- Unit, UserPreference, ViewLog, SupermarketCategory, Supermarket,
- SupermarketCategoryRelation, ImportLog, BookmarkletImport, UserFile, Automation)
+from cookbook.models import (Automation, BookmarkletImport, Comment, CookLog, Food, ImportLog,
+ Ingredient, Keyword, MealPlan, MealType, NutritionInformation, Recipe,
+ RecipeBook, RecipeBookEntry, RecipeImport, ShareLink, ShoppingList,
+ ShoppingListEntry, ShoppingListRecipe, Step, Storage, Supermarket,
+ SupermarketCategory, SupermarketCategoryRelation, Sync, SyncLog, Unit,
+ UserFile, UserPreference, ViewLog)
from cookbook.templatetags.custom_tags import markdown
@@ -34,12 +33,19 @@ class ExtendedRecipeMixin(serializers.ModelSerializer):
except KeyError:
api_serializer = None
# extended values are computationally expensive and not needed in normal circumstances
- if self.context.get('request', False) and bool(int(self.context['request'].query_params.get('extended', False))) and self.__class__ == api_serializer:
- return fields
- else:
+ try:
+ if bool(int(self.context['request'].query_params.get('extended', False))) and self.__class__ == api_serializer:
+ return fields
+ except AttributeError:
+ pass
+ except KeyError:
+ pass
+ try:
del fields['image']
del fields['numrecipe']
- return fields
+ except KeyError:
+ pass
+ return fields
def get_image(self, obj):
# TODO add caching
@@ -158,7 +164,7 @@ class UserFileSerializer(serializers.ModelSerializer):
current_file_size_mb = 0
if ((validated_data['file'].size / 1000 / 1000 + current_file_size_mb - 5)
- > self.context['request'].space.max_file_storage_mb != 0):
+ > self.context['request'].space.max_file_storage_mb != 0):
raise ValidationError(_('You have reached your file upload limit.'))
def create(self, validated_data):
diff --git a/cookbook/templates/base.html b/cookbook/templates/base.html
index 1976ef331..6d7b04132 100644
--- a/cookbook/templates/base.html
+++ b/cookbook/templates/base.html
@@ -344,6 +344,7 @@
-
-
-
-
-
-
{% endblock %}
-{% block content %}
-
The meal plan module allows planning of meals both with recipes and notes.
-
Simply select a recipe from the list of recently viewed recipes or search the one you
- want and drag it to the desired plan position. You can also add a note and a title and
- then drag the recipe to create a plan entry with a custom title and note. Creating only
- Notes is possible by dragging the create note box into the plan.
-
Click on a recipe in order to open the detailed view. There you can also add it to the
- shopping list. You can also add all recipes of a day to the shopping list by
- clicking the shopping cart at the top of the table.
-
Since a common use case is to plan meals together you can define
- users you want to share your plan with in the settings.
-
-
You can also edit the types of meals you want to plan. If you share your plan with
- someone with
- different meals, their meal types will appear in your list as well. To prevent
- duplicates (e.g. Other and Misc.)
- name your meal types the same as the users you share your meals with and they will be
- merged.
- {% endblocktrans %}
-
-
-
-
-
-
-
-
-
+{% block script %}
+ {% if debug %}
+
+ {% else %}
+
+ {% endif %}
+
+ {% render_bundle 'meal_plan_view' %}
{% endblock %}
\ No newline at end of file
diff --git a/cookbook/templates/settings.html b/cookbook/templates/settings.html
index eba8ad14b..1f1e605c6 100644
--- a/cookbook/templates/settings.html
+++ b/cookbook/templates/settings.html
@@ -178,7 +178,7 @@
-
{% trans 'Percise' %}
+
{% trans 'Precise' %}
{% trans 'Allows fine control over search results but might not return results if too many spelling mistakes are made.' %}
{% trans 'Perfect for large Databases' %}
diff --git a/cookbook/templatetags/custom_tags.py b/cookbook/templatetags/custom_tags.py
index e22411c6e..76ac08b4f 100644
--- a/cookbook/templatetags/custom_tags.py
+++ b/cookbook/templatetags/custom_tags.py
@@ -142,7 +142,7 @@ def bookmarklet(request):
localStorage.setItem('redirectURL', '" + server + reverse('data_import_url') + "'); \
localStorage.setItem('token', '" + api_token.__str__() + "'); \
document.body.appendChild(document.createElement(\'script\')).src=\'" \
- + server + prefix + static('js/bookmarklet.js') + "? \
+ + server + prefix + static('js/bookmarklet.js') + "? \
r=\'+Math.floor(Math.random()*999999999);}})();"
return re.sub(r"[\n\t\s]*", "", bookmark)
@@ -153,3 +153,5 @@ def base_path(request, path_type):
return request._current_scheme_host + request.META.get('HTTP_X_SCRIPT_NAME', '')
elif path_type == 'script':
return request.META.get('HTTP_X_SCRIPT_NAME', '')
+ elif path_type == 'static_base':
+ return static('vue/manifest.json').replace('vue/manifest.json', '')
diff --git a/cookbook/urls.py b/cookbook/urls.py
index 80537c8d0..5ed5b825e 100644
--- a/cookbook/urls.py
+++ b/cookbook/urls.py
@@ -58,7 +58,6 @@ urlpatterns = [
path('search/v2/', views.search_v2, name='view_search_v2'),
path('books/', views.books, name='view_books'),
path('plan/', views.meal_plan, name='view_plan'),
- path('plan_new/', views.meal_plan_new, name='view_plan_new'),
path('plan/entry/', views.meal_plan_entry, name='view_plan_entry'),
path('shopping/', views.shopping_list, name='view_shopping'),
path('shopping/', views.shopping_list, name='view_shopping'),
diff --git a/cookbook/views/data.py b/cookbook/views/data.py
index 4ed41c403..6dd30b411 100644
--- a/cookbook/views/data.py
+++ b/cookbook/views/data.py
@@ -5,6 +5,7 @@ from io import BytesIO
import requests
from django.contrib import messages
+from django.core.exceptions import ObjectDoesNotExist
from django.core.files import File
from django.db.transaction import atomic
from django.http import HttpResponse, HttpResponseRedirect
@@ -150,8 +151,15 @@ def import_url(request):
recipe.steps.add(step)
for kw in data['keywords']:
- k, created = Keyword.objects.get_or_create(name=kw['text'], space=request.space)
- recipe.keywords.add(k)
+ if data['all_keywords']: # do not remove this check :) https://github.com/vabene1111/recipes/issues/645
+ k, created = Keyword.objects.get_or_create(name=kw['text'], space=request.space)
+ recipe.keywords.add(k)
+ else:
+ try:
+ k = Keyword.objects.get(name=kw['text'], space=request.space)
+ recipe.keywords.add(k)
+ except ObjectDoesNotExist:
+ pass
ingredient_parser = IngredientParser(request, True)
for ing in data['recipeIngredient']:
diff --git a/cookbook/views/views.py b/cookbook/views/views.py
index 5a94412b9..43e9c89ef 100644
--- a/cookbook/views/views.py
+++ b/cookbook/views/views.py
@@ -220,11 +220,6 @@ def meal_plan(request):
return render(request, 'meal_plan.html', {})
-@group_required('user')
-def meal_plan_new(request):
- return render(request, 'meal_plan_new.html', {})
-
-
@group_required('user')
def supermarket(request):
return render(request, 'supermarket.html', {})
@@ -344,10 +339,13 @@ def user_settings(request):
if fields_searched == 0:
search_form.add_error(None, _('You must select at least one field to search!'))
search_error = True
- elif search_form.cleaned_data['search'] in ['websearch', 'raw'] and len(search_form.cleaned_data['fulltext']) == 0:
- search_form.add_error('search', _('To use this search method you must select at least one full text search field!'))
+ elif search_form.cleaned_data['search'] in ['websearch', 'raw'] and len(
+ search_form.cleaned_data['fulltext']) == 0:
+ search_form.add_error('search',
+ _('To use this search method you must select at least one full text search field!'))
search_error = True
- elif search_form.cleaned_data['search'] in ['websearch', 'raw'] and len(search_form.cleaned_data['trigram']) > 0:
+ elif search_form.cleaned_data['search'] in ['websearch', 'raw'] and len(
+ search_form.cleaned_data['trigram']) > 0:
search_form.add_error(None, _('Fuzzy search is not compatible with this search method!'))
search_error = True
else:
@@ -386,7 +384,8 @@ def user_settings(request):
else:
preference_form = UserPreferenceForm()
- fields_searched = len(sp.icontains.all()) + len(sp.istartswith.all()) + len(sp.trigram.all()) + len(sp.fulltext.all())
+ fields_searched = len(sp.icontains.all()) + len(sp.istartswith.all()) + len(sp.trigram.all()) + len(
+ sp.fulltext.all())
if sp and not search_error and fields_searched > 0:
search_form = SearchPreferenceForm(instance=sp)
elif not search_error:
@@ -396,7 +395,8 @@ def user_settings(request):
api_token = Token.objects.create(user=request.user)
# these fields require postgress - just disable them if postgress isn't available
- if not settings.DATABASES['default']['ENGINE'] in ['django.db.backends.postgresql_psycopg2', 'django.db.backends.postgresql']:
+ if not settings.DATABASES['default']['ENGINE'] in ['django.db.backends.postgresql_psycopg2',
+ 'django.db.backends.postgresql']:
search_form.fields['search'].disabled = True
search_form.fields['lookup'].disabled = True
search_form.fields['trigram'].disabled = True
diff --git a/docs/features/authentication.md b/docs/features/authentication.md
index d4b273fc6..6390e91ef 100644
--- a/docs/features/authentication.md
+++ b/docs/features/authentication.md
@@ -60,6 +60,25 @@ Use the superuser account to grant permissions to the newly created users.
To link an account to an already existing normal user go to the settings page of the user and link it.
Here you can also unlink your account if you no longer want to use a social login method.
+## LDAP
+
+LDAP authentication can be enabled in the `.env` file by setting `LDAP_AUTH=1`.
+If set, users listed in the LDAP instance will be able to sign in without signing up.
+These variables must be set to configure the connection to the LDAP instance:
+```
+AUTH_LDAP_SERVER_URI=ldap://ldap.example.org:389
+AUTH_LDAP_BIND_DN=uid=admin,ou=users,dc=example,dc=org
+AUTH_LDAP_BIND_PASSWORD=adminpassword
+AUTH_LDAP_USER_SEARCH_BASE_DN=ou=users,dc=example,dc=org
+```
+Additional optional variables:
+```
+AUTH_LDAP_USER_SEARCH_FILTER_STR=(uid=%(user)s)
+AUTH_LDAP_USER_ATTR_MAP={'first_name': 'givenName', 'last_name': 'sn', 'email': 'mail'}
+AUTH_LDAP_ALWAYS_UPDATE_USER=1
+AUTH_LDAP_CACHE_TIMEOUT=3600
+```
+
## Reverse Proxy Authentication
!!! Info "Community Contributed Tutorial"
diff --git a/docs/index.md b/docs/index.md
index 595a96a2c..e4e1bfb28 100644
--- a/docs/index.md
+++ b/docs/index.md
@@ -1,72 +1,79 @@
-
+
Tandoor Recipes
-
This is my personal beta of vabene's excellent recipe app. It includes many of the new features I've developed and should be considered experimental.
-## Experimental Features
-- Manual import recipes from URL & Source (HTML/JSON)
-- Bookmarklet to import recipes from any website
-- Full Text Search
-- Hierarchical Keywords
-
-## Coming Next
-- Heirarchical Ingredients
-- Faceted Search
-- Search filter by rating
-- What Can I Make Now?
-- Better ingredient/unit matching on import
-- Custom word replacement on import (e.g. 'grams' automatically imported as 'g')
-- improved ingredient parser (items in parens moved to notes)
-- quick view ingredients
-- quick view associated recipe
-- favorite recipes
-
The recipe manager that allows you to manage your ever growing collection of digital recipes.

-!!! info "WIP"
- The documentation is work in progress. New information will be added over time.
- Feel free to open pull requests to enhance the documentation.
+## Core Features
+- 🥗 **Manage your recipes** with a fast and intuitive editor
+- 📆 **Plan** multiple meals for each day
+- 🛒 **Shopping lists** via the meal plan or straight from recipes
+- 📚 **Cookbooks** collect recipes into books
+- 👪 **Share and collaborate** on recipes with friends and family
-## Features
+## Made by and for power users
-- 📦 **Sync** files with Dropbox and Nextcloud (more can easily be added)
-- 🔍 Powerful **search** with Djangos [TrigramSimilarity](https://docs.djangoproject.com/en/3.0/ref/contrib/postgres/search/#trigram-similarity)
+- 🔍 Powerful & customizable **search** with fulltext support and [TrigramSimilarity](https://docs.djangoproject.com/en/3.0/ref/contrib/postgres/search/#trigram-similarity)
- 🏷️ Create and search for **tags**, assign them in batch to all files matching certain filters
-- 📄 **Create recipes** locally within a nice, standardized web interface
-- ⬇️ **Import recipes** from thousands of websites supporting [ld+json or microdata](https://schema.org/Recipe)
-- 📱 Optimized for use on **mobile** devices like phones and tablets
-- 🛒 Generate **shopping** lists from recipes
-- 📆 Create a **Plan** on what to eat when
-- 👪 **Share** recipes with friends and comment on them to suggest or remember changes you made
-- ➗ automatically convert decimal units to **fractions** for those who like this
-- 🐳 Easy setup with **Docker** and included examples for Kubernetes, Unraid and Synology
+- ↔️ Quickly merge and rename ingredients, tags and units
+- 📥️ **Import recipes** from thousands of websites supporting [ld+json or microdata](https://schema.org/Recipe)
+- ➗ Support for **fractions** or decimals
+- 🐳 Easy setup with **Docker** and included examples for **Kubernetes**, **Unraid** and **Synology**
- 🎨 Customize your interface with **themes**
-- ✉️ Export and import recipes from other users
+- 📦 **Sync** files with Dropbox and Nextcloud
+
+## All the must haves
+
+- 📱Optimized for use on **mobile** devices
- 🌍 localized in many languages thanks to the awesome community
-- ➕ Many more like recipe scaling, image compression, cookbooks, printing views, ...
+- 📥️ **Import your collection** from many other [recipe managers](https://docs.tandoor.dev/features/import_export/)
+- ➕ Many more like recipe scaling, image compression, printing views and supermarkets
+This application is meant for people with a collection of recipes they want to share with family and friends or simply
+store them in a nicely organized way. A basic permission system exists but this application is not meant to be run as
+a public page.
+## Your Feedback
+
+Share some information on how you use Tandoor to help me improve the application [Google Survey](https://forms.gle/qNfLK2tWTeWHe9Qd7)
+
+## Get in touch
+
+