Compare commits

...

472 Commits
1.3.0 ... 1.4.6

Author SHA1 Message Date
vabene1111
b1028f49d6 Merge branch 'develop' 2023-02-03 16:04:58 +01:00
vabene1111
6d0cc96cc8 Merge pull request #2301 from RoGryza/patch-1
Fix path traversal in manual installation steps
2023-02-03 16:02:58 +01:00
Rodrigo Candido Gryzinski
969a91e751 Fix path traversal in manual installation steps 2023-02-03 08:42:44 +00:00
vabene1111
f47470a9ad wip rezeptsuitede importer 2023-01-29 22:06:08 +01:00
Tomasz Klimczak
9ad9fe275d Translated using Weblate (Polish)
Currently translated at 100.0% (478 of 478 strings)

Translation: Tandoor/Recipes Frontend
Translate-URL: http://translate.tandoor.dev/projects/tandoor/recipes-frontend/pl/
2023-01-28 21:59:00 +00:00
vabene1111
33448c98c0 fixed mark food on hand 2023-01-27 15:48:33 +01:00
vabene1111
90e389f2fa fixed add to shopping modal layout 2023-01-27 15:43:22 +01:00
Kaibu
af7acd7473 swapped simple-calender dependency
updated simple-calender to forked version with fix
2023-01-27 15:26:39 +01:00
vabene1111
dfa0794281 Merge pull request #2272 from MarcusWolschon/features/upstream/ingredients_special_cases
handle 2 more special cases in ingredients
2023-01-27 15:14:47 +01:00
vabene1111
a36d42df84 Merge pull request #2281 from juneboom/juneboom-fix-typo
Fix typos
2023-01-27 15:09:32 +01:00
June
269dded046 Fix typos
Edited some spelling and grammar.
2023-01-23 19:41:32 -05:00
vabene1111
826ccc2760 yarn 2023-01-23 16:27:42 +01:00
吕楪
7190dc17a7 Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (478 of 478 strings)

Translation: Tandoor/Recipes Frontend
Translate-URL: http://translate.tandoor.dev/projects/tandoor/recipes-frontend/zh_Hans/
2023-01-21 02:55:48 +00:00
vabene1111
5e332bb88c changed plural default 2023-01-20 10:20:03 +01:00
vabene1111
fb21622bfe adding test page 2023-01-19 20:31:52 +01:00
vabene1111
191c38db8f testing chunks 2023-01-19 20:31:48 +01:00
vabene1111
71132fe992 updated translations 2023-01-19 19:14:39 +01:00
vabene1111
d1d568a9d3 fixed import view step editor 2023-01-19 19:12:25 +01:00
Fall1ngStar
68d4fb3b59 Translated using Weblate (French)
Currently translated at 85.8% (450 of 524 strings)

Translation: Tandoor/Recipes Backend
Translate-URL: http://translate.tandoor.dev/projects/tandoor/recipes-backend/fr/
2023-01-18 01:55:46 +00:00
Marcus Wolschon
b4c26682c7 simplify 2023-01-17 15:20:59 +01:00
Alexander Eifler
e2cfb53ec4 Translated using Weblate (German)
Currently translated at 100.0% (476 of 476 strings)

Translation: Tandoor/Recipes Frontend
Translate-URL: http://translate.tandoor.dev/projects/tandoor/recipes-frontend/de/
2023-01-16 21:55:48 +00:00
vabene1111
274b17a860 improved unit factory and run CI on PR 2023-01-16 21:20:16 +01:00
vabene1111
97bcf1111b added simple edit modal to recipe import 2023-01-16 21:06:29 +01:00
vabene1111
3b9d221258 fixed importer loading empty ingredients 2023-01-16 20:41:29 +01:00
vabene1111
d8bcb8bcb6 emoji picker offline 2023-01-16 20:28:42 +01:00
vabene1111
5cb0f1761a fixed emoji picker 2023-01-16 20:23:36 +01:00
vabene1111
516b551528 Merge pull request #2273 from jacobgc/patch-1
Update .env.template
2023-01-16 19:38:10 +01:00
Oliver Cervera
f43d4d3971 Translated using Weblate (Italian)
Currently translated at 97.0% (462 of 476 strings)

Translation: Tandoor/Recipes Frontend
Translate-URL: http://translate.tandoor.dev/projects/tandoor/recipes-frontend/it/
2023-01-15 16:55:46 +00:00
Oliver Cervera
e0dd70027b Translated using Weblate (Italian)
Currently translated at 91.2% (478 of 524 strings)

Translation: Tandoor/Recipes Backend
Translate-URL: http://translate.tandoor.dev/projects/tandoor/recipes-backend/it/
2023-01-15 16:55:46 +00:00
Jacob Gee-Clarke
79efd94d6f Update .env.template
Fixed broken URLs in .env.template
2023-01-15 13:51:09 +00:00
Marcus Wolschon
f16870c59e fix an accidental commit 2023-01-15 14:31:37 +01:00
Marcus Wolschon
c690bc18a0 Fix CodeQL issue with regular expression 2023-01-15 14:27:55 +01:00
Marcus Wolschon
394f24c29f handle special cases in ingredients 2023-01-15 13:50:06 +01:00
arnaud
c2a8214290 Translated using Weblate (French)
Currently translated at 84.7% (444 of 524 strings)

Translation: Tandoor/Recipes Backend
Translate-URL: http://translate.tandoor.dev/projects/tandoor/recipes-backend/fr/
2023-01-12 21:55:48 +00:00
vabene1111
4d0cfc95e4 Merge pull request #2267 from besendorf/patch-2
Clarify the folder in which yarn needs to be run
2023-01-12 15:59:38 +01:00
besendorf
972c103538 fix error and add folder name 2023-01-12 15:37:53 +01:00
besendorf
2f62f51dc2 Clarify the folder in which yarn needs to be run 2023-01-11 16:43:01 +01:00
vabene1111
56ee173c07 fixed s3 file upload 2023-01-10 17:03:55 +01:00
Oliver Cervera
774c633d5c Translated using Weblate (Italian)
Currently translated at 96.6% (460 of 476 strings)

Translation: Tandoor/Recipes Frontend
Translate-URL: http://translate.tandoor.dev/projects/tandoor/recipes-frontend/it/
2023-01-10 11:55:47 +00:00
Oliver Cervera
93dd35fde3 Translated using Weblate (Italian)
Currently translated at 87.7% (460 of 524 strings)

Translation: Tandoor/Recipes Backend
Translate-URL: http://translate.tandoor.dev/projects/tandoor/recipes-backend/it/
2023-01-10 11:55:46 +00:00
Joachim Weber
666e4d282f Translated using Weblate (Portuguese)
Currently translated at 10.1% (48 of 474 strings)

Translation: Tandoor/Recipes Frontend
Translate-URL: http://translate.tandoor.dev/projects/tandoor/recipes-frontend/pt/
2023-01-08 17:55:49 +00:00
Joachim Weber
ed6ca613ff Translated using Weblate (French)
Currently translated at 84.1% (399 of 474 strings)

Translation: Tandoor/Recipes Frontend
Translate-URL: http://translate.tandoor.dev/projects/tandoor/recipes-frontend/fr/
2023-01-08 17:55:49 +00:00
Joachim Weber
285364e12a Translated using Weblate (German)
Currently translated at 98.9% (469 of 474 strings)

Translation: Tandoor/Recipes Frontend
Translate-URL: http://translate.tandoor.dev/projects/tandoor/recipes-frontend/de/
2023-01-08 17:55:49 +00:00
Joachim Weber
9a46a91652 Translated using Weblate (Portuguese)
Currently translated at 29.3% (154 of 524 strings)

Translation: Tandoor/Recipes Backend
Translate-URL: http://translate.tandoor.dev/projects/tandoor/recipes-backend/pt/
2023-01-08 17:55:49 +00:00
Joachim Weber
4e4078f3da Translated using Weblate (Latvian)
Currently translated at 39.8% (209 of 524 strings)

Translation: Tandoor/Recipes Backend
Translate-URL: http://translate.tandoor.dev/projects/tandoor/recipes-backend/lv/
2023-01-08 17:55:49 +00:00
Joachim Weber
41c9290ba8 Translated using Weblate (Armenian)
Currently translated at 100.0% (362 of 362 strings)

Translation: Tandoor/Recipes Backend
Translate-URL: http://translate.tandoor.dev/projects/tandoor/recipes-backend/hy/
2023-01-08 17:55:49 +00:00
Joachim Weber
4460fe013f Translated using Weblate (Czech)
Currently translated at 100.0% (362 of 362 strings)

Translation: Tandoor/Recipes Backend
Translate-URL: http://translate.tandoor.dev/projects/tandoor/recipes-backend/cs/
2023-01-08 17:55:49 +00:00
vabene1111
558a5d6554 js stuff 2023-01-07 10:32:35 +01:00
vabene1111
388f2f441f tests 2023-01-06 22:35:14 +01:00
vabene1111
11e8af4c46 comment out broken test 2023-01-06 22:12:59 +01:00
vabene1111
387893e1ef remove source map reference 2023-01-06 21:58:14 +01:00
vabene1111
78dc1bf9ec fixed missing .map files (by removing the reference) 2023-01-06 21:50:26 +01:00
vabene1111
12638096b1 use updated version of js reverse 2023-01-06 21:40:43 +01:00
vabene1111
3e82199c44 added author keyword import 2023-01-06 21:22:02 +01:00
vabene1111
b4bcf5c032 Merge remote-tracking branch 'origin/feature/import-automation' into develop 2023-01-06 20:26:31 +01:00
Tomasz Klimczak
afdd92c903 Translated using Weblate (Polish)
Currently translated at 100.0% (474 of 474 strings)

Translation: Tandoor/Recipes Frontend
Translate-URL: http://translate.tandoor.dev/projects/tandoor/recipes-frontend/pl/
2023-01-05 12:55:53 +00:00
Oliver Cervera
1462300eda Translated using Weblate (Italian)
Currently translated at 95.7% (454 of 474 strings)

Translation: Tandoor/Recipes Frontend
Translate-URL: http://translate.tandoor.dev/projects/tandoor/recipes-frontend/it/
2023-01-05 12:55:53 +00:00
Oliver Cervera
d0417d09db Translated using Weblate (Italian)
Currently translated at 83.3% (437 of 524 strings)

Translation: Tandoor/Recipes Backend
Translate-URL: http://translate.tandoor.dev/projects/tandoor/recipes-backend/it/
2023-01-05 12:55:53 +00:00
vabene1111
39c8da8305 Merge pull request #2248 from TandoorRecipes/dependabot/npm_and_yarn/vue/webpack-bundle-tracker-1.8.0
Bump webpack-bundle-tracker from 1.7.0 to 1.8.0 in /vue
2023-01-05 07:56:18 +01:00
vabene1111
c9af9277ae Merge pull request #2244 from TandoorRecipes/dependabot/pip/django-allauth-0.52.0
Bump django-allauth from 0.51.0 to 0.52.0
2023-01-05 07:53:02 +01:00
vabene1111
5e2be34f7b Merge pull request #2245 from TandoorRecipes/dependabot/pip/icalendar-5.0.4
Bump icalendar from 5.0.3 to 5.0.4
2023-01-05 07:52:30 +01:00
vabene1111
73a2476a79 Merge pull request #2242 from TandoorRecipes/dependabot/pip/django-storages-1.13.2
Bump django-storages from 1.13.1 to 1.13.2
2023-01-05 07:52:16 +01:00
dependabot[bot]
f61032cc74 Bump django-storages from 1.13.1 to 1.13.2
Bumps [django-storages](https://github.com/jschneier/django-storages) from 1.13.1 to 1.13.2.
- [Release notes](https://github.com/jschneier/django-storages/releases)
- [Changelog](https://github.com/jschneier/django-storages/blob/master/CHANGELOG.rst)
- [Commits](https://github.com/jschneier/django-storages/compare/1.13.1...1.13.2)

---
updated-dependencies:
- dependency-name: django-storages
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-01-05 06:52:11 +00:00
vabene1111
f6956388c7 Merge pull request #2243 from TandoorRecipes/dependabot/pip/lxml-4.9.2
Bump lxml from 4.9.1 to 4.9.2
2023-01-05 07:52:07 +01:00
vabene1111
7e9b303e8b Merge pull request #2246 from TandoorRecipes/dependabot/pip/boto3-1.26.41
Bump boto3 from 1.24.84 to 1.26.41
2023-01-05 07:51:52 +01:00
vabene1111
f617dedfa2 Merge pull request #2247 from TandoorRecipes/dependabot/npm_and_yarn/vue/core-js-3.27.1
Bump core-js from 3.26.1 to 3.27.1 in /vue
2023-01-05 07:51:48 +01:00
vabene1111
942c26c581 Merge pull request #2249 from TandoorRecipes/dependabot/npm_and_yarn/vue/vue-cookies-1.8.2
Bump vue-cookies from 1.8.1 to 1.8.2 in /vue
2023-01-05 07:51:43 +01:00
vabene1111
a00d90398e Merge pull request #2250 from TandoorRecipes/dependabot/npm_and_yarn/vue/vue/compiler-sfc-3.2.45
Bump @vue/compiler-sfc from 3.2.40 to 3.2.45 in /vue
2023-01-05 07:51:29 +01:00
vabene1111
61e6d855ec Merge pull request #2251 from TandoorRecipes/dependabot/npm_and_yarn/vue/typescript-eslint/parser-5.47.1
Bump @typescript-eslint/parser from 4.33.0 to 5.47.1 in /vue
2023-01-05 07:51:23 +01:00
vabene1111
6b59f53273 test wip but not working 2023-01-04 17:27:15 +01:00
vabene1111
798457e7e2 translated, documented and implemented instruction replace 2023-01-04 16:36:03 +01:00
vabene1111
6176eeb024 basics 2023-01-03 23:12:00 +01:00
Oliver Cervera
56252a707a Translated using Weblate (Italian)
Currently translated at 73.5% (347 of 472 strings)

Translation: Tandoor/Recipes Frontend
Translate-URL: http://translate.tandoor.dev/projects/tandoor/recipes-frontend/it/
2023-01-03 11:55:47 +00:00
vabene1111
9c649d743c fixed recettetek import ingredients 2023-01-01 15:07:58 +01:00
dependabot[bot]
00b1ca5454 Bump @typescript-eslint/parser from 4.33.0 to 5.47.1 in /vue
Bumps [@typescript-eslint/parser](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/parser) from 4.33.0 to 5.47.1.
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/parser/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v5.47.1/packages/parser)

---
updated-dependencies:
- dependency-name: "@typescript-eslint/parser"
  dependency-type: direct:development
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-01-01 00:16:48 +00:00
dependabot[bot]
f2e1648556 Bump @vue/compiler-sfc from 3.2.40 to 3.2.45 in /vue
Bumps [@vue/compiler-sfc](https://github.com/vuejs/core/tree/HEAD/packages/compiler-sfc) from 3.2.40 to 3.2.45.
- [Release notes](https://github.com/vuejs/core/releases)
- [Changelog](https://github.com/vuejs/core/blob/main/CHANGELOG.md)
- [Commits](https://github.com/vuejs/core/commits/v3.2.45/packages/compiler-sfc)

---
updated-dependencies:
- dependency-name: "@vue/compiler-sfc"
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-01-01 00:15:23 +00:00
dependabot[bot]
b1945edf04 Bump vue-cookies from 1.8.1 to 1.8.2 in /vue
Bumps [vue-cookies](https://github.com/cmp-cc/vue-cookies) from 1.8.1 to 1.8.2.
- [Release notes](https://github.com/cmp-cc/vue-cookies/releases)
- [Commits](https://github.com/cmp-cc/vue-cookies/compare/v1.8.1...v1.8.2)

---
updated-dependencies:
- dependency-name: vue-cookies
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-01-01 00:13:19 +00:00
dependabot[bot]
9f20db0ed3 Bump webpack-bundle-tracker from 1.7.0 to 1.8.0 in /vue
Bumps [webpack-bundle-tracker](https://github.com/django-webpack/webpack-bundle-tracker) from 1.7.0 to 1.8.0.
- [Release notes](https://github.com/django-webpack/webpack-bundle-tracker/releases)
- [Commits](https://github.com/django-webpack/webpack-bundle-tracker/compare/1.7.0...1.8.0)

---
updated-dependencies:
- dependency-name: webpack-bundle-tracker
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-01-01 00:12:24 +00:00
dependabot[bot]
852756f099 Bump core-js from 3.26.1 to 3.27.1 in /vue
Bumps [core-js](https://github.com/zloirock/core-js/tree/HEAD/packages/core-js) from 3.26.1 to 3.27.1.
- [Release notes](https://github.com/zloirock/core-js/releases)
- [Changelog](https://github.com/zloirock/core-js/blob/master/CHANGELOG.md)
- [Commits](https://github.com/zloirock/core-js/commits/v3.27.1/packages/core-js)

---
updated-dependencies:
- dependency-name: core-js
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-01-01 00:11:03 +00:00
dependabot[bot]
452617ef30 Bump boto3 from 1.24.84 to 1.26.41
Bumps [boto3](https://github.com/boto/boto3) from 1.24.84 to 1.26.41.
- [Release notes](https://github.com/boto/boto3/releases)
- [Changelog](https://github.com/boto/boto3/blob/develop/CHANGELOG.rst)
- [Commits](https://github.com/boto/boto3/compare/1.24.84...1.26.41)

---
updated-dependencies:
- dependency-name: boto3
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-01-01 00:04:44 +00:00
dependabot[bot]
4e972835e5 Bump icalendar from 5.0.3 to 5.0.4
Bumps [icalendar](https://github.com/collective/icalendar) from 5.0.3 to 5.0.4.
- [Release notes](https://github.com/collective/icalendar/releases)
- [Changelog](https://github.com/collective/icalendar/blob/master/CHANGES.rst)
- [Commits](https://github.com/collective/icalendar/compare/v5.0.3...v5.0.4)

---
updated-dependencies:
- dependency-name: icalendar
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-01-01 00:04:32 +00:00
dependabot[bot]
5e4d983b79 Bump django-allauth from 0.51.0 to 0.52.0
Bumps [django-allauth](https://github.com/pennersr/django-allauth) from 0.51.0 to 0.52.0.
- [Release notes](https://github.com/pennersr/django-allauth/releases)
- [Changelog](https://github.com/pennersr/django-allauth/blob/master/ChangeLog.rst)
- [Commits](https://github.com/pennersr/django-allauth/compare/0.51.0...0.52.0)

---
updated-dependencies:
- dependency-name: django-allauth
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-01-01 00:04:28 +00:00
dependabot[bot]
0de8065212 Bump lxml from 4.9.1 to 4.9.2
Bumps [lxml](https://github.com/lxml/lxml) from 4.9.1 to 4.9.2.
- [Release notes](https://github.com/lxml/lxml/releases)
- [Changelog](https://github.com/lxml/lxml/blob/master/CHANGES.txt)
- [Commits](https://github.com/lxml/lxml/compare/lxml-4.9.1...lxml-4.9.2)

---
updated-dependencies:
- dependency-name: lxml
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-01-01 00:04:22 +00:00
vabene1111
1a6e677c06 allow filter in add to shopping modal 2022-12-31 17:35:16 +01:00
vabene1111
8307828528 added ingredient auto sort feature 2022-12-31 16:19:13 +01:00
vabene1111
12a1f261db Merge branch 'develop' of http://translate.tandoor.dev/git/tandoor/recipes-backend into develop
# Conflicts:
#	vue/src/locales/ru.json
2022-12-31 13:36:34 +01:00
vabene1111
79e400ce6f updated 2022-12-19 16:49:26 +01:00
vabene1111
d51de1bd79 updated ci 2022-12-19 16:28:40 +01:00
vabene1111
6b3f1aa038 Merge pull request #2205 from fliiiix/doc/fix-typo
fix install guide link and typo
2022-12-19 16:23:09 +01:00
vabene1111
7d7803a07e Merge pull request #2215 from jwalker343/develop
Allow PWA Manifest to work behind CF Access / certain proxy configs
2022-12-19 16:09:01 +01:00
vabene1111
559c574fd6 Merge pull request #2195 from TandoorRecipes/dependabot/npm_and_yarn/vue/typescript-4.9.3
Bump typescript from 4.8.4 to 4.9.3 in /vue
2022-12-19 16:07:47 +01:00
vabene1111
5e0131acd9 Merge pull request #2193 from TandoorRecipes/dependabot/pip/cryptography-38.0.4
Bump cryptography from 38.0.3 to 38.0.4
2022-12-19 16:07:21 +01:00
vabene1111
d560b7a143 Merge pull request #2197 from TandoorRecipes/dependabot/npm_and_yarn/vue/bootstrap-vue-2.23.1
Bump bootstrap-vue from 2.22.0 to 2.23.1 in /vue
2022-12-19 16:07:04 +01:00
vabene1111
15e5366dbb Merge pull request #2199 from TandoorRecipes/dependabot/npm_and_yarn/vue/webpack-bundle-tracker-1.7.0
Bump webpack-bundle-tracker from 1.6.0 to 1.7.0 in /vue
2022-12-19 16:06:45 +01:00
vabene1111
6ccfdd6f2e Merge pull request #2216 from TandoorRecipes/dependabot/pip/django-webpack-loader-1.8.0
Bump django-webpack-loader from 1.6.0 to 1.8.0
2022-12-19 16:06:39 +01:00
dependabot[bot]
d2b79990cb Bump bootstrap-vue from 2.22.0 to 2.23.1 in /vue
Bumps [bootstrap-vue](https://github.com/bootstrap-vue/bootstrap-vue) from 2.22.0 to 2.23.1.
- [Release notes](https://github.com/bootstrap-vue/bootstrap-vue/releases)
- [Changelog](https://github.com/bootstrap-vue/bootstrap-vue/blob/dev/CHANGELOG.md)
- [Commits](https://github.com/bootstrap-vue/bootstrap-vue/compare/v2.22.0...v2.23.1)

---
updated-dependencies:
- dependency-name: bootstrap-vue
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-12-15 19:43:02 +00:00
vabene1111
01bb84e40b Merge pull request #2196 from TandoorRecipes/dependabot/npm_and_yarn/vue/axios-1.2.0
Bump axios from 1.1.3 to 1.2.0 in /vue
2022-12-15 20:42:11 +01:00
vabene1111
f66b422f68 Merge pull request #2198 from TandoorRecipes/dependabot/npm_and_yarn/vue/core-js-3.26.1
Bump core-js from 3.26.0 to 3.26.1 in /vue
2022-12-15 20:42:02 +01:00
dependabot[bot]
abab970f08 Bump django-webpack-loader from 1.6.0 to 1.8.0
Bumps [django-webpack-loader](https://github.com/django-webpack/django-webpack-loader) from 1.6.0 to 1.8.0.
- [Release notes](https://github.com/django-webpack/django-webpack-loader/releases)
- [Changelog](https://github.com/django-webpack/django-webpack-loader/blob/master/CHANGELOG.md)
- [Commits](https://github.com/django-webpack/django-webpack-loader/compare/1.6.0...1.8.0)

---
updated-dependencies:
- dependency-name: django-webpack-loader
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-12-15 19:41:59 +00:00
vabene1111
edaa6de71d Merge pull request #2194 from TandoorRecipes/dependabot/pip/recipe-scrapers-14.24.0
Bump recipe-scrapers from 14.23.0 to 14.24.0
2022-12-15 20:41:29 +01:00
vabene1111
85a65127cf Merge pull request #2192 from TandoorRecipes/dependabot/pip/icalendar-5.0.3
Bump icalendar from 5.0.1 to 5.0.3
2022-12-15 20:41:24 +01:00
vabene1111
99035190f4 Merge pull request #2191 from TandoorRecipes/dependabot/pip/pytest-7.2.0
Bump pytest from 7.1.3 to 7.2.0
2022-12-15 20:41:18 +01:00
Johnny Walker
f1611fbafd set manifest link to use-credentials ensuring that cookies are passed with that request 2022-12-14 21:36:19 -05:00
fliiiix
e42ff2fb8b fix install guide link and typo 2022-12-10 09:20:34 +01:00
vabene1111
bc93071167 Merge pull request #2204 from TandoorRecipes/dependabot/npm_and_yarn/vue/decode-uri-component-0.2.2
Bump decode-uri-component from 0.2.0 to 0.2.2 in /vue
2022-12-09 16:53:31 +01:00
dependabot[bot]
410fa58d47 Bump decode-uri-component from 0.2.0 to 0.2.2 in /vue
Bumps [decode-uri-component](https://github.com/SamVerschueren/decode-uri-component) from 0.2.0 to 0.2.2.
- [Release notes](https://github.com/SamVerschueren/decode-uri-component/releases)
- [Commits](https://github.com/SamVerschueren/decode-uri-component/compare/v0.2.0...v0.2.2)

---
updated-dependencies:
- dependency-name: decode-uri-component
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-12-08 20:50:23 +00:00
dependabot[bot]
06fd03fbde Bump webpack-bundle-tracker from 1.6.0 to 1.7.0 in /vue
Bumps [webpack-bundle-tracker](https://github.com/django-webpack/webpack-bundle-tracker) from 1.6.0 to 1.7.0.
- [Release notes](https://github.com/django-webpack/webpack-bundle-tracker/releases)
- [Commits](https://github.com/django-webpack/webpack-bundle-tracker/compare/1.6.0...1.7.0)

---
updated-dependencies:
- dependency-name: webpack-bundle-tracker
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-12-01 00:08:43 +00:00
dependabot[bot]
d169456c78 Bump core-js from 3.26.0 to 3.26.1 in /vue
Bumps [core-js](https://github.com/zloirock/core-js/tree/HEAD/packages/core-js) from 3.26.0 to 3.26.1.
- [Release notes](https://github.com/zloirock/core-js/releases)
- [Changelog](https://github.com/zloirock/core-js/blob/master/CHANGELOG.md)
- [Commits](https://github.com/zloirock/core-js/commits/v3.26.1/packages/core-js)

---
updated-dependencies:
- dependency-name: core-js
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-12-01 00:08:18 +00:00
dependabot[bot]
7785aa4904 Bump axios from 1.1.3 to 1.2.0 in /vue
Bumps [axios](https://github.com/axios/axios) from 1.1.3 to 1.2.0.
- [Release notes](https://github.com/axios/axios/releases)
- [Changelog](https://github.com/axios/axios/blob/v1.x/CHANGELOG.md)
- [Commits](https://github.com/axios/axios/compare/v1.1.3...v1.2.0)

---
updated-dependencies:
- dependency-name: axios
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-12-01 00:06:01 +00:00
dependabot[bot]
33798fe47e Bump typescript from 4.8.4 to 4.9.3 in /vue
Bumps [typescript](https://github.com/Microsoft/TypeScript) from 4.8.4 to 4.9.3.
- [Release notes](https://github.com/Microsoft/TypeScript/releases)
- [Commits](https://github.com/Microsoft/TypeScript/compare/v4.8.4...v4.9.3)

---
updated-dependencies:
- dependency-name: typescript
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-12-01 00:04:33 +00:00
dependabot[bot]
0522b15cfd Bump recipe-scrapers from 14.23.0 to 14.24.0
Bumps [recipe-scrapers](https://github.com/hhursev/recipe-scrapers) from 14.23.0 to 14.24.0.
- [Release notes](https://github.com/hhursev/recipe-scrapers/releases)
- [Commits](https://github.com/hhursev/recipe-scrapers/compare/14.23.0...14.24.0)

---
updated-dependencies:
- dependency-name: recipe-scrapers
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-12-01 00:01:58 +00:00
dependabot[bot]
8cfd3995d0 Bump cryptography from 38.0.3 to 38.0.4
Bumps [cryptography](https://github.com/pyca/cryptography) from 38.0.3 to 38.0.4.
- [Release notes](https://github.com/pyca/cryptography/releases)
- [Changelog](https://github.com/pyca/cryptography/blob/main/CHANGELOG.rst)
- [Commits](https://github.com/pyca/cryptography/compare/38.0.3...38.0.4)

---
updated-dependencies:
- dependency-name: cryptography
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-12-01 00:01:48 +00:00
dependabot[bot]
84cdd8bb78 Bump icalendar from 5.0.1 to 5.0.3
Bumps [icalendar](https://github.com/collective/icalendar) from 5.0.1 to 5.0.3.
- [Release notes](https://github.com/collective/icalendar/releases)
- [Changelog](https://github.com/collective/icalendar/blob/master/CHANGES.rst)
- [Commits](https://github.com/collective/icalendar/compare/v5.0.1...v5.0.3)

---
updated-dependencies:
- dependency-name: icalendar
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-12-01 00:01:39 +00:00
dependabot[bot]
ad0177235d Bump pytest from 7.1.3 to 7.2.0
Bumps [pytest](https://github.com/pytest-dev/pytest) from 7.1.3 to 7.2.0.
- [Release notes](https://github.com/pytest-dev/pytest/releases)
- [Changelog](https://github.com/pytest-dev/pytest/blob/main/CHANGELOG.rst)
- [Commits](https://github.com/pytest-dev/pytest/compare/7.1.3...7.2.0)

---
updated-dependencies:
- dependency-name: pytest
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-12-01 00:01:31 +00:00
Alex
e5782151a1 Translated using Weblate (Russian)
Currently translated at 9.0% (45 of 496 strings)

Translation: Tandoor/Recipes Backend
Translate-URL: http://translate.tandoor.dev/projects/tandoor/recipes-backend/ru/
2022-11-30 19:09:40 +00:00
Alex
adf4dafd01 Translated using Weblate (Russian)
Currently translated at 74.3% (342 of 460 strings)

Translation: Tandoor/Recipes Frontend
Translate-URL: http://translate.tandoor.dev/projects/tandoor/recipes-frontend/ru/
2022-11-30 19:09:40 +00:00
vabene1111
4214ef4a9f update oauth toolkit 2022-11-24 22:36:22 +01:00
vabene1111
1df0ad202f fixed urlize markdown 3.4 compatibility 2022-11-24 22:29:29 +01:00
vabene1111
e35cbba8b2 Merge pull request #2179 from bee395/feature/unpin-pins
Unpin pin from recipe page.
2022-11-22 20:21:44 +01:00
vabene1111
e6fa660c8f Merge branch 'develop' into feature/unpin-pins 2022-11-22 20:21:36 +01:00
Jasper Hartog
8aefdb71bb Add missing translation key for Split_All_Steps and Combine_All_Steps. 2022-11-22 19:11:42 +01:00
Jasper Hartog
5da8c6fe7b Unpin pin from recipe page. 2022-11-22 19:06:10 +01:00
vabene1111
520697e988 Merge pull request #2182 from TandoorRecipes/dependabot/npm_and_yarn/vue/babel-loader-9.1.0
Bump babel-loader from 9.0.1 to 9.1.0 in /vue
2022-11-22 08:44:35 +01:00
dependabot[bot]
7fe6fd3462 Bump babel-loader from 9.0.1 to 9.1.0 in /vue
Bumps [babel-loader](https://github.com/babel/babel-loader) from 9.0.1 to 9.1.0.
- [Release notes](https://github.com/babel/babel-loader/releases)
- [Changelog](https://github.com/babel/babel-loader/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel-loader/compare/v9.0.1...v9.1.0)

---
updated-dependencies:
- dependency-name: babel-loader
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-11-22 07:41:21 +00:00
vabene1111
85b3941539 Merge pull request #2173 from TandoorRecipes/dependabot/npm_and_yarn/vue/loader-utils-1.4.2
Bump loader-utils from 1.4.1 to 1.4.2 in /vue
2022-11-22 08:38:30 +01:00
vabene1111
6f5ea7bb48 Merge pull request #2164 from TandoorRecipes/dependabot/pip/django-4.1.3
Bump django from 4.0.8 to 4.1.3
2022-11-22 08:38:26 +01:00
vabene1111
e9a2b101d8 Merge pull request #2153 from TandoorRecipes/dependabot/npm_and_yarn/vue/vue-i18n-8.28.2
Bump vue-i18n from 8.27.2 to 8.28.2 in /vue
2022-11-22 08:38:21 +01:00
vabene1111
c01faff135 Merge pull request #2152 from TandoorRecipes/dependabot/npm_and_yarn/vue/core-js-3.26.0
Bump core-js from 3.25.3 to 3.26.0 in /vue
2022-11-22 08:38:05 +01:00
vabene1111
bcee0007a5 Merge pull request #2151 from TandoorRecipes/dependabot/npm_and_yarn/vue/babel-loader-9.0.1
Bump babel-loader from 8.2.5 to 9.0.1 in /vue
2022-11-22 08:38:00 +01:00
vabene1111
f5ec956e08 Merge pull request #2149 from TandoorRecipes/dependabot/npm_and_yarn/vue/axios-1.1.3
Bump axios from 0.27.2 to 1.1.3 in /vue
2022-11-22 08:37:54 +01:00
vabene1111
56926d55ba Merge pull request #2148 from TandoorRecipes/dependabot/pip/icalendar-5.0.1
Bump icalendar from 4.1.0 to 5.0.1
2022-11-22 08:37:48 +01:00
vabene1111
55cfe9e9e7 Merge pull request #2146 from TandoorRecipes/dependabot/pip/markdown-3.4.1
Bump markdown from 3.3.7 to 3.4.1
2022-11-22 08:37:43 +01:00
vabene1111
31bdd97a56 Merge pull request #2145 from TandoorRecipes/dependabot/pip/django-debug-toolbar-3.7.0
Bump django-debug-toolbar from 3.6.0 to 3.7.0
2022-11-22 08:37:34 +01:00
vabene1111
eb60cbdd6b Merge pull request #2144 from TandoorRecipes/dependabot/pip/psycopg2-binary-2.9.5
Bump psycopg2-binary from 2.9.3 to 2.9.5
2022-11-22 08:37:28 +01:00
vabene1111
39ccf7bbcf added plural migration and pass param trough in recipe view 2022-11-22 07:58:12 +01:00
vabene1111
f92ee32c01 Merge pull request #1860 from Knalltuete5000/feature/simple_plural
Add optional plural name for unit and food
2022-11-22 07:34:23 +01:00
Alex
5aecf7e61c Translated using Weblate (German)
Currently translated at 100.0% (524 of 524 strings)

Translation: Tandoor/Recipes Backend
Translate-URL: http://translate.tandoor.dev/projects/tandoor/recipes-backend/de/
2022-11-21 19:09:37 +00:00
dependabot[bot]
20435450f3 Bump loader-utils from 1.4.1 to 1.4.2 in /vue
Bumps [loader-utils](https://github.com/webpack/loader-utils) from 1.4.1 to 1.4.2.
- [Release notes](https://github.com/webpack/loader-utils/releases)
- [Changelog](https://github.com/webpack/loader-utils/blob/v1.4.2/CHANGELOG.md)
- [Commits](https://github.com/webpack/loader-utils/compare/v1.4.1...v1.4.2)

---
updated-dependencies:
- dependency-name: loader-utils
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-11-16 06:54:03 +00:00
dependabot[bot]
13e5fb4143 Bump psycopg2-binary from 2.9.3 to 2.9.5
Bumps [psycopg2-binary](https://github.com/psycopg/psycopg2) from 2.9.3 to 2.9.5.
- [Release notes](https://github.com/psycopg/psycopg2/releases)
- [Changelog](https://github.com/psycopg/psycopg2/blob/master/NEWS)
- [Commits](https://github.com/psycopg/psycopg2/commits)

---
updated-dependencies:
- dependency-name: psycopg2-binary
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-11-16 06:54:01 +00:00
dependabot[bot]
bdc6434839 Bump markdown from 3.3.7 to 3.4.1
Bumps [markdown](https://github.com/Python-Markdown/markdown) from 3.3.7 to 3.4.1.
- [Release notes](https://github.com/Python-Markdown/markdown/releases)
- [Commits](https://github.com/Python-Markdown/markdown/compare/3.3.7...3.4.1)

---
updated-dependencies:
- dependency-name: markdown
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-11-16 06:54:00 +00:00
vabene1111
796609de37 Merge pull request #2172 from TandoorRecipes/dependabot/pip/pillow-9.3.0
Bump pillow from 9.2.0 to 9.3.0
2022-11-16 07:53:15 +01:00
dependabot[bot]
8759e8dd73 Bump pillow from 9.2.0 to 9.3.0
Bumps [pillow](https://github.com/python-pillow/Pillow) from 9.2.0 to 9.3.0.
- [Release notes](https://github.com/python-pillow/Pillow/releases)
- [Changelog](https://github.com/python-pillow/Pillow/blob/main/CHANGES.rst)
- [Commits](https://github.com/python-pillow/Pillow/compare/9.2.0...9.3.0)

---
updated-dependencies:
- dependency-name: pillow
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-11-15 13:49:56 +00:00
vabene1111
f8bf54189e Merge pull request #2167 from TandoorRecipes/revert-2128-develop
Revert "Fixed: ports are on the wrong service"
2022-11-13 13:24:25 +01:00
vabene1111
3e2988f998 Revert "Fixed: ports are on the wrong service" 2022-11-13 13:24:13 +01:00
Niklas Schwarz
2e5571f0a9 Fix naming of exported SpaceManageView 2022-11-11 18:15:57 +01:00
Niklas Schwarz
c97bb900a3 Add documentation page for the space settings 2022-11-11 18:15:57 +01:00
Niklas Schwarz
b1ad5ef205 Add option to space to enable plural name usage
* Add option to space to enable usage of plural name
  for food and unit per space

* Respect option per space to use the plural name
2022-11-11 18:15:57 +01:00
Niklas Schwarz
9994b6f9c2 Add plural name for unit and food
Add an optional plural name for unit and food.
Additional options to show always the plural name for unit and food
for an ingredient is added.
2022-11-11 18:15:57 +01:00
vabene1111
a8a590a942 Update SECURITY.md 2022-11-10 12:46:29 +01:00
vabene1111
4e13fb3b8c Merge branch 'develop' 2022-11-09 14:30:02 +01:00
vabene1111
24f331c208 improved ingredient parser handling of amount unit without space 2022-11-09 14:24:12 +01:00
vabene1111
16d0fc38f9 improved paste ingredient function to retain order and add to correct step 2022-11-09 14:01:03 +01:00
vabene1111
5e4cac52d6 fixed recipe image size with nutrition information 2022-11-09 13:23:43 +01:00
vabene1111
b489a2d849 Merge pull request #2101 from Mikhail5555/patch-2
Allow 1/16th to be a fraction (small salt amounts)
2022-11-09 12:53:13 +01:00
vabene1111
7c5707e0c0 Merge pull request #2128 from rdangdev/develop
Fixed: ports are on the wrong service
2022-11-09 12:52:40 +01:00
vabene1111
946699a335 Merge pull request #2154 from mh166/feature-enable-search-plugin
Enable search plugin for docs
2022-11-09 12:52:14 +01:00
dependabot[bot]
4888e2d476 Bump django from 4.0.8 to 4.1.3
Bumps [django](https://github.com/django/django) from 4.0.8 to 4.1.3.
- [Release notes](https://github.com/django/django/releases)
- [Commits](https://github.com/django/django/compare/4.0.8...4.1.3)

---
updated-dependencies:
- dependency-name: django
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-11-09 11:50:09 +00:00
vabene1111
44b2c02034 updated cryptography 2022-11-09 12:49:23 +01:00
vabene1111
c150c7f84e fixed recipekeeper integration 2022-11-09 12:48:34 +01:00
vabene1111
97503a68d8 Merge pull request #2163 from TandoorRecipes/dependabot/npm_and_yarn/vue/loader-utils-1.4.1
Bump loader-utils from 1.4.0 to 1.4.1 in /vue
2022-11-09 12:17:21 +01:00
dependabot[bot]
126a2d870e Bump loader-utils from 1.4.0 to 1.4.1 in /vue
Bumps [loader-utils](https://github.com/webpack/loader-utils) from 1.4.0 to 1.4.1.
- [Release notes](https://github.com/webpack/loader-utils/releases)
- [Changelog](https://github.com/webpack/loader-utils/blob/v1.4.1/CHANGELOG.md)
- [Commits](https://github.com/webpack/loader-utils/compare/v1.4.0...v1.4.1)

---
updated-dependencies:
- dependency-name: loader-utils
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-11-09 03:45:14 +00:00
Gorkem
02bad8cfb9 Translated using Weblate (Turkish)
Currently translated at 27.1% (125 of 460 strings)

Translation: Tandoor/Recipes Frontend
Translate-URL: http://translate.tandoor.dev/projects/tandoor/recipes-frontend/tr/
2022-11-06 22:09:31 +00:00
Gorkem
d9465c7f9d Translated using Weblate (Turkish)
Currently translated at 2.2% (12 of 524 strings)

Translation: Tandoor/Recipes Backend
Translate-URL: http://translate.tandoor.dev/projects/tandoor/recipes-backend/tr/
2022-11-06 22:09:31 +00:00
Gorkem
ead3168d80 Added translation using Weblate (Turkish) 2022-11-05 21:09:24 +00:00
mh166
a71bba307e Enable search plugin for docs
I think mkdocs-material's search function would be a great addition to make the documentation even more accessible. At least I was looking for some info just moments ago and would've liked a search bar.

From my own projects, I remembered that mkdocs-material does come with the search search plugin enabled by default. But it must be re-added to mkdocs.yml when other plugins are used. So I did. Hope this helps. :)
2022-11-01 11:44:56 +01:00
dependabot[bot]
d2a652891c Bump vue-i18n from 8.27.2 to 8.28.2 in /vue
Bumps [vue-i18n](https://github.com/intlify/vue-i18n-next/tree/HEAD/packages/vue-i18n) from 8.27.2 to 8.28.2.
- [Release notes](https://github.com/intlify/vue-i18n-next/releases)
- [Changelog](https://github.com/intlify/vue-i18n-next/blob/master/CHANGELOG.md)
- [Commits](https://github.com/intlify/vue-i18n-next/commits/HEAD/packages/vue-i18n)

---
updated-dependencies:
- dependency-name: vue-i18n
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-11-01 00:20:16 +00:00
dependabot[bot]
a70ac42717 Bump core-js from 3.25.3 to 3.26.0 in /vue
Bumps [core-js](https://github.com/zloirock/core-js/tree/HEAD/packages/core-js) from 3.25.3 to 3.26.0.
- [Release notes](https://github.com/zloirock/core-js/releases)
- [Changelog](https://github.com/zloirock/core-js/blob/master/CHANGELOG.md)
- [Commits](https://github.com/zloirock/core-js/commits/v3.26.0/packages/core-js)

---
updated-dependencies:
- dependency-name: core-js
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-11-01 00:20:00 +00:00
dependabot[bot]
a7bcf105dc Bump babel-loader from 8.2.5 to 9.0.1 in /vue
Bumps [babel-loader](https://github.com/babel/babel-loader) from 8.2.5 to 9.0.1.
- [Release notes](https://github.com/babel/babel-loader/releases)
- [Changelog](https://github.com/babel/babel-loader/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel-loader/compare/v8.2.5...v9.0.1)

---
updated-dependencies:
- dependency-name: babel-loader
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-11-01 00:19:20 +00:00
dependabot[bot]
8be02b4e74 Bump axios from 0.27.2 to 1.1.3 in /vue
Bumps [axios](https://github.com/axios/axios) from 0.27.2 to 1.1.3.
- [Release notes](https://github.com/axios/axios/releases)
- [Changelog](https://github.com/axios/axios/blob/v1.x/CHANGELOG.md)
- [Commits](https://github.com/axios/axios/compare/v0.27.2...v1.1.3)

---
updated-dependencies:
- dependency-name: axios
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-11-01 00:17:07 +00:00
dependabot[bot]
9ba9cda1c0 Bump icalendar from 4.1.0 to 5.0.1
Bumps [icalendar](https://github.com/collective/icalendar) from 4.1.0 to 5.0.1.
- [Release notes](https://github.com/collective/icalendar/releases)
- [Changelog](https://github.com/collective/icalendar/blob/master/CHANGES.rst)
- [Commits](https://github.com/collective/icalendar/compare/4.1.0...v5.0.1)

---
updated-dependencies:
- dependency-name: icalendar
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-11-01 00:09:27 +00:00
dependabot[bot]
4310282dc3 Bump django-debug-toolbar from 3.6.0 to 3.7.0
Bumps [django-debug-toolbar](https://github.com/jazzband/django-debug-toolbar) from 3.6.0 to 3.7.0.
- [Release notes](https://github.com/jazzband/django-debug-toolbar/releases)
- [Changelog](https://github.com/jazzband/django-debug-toolbar/blob/main/docs/changes.rst)
- [Commits](https://github.com/jazzband/django-debug-toolbar/compare/3.6...3.7)

---
updated-dependencies:
- dependency-name: django-debug-toolbar
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-11-01 00:09:07 +00:00
vabene1111
c2c08391cc fixed sharing pref saving 2022-10-31 19:51:06 +01:00
vabene1111
bc9d077b9d added used md spec to docs 2022-10-31 19:51:06 +01:00
vabene1111
fe0f739bd5 Merge pull request #2020 from ignis-draco/docuUpdate
Update Doku for manual Installation
2022-10-31 19:49:30 +01:00
vabene1111
e5b11a34f6 Update manual.md 2022-10-31 19:49:24 +01:00
vabene1111
1df7a4df91 Merge pull request #2121 from raj3000k/develop
Improved Overall Docs.
2022-10-31 19:47:29 +01:00
vabene1111
d401c143ec added faq for makrdown preview 2022-10-31 14:58:50 +01:00
vabene1111
00a59baa92 Merge pull request #2140 from TiagoRascazzi/develop
Added nextcloud export implementation
2022-10-31 14:34:16 +01:00
vabene1111
327c83ce32 improved share link tests 2022-10-31 14:30:39 +01:00
vabene1111
3371102e64 improved and added tests for share link creation 2022-10-31 14:24:41 +01:00
vabene1111
aec396e214 Merge pull request #2106 from swnf/fix-share-permissions
Fix share permission check
2022-10-31 14:16:01 +01:00
vabene1111
2b52b5c264 Merge pull request #2142 from TandoorRecipes/dependabot/pip/django-4.0.8
Bump django from 4.0.7 to 4.0.8
2022-10-31 09:03:24 +01:00
dependabot[bot]
19c24a85a1 Bump django from 4.0.7 to 4.0.8
Bumps [django](https://github.com/django/django) from 4.0.7 to 4.0.8.
- [Release notes](https://github.com/django/django/releases)
- [Commits](https://github.com/django/django/compare/4.0.7...4.0.8)

---
updated-dependencies:
- dependency-name: django
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-10-31 08:02:46 +00:00
vabene1111
c147903f1e updated recipe scrapers and compiled translations 2022-10-31 09:02:14 +01:00
Tiago Rascazzi
9dedc5b8fa Added nextcloud export implementation 2022-10-29 14:08:12 -04:00
Tomasz Klimczak
d781cbe743 Translated using Weblate (Polish)
Currently translated at 100.0% (460 of 460 strings)

Translation: Tandoor/Recipes Frontend
Translate-URL: http://translate.tandoor.dev/projects/tandoor/recipes-frontend/pl/
2022-10-22 20:33:14 +00:00
rdang
37bd2017b0 ports are on the wrong service
the actual webserver is running on port 8080 and on the web_recipes service, as per all the documentation. 80 will not work, at least on the latest patch.
2022-10-21 19:13:18 +11:00
Oliver Cervera
2de8070156 Translated using Weblate (Italian)
Currently translated at 75.2% (346 of 460 strings)

Translation: Tandoor/Recipes Frontend
Translate-URL: http://translate.tandoor.dev/projects/tandoor/recipes-frontend/it/
2022-10-19 22:33:13 +00:00
Sokratis Potamias
f70377c59b Translated using Weblate (Greek)
Currently translated at 0.5% (3 of 524 strings)

Translation: Tandoor/Recipes Backend
Translate-URL: http://translate.tandoor.dev/projects/tandoor/recipes-backend/el/
2022-10-17 11:33:12 +00:00
Sokratis Potamias
6fc4151de5 Added translation using Weblate (Greek) 2022-10-16 11:01:56 +00:00
Shaxine
1fa001aad3 Translated using Weblate (Portuguese)
Currently translated at 28.4% (149 of 524 strings)

Translation: Tandoor/Recipes Backend
Translate-URL: http://translate.tandoor.dev/projects/tandoor/recipes-backend/pt/
2022-10-14 17:19:28 +00:00
Raj Motwani
b84e03c58b Merge branch 'TandoorRecipes:develop' into develop 2022-10-14 22:26:12 +05:30
wella
e9dac25ff4 Translated using Weblate (Indonesian)
Currently translated at 11.4% (60 of 524 strings)

Translation: Tandoor/Recipes Backend
Translate-URL: http://translate.tandoor.dev/projects/tandoor/recipes-backend/id/
2022-10-12 08:33:09 +00:00
Oliver Cervera
611787dbb6 Translated using Weblate (Italian)
Currently translated at 75.2% (346 of 460 strings)

Translation: Tandoor/Recipes Frontend
Translate-URL: http://translate.tandoor.dev/projects/tandoor/recipes-frontend/it/
2022-10-10 17:33:11 +00:00
Oliver Cervera
bfbfb1d2a8 Translated using Weblate (Italian)
Currently translated at 83.3% (437 of 524 strings)

Translation: Tandoor/Recipes Backend
Translate-URL: http://translate.tandoor.dev/projects/tandoor/recipes-backend/it/
2022-10-10 17:33:11 +00:00
Raj Motwani
d9662f7fa5 Update import_export.md
Fixed some typos and spellings.
2022-10-08 13:47:02 +05:30
Raj Motwani
9e44944b1d Update SECURITY.md 2022-10-08 13:38:52 +05:30
swnf
4de9a7ff89 Fix share permission check 2022-10-06 17:44:33 +02:00
vabene1111
32a663c5d7 Merge pull request #2104 from chiaramistro/feature/login-btn-reset-password
Remove yellow button to unify GUI of reset password in mobile/web viewports #2102
2022-10-05 16:42:42 +02:00
Chiara
3bee5ed35a Remove yellow button to unify GUI of reset password in mobile/web viewports #2102 2022-10-05 12:25:29 +02:00
Mikhail Epifanov
bee5d6b7eb Allow 1/16th to be a fraction (small salt amounts) 2022-10-03 12:37:09 +02:00
wella
00ed9b07b6 Translated using Weblate (Indonesian)
Currently translated at 8.3% (44 of 524 strings)

Translation: Tandoor/Recipes Backend
Translate-URL: http://translate.tandoor.dev/projects/tandoor/recipes-backend/id/
2022-10-02 17:33:10 +00:00
wella
2279bba838 Translated using Weblate (Indonesian)
Currently translated at 31.5% (145 of 460 strings)

Translation: Tandoor/Recipes Frontend
Translate-URL: http://translate.tandoor.dev/projects/tandoor/recipes-frontend/id/
2022-10-02 17:33:10 +00:00
Andrea
57f5343c77 Translated using Weblate (Spanish)
Currently translated at 75.2% (346 of 460 strings)

Translation: Tandoor/Recipes Frontend
Translate-URL: http://translate.tandoor.dev/projects/tandoor/recipes-frontend/es/
2022-10-02 17:33:10 +00:00
vabene1111
da8262a9b5 Merge pull request #2088 from TandoorRecipes/dependabot/npm_and_yarn/vue/workbox-webpack-plugin-6.5.4
Bump workbox-webpack-plugin from 6.5.3 to 6.5.4 in /vue
2022-10-02 12:28:29 +02:00
vabene1111
f0cf4a23e4 Merge pull request #2095 from TandoorRecipes/feature/ingredient-actions
Feature/ingredient actions
2022-10-01 19:28:23 +02:00
vabene1111
489c81c378 Merge pull request #2094 from TandoorRecipes/feature/step-time-format
format step time
2022-10-01 19:28:13 +02:00
vabene1111
730344e326 duplicate ingredient to correct location 2022-10-01 19:27:09 +02:00
vabene1111
7e6b1d3638 added move and copy buttons to ingredient 2022-10-01 19:25:18 +02:00
vabene1111
15f65cd711 format step time 2022-10-01 19:10:58 +02:00
vabene1111
dba205dafb Merge pull request #2092 from wellart/develop
add indonesian language
2022-10-01 19:04:29 +02:00
KangAlleW
5ae149a1b6 add indonesian language 2022-10-01 23:55:37 +07:00
dependabot[bot]
4bb2307007 Bump workbox-webpack-plugin from 6.5.3 to 6.5.4 in /vue
Bumps [workbox-webpack-plugin](https://github.com/googlechrome/workbox) from 6.5.3 to 6.5.4.
- [Release notes](https://github.com/googlechrome/workbox/releases)
- [Commits](https://github.com/googlechrome/workbox/compare/v6.5.3...v6.5.4)

---
updated-dependencies:
- dependency-name: workbox-webpack-plugin
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-10-01 16:45:04 +00:00
vabene1111
be0088aec6 Merge pull request #2091 from TandoorRecipes/feature/fix-nc-import-nutrition
fixed nc importer nutritions
2022-10-01 18:42:16 +02:00
vabene1111
c56710ae0c fixed nc importer nutritions 2022-10-01 18:42:03 +02:00
vabene1111
1a420bc002 Merge pull request #2090 from TandoorRecipes/feature/fix-dependencies
fixed vue template compiler version and removed debug print from perm…
2022-10-01 18:41:21 +02:00
wella
545e4f7af4 Translated using Weblate (Indonesian)
Currently translated at 5.3% (28 of 524 strings)

Translation: Tandoor/Recipes Backend
Translate-URL: http://translate.tandoor.dev/projects/tandoor/recipes-backend/id/
2022-10-01 16:38:41 +00:00
wella
d2a148ae7d Translated using Weblate (Indonesian)
Currently translated at 22.8% (105 of 460 strings)

Translation: Tandoor/Recipes Frontend
Translate-URL: http://translate.tandoor.dev/projects/tandoor/recipes-frontend/id/
2022-10-01 16:32:26 +00:00
wella
580591a69e Added translation using Weblate (Indonesian) 2022-10-01 16:32:26 +00:00
vabene1111
409b438776 fixed vue template compiler version and removed debug print from permission helper 2022-10-01 18:18:00 +02:00
vabene1111
549175b56d Merge pull request #2086 from TandoorRecipes/dependabot/npm_and_yarn/vue/vue/cli-service-5.0.8
Bump @vue/cli-service from 5.0.4 to 5.0.8 in /vue
2022-10-01 18:09:21 +02:00
vabene1111
0e3f5006b1 Merge pull request #2087 from TandoorRecipes/dependabot/npm_and_yarn/vue/workbox-expiration-6.5.4
Bump workbox-expiration from 6.5.3 to 6.5.4 in /vue
2022-10-01 18:09:14 +02:00
vabene1111
54043a0ae5 Merge pull request #2089 from TandoorRecipes/dependabot/npm_and_yarn/vue/typescript-4.8.4
Bump typescript from 4.8.2 to 4.8.4 in /vue
2022-10-01 18:09:06 +02:00
dependabot[bot]
36fdc8cd9e Bump typescript from 4.8.2 to 4.8.4 in /vue
Bumps [typescript](https://github.com/Microsoft/TypeScript) from 4.8.2 to 4.8.4.
- [Release notes](https://github.com/Microsoft/TypeScript/releases)
- [Commits](https://github.com/Microsoft/TypeScript/compare/v4.8.2...v4.8.4)

---
updated-dependencies:
- dependency-name: typescript
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-10-01 16:04:36 +00:00
dependabot[bot]
87cf3b2289 Bump workbox-expiration from 6.5.3 to 6.5.4 in /vue
Bumps [workbox-expiration](https://github.com/googlechrome/workbox) from 6.5.3 to 6.5.4.
- [Release notes](https://github.com/googlechrome/workbox/releases)
- [Commits](https://github.com/googlechrome/workbox/compare/v6.5.3...v6.5.4)

---
updated-dependencies:
- dependency-name: workbox-expiration
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-10-01 16:03:36 +00:00
dependabot[bot]
adb4071fdb Bump @vue/cli-service from 5.0.4 to 5.0.8 in /vue
Bumps [@vue/cli-service](https://github.com/vuejs/vue-cli/tree/HEAD/packages/@vue/cli-service) from 5.0.4 to 5.0.8.
- [Release notes](https://github.com/vuejs/vue-cli/releases)
- [Changelog](https://github.com/vuejs/vue-cli/blob/dev/CHANGELOG.md)
- [Commits](https://github.com/vuejs/vue-cli/commits/v5.0.8/packages/@vue/cli-service)

---
updated-dependencies:
- dependency-name: "@vue/cli-service"
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-10-01 16:03:22 +00:00
vabene1111
2a20f5e6e2 Merge pull request #2082 from TandoorRecipes/dependabot/npm_and_yarn/vue/prismjs-1.29.0
Bump prismjs from 1.28.0 to 1.29.0 in /vue
2022-10-01 17:59:43 +02:00
vabene1111
00f7ae3d66 Merge pull request #2084 from TandoorRecipes/dependabot/npm_and_yarn/vue/vue/cli-plugin-babel-5.0.8
Bump @vue/cli-plugin-babel from 5.0.4 to 5.0.8 in /vue
2022-10-01 17:59:36 +02:00
vabene1111
f1f4e7ca8e Merge pull request #2085 from TandoorRecipes/dependabot/npm_and_yarn/vue/workbox-precaching-6.5.4
Bump workbox-precaching from 6.5.3 to 6.5.4 in /vue
2022-10-01 17:59:32 +02:00
wella
6d7b3b8bfa Translated using Weblate (Indonesian)
Currently translated at 5.0% (23 of 460 strings)

Translation: Tandoor/Recipes Frontend
Translate-URL: http://translate.tandoor.dev/projects/tandoor/recipes-frontend/id/
2022-10-01 15:56:03 +00:00
wella
7ebccf564d Added translation using Weblate (Indonesian) 2022-10-01 15:44:15 +00:00
dependabot[bot]
0421a1aa6c Bump prismjs from 1.28.0 to 1.29.0 in /vue
Bumps [prismjs](https://github.com/PrismJS/prism) from 1.28.0 to 1.29.0.
- [Release notes](https://github.com/PrismJS/prism/releases)
- [Changelog](https://github.com/PrismJS/prism/blob/master/CHANGELOG.md)
- [Commits](https://github.com/PrismJS/prism/compare/v1.28.0...v1.29.0)

---
updated-dependencies:
- dependency-name: prismjs
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-10-01 15:09:02 +00:00
dependabot[bot]
c118ab9a3c Bump workbox-precaching from 6.5.3 to 6.5.4 in /vue
Bumps [workbox-precaching](https://github.com/googlechrome/workbox) from 6.5.3 to 6.5.4.
- [Release notes](https://github.com/googlechrome/workbox/releases)
- [Commits](https://github.com/googlechrome/workbox/compare/v6.5.3...v6.5.4)

---
updated-dependencies:
- dependency-name: workbox-precaching
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-10-01 15:09:01 +00:00
dependabot[bot]
02a12cf724 Bump @vue/cli-plugin-babel from 5.0.4 to 5.0.8 in /vue
Bumps [@vue/cli-plugin-babel](https://github.com/vuejs/vue-cli/tree/HEAD/packages/@vue/cli-plugin-babel) from 5.0.4 to 5.0.8.
- [Release notes](https://github.com/vuejs/vue-cli/releases)
- [Changelog](https://github.com/vuejs/vue-cli/blob/dev/CHANGELOG.md)
- [Commits](https://github.com/vuejs/vue-cli/commits/v5.0.8/packages/@vue/cli-plugin-babel)

---
updated-dependencies:
- dependency-name: "@vue/cli-plugin-babel"
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-10-01 15:08:51 +00:00
vabene1111
f28ca41b7b Merge pull request #2080 from TandoorRecipes/dependabot/npm_and_yarn/vue/vue-i18n-8.27.2
Bump vue-i18n from 8.27.1 to 8.27.2 in /vue
2022-10-01 17:05:50 +02:00
vabene1111
6e677cf3cd Merge pull request #1972 from TandoorRecipes/dependabot/npm_and_yarn/vue/vue/cli-plugin-typescript-5.0.8
Bump @vue/cli-plugin-typescript from 5.0.4 to 5.0.8 in /vue
2022-10-01 15:58:53 +02:00
vabene1111
d30a23f7ef Merge pull request #2079 from TandoorRecipes/dependabot/npm_and_yarn/vue/vue/cli-plugin-eslint-5.0.8
Bump @vue/cli-plugin-eslint from 5.0.4 to 5.0.8 in /vue
2022-10-01 15:58:49 +02:00
vabene1111
88fea6f25d Merge pull request #2081 from TandoorRecipes/dependabot/npm_and_yarn/vue/vue/compiler-sfc-3.2.40
Bump @vue/compiler-sfc from 3.2.36 to 3.2.40 in /vue
2022-10-01 15:58:42 +02:00
dependabot[bot]
fc0b5bd738 Bump @vue/cli-plugin-eslint from 5.0.4 to 5.0.8 in /vue
Bumps [@vue/cli-plugin-eslint](https://github.com/vuejs/vue-cli/tree/HEAD/packages/@vue/cli-plugin-eslint) from 5.0.4 to 5.0.8.
- [Release notes](https://github.com/vuejs/vue-cli/releases)
- [Changelog](https://github.com/vuejs/vue-cli/blob/dev/CHANGELOG.md)
- [Commits](https://github.com/vuejs/vue-cli/commits/v5.0.8/packages/@vue/cli-plugin-eslint)

---
updated-dependencies:
- dependency-name: "@vue/cli-plugin-eslint"
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-10-01 08:44:05 +00:00
dependabot[bot]
5174f9939c Bump @vue/cli-plugin-typescript from 5.0.4 to 5.0.8 in /vue
Bumps [@vue/cli-plugin-typescript](https://github.com/vuejs/vue-cli/tree/HEAD/packages/@vue/cli-plugin-typescript) from 5.0.4 to 5.0.8.
- [Release notes](https://github.com/vuejs/vue-cli/releases)
- [Changelog](https://github.com/vuejs/vue-cli/blob/dev/CHANGELOG.md)
- [Commits](https://github.com/vuejs/vue-cli/commits/v5.0.8/packages/@vue/cli-plugin-typescript)

---
updated-dependencies:
- dependency-name: "@vue/cli-plugin-typescript"
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-10-01 08:43:38 +00:00
dependabot[bot]
8f9a489c7e Bump @vue/compiler-sfc from 3.2.36 to 3.2.40 in /vue
Bumps [@vue/compiler-sfc](https://github.com/vuejs/core/tree/HEAD/packages/compiler-sfc) from 3.2.36 to 3.2.40.
- [Release notes](https://github.com/vuejs/core/releases)
- [Changelog](https://github.com/vuejs/core/blob/main/CHANGELOG.md)
- [Commits](https://github.com/vuejs/core/commits/v3.2.40/packages/compiler-sfc)

---
updated-dependencies:
- dependency-name: "@vue/compiler-sfc"
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-10-01 08:41:31 +00:00
dependabot[bot]
fc72efac04 Bump vue-i18n from 8.27.1 to 8.27.2 in /vue
Bumps [vue-i18n](https://github.com/intlify/vue-i18n-next/tree/HEAD/packages/vue-i18n) from 8.27.1 to 8.27.2.
- [Release notes](https://github.com/intlify/vue-i18n-next/releases)
- [Changelog](https://github.com/intlify/vue-i18n-next/blob/master/CHANGELOG.md)
- [Commits](https://github.com/intlify/vue-i18n-next/commits/HEAD/packages/vue-i18n)

---
updated-dependencies:
- dependency-name: vue-i18n
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-10-01 08:41:18 +00:00
vabene1111
72f57cf671 Merge pull request #2078 from TandoorRecipes/dependabot/npm_and_yarn/vue/vue-template-compiler-2.7.10
Bump vue-template-compiler from 2.6.14 to 2.7.10 in /vue
2022-10-01 10:41:05 +02:00
vabene1111
85b95d1e96 Merge pull request #2076 from TandoorRecipes/dependabot/npm_and_yarn/vue/core-js-3.25.3
Bump core-js from 3.25.0 to 3.25.3 in /vue
2022-10-01 10:40:55 +02:00
vabene1111
35dee43f0b Merge pull request #2077 from TandoorRecipes/dependabot/npm_and_yarn/vue/popperjs/core-2.11.6
Bump @popperjs/core from 2.11.5 to 2.11.6 in /vue
2022-10-01 10:40:46 +02:00
dependabot[bot]
fb683bf230 Bump vue-template-compiler from 2.6.14 to 2.7.10 in /vue
Bumps [vue-template-compiler](https://github.com/vuejs/vue) from 2.6.14 to 2.7.10.
- [Release notes](https://github.com/vuejs/vue/releases)
- [Changelog](https://github.com/vuejs/vue/blob/main/CHANGELOG.md)
- [Commits](https://github.com/vuejs/vue/compare/v2.6.14...v2.7.10)

---
updated-dependencies:
- dependency-name: vue-template-compiler
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-10-01 08:40:45 +00:00
dependabot[bot]
a852f581ba Bump @popperjs/core from 2.11.5 to 2.11.6 in /vue
Bumps [@popperjs/core](https://github.com/popperjs/popper-core) from 2.11.5 to 2.11.6.
- [Release notes](https://github.com/popperjs/popper-core/releases)
- [Commits](https://github.com/popperjs/popper-core/compare/v2.11.5...v2.11.6)

---
updated-dependencies:
- dependency-name: "@popperjs/core"
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-10-01 08:40:32 +00:00
dependabot[bot]
cc417f1499 Bump core-js from 3.25.0 to 3.25.3 in /vue
Bumps [core-js](https://github.com/zloirock/core-js) from 3.25.0 to 3.25.3.
- [Release notes](https://github.com/zloirock/core-js/releases)
- [Changelog](https://github.com/zloirock/core-js/blob/master/CHANGELOG.md)
- [Commits](https://github.com/zloirock/core-js/compare/v3.25.0...v3.25.3)

---
updated-dependencies:
- dependency-name: core-js
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-10-01 08:40:08 +00:00
vabene1111
7f9da4c4fb Merge pull request #2074 from TandoorRecipes/dependabot/npm_and_yarn/vue/babel/eslint-parser-7.19.1
Bump @babel/eslint-parser from 7.18.2 to 7.19.1 in /vue
2022-10-01 10:39:57 +02:00
dependabot[bot]
31d3f9abee Bump @babel/eslint-parser from 7.18.2 to 7.19.1 in /vue
Bumps [@babel/eslint-parser](https://github.com/babel/babel/tree/HEAD/eslint/babel-eslint-parser) from 7.18.2 to 7.19.1.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.19.1/eslint/babel-eslint-parser)

---
updated-dependencies:
- dependency-name: "@babel/eslint-parser"
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-10-01 08:39:30 +00:00
vabene1111
f9670e9833 Merge pull request #2073 from TandoorRecipes/dependabot/npm_and_yarn/vue/vue/cli-5.0.8
Bump @vue/cli from 5.0.4 to 5.0.8 in /vue
2022-10-01 10:39:16 +02:00
dependabot[bot]
465af8c1a4 Bump @vue/cli from 5.0.4 to 5.0.8 in /vue
Bumps [@vue/cli](https://github.com/vuejs/vue-cli/tree/HEAD/packages/@vue/cli) from 5.0.4 to 5.0.8.
- [Release notes](https://github.com/vuejs/vue-cli/releases)
- [Changelog](https://github.com/vuejs/vue-cli/blob/dev/CHANGELOG.md)
- [Commits](https://github.com/vuejs/vue-cli/commits/v5.0.8/packages/@vue/cli)

---
updated-dependencies:
- dependency-name: "@vue/cli"
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-10-01 08:39:06 +00:00
vabene1111
ffe743e233 Merge pull request #2070 from TandoorRecipes/dependabot/pip/python-ldap-3.4.3
Bump python-ldap from 3.4.2 to 3.4.3
2022-10-01 10:38:54 +02:00
vabene1111
6b09731a55 Merge pull request #2071 from TandoorRecipes/dependabot/pip/djangorestframework-3.14.0
Bump djangorestframework from 3.13.1 to 3.14.0
2022-10-01 10:38:46 +02:00
vabene1111
182a94e0c7 Merge pull request #1971 from TandoorRecipes/dependabot/npm_and_yarn/vue/vue/cli-plugin-pwa-5.0.8
Bump @vue/cli-plugin-pwa from 5.0.4 to 5.0.8 in /vue
2022-10-01 10:38:33 +02:00
vabene1111
2adaedfd1a Merge pull request #2028 from TandoorRecipes/dependabot/npm_and_yarn/vue/workbox-navigation-preload-6.5.4
Bump workbox-navigation-preload from 6.5.3 to 6.5.4 in /vue
2022-10-01 10:38:19 +02:00
dependabot[bot]
5074326471 Bump python-ldap from 3.4.2 to 3.4.3
Bumps [python-ldap](https://github.com/python-ldap/python-ldap) from 3.4.2 to 3.4.3.
- [Release notes](https://github.com/python-ldap/python-ldap/releases)
- [Commits](https://github.com/python-ldap/python-ldap/compare/python-ldap-3.4.2...python-ldap-3.4.3)

---
updated-dependencies:
- dependency-name: python-ldap
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-10-01 08:38:06 +00:00
dependabot[bot]
4807a16a0f Bump djangorestframework from 3.13.1 to 3.14.0
Bumps [djangorestframework](https://github.com/encode/django-rest-framework) from 3.13.1 to 3.14.0.
- [Release notes](https://github.com/encode/django-rest-framework/releases)
- [Commits](https://github.com/encode/django-rest-framework/compare/3.13.1...3.14.0)

---
updated-dependencies:
- dependency-name: djangorestframework
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-10-01 08:38:05 +00:00
vabene1111
af044f1002 Merge pull request #2072 from TandoorRecipes/dependabot/pip/recipe-scrapers-14.14.1
Bump recipe-scrapers from 14.14.0 to 14.14.1
2022-10-01 10:37:47 +02:00
dependabot[bot]
cdf77c8796 Bump recipe-scrapers from 14.14.0 to 14.14.1
Bumps [recipe-scrapers](https://github.com/hhursev/recipe-scrapers) from 14.14.0 to 14.14.1.
- [Release notes](https://github.com/hhursev/recipe-scrapers/releases)
- [Commits](https://github.com/hhursev/recipe-scrapers/compare/14.14.0...14.14.1)

---
updated-dependencies:
- dependency-name: recipe-scrapers
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-10-01 08:37:36 +00:00
vabene1111
e68bedf7eb Merge pull request #2037 from TandoorRecipes/dependabot/pip/django-storages-1.13.1
Bump django-storages from 1.12.3 to 1.13.1
2022-10-01 10:37:20 +02:00
vabene1111
5e21e7fa8e Merge pull request #1896 from TandoorRecipes/dependabot/npm_and_yarn/vue/webpack-bundle-tracker-1.6.0
Bump webpack-bundle-tracker from 1.5.0 to 1.6.0 in /vue
2022-10-01 10:37:07 +02:00
dependabot[bot]
f49b39b216 Bump django-storages from 1.12.3 to 1.13.1
Bumps [django-storages](https://github.com/jschneier/django-storages) from 1.12.3 to 1.13.1.
- [Release notes](https://github.com/jschneier/django-storages/releases)
- [Changelog](https://github.com/jschneier/django-storages/blob/master/CHANGELOG.rst)
- [Commits](https://github.com/jschneier/django-storages/compare/1.12.3...1.13.1)

---
updated-dependencies:
- dependency-name: django-storages
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-10-01 08:37:02 +00:00
vabene1111
0d24292f52 Merge pull request #1890 from TandoorRecipes/dependabot/pip/django-webpack-loader-1.6.0
Bump django-webpack-loader from 1.5.0 to 1.6.0
2022-10-01 10:37:02 +02:00
vabene1111
f3b7016be8 Merge pull request #2027 from TandoorRecipes/dependabot/npm_and_yarn/vue/workbox-routing-6.5.4
Bump workbox-routing from 6.5.3 to 6.5.4 in /vue
2022-10-01 10:36:41 +02:00
vabene1111
0f77c831c9 Merge pull request #2026 from TandoorRecipes/dependabot/pip/cryptography-38.0.1
Bump cryptography from 37.0.2 to 38.0.1
2022-10-01 10:36:35 +02:00
vabene1111
be48e57453 Merge pull request #2068 from TandoorRecipes/dependabot/pip/pytest-7.1.3
Bump pytest from 7.1.2 to 7.1.3
2022-10-01 10:36:24 +02:00
vabene1111
3b45ca18af Merge pull request #2069 from TandoorRecipes/dependabot/pip/boto3-1.24.84
Bump boto3 from 1.24.21 to 1.24.84
2022-10-01 10:36:18 +02:00
dependabot[bot]
da1b22c148 Bump boto3 from 1.24.21 to 1.24.84
Bumps [boto3](https://github.com/boto/boto3) from 1.24.21 to 1.24.84.
- [Release notes](https://github.com/boto/boto3/releases)
- [Changelog](https://github.com/boto/boto3/blob/develop/CHANGELOG.rst)
- [Commits](https://github.com/boto/boto3/compare/1.24.21...1.24.84)

---
updated-dependencies:
- dependency-name: boto3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-10-01 00:03:55 +00:00
dependabot[bot]
9dab21f972 Bump pytest from 7.1.2 to 7.1.3
Bumps [pytest](https://github.com/pytest-dev/pytest) from 7.1.2 to 7.1.3.
- [Release notes](https://github.com/pytest-dev/pytest/releases)
- [Changelog](https://github.com/pytest-dev/pytest/blob/main/CHANGELOG.rst)
- [Commits](https://github.com/pytest-dev/pytest/compare/7.1.2...7.1.3)

---
updated-dependencies:
- dependency-name: pytest
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-10-01 00:03:45 +00:00
vabene1111
89a5f92ace Merge branch 'develop' 2022-09-30 15:43:51 +02:00
vabene1111
7be705f6a1 fixed error in token generation endpoint 2022-09-30 15:42:07 +02:00
vabene1111
8e60566311 fixed recipekeeper import 2022-09-27 15:49:13 +02:00
vabene1111
33e5bb7d0a Merge branch 'develop' 2022-09-27 14:18:29 +02:00
vabene1111
0cf63cd715 Merge pull request #2065 from smarth42/patch-1
Update faq.md
2022-09-27 07:38:15 +02:00
Noé Feutry
5dc7bf5b0e Translated using Weblate (French)
Currently translated at 86.7% (399 of 460 strings)

Translation: Tandoor/Recipes Frontend
Translate-URL: http://translate.tandoor.dev/projects/tandoor/recipes-frontend/fr/
2022-09-26 16:33:07 +00:00
Noé Feutry
c4c66aa640 Translated using Weblate (French)
Currently translated at 84.7% (444 of 524 strings)

Translation: Tandoor/Recipes Backend
Translate-URL: http://translate.tandoor.dev/projects/tandoor/recipes-backend/fr/
2022-09-26 16:33:06 +00:00
vabene1111
f64be72a98 compiled translations 2022-09-26 09:18:19 +02:00
vabene1111
a3ed2bdcac Merge branch 'develop' of https://github.com/vabene1111/recipes into develop 2022-09-26 07:58:57 +02:00
vabene1111
996b8bedac fixed fuzzy search postgres 2022-09-26 07:58:52 +02:00
smarth42
a05a785e22 Update faq.md
updated user invite location.
2022-09-25 21:04:10 -05:00
Oliver Cervera
b470602317 Translated using Weblate (Italian)
Currently translated at 75.0% (345 of 460 strings)

Translation: Tandoor/Recipes Frontend
Translate-URL: http://translate.tandoor.dev/projects/tandoor/recipes-frontend/it/
2022-09-25 12:33:12 +00:00
Oliver Cervera
cf8ab02d0e Translated using Weblate (Italian)
Currently translated at 81.1% (425 of 524 strings)

Translation: Tandoor/Recipes Backend
Translate-URL: http://translate.tandoor.dev/projects/tandoor/recipes-backend/it/
2022-09-25 12:33:12 +00:00
vabene1111
60043fff59 changed contributions guidelines, please ask/talk first about new features 2022-09-23 17:07:04 +02:00
vabene1111
16c0189b80 Merge branch 'develop' 2022-09-23 17:00:11 +02:00
vabene1111
36c30f9e11 fixed print from card when recipe is not open 2022-09-23 16:55:25 +02:00
vabene1111
12a8582a9a fixed importer and copy recipe 2022-09-23 16:43:22 +02:00
vabene1111
13b91e5b91 improved swiping behavior on shopping list 2022-09-23 16:27:16 +02:00
vabene1111
d02b253242 fixed search settings not working with sqlite DB 2022-09-23 16:16:44 +02:00
henrique roberto lino
16528c4c89 Translated using Weblate (Portuguese)
Currently translated at 10.4% (48 of 460 strings)

Translation: Tandoor/Recipes Frontend
Translate-URL: http://translate.tandoor.dev/projects/tandoor/recipes-frontend/pt/
2022-09-23 02:33:11 +00:00
henrique roberto lino
6442e174b3 Translated using Weblate (Portuguese (Brazil))
Currently translated at 35.0% (161 of 460 strings)

Translation: Tandoor/Recipes Frontend
Translate-URL: http://translate.tandoor.dev/projects/tandoor/recipes-frontend/pt_BR/
2022-09-23 02:33:11 +00:00
vabene1111
fd325c1797 Merge branch 'develop' 2022-09-21 20:17:08 +02:00
vabene1111
12491d1302 changed gunicorn default settings 2022-09-21 20:17:00 +02:00
vabene1111
b7a4613310 Merge pull request #2052 from Szeraax/patch-1
Update boot.sh
2022-09-21 20:16:25 +02:00
Szeraax
39f5fca89b Update boot.sh
Set default value to 1 if null or unset for gunicorn workers/threads.
2022-09-21 11:06:55 -06:00
vabene1111
2902262503 Merge branch 'develop' 2022-09-21 17:05:17 +02:00
vabene1111
b49393357a fixed tests 2022-09-21 16:54:41 +02:00
vabene1111
cc1a69eac0 fixed cache key uniqueness even in tests 2022-09-21 16:28:54 +02:00
vabene1111
13d498658c fixed DB setting 2022-09-19 07:34:26 +02:00
vabene1111
cad93b2dd1 equal button styling in editor 2022-09-19 07:32:51 +02:00
vabene1111
f0b8bac221 improved loading animation 2022-09-19 07:31:57 +02:00
vabene1111
13ef843edb fixed sort dropdown visibility 2022-09-19 07:28:20 +02:00
dependabot[bot]
ca9c96647e Bump cryptography from 37.0.2 to 38.0.1
Bumps [cryptography](https://github.com/pyca/cryptography) from 37.0.2 to 38.0.1.
- [Release notes](https://github.com/pyca/cryptography/releases)
- [Changelog](https://github.com/pyca/cryptography/blob/main/CHANGELOG.rst)
- [Commits](https://github.com/pyca/cryptography/compare/37.0.2...38.0.1)

---
updated-dependencies:
- dependency-name: cryptography
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-09-19 05:14:20 +00:00
vabene1111
902ef3cd1e downgrade django to fix DRF 2022-09-19 07:13:22 +02:00
vabene1111
0b69bcddcc downgrade django to fix DRF 2022-09-19 07:13:09 +02:00
vabene1111
9089fc7ad3 fixed admin userspace search 2022-09-17 08:49:00 +02:00
vabene1111
6d866ae62b fixed keyword serialization 2022-09-17 08:43:18 +02:00
vabene1111
9fa82c2ddb something broke with md, dont want to fix right now 2022-09-17 08:43:10 +02:00
vabene1111
0ca29cd677 small visual tweaks to search page 2022-09-17 08:06:38 +02:00
vabene1111
54c9e200a0 Merge pull request #2044 from ambroisie/fix/markdown_md_globals_deprecated
Fix 'markdown' 3.4 version incompatibility
2022-09-17 07:14:03 +02:00
vabene1111
fc67525dcb Merge pull request #1969 from TandoorRecipes/dependabot/pip/markdown-3.4.1
Bump markdown from 3.3.7 to 3.4.1
2022-09-17 07:13:54 +02:00
Bruno BELANYI
37e292cab9 Fix 'markdown' 3.4 version incompatibility 2022-09-16 19:11:52 +02:00
vabene1111
e391abd23d moved annotation to default query manager 2022-09-16 18:18:59 +02:00
vabene1111
947986277a fixed recipe detail query 2022-09-16 18:15:05 +02:00
vabene1111
b2a10f269c permission and search preference caching 2022-09-16 14:35:35 +02:00
vabene1111
dc076d25d6 improved hash generation time 2022-09-16 13:31:00 +02:00
vabene1111
845408244b optimized recipe search query annotation performance 2022-09-16 13:24:57 +02:00
vabene1111
e06c82297d added loading animation to main search page 2022-09-15 20:53:22 +02:00
vabene1111
459be74a7c changed local DB setting 2022-09-15 19:05:46 +02:00
vabene1111
37e81275b5 align dependencies with oauth toolkit 2022-09-15 18:37:15 +02:00
vabene1111
8417b0ec3f Merge branch 'develop' of https://github.com/vabene1111/recipes into develop
# Conflicts:
#	recipes/settings.py
2022-09-15 18:31:46 +02:00
vabene1111
7d834ee088 debug toobar stuff 2022-09-15 18:31:30 +02:00
vabene1111
eb119b7443 remove "favorite" as default sort order due to performacne issues 2022-09-15 18:31:22 +02:00
vabene1111
cc342cbae3 add community contributed to HA docs and link to alexbelgium directly on top 2022-09-12 20:06:12 +02:00
vabene1111
75ae26fd28 Merge pull request #2032 from alexbelgium/develop
Home Assistant documentation
2022-09-12 20:00:31 +02:00
dependabot[bot]
70e6585669 Bump django-webpack-loader from 1.5.0 to 1.6.0
Bumps [django-webpack-loader](https://github.com/django-webpack/django-webpack-loader) from 1.5.0 to 1.6.0.
- [Release notes](https://github.com/django-webpack/django-webpack-loader/releases)
- [Changelog](https://github.com/django-webpack/django-webpack-loader/blob/master/CHANGELOG.md)
- [Commits](https://github.com/django-webpack/django-webpack-loader/compare/1.5.0...1.6.0)

---
updated-dependencies:
- dependency-name: django-webpack-loader
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-09-12 18:00:05 +00:00
vabene1111
94f58f4608 Merge pull request #2029 from TandoorRecipes/dependabot/pip/django-4.1.1
Bump django from 4.0.7 to 4.1.1
2022-09-12 19:59:37 +02:00
vabene1111
5478a8d49a Merge pull request #2036 from TandoorRecipes/dependabot/pip/recipe-scrapers-14.14.0
Bump recipe-scrapers from 14.11.0 to 14.14.0
2022-09-12 19:59:26 +02:00
dependabot[bot]
23180622e8 Bump django from 4.0.7 to 4.1.1
Bumps [django](https://github.com/django/django) from 4.0.7 to 4.1.1.
- [Release notes](https://github.com/django/django/releases)
- [Commits](https://github.com/django/django/commits)

---
updated-dependencies:
- dependency-name: django
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-09-12 15:59:22 +00:00
dependabot[bot]
62187fbbdf Bump recipe-scrapers from 14.11.0 to 14.14.0
Bumps [recipe-scrapers](https://github.com/hhursev/recipe-scrapers) from 14.11.0 to 14.14.0.
- [Release notes](https://github.com/hhursev/recipe-scrapers/releases)
- [Commits](https://github.com/hhursev/recipe-scrapers/compare/14.11.0...14.14.0)

---
updated-dependencies:
- dependency-name: recipe-scrapers
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-09-12 15:59:09 +00:00
vabene1111
bd6b04f95e Merge branch 'develop' of https://github.com/vabene1111/recipes into develop 2022-09-12 17:58:22 +02:00
vabene1111
b315d6e171 adding debug toolbar 2022-09-12 17:58:20 +02:00
Alexandre
35bb3c9eb1 Add updates and backup instructions 2022-09-10 22:33:06 +02:00
Noé Feutry
84e7850e91 Translated using Weblate (French)
Currently translated at 85.2% (392 of 460 strings)

Translation: Tandoor/Recipes Frontend
Translate-URL: http://translate.tandoor.dev/projects/tandoor/recipes-frontend/fr/
2022-09-10 19:33:01 +00:00
David Schenk
4b40d75d1d Translated using Weblate (German)
Currently translated at 99.6% (522 of 524 strings)

Translation: Tandoor/Recipes Backend
Translate-URL: http://translate.tandoor.dev/projects/tandoor/recipes-backend/de/
2022-09-10 19:33:01 +00:00
Alexandre
5423019a14 Add self promotion 2022-09-10 11:57:27 +02:00
Alexandre
e8c5c610b7 Improve layout 2022-09-10 11:54:21 +02:00
Alexandre
3f0cef59b8 Create home assistant install instructions 2022-09-10 11:53:56 +02:00
Alexandre
867c3595ff Merge branch 'TandoorRecipes:develop' into develop 2022-09-10 10:51:58 +02:00
vabene1111
631dd58c1f fixed share link guest user error message 2022-09-09 18:28:01 +02:00
vabene1111
ba235b26b7 fixed make header not removing food/unit/amount 2022-09-09 18:18:29 +02:00
dependabot[bot]
e54e850241 Bump markdown from 3.3.7 to 3.4.1
Bumps [markdown](https://github.com/Python-Markdown/markdown) from 3.3.7 to 3.4.1.
- [Release notes](https://github.com/Python-Markdown/markdown/releases)
- [Commits](https://github.com/Python-Markdown/markdown/compare/3.3.7...3.4.1)

---
updated-dependencies:
- dependency-name: markdown
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-09-09 16:09:40 +00:00
dependabot[bot]
d0cb7a79f9 Bump webpack-bundle-tracker from 1.5.0 to 1.6.0 in /vue
Bumps [webpack-bundle-tracker](https://github.com/django-webpack/webpack-bundle-tracker) from 1.5.0 to 1.6.0.
- [Release notes](https://github.com/django-webpack/webpack-bundle-tracker/releases)
- [Commits](https://github.com/django-webpack/webpack-bundle-tracker/compare/1.5.0...1.6.0)

---
updated-dependencies:
- dependency-name: webpack-bundle-tracker
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-09-09 16:09:30 +00:00
vabene1111
40c85c512c Merge pull request #1968 from TandoorRecipes/dependabot/pip/pillow-9.2.0
Bump pillow from 9.1.1 to 9.2.0
2022-09-09 18:09:02 +02:00
vabene1111
ca5eb7b2b6 Merge pull request #1984 from andyjayne/fix-nl-ingredients
fix: ingredient parsing for non-latin languages
2022-09-09 18:07:55 +02:00
dependabot[bot]
cfd24de72a Bump workbox-navigation-preload from 6.5.3 to 6.5.4 in /vue
Bumps [workbox-navigation-preload](https://github.com/googlechrome/workbox) from 6.5.3 to 6.5.4.
- [Release notes](https://github.com/googlechrome/workbox/releases)
- [Commits](https://github.com/googlechrome/workbox/compare/v6.5.3...v6.5.4)

---
updated-dependencies:
- dependency-name: workbox-navigation-preload
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-09-09 16:07:08 +00:00
dependabot[bot]
54acfe3e39 Bump workbox-routing from 6.5.3 to 6.5.4 in /vue
Bumps [workbox-routing](https://github.com/googlechrome/workbox) from 6.5.3 to 6.5.4.
- [Release notes](https://github.com/googlechrome/workbox/releases)
- [Commits](https://github.com/googlechrome/workbox/compare/v6.5.3...v6.5.4)

---
updated-dependencies:
- dependency-name: workbox-routing
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-09-09 16:06:53 +00:00
dependabot[bot]
574a6ab5f4 Bump pillow from 9.1.1 to 9.2.0
Bumps [pillow](https://github.com/python-pillow/Pillow) from 9.1.1 to 9.2.0.
- [Release notes](https://github.com/python-pillow/Pillow/releases)
- [Changelog](https://github.com/python-pillow/Pillow/blob/main/CHANGES.rst)
- [Commits](https://github.com/python-pillow/Pillow/compare/9.1.1...9.2.0)

---
updated-dependencies:
- dependency-name: pillow
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-09-09 16:06:44 +00:00
vabene1111
39070d32bd Merge pull request #1986 from CameronJGrant/develop
Solves #1830 (Split times over 60 min into hours and minutes)
2022-09-09 18:06:34 +02:00
vabene1111
9aa3d2d87a Merge pull request #2013 from nough/develop
Update documentation on external recipes
2022-09-09 18:05:29 +02:00
vabene1111
02926516b9 Merge pull request #2025 from TandoorRecipes/dependabot/pip/python-dotenv-0.21.0
Bump python-dotenv from 0.20.0 to 0.21.0
2022-09-09 18:05:18 +02:00
dependabot[bot]
215f561623 Bump python-dotenv from 0.20.0 to 0.21.0
Bumps [python-dotenv](https://github.com/theskumar/python-dotenv) from 0.20.0 to 0.21.0.
- [Release notes](https://github.com/theskumar/python-dotenv/releases)
- [Changelog](https://github.com/theskumar/python-dotenv/blob/main/CHANGELOG.md)
- [Commits](https://github.com/theskumar/python-dotenv/compare/v0.20.0...v0.21.0)

---
updated-dependencies:
- dependency-name: python-dotenv
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-09-09 16:04:58 +00:00
vabene1111
e2c2f5d757 Merge pull request #2017 from TandoorRecipes/dependabot/pip/drf-writable-nested-0.7.0
Bump drf-writable-nested from 0.6.4 to 0.7.0
2022-09-09 18:04:33 +02:00
vabene1111
d887405ab3 Merge pull request #2018 from TandoorRecipes/dependabot/npm_and_yarn/vue/typescript-4.8.2
Bump typescript from 4.7.2 to 4.8.2 in /vue
2022-09-09 18:04:29 +02:00
vabene1111
00deb75195 Merge pull request #2019 from TandoorRecipes/dependabot/npm_and_yarn/vue/core-js-3.25.0
Bump core-js from 3.22.7 to 3.25.0 in /vue
2022-09-09 18:04:21 +02:00
Tomasz Klimczak
b228b0f42a Translated using Weblate (Polish)
Currently translated at 100.0% (460 of 460 strings)

Translation: Tandoor/Recipes Frontend
Translate-URL: http://translate.tandoor.dev/projects/tandoor/recipes-frontend/pl/
2022-09-05 21:32:56 +00:00
Tomasz Klimczak
3d5ff23433 Translated using Weblate (Polish)
Currently translated at 99.3% (457 of 460 strings)

Translation: Tandoor/Recipes Frontend
Translate-URL: http://translate.tandoor.dev/projects/tandoor/recipes-frontend/pl/
2022-09-03 23:32:55 +00:00
Alexandre
1a24f34499 Create homeassistant.md 2022-09-03 18:59:14 +02:00
1k2
8459b40743 Translated using Weblate (Dutch)
Currently translated at 99.3% (457 of 460 strings)

Translation: Tandoor/Recipes Frontend
Translate-URL: http://translate.tandoor.dev/projects/tandoor/recipes-frontend/nl/
2022-09-01 20:32:55 +00:00
1k2
75cb5d2d4c Translated using Weblate (Dutch)
Currently translated at 99.4% (521 of 524 strings)

Translation: Tandoor/Recipes Backend
Translate-URL: http://translate.tandoor.dev/projects/tandoor/recipes-backend/nl/
2022-09-01 20:32:53 +00:00
Arne Hüffmeier
12ad6af8c3 fix path 2022-09-01 13:45:49 +02:00
Arne Hüffmeier
cf24e1014a add two infos 2022-09-01 13:38:47 +02:00
dependabot[bot]
bd1b40dd94 Bump core-js from 3.22.7 to 3.25.0 in /vue
Bumps [core-js](https://github.com/zloirock/core-js) from 3.22.7 to 3.25.0.
- [Release notes](https://github.com/zloirock/core-js/releases)
- [Changelog](https://github.com/zloirock/core-js/blob/master/CHANGELOG.md)
- [Commits](https://github.com/zloirock/core-js/compare/v3.22.7...v3.25.0)

---
updated-dependencies:
- dependency-name: core-js
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-09-01 00:37:12 +00:00
dependabot[bot]
95d4bfb2bd Bump typescript from 4.7.2 to 4.8.2 in /vue
Bumps [typescript](https://github.com/Microsoft/TypeScript) from 4.7.2 to 4.8.2.
- [Release notes](https://github.com/Microsoft/TypeScript/releases)
- [Commits](https://github.com/Microsoft/TypeScript/compare/v4.7.2...v4.8.2)

---
updated-dependencies:
- dependency-name: typescript
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-09-01 00:34:33 +00:00
dependabot[bot]
23caac9d09 Bump drf-writable-nested from 0.6.4 to 0.7.0
Bumps [drf-writable-nested](https://github.com/beda-software/drf-writable-nested) from 0.6.4 to 0.7.0.
- [Release notes](https://github.com/beda-software/drf-writable-nested/releases)
- [Changelog](https://github.com/beda-software/drf-writable-nested/blob/master/CHANGELOG.md)
- [Commits](https://github.com/beda-software/drf-writable-nested/compare/v0.6.4...v0.7.0)

---
updated-dependencies:
- dependency-name: drf-writable-nested
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-09-01 00:15:01 +00:00
nough
ece4f6e32d Update updating.md 2022-08-28 20:52:52 +01:00
nough
5e7d1ba827 Update updating.md
remove outline of docker update batch script - it wasn't ready and I committed it when I didn't understand how github branches work (I still don't, really).
2022-08-28 20:51:26 +01:00
nough
a88214eea6 Update external_recipes.md
fixed my mistakes
2022-08-27 22:18:52 +01:00
nough
7ec5646338 update external_recipes.md with docker info
added info on docker external recipes, as i've just followed this process myself. unsure if I needed to add the externalfiles folder to the nginx_recipes volume, but I have anyway in my personal one, and that worked.
2022-08-27 22:12:33 +01:00
nough
c020bea41e Merge branch 'TandoorRecipes:develop' into develop 2022-08-27 21:57:02 +01:00
吕楪
e6f79a6fa3 Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (460 of 460 strings)

Translation: Tandoor/Recipes Frontend
Translate-URL: http://translate.tandoor.dev/projects/tandoor/recipes-frontend/zh_Hans/
2022-08-23 13:32:56 +00:00
Mike Miller
0ab430ea82 Translated using Weblate (German)
Currently translated at 99.7% (459 of 460 strings)

Translation: Tandoor/Recipes Frontend
Translate-URL: http://translate.tandoor.dev/projects/tandoor/recipes-frontend/de/
2022-08-23 13:32:56 +00:00
Kirstin Seidel-Gebert
3d95657b8a Translated using Weblate (German)
Currently translated at 99.7% (459 of 460 strings)

Translation: Tandoor/Recipes Frontend
Translate-URL: http://translate.tandoor.dev/projects/tandoor/recipes-frontend/de/
2022-08-23 13:32:56 +00:00
吕楪
726157a062 Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (524 of 524 strings)

Translation: Tandoor/Recipes Backend
Translate-URL: http://translate.tandoor.dev/projects/tandoor/recipes-backend/zh_Hans/
2022-08-23 13:32:51 +00:00
Kirstin Seidel-Gebert
f8793f3ec8 Translated using Weblate (German)
Currently translated at 99.6% (522 of 524 strings)

Translation: Tandoor/Recipes Backend
Translate-URL: http://translate.tandoor.dev/projects/tandoor/recipes-backend/de/
2022-08-23 13:32:51 +00:00
吕楪
09929beeb9 Translated using Weblate (Chinese (Simplified))
Currently translated at 98.6% (454 of 460 strings)

Translation: Tandoor/Recipes Frontend
Translate-URL: http://translate.tandoor.dev/projects/tandoor/recipes-frontend/zh_Hans/
2022-08-22 09:56:23 +00:00
Mathias Rasmussen
2a1b2c18fc Translated using Weblate (Danish)
Currently translated at 100.0% (528 of 528 strings)

Translation: Tandoor/Recipes Backend
Translate-URL: http://translate.tandoor.dev/projects/tandoor/recipes-backend/da/
2022-08-18 14:32:52 +00:00
Mathias Rasmussen
0cc3df71d2 Translated using Weblate (Danish)
Currently translated at 100.0% (460 of 460 strings)

Translation: Tandoor/Recipes Frontend
Translate-URL: http://translate.tandoor.dev/projects/tandoor/recipes-frontend/da/
2022-08-14 16:32:49 +00:00
Thorin
e124c211ac Translated using Weblate (Spanish)
Currently translated at 73.2% (315 of 430 strings)

Translation: Tandoor/Recipes Frontend
Translate-URL: http://translate.tandoor.dev/projects/tandoor/recipes-frontend/es/
2022-08-12 21:32:52 +00:00
Thorin
dc2f62dc9d Translated using Weblate (Spanish)
Currently translated at 53.0% (278 of 524 strings)

Translation: Tandoor/Recipes Backend
Translate-URL: http://translate.tandoor.dev/projects/tandoor/recipes-backend/es/
2022-08-12 21:32:52 +00:00
Cameron Grant
38921f1254 Updated time to use hours and minutes split 2022-08-11 14:23:31 -07:00
vabene1111
4fec9a493e Merge pull request #1989 from TandoorRecipes/dependabot/pip/django-4.0.7
Bump django from 4.0.6 to 4.0.7
2022-08-11 23:07:43 +02:00
dependabot[bot]
71c5adda79 Bump django from 4.0.6 to 4.0.7
Bumps [django](https://github.com/django/django) from 4.0.6 to 4.0.7.
- [Release notes](https://github.com/django/django/releases)
- [Commits](https://github.com/django/django/compare/4.0.6...4.0.7)

---
updated-dependencies:
- dependency-name: django
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-08-11 15:25:12 +00:00
Andrew Jayne
cffa731106 fix: ingredient parsing for non-latin languages
Before this change the ingredient string for non-latin
languages was not being parsed into the correct amount
or units when the food is found at the start of the
ingredient string.

This was because the regex being used was restricted to
latin characters.

With this change the amount and units are correctly
parsed from such a string.

Fixes https://github.com/TandoorRecipes/recipes/issues/1983
2022-08-07 21:37:59 +01:00
vabene1111
c7f75fe58f boot.sh 2022-08-05 17:55:25 +02:00
vabene1111
2eed5143fe boot.sh 2022-08-05 17:43:59 +02:00
vabene1111
6e4ea518d9 boot.sh 2022-08-05 17:36:00 +02:00
vabene1111
a898d722d6 fixed typo 2022-08-05 17:21:59 +02:00
vabene1111
904358bb00 allow changing gunicorn settings 2022-08-05 17:13:27 +02:00
vabene1111
6605b87c5c new settings page finished 2022-08-05 16:54:53 +02:00
vabene1111
64688ca5e1 Merge branch 'develop' of https://github.com/vabene1111/recipes into develop 2022-08-05 14:28:22 +02:00
vabene1111
e9a1a06bda fixed recipe share permission 2022-08-05 14:28:17 +02:00
vabene1111
a8da28f877 Merge pull request #1980 from 8633brown/nplusone
prefetch food relations #1965
2022-08-05 07:53:01 +02:00
8633brown
70b2bd6ccf prefetch food relations #1965 2022-08-04 22:11:48 +01:00
vabene1111
8ed5d52ddf fixed space settings saving issue 2022-08-04 18:54:00 +02:00
vabene1111
f7af0741fe fixed bookmarklet 2022-08-04 18:45:40 +02:00
vabene1111
3ec4afb02f fixed scoping and permissions for tokens 2022-08-04 18:33:45 +02:00
vabene1111
3f77b73a61 add multiple API tokens per user, removes old API tokens 2022-08-04 17:24:54 +02:00
Oliver Cervera
9e62d8a3a3 Translated using Weblate (Italian)
Currently translated at 79.9% (419 of 524 strings)

Translation: Tandoor/Recipes Backend
Translate-URL: http://translate.tandoor.dev/projects/tandoor/recipes-backend/it/
2022-08-04 11:32:45 +00:00
vabene1111
9ef21241bf markdown editor adjustments 2022-08-01 17:13:44 +02:00
vabene1111
5e77adf7e6 fixed mail send 2022-08-01 16:56:18 +02:00
vabene1111
4df0a46701 Merge pull request #1975 from TandoorRecipes/dependabot/pip/icalendar-4.1.0
Bump icalendar from 4.0.9 to 4.1.0
2022-08-01 16:28:48 +02:00
dependabot[bot]
f186404628 Bump icalendar from 4.0.9 to 4.1.0
Bumps [icalendar](https://github.com/collective/icalendar) from 4.0.9 to 4.1.0.
- [Release notes](https://github.com/collective/icalendar/releases)
- [Changelog](https://github.com/collective/icalendar/blob/4.1.0/CHANGES.rst)
- [Commits](https://github.com/collective/icalendar/compare/4.0.9...4.1.0)

---
updated-dependencies:
- dependency-name: icalendar
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-08-01 14:28:24 +00:00
vabene1111
8e3ec91f3c Merge pull request #1961 from AquaticLava/develop
Fixes  #1234
2022-08-01 16:28:18 +02:00
vabene1111
2605addf34 Merge pull request #1974 from TandoorRecipes/dependabot/pip/drf-writable-nested-0.6.4
Bump drf-writable-nested from 0.6.3 to 0.6.4
2022-08-01 16:27:48 +02:00
vabene1111
1ab3e57b83 Merge pull request #1947 from dmaes/develop
Add S3_CUSTOM_DOMAIN settings, closes #1943
2022-08-01 16:27:16 +02:00
dependabot[bot]
2f36ae5112 Bump drf-writable-nested from 0.6.3 to 0.6.4
Bumps [drf-writable-nested](https://github.com/beda-software/drf-writable-nested) from 0.6.3 to 0.6.4.
- [Release notes](https://github.com/beda-software/drf-writable-nested/releases)
- [Changelog](https://github.com/beda-software/drf-writable-nested/blob/master/CHANGELOG.md)
- [Commits](https://github.com/beda-software/drf-writable-nested/compare/v0.6.3...v0.6.4)

---
updated-dependencies:
- dependency-name: drf-writable-nested
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-08-01 14:27:15 +00:00
vabene1111
acc19ca65e Merge pull request #1973 from TandoorRecipes/dependabot/pip/python-ldap-3.4.2
Bump python-ldap from 3.4.0 to 3.4.2
2022-08-01 16:26:46 +02:00
vabene1111
ea213e2dfd Merge pull request #1948 from iamkarlson/feature/fix_csrf_django_40
fixed csrf
2022-08-01 16:25:56 +02:00
vabene1111
02cf3264a3 Merge branch 'develop' of https://github.com/vabene1111/recipes into develop 2022-08-01 16:18:46 +02:00
vabene1111
a0b1186558 settings wip 2022-08-01 16:18:43 +02:00
dependabot[bot]
27e47718bb Bump python-ldap from 3.4.0 to 3.4.2
Bumps [python-ldap](https://github.com/python-ldap/python-ldap) from 3.4.0 to 3.4.2.
- [Release notes](https://github.com/python-ldap/python-ldap/releases)
- [Commits](https://github.com/python-ldap/python-ldap/compare/python-ldap-3.4.0...python-ldap-3.4.2)

---
updated-dependencies:
- dependency-name: python-ldap
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-08-01 05:52:23 +00:00
vabene1111
f78dd209bd Merge pull request #1967 from TandoorRecipes/dependabot/pip/recipe-scrapers-14.11.0
Bump recipe-scrapers from 14.6.0 to 14.11.0
2022-08-01 07:51:58 +02:00
dependabot[bot]
2f8b479fdd Bump @vue/cli-plugin-pwa from 5.0.4 to 5.0.8 in /vue
Bumps [@vue/cli-plugin-pwa](https://github.com/vuejs/vue-cli/tree/HEAD/packages/@vue/cli-plugin-pwa) from 5.0.4 to 5.0.8.
- [Release notes](https://github.com/vuejs/vue-cli/releases)
- [Changelog](https://github.com/vuejs/vue-cli/blob/dev/CHANGELOG.md)
- [Commits](https://github.com/vuejs/vue-cli/commits/v5.0.8/packages/@vue/cli-plugin-pwa)

---
updated-dependencies:
- dependency-name: "@vue/cli-plugin-pwa"
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-08-01 00:28:30 +00:00
dependabot[bot]
b4e0b51f5b Bump recipe-scrapers from 14.6.0 to 14.11.0
Bumps [recipe-scrapers](https://github.com/hhursev/recipe-scrapers) from 14.6.0 to 14.11.0.
- [Release notes](https://github.com/hhursev/recipe-scrapers/releases)
- [Commits](https://github.com/hhursev/recipe-scrapers/compare/14.6.0...14.11.0)

---
updated-dependencies:
- dependency-name: recipe-scrapers
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-08-01 00:13:53 +00:00
AquaticLava
eedce4dcfd fixes shopping list number showing completed items 2022-07-27 21:03:21 -06:00
AquaticLava
006be92180 Fixes #1234 2022-07-26 17:49:43 -06:00
Tomasz Klimczak
1fae004785 Translated using Weblate (Polish)
Currently translated at 100.0% (427 of 427 strings)

Translation: Tandoor/Recipes Frontend
Translate-URL: http://translate.tandoor.dev/projects/tandoor/recipes-frontend/pl/
2022-07-26 21:32:41 +00:00
Huth Jimmy
239a88cd24 Translated using Weblate (French)
Currently translated at 88.5% (378 of 427 strings)

Translation: Tandoor/Recipes Frontend
Translate-URL: http://translate.tandoor.dev/projects/tandoor/recipes-frontend/fr/
2022-07-26 21:32:41 +00:00
vabene1111
22b432a6ae Merge pull request #1952 from tomtjes/patch-1
fix minor issues in CopyMeThat importer
2022-07-25 14:48:26 +02:00
tomtjes
c88566a4ae fix minor issues in CopyMeThat importer
improve handling of empty fields and fields that exceed character limits
2022-07-22 12:14:23 -04:00
vabene1111
5f8e371793 Merge pull request #1949 from TandoorRecipes/dependabot/npm_and_yarn/vue/terser-4.8.1
Bump terser from 4.8.0 to 4.8.1 in /vue
2022-07-21 07:46:56 +02:00
dependabot[bot]
94d9ac03ea Bump terser from 4.8.0 to 4.8.1 in /vue
Bumps [terser](https://github.com/terser/terser) from 4.8.0 to 4.8.1.
- [Release notes](https://github.com/terser/terser/releases)
- [Changelog](https://github.com/terser/terser/blob/master/CHANGELOG.md)
- [Commits](https://github.com/terser/terser/commits)

---
updated-dependencies:
- dependency-name: terser
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-07-21 05:21:46 +00:00
George Green
897ac97423 fixed csrf 2022-07-20 21:17:29 +02:00
dmaes
24aeae6de9 update .env.template 2022-07-20 10:45:52 +02:00
dmaes
ce941db3be add S3_CUSTOM_DOMAIN setting 2022-07-20 08:54:34 +02:00
vabene1111
5ff91ee47f more settings in 2022-07-15 17:39:44 +02:00
vabene1111
ce1f55ffd1 settings wip 2022-07-15 17:12:01 +02:00
vabene1111
8700e2df69 basics of new settings page working 2022-07-14 17:50:20 +02:00
vabene1111
f4df84b609 added ability to set space images 2022-07-14 15:23:59 +02:00
vabene1111
ba473123ba added ability to use invite link more than once 2022-07-14 11:28:13 +02:00
vabene1111
98a54ef38f removed lots of unused stuff 2022-07-14 10:37:15 +02:00
vabene1111
7fdc9c7cb8 added sharing permission test 2022-07-14 10:30:45 +02:00
vabene1111
dc3b1566d7 made shared field for recipe api optional 2022-07-14 10:21:35 +02:00
vabene1111
5429c4d557 Merge pull request #1937 from tomtjes/patch-1
Update copymethat.py
2022-07-14 09:53:43 +02:00
tomtjes
dabcea6ba7 Update copymethat.py
- make use of field for source URL
- preserve "I made this" flag as keyword
- preserve long descriptions in full at bottom of steps
- preserve ingredient and step headers
2022-07-13 14:37:16 -04:00
vabene1111
e91790f5ac added ability to mark recipes as private 2022-07-13 15:46:39 +02:00
vabene1111
51076d4ced Merge branch 'master' into develop 2022-07-13 10:25:39 +02:00
vabene1111
1cb37fe2d2 dont allow space manage page in demo 2022-07-13 10:25:22 +02:00
vabene1111
61a9f0647b added userspace admin 2022-07-13 10:24:12 +02:00
vabene1111
ac2ab62050 moved import functions to proper api function 2022-07-12 21:14:51 +02:00
vabene1111
c50efac00e basics for profile page 2022-07-12 20:57:13 +02:00
vabene1111
bf16e61a1f removed unused stuff and fixed manifest 2022-07-12 20:52:32 +02:00
vabene1111
d464633c70 removed django filters 2022-07-12 20:38:18 +02:00
vabene1111
b78d0ec30b added userspace admin 2022-07-12 20:37:38 +02:00
vabene1111
da09602834 removed old search pages 2022-07-12 20:05:59 +02:00
vabene1111
5ead4967a5 removed ingredient list shopping 2022-07-12 19:54:18 +02:00
vabene1111
8bb7ce2062 removed user servings feature 2022-07-12 19:43:11 +02:00
vabene1111
0068c75e31 Merge branch 'develop' 2022-07-12 19:41:50 +02:00
vabene1111
5de7fa9d48 fixed another social auth issues 2022-07-12 19:41:46 +02:00
vabene1111
3dc3592783 Merge branch 'develop' 2022-07-12 19:20:42 +02:00
vabene1111
43a082a51a updated translations 2022-07-12 19:20:35 +02:00
vabene1111
4c264673df fixed copy me that importer 2022-07-12 19:20:05 +02:00
vabene1111
d537d73c6a Merge pull request #1930 from Mikhail5555/patch-1
Add documentation for migrating from sqlite3 database to postgresql (Unraid)
2022-07-12 14:46:00 +02:00
vabene1111
5c227ecc57 Merge pull request #1931 from smilerz/fix_cookbookapp_import
updated cookbookapp importer to handle multi-step recipes
2022-07-12 08:45:17 +02:00
smilerz
b03fa4fdf2 updated cookbookapp importer to handle multi-step recipes 2022-07-11 17:21:56 -05:00
vabene1111
38219a22ca fixed issue with social default access and multi space tennany 2022-07-11 23:42:26 +02:00
Mikhail5555
9d6a5efa72 Update migration_sqlite-postgres.md 2022-07-11 23:14:55 +02:00
Mikhail5555
aaa0520a6d Update migration_sqlite-postgres.md 2022-07-11 23:12:42 +02:00
Mikhail5555
eb0f231a80 Create migration_sqlite-postgres.md 2022-07-11 23:09:41 +02:00
vabene1111
17f3da5a37 removed dead invite link button from system page 2022-07-11 14:54:00 +02:00
vabene1111
608039b7e4 cookbookapp importer more or less broken (more) 2022-07-11 14:46:54 +02:00
vabene1111
bb424cc3d6 Merge pull request #1917 from smilerz/bookmarklet_fix
Bookmarklet fix
2022-07-11 14:28:08 +02:00
Mike Miller
9eaf0f9530 Translated using Weblate (German)
Currently translated at 96.9% (414 of 427 strings)

Translation: Tandoor/Recipes Frontend
Translate-URL: http://translate.tandoor.dev/projects/tandoor/recipes-frontend/de/
2022-07-10 08:32:35 +00:00
Kalli_1
b44bb552e0 Translated using Weblate (German)
Currently translated at 96.9% (414 of 427 strings)

Translation: Tandoor/Recipes Frontend
Translate-URL: http://translate.tandoor.dev/projects/tandoor/recipes-frontend/de/
2022-07-10 08:32:35 +00:00
nough
c86ff27bef Update backup.md
Further information, backup using import/export.
2022-07-08 09:19:48 +01:00
nough
be6bb5f039 Merge branch 'TandoorRecipes:develop' into develop 2022-07-08 09:14:57 +01:00
smilerz
e40b73f420 deprecate get_recipe_from_source 2022-07-07 15:09:22 -05:00
nough
9961746f1f update, adding docker backup script outline 2022-07-07 15:46:31 +01:00
smilerz
b1c0334947 quick hack to allow scraper to work correctly 2022-07-07 07:50:57 -05:00
smilerz
25a41bd293 reverting scraper to just using wildmode 2022-07-07 06:43:07 -05:00
smilerz
e23d514d89 fix bookmarklet 2022-07-06 16:16:53 -05:00
246 changed files with 25635 additions and 14935 deletions

View File

@@ -68,6 +68,10 @@ SHOPPING_MIN_AUTOSYNC_INTERVAL=5
# when unset: 1 (true) - this is temporary until an appropriate amount of time has passed for everyone to migrate
GUNICORN_MEDIA=0
# GUNICORN SERVER RELATED SETTINGS (see https://docs.gunicorn.org/en/stable/design.html#how-many-workers for recommended settings)
# GUNICORN_WORKERS=1
# GUNICORN_THREADS=1
# S3 Media settings: store mediafiles in s3 or any compatible storage backend (e.g. minio)
# as long as S3_ACCESS_KEY is not set S3 features are disabled
# S3_ACCESS_KEY=
@@ -77,6 +81,7 @@ GUNICORN_MEDIA=0
# S3_QUERYSTRING_AUTH=1 # default true, set to 0 to serve media from a public bucket without signed urls
# S3_QUERYSTRING_EXPIRE=3600 # number of seconds querystring are valid for
# S3_ENDPOINT_URL= # when using a custom endpoint like minio
# S3_CUSTOM_DOMAIN= # when using a CDN/proxy to S3 (see https://github.com/TandoorRecipes/recipes/issues/1943)
# Email Settings, see https://docs.djangoproject.com/en/3.2/ref/settings/#email-host
# Required for email confirmation and password reset (automatically activates if host is set)
@@ -92,7 +97,7 @@ GUNICORN_MEDIA=0
# ACCOUNT_EMAIL_SUBJECT_PREFIX=
# allow authentication via reverse proxy (e.g. authelia), leave off if you dont know what you are doing
# see docs for more information https://vabene1111.github.io/recipes/features/authentication/
# see docs for more information https://docs.tandoor.dev/features/authentication/
# when unset: 0 (false)
REVERSE_PROXY_AUTH=0
@@ -121,7 +126,7 @@ REVERSE_PROXY_AUTH=0
# ENABLE_METRICS=0
# allows you to setup OAuth providers
# see docs for more information https://vabene1111.github.io/recipes/features/authentication/
# see docs for more information https://docs.tandoor.dev/features/authentication/
# SOCIAL_PROVIDERS = allauth.socialaccount.providers.github, allauth.socialaccount.providers.nextcloud,
# Should a newly created user from a social provider get assigned to the default space and given permission by default ?

View File

@@ -1,6 +1,6 @@
name: Continuous Integration
on: [push]
on: [push, pull_request]
jobs:
build:
@@ -14,13 +14,13 @@ jobs:
steps:
- uses: actions/checkout@v1
- name: Set up Python 3.10
uses: actions/setup-python@v1
uses: actions/setup-python@v4
with:
python-version: '3.10'
# Build Vue frontend
- uses: actions/setup-node@v2
- uses: actions/setup-node@v3
with:
node-version: '14'
node-version: '16'
- name: Install Vue dependencies
working-directory: ./vue
run: yarn install
@@ -30,7 +30,7 @@ jobs:
- name: Install Django dependencies
run: |
sudo apt-get -y update
sudo apt-get install -y libsasl2-dev python-dev libldap2-dev libssl-dev
sudo apt-get install -y libsasl2-dev python3-dev libldap2-dev libssl-dev
python -m pip install --upgrade pip
pip install -r requirements.txt
python3 manage.py collectstatic --noinput

1
.gitignore vendored
View File

@@ -84,3 +84,4 @@ cookbook/static/vue
vue/webpack-stats.json
cookbook/templates/sw.js
.prettierignore
vue/.yarn

2
.idea/recipes.iml generated
View File

@@ -18,7 +18,7 @@
<excludeFolder url="file://$MODULE_DIR$/staticfiles" />
<excludeFolder url="file://$MODULE_DIR$/venv" />
</content>
<orderEntry type="jdk" jdkName="Python 3.9 (recipes)" jdkType="Python SDK" />
<orderEntry type="jdk" jdkName="Python 3.11 (recipes)" jdkType="Python SDK" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
<component name="TemplatesService">

View File

@@ -71,8 +71,7 @@ Because of that there are several ways you can support us
- **Let us host for you** We are offering a [hosted version](https://app.tandoor.dev) where all profits support us and the development of tandoor (currently only available in germany).
## 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.
Contributions are welcome but please read [this](https://docs.tandoor.dev/contribute/#contributing-code) **BEFORE** contributing anything!
## Your Feedback

View File

@@ -6,5 +6,4 @@ Since this software is still considered beta/WIP support is always only given fo
## Reporting a Vulnerability
Please open a normal public issue if you have any security related concerns. If you feel like the issue should not be discussed in
public just open a generic issue and we will discuss further communication there (since GitHub does not allow everyone to create a security advisory :/).
Please use GitHub Security Advisories to report any kind of security vulnerabilities.

View File

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

View File

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

View File

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

View File

@@ -45,8 +45,7 @@ class UserPreferenceForm(forms.ModelForm):
model = UserPreference
fields = (
'default_unit', 'use_fractions', 'use_kj', 'theme', 'nav_color',
'sticky_navbar', 'default_page', 'show_recent', 'search_style',
'plan_share', 'ingredient_decimals', 'comments', 'left_handed',
'sticky_navbar', 'default_page', 'plan_share', 'ingredient_decimals', 'comments', 'left_handed',
)
labels = {
@@ -57,8 +56,6 @@ class UserPreferenceForm(forms.ModelForm):
'nav_color': _('Navbar color'),
'sticky_navbar': _('Sticky navbar'),
'default_page': _('Default page'),
'show_recent': _('Show recent recipes'),
'search_style': _('Search style'),
'plan_share': _('Plan sharing'),
'ingredient_decimals': _('Ingredient decimal places'),
'shopping_auto_sync': _('Shopping list auto sync period'),
@@ -68,23 +65,21 @@ class UserPreferenceForm(forms.ModelForm):
help_texts = {
'nav_color': _('Color of the top navigation bar. Not all colors work with all themes, just try them out!'),
# noqa: E501
'default_unit': _('Default Unit to be used when inserting a new ingredient into a recipe.'), # noqa: E501
'default_unit': _('Default Unit to be used when inserting a new ingredient into a recipe.'),
'use_fractions': _(
'Enables support for fractions in ingredient amounts (e.g. convert decimals to fractions automatically)'),
# noqa: E501
'use_kj': _('Display nutritional energy amounts in joules instead of calories'), # noqa: E501
'use_kj': _('Display nutritional energy amounts in joules instead of calories'),
'plan_share': _('Users with whom newly created meal plans should be shared by default.'),
'shopping_share': _('Users with whom to share shopping lists.'),
# noqa: E501
'show_recent': _('Show recently viewed recipes on search page.'), # noqa: E501
'ingredient_decimals': _('Number of decimals to round ingredients.'), # noqa: E501
'comments': _('If you want to be able to create and see comments underneath recipes.'), # noqa: E501
'ingredient_decimals': _('Number of decimals to round ingredients.'),
'comments': _('If you want to be able to create and see comments underneath recipes.'),
'shopping_auto_sync': _(
'Setting to 0 will disable auto sync. When viewing a shopping list the list is updated every set seconds to sync changes someone else might have made. Useful when shopping with multiple people but might use a little bit ' # noqa: E501
'of mobile data. If lower than instance limit it is reset when saving.' # noqa: E501
'Setting to 0 will disable auto sync. When viewing a shopping list the list is updated every set seconds to sync changes someone else might have made. Useful when shopping with multiple people but might use a little bit '
'of mobile data. If lower than instance limit it is reset when saving.'
),
'sticky_navbar': _('Makes the navbar stick to the top of the page.'), # noqa: E501
'sticky_navbar': _('Makes the navbar stick to the top of the page.'),
'mealplan_autoadd_shopping': _('Automatically add meal plan ingredients to shopping list.'),
'mealplan_autoexclude_onhand': _('Exclude ingredients that are on hand.'),
'left_handed': _('Will optimize the UI for use with your left hand.')
@@ -159,6 +154,7 @@ class ImportExportBase(forms.Form):
COOKBOOKAPP = 'COOKBOOKAPP'
COPYMETHAT = 'COPYMETHAT'
COOKMATE = 'COOKMATE'
REZEPTSUITEDE = 'REZEPTSUITEDE'
PDF = 'PDF'
type = forms.ChoiceField(choices=(
@@ -167,7 +163,7 @@ class ImportExportBase(forms.Form):
(PEPPERPLATE, 'Pepperplate'), (RECETTETEK, 'RecetteTek'), (RECIPESAGE, 'Recipe Sage'), (DOMESTICA, 'Domestica'),
(MEALMASTER, 'MealMaster'), (REZKONV, 'RezKonv'), (OPENEATS, 'Openeats'), (RECIPEKEEPER, 'Recipe Keeper'),
(PLANTOEAT, 'Plantoeat'), (COOKBOOKAPP, 'CookBookApp'), (COPYMETHAT, 'CopyMeThat'), (PDF, 'PDF'), (MELARECIPES, 'Melarecipes'),
(COOKMATE, 'Cookmate')
(COOKMATE, 'Cookmate'), (REZEPTSUITEDE, 'Recipesuite.de')
))
@@ -336,9 +332,9 @@ class MealPlanForm(forms.ModelForm):
)
help_texts = {
'shared': _('You can list default users to share recipes with in the settings.'), # noqa: E501
'shared': _('You can list default users to share recipes with in the settings.'),
'note': _('You can use markdown to format this field. See the <a href="/docs/markdown/">docs here</a>')
# noqa: E501
}
widgets = {
@@ -493,8 +489,8 @@ class ShoppingPreferenceForm(forms.ModelForm):
help_texts = {
'shopping_share': _('Users will see all items you add to your shopping list. They must add you to see items on their list.'),
'shopping_auto_sync': _(
'Setting to 0 will disable auto sync. When viewing a shopping list the list is updated every set seconds to sync changes someone else might have made. Useful when shopping with multiple people but might use a little bit ' # noqa: E501
'of mobile data. If lower than instance limit it is reset when saving.' # noqa: E501
'Setting to 0 will disable auto sync. When viewing a shopping list the list is updated every set seconds to sync changes someone else might have made. Useful when shopping with multiple people but might use a little bit '
'of mobile data. If lower than instance limit it is reset when saving.'
),
'mealplan_autoadd_shopping': _('Automatically add meal plan ingredients to shopping list.'),
'mealplan_autoinclude_related': _('When adding a meal plan to the shopping list (manually or automatically), include all related recipes.'),
@@ -538,11 +534,13 @@ class SpacePreferenceForm(forms.ModelForm):
class Meta:
model = Space
fields = ('food_inherit', 'reset_food_inherit', 'show_facet_count')
fields = ('food_inherit', 'reset_food_inherit', 'show_facet_count', 'use_plural')
help_texts = {
'food_inherit': _('Fields on food that should be inherited by default.'),
'show_facet_count': _('Show recipe counts on search filters'), }
'show_facet_count': _('Show recipe counts on search filters'),
'use_plural': _('Use the plural form for units and food inside this space.'),
}
widgets = {
'food_inherit': MultiSelectWidget

View File

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

View File

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

View File

@@ -28,7 +28,7 @@ class IngredientParser:
self.food_aliases = c
caches['default'].touch(FOOD_CACHE_KEY, 30)
else:
for a in Automation.objects.filter(space=self.request.space, disabled=False, type=Automation.FOOD_ALIAS).only('param_1', 'param_2').all():
for a in Automation.objects.filter(space=self.request.space, disabled=False, type=Automation.FOOD_ALIAS).only('param_1', 'param_2').order_by('order').all():
self.food_aliases[a.param_1] = a.param_2
caches['default'].set(FOOD_CACHE_KEY, self.food_aliases, 30)
@@ -37,7 +37,7 @@ class IngredientParser:
self.unit_aliases = c
caches['default'].touch(UNIT_CACHE_KEY, 30)
else:
for a in Automation.objects.filter(space=self.request.space, disabled=False, type=Automation.UNIT_ALIAS).only('param_1', 'param_2').all():
for a in Automation.objects.filter(space=self.request.space, disabled=False, type=Automation.UNIT_ALIAS).only('param_1', 'param_2').order_by('order').all():
self.unit_aliases[a.param_1] = a.param_2
caches['default'].set(UNIT_CACHE_KEY, self.unit_aliases, 30)
else:
@@ -59,7 +59,7 @@ class IngredientParser:
except KeyError:
return food
else:
if automation := Automation.objects.filter(space=self.request.space, type=Automation.FOOD_ALIAS, param_1=food, disabled=False).first():
if automation := Automation.objects.filter(space=self.request.space, type=Automation.FOOD_ALIAS, param_1=food, disabled=False).order_by('order').first():
return automation.param_2
return food
@@ -78,7 +78,7 @@ class IngredientParser:
except KeyError:
return unit
else:
if automation := Automation.objects.filter(space=self.request.space, type=Automation.UNIT_ALIAS, param_1=unit, disabled=False).first():
if automation := Automation.objects.filter(space=self.request.space, type=Automation.UNIT_ALIAS, param_1=unit, disabled=False).order_by('order').first():
return automation.param_2
return unit
@@ -221,8 +221,8 @@ class IngredientParser:
# some people/languages put amount and unit at the end of the ingredient string
# if something like this is detected move it to the beginning so the parser can handle it
if len(ingredient) < 1000 and re.search(r'^([A-z])+(.)*[1-9](\d)*\s([A-z])+', ingredient):
match = re.search(r'[1-9](\d)*\s([A-z])+', ingredient)
if len(ingredient) < 1000 and re.search(r'^([^\W\d_])+(.)*[1-9](\d)*\s*([^\W\d_])+', ingredient):
match = re.search(r'[1-9](\d)*\s*([^\W\d_])+', ingredient)
print(f'reording from {ingredient} to {ingredient[match.start():match.end()] + " " + ingredient.replace(ingredient[match.start():match.end()], "")}')
ingredient = ingredient[match.start():match.end()] + ' ' + ingredient.replace(ingredient[match.start():match.end()], '')
@@ -235,6 +235,14 @@ class IngredientParser:
# leading spaces before commas result in extra tokens, clean them out
ingredient = ingredient.replace(' ,', ',')
# handle "(from) - (to)" amounts by using the minimum amount and adding the range to the description
# "10.5 - 200 g XYZ" => "100 g XYZ (10.5 - 200)"
ingredient = re.sub("^(\d+|\d+[\\.,]\d+) - (\d+|\d+[\\.,]\d+) (.*)", "\\1 \\3 (\\1 - \\2)", ingredient)
# if amount and unit are connected add space in between
if re.match('([0-9])+([A-z])+\s', ingredient):
ingredient = re.sub(r'(?<=([a-z])|\d)(?=(?(1)\d|[a-z]))', ' ', ingredient)
tokens = ingredient.split() # split at each space into tokens
if len(tokens) == 1:
# there only is one argument, that must be the food

View File

@@ -35,6 +35,7 @@ Negative examples:
u'<p>del.icio.us</p>'
"""
from xml.etree.ElementTree import Element
import markdown
@@ -64,7 +65,7 @@ class UrlizePattern(markdown.inlinepatterns.Pattern):
else:
url = 'http://' + url
el = markdown.util.etree.Element("a")
el = Element("a")
el.set('href', url)
el.text = markdown.util.AtomicString(text)
return el
@@ -73,9 +74,9 @@ class UrlizePattern(markdown.inlinepatterns.Pattern):
class UrlizeExtension(markdown.Extension):
""" Urlize Extension for Python-Markdown. """
def extendMarkdown(self, md, md_globals):
def extendMarkdown(self, md):
""" Replace autolink with UrlizePattern """
md.inlinePatterns['autolink'] = UrlizePattern(URLIZE_RE, md)
md.inlinePatterns.register(UrlizePattern(URLIZE_RE, md), 'autolink', 120)
def makeExtension(*args, **kwargs):

View File

@@ -1,15 +1,19 @@
import inspect
from django.conf import settings
from django.contrib import messages
from django.contrib.auth.decorators import user_passes_test
from django.core.cache import caches
from django.core.cache import cache
from django.core.exceptions import ValidationError, ObjectDoesNotExist
from django.http import HttpResponseRedirect
from django.urls import reverse, reverse_lazy
from django.utils.translation import gettext as _
from oauth2_provider.contrib.rest_framework import TokenHasScope, TokenHasReadWriteScope
from oauth2_provider.models import AccessToken
from rest_framework import permissions
from rest_framework.permissions import SAFE_METHODS
from cookbook.models import ShareLink, Recipe, UserPreference, UserSpace
from cookbook.models import ShareLink, Recipe, UserSpace
def get_allowed_groups(groups_required):
@@ -27,11 +31,12 @@ def get_allowed_groups(groups_required):
return groups_allowed
def has_group_permission(user, groups):
def has_group_permission(user, groups, no_cache=False):
"""
Tests if a given user is member of a certain group (or any higher group)
Superusers always bypass permission checks.
Unauthenticated users can't be member of any group thus always return false.
:param no_cache: (optional) do not return cached results, always check agains DB
:param user: django auth user object
:param groups: list or tuple of groups the user should be checked for
:return: True if user is in allowed groups, false otherwise
@@ -39,13 +44,23 @@ def has_group_permission(user, groups):
if not user.is_authenticated:
return False
groups_allowed = get_allowed_groups(groups)
CACHE_KEY = hash((inspect.stack()[0][3], (user.pk, user.username, user.email), groups_allowed))
if not no_cache:
cached_result = cache.get(CACHE_KEY, default=None)
if cached_result is not None:
return cached_result
result = False
if user.is_authenticated:
if user_space := user.userspace_set.filter(active=True):
if len(user_space) != 1:
return False # do not allow any group permission if more than one space is active, needs to be changed when simultaneous multi-space-tenancy is added
if bool(user_space.first().groups.filter(name__in=groups_allowed)):
return True
return False
result = False # do not allow any group permission if more than one space is active, needs to be changed when simultaneous multi-space-tenancy is added
elif bool(user_space.first().groups.filter(name__in=groups_allowed)):
result = True
cache.set(CACHE_KEY, result, timeout=10)
return result
def is_object_owner(user, obj):
@@ -104,7 +119,7 @@ def share_link_valid(recipe, share):
"""
try:
CACHE_KEY = f'recipe_share_{recipe.pk}_{share}'
if c := caches['default'].get(CACHE_KEY, False):
if c := cache.get(CACHE_KEY, False):
return c
if link := ShareLink.objects.filter(recipe=recipe, uuid=share, abuse_blocked=False).first():
@@ -112,7 +127,7 @@ def share_link_valid(recipe, share):
return False
link.request_count += 1
link.save()
caches['default'].set(CACHE_KEY, True, timeout=3)
cache.set(CACHE_KEY, True, timeout=3)
return True
return False
except ValidationError:
@@ -299,6 +314,73 @@ class CustomIsShare(permissions.BasePermission):
return False
class CustomRecipePermission(permissions.BasePermission):
"""
Custom permission class for recipe api endpoint
"""
message = _('You do not have the required permissions to view this page!')
def has_permission(self, request, view): # user is either at least a guest or a share link is given and the request is safe
share = request.query_params.get('share', None)
return has_group_permission(request.user, ['guest']) or (share and request.method in SAFE_METHODS and 'pk' in view.kwargs)
def has_object_permission(self, request, view, obj):
share = request.query_params.get('share', None)
if share:
return share_link_valid(obj, share)
else:
if obj.private:
return ((obj.created_by == request.user) or (request.user in obj.shared.all())) and obj.space == request.space
else:
return has_group_permission(request.user, ['guest']) and obj.space == request.space
class CustomUserPermission(permissions.BasePermission):
"""
Custom permission class for user api endpoint
"""
message = _('You do not have the required permissions to view this page!')
def has_permission(self, request, view): # a space filtered user list is visible for everyone
return has_group_permission(request.user, ['guest'])
def has_object_permission(self, request, view, obj): # object write permissions are only available for user
if request.method in SAFE_METHODS and 'pk' in view.kwargs and has_group_permission(request.user, ['guest']) and request.space in obj.userspace_set.all():
return True
elif request.user == obj:
return True
else:
return False
class CustomTokenHasScope(TokenHasScope):
"""
Custom implementation of Django OAuth Toolkit TokenHasScope class
Only difference: if any other authentication method except OAuth2Authentication is used the scope check is ignored
IMPORTANT: do not use this class without any other permission class as it will not check anything besides token scopes
"""
def has_permission(self, request, view):
if type(request.auth) == AccessToken:
return super().has_permission(request, view)
else:
return request.user.is_authenticated
class CustomTokenHasReadWriteScope(TokenHasReadWriteScope):
"""
Custom implementation of Django OAuth Toolkit TokenHasReadWriteScope class
Only difference: if any other authentication method except OAuth2Authentication is used the scope check is ignored
IMPORTANT: do not use this class without any other permission class as it will not check anything besides token scopes
"""
def has_permission(self, request, view):
if type(request.auth) == AccessToken:
return super().has_permission(request, view)
else:
return True
def above_space_limit(space): # TODO add file storage limit
"""
Test if the space has reached any limit (e.g. max recipes, users, ..)

View File

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

View File

@@ -3,18 +3,16 @@ from collections import Counter
from datetime import date, timedelta
from django.contrib.postgres.search import SearchQuery, SearchRank, SearchVector, TrigramSimilarity
from django.core.cache import cache
from django.core.cache import caches
from django.db.models import (Avg, Case, Count, Exists, F, Func, Max, OuterRef, Q, Subquery, Sum,
Value, When)
from django.db.models import (Avg, Case, Count, Exists, F, Func, Max, OuterRef, Q, Subquery, Value, When, FilteredRelation)
from django.db.models.functions import Coalesce, Lower, Substr
from django.utils import timezone, translation
from django.utils.translation import gettext as _
from cookbook.filters import RecipeFilter
from cookbook.helper.HelperFunctions import Round, str2bool
from cookbook.helper.permission_helper import has_group_permission
from cookbook.managers import DICTIONARY
from cookbook.models import (CookLog, CustomFilter, Food, Keyword, Recipe, RecipeBook, SearchFields,
from cookbook.models import (CookLog, CustomFilter, Food, Keyword, Recipe, SearchFields,
SearchPreference, ViewLog)
from recipes import settings
@@ -24,7 +22,7 @@ from recipes import settings
class RecipeSearch():
_postgres = settings.DATABASES['default']['ENGINE'] in ['django.db.backends.postgresql_psycopg2', 'django.db.backends.postgresql']
def __init__(self, request, **params):
def __init__(self, request, **params):
self._request = request
self._queryset = None
if f := params.get('filter', None):
@@ -38,7 +36,13 @@ class RecipeSearch():
else:
self._params = {**(params or {})}
if self._request.user.is_authenticated:
self._search_prefs = request.user.searchpreference
CACHE_KEY = f'search_pref_{request.user.id}'
cached_result = cache.get(CACHE_KEY, default=None)
if cached_result is not None:
self._search_prefs = cached_result
else:
self._search_prefs = request.user.searchpreference
cache.set(CACHE_KEY, self._search_prefs, timeout=10)
else:
self._search_prefs = SearchPreference()
self._string = self._params.get('query').strip() if self._params.get('query', None) else None
@@ -113,19 +117,20 @@ class RecipeSearch():
)
self.search_rank = None
self.orderby = []
self._default_sort = ['-favorite'] # TODO add user setting
self._filters = None
self._fuzzy_match = None
def get_queryset(self, queryset):
self._queryset = queryset
self._queryset = self._queryset.prefetch_related('keywords')
self._build_sort_order()
self._recently_viewed(num_recent=self._num_recent)
self._cooked_on_filter(cooked_date=self._cookedon)
self._created_on_filter(created_date=self._createdon)
self._updated_on_filter(updated_date=self._updatedon)
self._viewed_on_filter(viewed_date=self._viewedon)
self._favorite_recipes(timescooked=self._timescooked)
self._favorite_recipes(times_cooked=self._timescooked)
self._new_recipes()
self.keyword_filters(**self._keywords)
self.food_filters(**self._foods)
@@ -152,7 +157,7 @@ class RecipeSearch():
else:
order = []
# TODO add userpreference for default sort order and replace '-favorite'
default_order = ['-favorite']
default_order = ['-name']
# recent and new_recipe are always first; they float a few recipes to the top
if self._num_recent:
order += ['-recent']
@@ -209,7 +214,7 @@ class RecipeSearch():
else:
self._queryset = self._queryset.annotate(simularity=Coalesce(Subquery(simularity), 0.0))
if self._sort_includes('score') and self._fulltext_include and self._fuzzy_match is not None:
self._queryset = self._queryset.annotate(score=F('rank')+F('simularity'))
self._queryset = self._queryset.annotate(score=F('rank') + F('simularity'))
else:
query_filter = Q()
for f in [x + '__unaccent__iexact' if x in self._unaccent_include else x + '__iexact' for x in SearchFields.objects.all().values_list('field', flat=True)]:
@@ -290,25 +295,25 @@ class RecipeSearch():
'recipe').annotate(recent=Max('created_at')).order_by('-recent')[:num_recent]
self._queryset = self._queryset.annotate(recent=Coalesce(Max(Case(When(pk__in=num_recent_recipes.values('recipe'), then='viewlog__pk'))), Value(0)))
def _favorite_recipes(self, timescooked=None):
if self._sort_includes('favorite') or timescooked:
lessthan = '-' in (timescooked or []) or not self._sort_includes('-favorite')
if lessthan:
def _favorite_recipes(self, times_cooked=None):
if self._sort_includes('favorite') or times_cooked:
less_than = '-' in (times_cooked or []) or not self._sort_includes('-favorite')
if less_than:
default = 1000
else:
default = 0
favorite_recipes = CookLog.objects.filter(created_by=self._request.user, space=self._request.space, recipe=OuterRef('pk')
).values('recipe').annotate(count=Count('pk', distinct=True)).values('count')
self._queryset = self._queryset.annotate(favorite=Coalesce(Subquery(favorite_recipes), default))
if timescooked is None:
if times_cooked is None:
return
if timescooked == '0':
if times_cooked == '0':
self._queryset = self._queryset.filter(favorite=0)
elif lessthan:
self._queryset = self._queryset.filter(favorite__lte=int(timescooked[1:])).exclude(favorite=0)
elif less_than:
self._queryset = self._queryset.filter(favorite__lte=int(times_cooked[1:])).exclude(favorite=0)
else:
self._queryset = self._queryset.filter(favorite__gte=int(timescooked))
self._queryset = self._queryset.filter(favorite__gte=int(times_cooked))
def keyword_filters(self, **kwargs):
if all([kwargs[x] is None for x in kwargs]):
@@ -508,10 +513,10 @@ class RecipeSearch():
shopping_users = [*self._request.user.get_shopping_share(), self._request.user]
onhand_filter = (
Q(steps__ingredients__food__onhand_users__in=shopping_users) # food onhand
| Q(steps__ingredients__food__substitute__onhand_users__in=shopping_users) # or substitute food onhand
| Q(steps__ingredients__food__in=self.__children_substitute_filter(shopping_users))
| Q(steps__ingredients__food__in=self.__sibling_substitute_filter(shopping_users))
Q(steps__ingredients__food__onhand_users__in=shopping_users) # food onhand
| Q(steps__ingredients__food__substitute__onhand_users__in=shopping_users) # or substitute food onhand
| Q(steps__ingredients__food__in=self.__children_substitute_filter(shopping_users))
| Q(steps__ingredients__food__in=self.__sibling_substitute_filter(shopping_users))
)
makenow_recipes = Recipe.objects.annotate(
count_food=Count('steps__ingredients__food__pk', filter=Q(steps__ingredients__food__isnull=False), distinct=True),
@@ -520,10 +525,10 @@ class RecipeSearch():
steps__ingredients__food__recipe__isnull=True), distinct=True),
has_child_sub=Case(When(steps__ingredients__food__in=self.__children_substitute_filter(shopping_users), then=Value(1)), default=Value(0)),
has_sibling_sub=Case(When(steps__ingredients__food__in=self.__sibling_substitute_filter(shopping_users), then=Value(1)), default=Value(0))
).annotate(missingfood=F('count_food')-F('count_onhand')-F('count_ignore_shopping')).filter(missingfood=missing)
).annotate(missingfood=F('count_food') - F('count_onhand') - F('count_ignore_shopping')).filter(missingfood=missing)
self._queryset = self._queryset.distinct().filter(id__in=makenow_recipes.values('id'))
@ staticmethod
@staticmethod
def __children_substitute_filter(shopping_users=None):
children_onhand_subquery = Food.objects.filter(
path__startswith=OuterRef('path'),
@@ -539,10 +544,10 @@ class RecipeSearch():
).annotate(child_onhand_count=Exists(children_onhand_subquery)
).filter(child_onhand_count=True)
@ staticmethod
@staticmethod
def __sibling_substitute_filter(shopping_users=None):
sibling_onhand_subquery = Food.objects.filter(
path__startswith=Substr(OuterRef('path'), 1, Food.steplen*(OuterRef('depth')-1)),
path__startswith=Substr(OuterRef('path'), 1, Food.steplen * (OuterRef('depth') - 1)),
depth=OuterRef('depth'),
onhand_users__in=shopping_users
)
@@ -566,7 +571,7 @@ class RecipeFacet():
self._request = request
self._queryset = queryset
self.hash_key = hash_key or str(hash(frozenset(self._queryset.values_list('pk'))))
self.hash_key = hash_key or str(hash(self._queryset.query))
self._SEARCH_CACHE_KEY = f"recipes_filter_{self.hash_key}"
self._cache_timeout = cache_timeout
self._cache = caches['default'].get(self._SEARCH_CACHE_KEY, {})
@@ -746,7 +751,7 @@ class RecipeFacet():
).filter(depth=depth, count__gt=0
).values('id', 'name', 'count', 'numchild').order_by(Lower('name').asc())[:200]
else:
return queryset.filter(depth=depth).values('id', 'name', 'numchild').order_by(Lower('name').asc())
return queryset.filter(depth=depth).values('id', 'name', 'numchild').order_by(Lower('name').asc())
def _food_queryset(self, queryset, food=None):
depth = getattr(food, 'depth', 0) + 1
@@ -758,13 +763,3 @@ class RecipeFacet():
).values('id', 'name', 'count', 'numchild').order_by(Lower('name').asc())[:200]
else:
return queryset.filter(depth__lte=depth).values('id', 'name', 'numchild').order_by(Lower('name').asc())
def old_search(request):
if has_group_permission(request.user, ('guest',)):
params = dict(request.GET)
params['internal'] = None
f = RecipeFilter(params,
queryset=Recipe.objects.filter(space=request.space).all().order_by(Lower('name').asc()),
space=request.space)
return f.qs

View File

@@ -1,19 +1,18 @@
import random
import re
from html import unescape
from pytube import YouTube
from unicodedata import decomposition
from django.utils.dateparse import parse_duration
from django.utils.translation import gettext as _
from isodate import parse_duration as iso_parse_duration
from isodate.isoerror import ISO8601Error
from recipe_scrapers._utils import get_minutes
from pytube import YouTube
from recipe_scrapers._utils import get_host_name, get_minutes
from cookbook.helper import recipe_url_import as helper
from cookbook.helper.ingredient_parser import IngredientParser
from cookbook.models import Keyword
from cookbook.models import Keyword, Automation
# from recipe_scrapers._utils import get_minutes ## temporary until/unless upstream incorporates get_minutes() PR
@@ -23,7 +22,7 @@ def get_from_scraper(scrape, request):
# converting the scrape_me object to the existing json format based on ld+json
recipe_json = {}
try:
recipe_json['name'] = parse_name(scrape.title() or None)
recipe_json['name'] = parse_name(scrape.title()[:128] or None)
except Exception:
recipe_json['name'] = None
if not recipe_json['name']:
@@ -123,7 +122,13 @@ def get_from_scraper(scrape, request):
try:
keywords.append(source_url.replace('http://', '').replace('https://', '').split('/')[0])
except Exception:
pass
recipe_json['source_url'] = ''
try:
if scrape.author():
keywords.append(scrape.author())
except:
pass
try:
recipe_json['keywords'] = parse_keywords(list(set(map(str.casefold, keywords))), request.space)
@@ -141,42 +146,58 @@ def get_from_scraper(scrape, request):
if len(recipe_json['steps']) == 0:
recipe_json['steps'].append({'instruction': '', 'ingredients': [], })
if len(parse_description(description)) > 256: # split at 256 as long descriptions dont look good on recipe cards
recipe_json['steps'][0]['instruction'] = f'*{parse_description(description)}* \n\n' + recipe_json['steps'][0]['instruction']
parsed_description = parse_description(description)
# TODO notify user about limit if reached
# limits exist to limit the attack surface for dos style attacks
automations = Automation.objects.filter(type=Automation.DESCRIPTION_REPLACE, space=request.space, disabled=False).only('param_1', 'param_2', 'param_3').all().order_by('order')[:512]
for a in automations:
if re.match(a.param_1, (recipe_json['source_url'])[:512]):
parsed_description = re.sub(a.param_2, a.param_3, parsed_description, count=1)
if len(parsed_description) > 256: # split at 256 as long descriptions don't look good on recipe cards
recipe_json['steps'][0]['instruction'] = f'*{parsed_description}* \n\n' + recipe_json['steps'][0]['instruction']
else:
recipe_json['description'] = parse_description(description)[:512]
recipe_json['description'] = parsed_description[:512]
try:
for x in scrape.ingredients():
try:
amount, unit, ingredient, note = ingredient_parser.parse(x)
ingredient = {
'amount': amount,
'food': {
'name': ingredient,
},
'unit': None,
'note': note,
'original_text': x
}
if unit:
ingredient['unit'] = {'name': unit, }
recipe_json['steps'][0]['ingredients'].append(ingredient)
except Exception:
recipe_json['steps'][0]['ingredients'].append(
{
'amount': 0,
'unit': None,
if x.strip() != '':
try:
amount, unit, ingredient, note = ingredient_parser.parse(x)
ingredient = {
'amount': amount,
'food': {
'name': x,
'name': ingredient,
},
'note': '',
'unit': None,
'note': note,
'original_text': x
}
)
if unit:
ingredient['unit'] = {'name': unit, }
recipe_json['steps'][0]['ingredients'].append(ingredient)
except Exception:
recipe_json['steps'][0]['ingredients'].append(
{
'amount': 0,
'unit': None,
'food': {
'name': x,
},
'note': '',
'original_text': x
}
)
except Exception:
pass
if recipe_json['source_url']:
automations = Automation.objects.filter(type=Automation.INSTRUCTION_REPLACE, space=request.space, disabled=False).only('param_1', 'param_2', 'param_3').order_by('order').all()[:512]
for a in automations:
if re.match(a.param_1, (recipe_json['source_url'])[:512]):
for s in recipe_json['steps']:
s['instruction'] = re.sub(a.param_2, a.param_3, s['instruction'])
return recipe_json
@@ -369,3 +390,32 @@ def iso_duration_to_minutes(string):
string
).groupdict()
return int(match['days'] or 0) * 24 * 60 + int(match['hours'] or 0) * 60 + int(match['minutes'] or 0)
def get_images_from_soup(soup, url):
sources = ['src', 'srcset', 'data-src']
images = []
img_tags = soup.find_all('img')
if url:
site = get_host_name(url)
prot = url.split(':')[0]
urls = []
for img in img_tags:
for src in sources:
try:
urls.append(img[src])
except KeyError:
pass
for u in urls:
u = u.split('?')[0]
filename = re.search(r'/([\w_-]+[.](jpg|jpeg|gif|png))$', u)
if filename:
if (('http' not in u) and (url)):
# sometimes an image source can be relative
# if it is provide the base url
u = '{}://{}{}'.format(prot, site, u)
if 'http' in u:
images.append(u)
return images

View File

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

View File

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

View File

@@ -22,10 +22,25 @@ class IngredientObject(object):
else:
self.amount = f"<scalable-number v-bind:number='{bleach.clean(str(ingredient.amount))}' v-bind:factor='ingredient_factor'></scalable-number>"
if ingredient.unit:
self.unit = bleach.clean(str(ingredient.unit))
if ingredient.unit.plural_name in (None, ""):
self.unit = bleach.clean(str(ingredient.unit))
else:
if ingredient.always_use_plural_unit or ingredient.amount > 1 and not ingredient.no_amount:
self.unit = bleach.clean(ingredient.unit.plural_name)
else:
self.unit = bleach.clean(str(ingredient.unit))
else:
self.unit = ""
self.food = bleach.clean(str(ingredient.food))
if ingredient.food:
if ingredient.food.plural_name in (None, ""):
self.food = bleach.clean(str(ingredient.food))
else:
if ingredient.always_use_plural_food or ingredient.amount > 1 and not ingredient.no_amount:
self.food = bleach.clean(str(ingredient.food.plural_name))
else:
self.food = bleach.clean(str(ingredient.food))
else:
self.food = ""
self.note = bleach.clean(str(ingredient.note))
def __str__(self):

View File

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

View File

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

View File

@@ -1,16 +1,12 @@
import time
import traceback
import datetime
import json
import traceback
import uuid
from io import BytesIO, StringIO
from io import BytesIO
from zipfile import BadZipFile, ZipFile
import lxml
from django.core.cache import cache
import datetime
from bs4 import Tag
from django.core.cache import cache
from django.core.exceptions import ObjectDoesNotExist
from django.core.files import File
from django.db import IntegrityError
@@ -20,8 +16,7 @@ from django.utils.translation import gettext as _
from django_scopes import scope
from lxml import etree
from cookbook.forms import ImportExportBase
from cookbook.helper.image_processing import get_filetype, handle_image
from cookbook.helper.image_processing import handle_image
from cookbook.models import Keyword, Recipe
from recipes.settings import DEBUG
from recipes.settings import EXPORT_FILE_CACHE_DURATION
@@ -43,7 +38,7 @@ class Integration:
self.export_type = export_type
self.ignored_recipes = []
description = f'Imported by {request.user.get_user_name()} at {date_format(datetime.datetime.now(), "DATETIME_FORMAT")}. Type: {export_type}'
description = f'Imported by {request.user.get_user_display_name()} at {date_format(datetime.datetime.now(), "DATETIME_FORMAT")}. Type: {export_type}'
icon = '📥'
try:
@@ -169,7 +164,7 @@ class Integration:
for z in file_list:
try:
if not hasattr(z, 'filename'):
if not hasattr(z, 'filename') or type(z) == Tag:
recipe = self.get_recipe_from_file(z)
else:
recipe = self.get_recipe_from_file(BytesIO(import_zip.read(z.filename)))
@@ -182,7 +177,7 @@ class Integration:
traceback.print_exc()
self.handle_exception(e, log=il, message=f'-------------------- \nERROR \n{e}\n--------------------\n')
import_zip.close()
elif '.json' in f['name'] or '.txt' in f['name'] or '.mmf' in f['name'] or '.rk' in f['name'] or '.melarecipe' in f['name']:
elif '.json' in f['name'] or '.xml' in f['name'] or '.txt' in f['name'] or '.mmf' in f['name'] or '.rk' in f['name'] or '.melarecipe' in f['name']:
data_list = self.split_recipe_file(f['file'])
il.total_recipes += len(data_list)
for d in data_list:

View File

@@ -1,13 +1,14 @@
import json
import re
from io import BytesIO
from io import BytesIO, StringIO
from zipfile import ZipFile
from PIL import Image
from cookbook.helper.image_processing import get_filetype
from cookbook.helper.ingredient_parser import IngredientParser
from cookbook.helper.recipe_url_import import iso_duration_to_minutes
from cookbook.integration.integration import Integration
from cookbook.models import Ingredient, Keyword, Recipe, Step
from cookbook.models import Ingredient, Keyword, Recipe, Step, NutritionInformation
class NextcloudCookbook(Integration):
@@ -70,12 +71,21 @@ class NextcloudCookbook(Integration):
recipe.steps.add(step)
if 'nutrition' in recipe_json:
nutrition = {}
try:
recipe.nutrition.calories = recipe_json['nutrition']['calories'].replace(' kcal', '').replace(' ', '')
recipe.nutrition.proteins = recipe_json['nutrition']['calories'].replace(' g', '').replace(',', '.').replace(' ', '')
recipe.nutrition.fats = recipe_json['nutrition']['calories'].replace(' g', '').replace(',', '.').replace(' ', '')
recipe.nutrition.carbohydrates = recipe_json['nutrition']['calories'].replace(' g', '').replace(',', '.').replace(' ', '')
except Exception:
if 'calories' in recipe_json['nutrition']:
nutrition['calories'] = int(re.search(r'\d+', recipe_json['nutrition']['calories']).group())
if 'proteinContent' in recipe_json['nutrition']:
nutrition['proteins'] = int(re.search(r'\d+', recipe_json['nutrition']['proteinContent']).group())
if 'fatContent' in recipe_json['nutrition']:
nutrition['fats'] = int(re.search(r'\d+', recipe_json['nutrition']['fatContent']).group())
if 'carbohydrateContent' in recipe_json['nutrition']:
nutrition['carbohydrates'] = int(re.search(r'\d+', recipe_json['nutrition']['carbohydrateContent']).group())
if nutrition != {}:
recipe.nutrition = NutritionInformation.objects.create(**nutrition, space=self.request.space)
recipe.save()
except Exception as e:
pass
for f in self.files:
@@ -87,5 +97,92 @@ class NextcloudCookbook(Integration):
return recipe
def formatTime(self, min):
h = min//60
m = min % 60
return f'PT{h}H{m}M0S'
def get_file_from_recipe(self, recipe):
raise NotImplementedError('Method not implemented in storage integration')
export = {}
export['name'] = recipe.name
export['description'] = recipe.description
export['url'] = recipe.source_url
export['prepTime'] = self.formatTime(recipe.working_time)
export['cookTime'] = self.formatTime(recipe.waiting_time)
export['totalTime'] = self.formatTime(recipe.working_time+recipe.waiting_time)
export['recipeYield'] = recipe.servings
export['image'] = f'/Recipes/{recipe.name}/full.jpg'
export['imageUrl'] = f'/Recipes/{recipe.name}/full.jpg'
recipeKeyword = []
for k in recipe.keywords.all():
recipeKeyword.append(k.name)
export['keywords'] = recipeKeyword
recipeInstructions = []
recipeIngredient = []
for s in recipe.steps.all():
recipeInstructions.append(s.instruction)
for i in s.ingredients.all():
recipeIngredient.append(f'{float(i.amount)} {i.unit} {i.food}')
export['recipeIngredient'] = recipeIngredient
export['recipeInstructions'] = recipeInstructions
return "recipe.json", json.dumps(export)
def get_files_from_recipes(self, recipes, el, cookie):
export_zip_stream = BytesIO()
export_zip_obj = ZipFile(export_zip_stream, 'w')
for recipe in recipes:
if recipe.internal and recipe.space == self.request.space:
recipe_stream = StringIO()
filename, data = self.get_file_from_recipe(recipe)
recipe_stream.write(data)
export_zip_obj.writestr(f'{recipe.name}/{filename}', recipe_stream.getvalue())
recipe_stream.close()
try:
imageByte = recipe.image.file.read()
export_zip_obj.writestr(f'{recipe.name}/full.jpg', self.getJPEG(imageByte))
export_zip_obj.writestr(f'{recipe.name}/thumb.jpg', self.getThumb(171, imageByte))
export_zip_obj.writestr(f'{recipe.name}/thumb16.jpg', self.getThumb(16, imageByte))
except ValueError:
pass
el.exported_recipes += 1
el.msg += self.get_recipe_processed_msg(recipe)
el.save()
export_zip_obj.close()
return [[ self.get_export_file_name(), export_zip_stream.getvalue() ]]
def getJPEG(self, imageByte):
image = Image.open(BytesIO(imageByte))
image = image.convert('RGB')
bytes = BytesIO()
image.save(bytes, "JPEG")
return bytes.getvalue()
def getThumb(self, size, imageByte):
image = Image.open(BytesIO(imageByte))
w, h = image.size
m = min(w, h)
image = image.crop(((w-m)//2, (h-m)//2, (w+m)//2, (h+m)//2))
image = image.resize([size, size], Image.Resampling.LANCZOS)
image = image.convert('RGB')
bytes = BytesIO()
image.save(bytes, "JPEG")
return bytes.getvalue()

View File

@@ -61,7 +61,7 @@ class RecetteTek(Integration):
ingredient_parser = IngredientParser(self.request, True)
for ingredient in file['ingredients'].split('\n'):
if len(ingredient.strip()) > 0:
amount, unit, food, note = ingredient_parser.parse(food)
amount, unit, food, note = ingredient_parser.parse(ingredient.strip())
f = ingredient_parser.get_food(ingredient)
u = ingredient_parser.get_unit(unit)
step.ingredients.add(Ingredient.objects.create(

View File

@@ -41,7 +41,7 @@ class RecipeKeeper(Integration):
except AttributeError:
pass
step = Step.objects.create(instruction='', space=self.request.space,)
step = Step.objects.create(instruction='', space=self.request.space, )
ingredient_parser = IngredientParser(self.request, True)
for ingredient in file.find("div", {"itemprop": "recipeIngredients"}).findChildren("p"):
@@ -51,13 +51,20 @@ class RecipeKeeper(Integration):
f = ingredient_parser.get_food(food)
u = ingredient_parser.get_unit(unit)
step.ingredients.add(Ingredient.objects.create(
food=f, unit=u, amount=amount, note=note, original_text=ingredient, space=self.request.space,
food=f, unit=u, amount=amount, note=note, original_text=str(ingredient).replace('<p>', '').replace('</p>', ''), space=self.request.space,
))
for s in file.find("div", {"itemprop": "recipeDirections"}).find_all("p"):
if s.text == "":
continue
step.instruction += s.text + ' \n'
step.save()
for s in file.find("div", {"itemprop": "recipeNotes"}).find_all("p"):
if s.text == "":
continue
step.instruction += s.text + ' \n'
step.save()
if file.find("span", {"itemprop": "recipeSource"}).text != '':
step.instruction += "\n\n" + _("Imported from") + ": " + file.find("span", {"itemprop": "recipeSource"}).text

View File

@@ -0,0 +1,59 @@
from xml import etree
from lxml import etree
from cookbook.helper.ingredient_parser import IngredientParser
from cookbook.helper.recipe_url_import import parse_time, parse_servings, parse_servings_text
from cookbook.integration.integration import Integration
from cookbook.models import Ingredient, Recipe, Step
class Rezeptsuitede(Integration):
def split_recipe_file(self, file):
xml_file = etree.parse(file).getroot().getchildren()
recipe_list = xml_file.find('recipe')
return recipe_list
def get_recipe_from_file(self, file):
recipe_xml = file
recipe = Recipe.objects.create(
name=recipe_xml.find('title').text.strip(),
created_by=self.request.user, internal=True, space=self.request.space)
if recipe_xml.find('servingtype') is not None and recipe_xml.find('servingtype').text is not None:
recipe.servings = parse_servings(recipe_xml.find('servingtype').text.strip())
recipe.servings_text = parse_servings_text(recipe_xml.find('servingtype').text.strip())
if recipe_xml.find('description') is not None: # description is a list of <li>'s with text
if len(recipe_xml.find('description')) > 0:
recipe.description = recipe_xml.find('description')[0].text[:512]
for step in recipe_xml.find('step'):
if step.text:
step = Step.objects.create(
instruction=step.text.strip(), space=self.request.space,
)
recipe.steps.add(step)
ingredient_parser = IngredientParser(self.request, True)
if recipe_xml.find('ingredient'):
ingredient_step = recipe.steps.first()
if ingredient_step is None:
ingredient_step = Step.objects.create(space=self.request.space, instruction='')
for ingredient in recipe_xml.find('ingredient'):
f = ingredient_parser.get_food(ingredient.attrib['item'])
u = ingredient_parser.get_unit(ingredient.attrib['unit'])
ingredient_step.ingredients.add(Ingredient.objects.create(
food=f, unit=u, amount=ingredient.attrib['qty'], original_text=ingredient.text.strip(), space=self.request.space,
))
recipe.save()
return recipe
def get_file_from_recipe(self, recipe):
raise NotImplementedError('Method not implemented in storage integration')

Binary file not shown.

File diff suppressed because it is too large Load Diff

View File

@@ -6,20 +6,22 @@
# Translators:
# Pavel Solař <pavelsolar86@gmail.com>, 2021
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-02-09 18:01+0100\n"
"PO-Revision-Date: 2020-06-02 19:28+0000\n"
"Last-Translator: Pavel Solař <pavelsolar86@gmail.com>, 2021\n"
"Language-Team: Czech (https://www.transifex.com/django-recipes/teams/110507/cs/)\n"
"PO-Revision-Date: 2023-01-08 17:55+0000\n"
"Last-Translator: Joachim Weber <joachim.weber@gmx.de>\n"
"Language-Team: Czech <http://translate.tandoor.dev/projects/tandoor/"
"recipes-backend/cs/>\n"
"Language: cs\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Language: cs\n"
"Plural-Forms: nplurals=4; plural=(n == 1 && n % 1 == 0) ? 0 : (n >= 2 && n <= 4 && n % 1 == 0) ? 1: (n % 1 != 0 ) ? 2 : 3;\n"
"Plural-Forms: nplurals=4; plural=(n == 1 && n % 1 == 0) ? 0 : (n >= 2 && n "
"<= 4 && n % 1 == 0) ? 1: (n % 1 != 0 ) ? 2 : 3;\n"
"X-Generator: Weblate 4.15\n"
#: .\cookbook\filters.py:22 .\cookbook\templates\base.html:87
#: .\cookbook\templates\forms\edit_internal_recipe.html:219
@@ -173,7 +175,7 @@ msgstr "Potravina, která by měla být nahrazena."
#: .\cookbook\forms.py:198
msgid "Add your comment: "
msgstr "Přidat vlastní komentář:"
msgstr "Přidat vlastní komentář: "
#: .\cookbook\forms.py:229
msgid "Leave empty for dropbox and enter app password for nextcloud."

View File

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

File diff suppressed because it is too large Load Diff

Binary file not shown.

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -11,8 +11,8 @@ msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-02-09 18:01+0100\n"
"PO-Revision-Date: 2021-10-13 12:50+0000\n"
"Last-Translator: Hrachya Kocharyan <hkocharyan@ctemplar.com>\n"
"PO-Revision-Date: 2023-01-08 17:55+0000\n"
"Last-Translator: Joachim Weber <joachim.weber@gmx.de>\n"
"Language-Team: Armenian <http://translate.tandoor.dev/projects/tandoor/"
"recipes-backend/hy/>\n"
"Language: hy\n"
@@ -20,7 +20,7 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
"X-Generator: Weblate 4.8\n"
"X-Generator: Weblate 4.15\n"
#: .\cookbook\filters.py:22 .\cookbook\templates\base.html:87
#: .\cookbook\templates\forms\edit_internal_recipe.html:219
@@ -410,7 +410,7 @@ msgstr "Դուրս գալ"
#: .\cookbook\templates\account\logout.html:11
msgid "Are you sure you want to sign out?"
msgstr "Համոզվա՞ծ եք, որ ցանկանում եք դուրս գալ:"
msgstr "Համոզվա՞ծ եք, որ ցանկանում եք դուրս գալ՞"
#: .\cookbook\templates\account\password_reset.html:5
#: .\cookbook\templates\account\password_reset_done.html:5

Binary file not shown.

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -8,8 +8,8 @@ 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: 2022-04-07 19:32+0000\n"
"Last-Translator: Artem Aksenov <artemmillerr@gmail.com>\n"
"PO-Revision-Date: 2022-11-30 19:09+0000\n"
"Last-Translator: Alex <kovsharoff@gmail.com>\n"
"Language-Team: Russian <http://translate.tandoor.dev/projects/tandoor/"
"recipes-backend/ru/>\n"
"Language: ru\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 4.10.1\n"
"X-Generator: Weblate 4.14.1\n"
#: .\cookbook\filters.py:23 .\cookbook\templates\base.html:125
#: .\cookbook\templates\forms\ingredients.html:34
@@ -396,8 +396,9 @@ msgstr ""
#: .\cookbook\templates\include\log_cooking.html:16
#: .\cookbook\templates\url_import.html:224
#: .\cookbook\templates\url_import.html:455
#, fuzzy
msgid "Servings"
msgstr ""
msgstr "Порции"
#: .\cookbook\integration\safron.py:25
msgid "Waiting time"
@@ -468,7 +469,7 @@ msgstr ""
#: .\cookbook\models.py:198 .\cookbook\templates\base.html:90
msgid "Books"
msgstr ""
msgstr "Книги"
#: .\cookbook\models.py:206
msgid "Small"

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,19 @@
# Generated by Django 4.0.7 on 2022-09-12 10:29
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('cookbook', '0183_alter_space_image'),
]
operations = [
migrations.AlterField(
model_name='userpreference',
name='image',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='user_image', to='cookbook.userfile'),
),
]

View File

@@ -0,0 +1,38 @@
# Generated by Django 4.0.8 on 2022-11-22 06:34
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('cookbook', '0184_alter_userpreference_image'),
]
operations = [
migrations.AddField(
model_name='food',
name='plural_name',
field=models.CharField(blank=True, default=None, max_length=128, null=True),
),
migrations.AddField(
model_name='ingredient',
name='always_use_plural_food',
field=models.BooleanField(default=False),
),
migrations.AddField(
model_name='ingredient',
name='always_use_plural_unit',
field=models.BooleanField(default=False),
),
migrations.AddField(
model_name='space',
name='use_plural',
field=models.BooleanField(default=False),
),
migrations.AddField(
model_name='unit',
name='plural_name',
field=models.CharField(blank=True, default=None, max_length=128, null=True),
),
]

View File

@@ -0,0 +1,23 @@
# Generated by Django 4.1.4 on 2023-01-03 21:38
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('cookbook', '0185_food_plural_name_ingredient_always_use_plural_food_and_more'),
]
operations = [
migrations.AddField(
model_name='automation',
name='order',
field=models.IntegerField(default=1000),
),
migrations.AlterField(
model_name='automation',
name='type',
field=models.CharField(choices=[('FOOD_ALIAS', 'Food Alias'), ('UNIT_ALIAS', 'Unit Alias'), ('KEYWORD_ALIAS', 'Keyword Alias'), ('DESCRIPTION_REPLACE', 'Description Replace'), ('INSTRUCTION_REPLACE', 'Instruction Replace')], max_length=128),
),
]

View File

@@ -0,0 +1,18 @@
# Generated by Django 4.1.4 on 2023-01-20 09:19
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('cookbook', '0186_automation_order_alter_automation_type'),
]
operations = [
migrations.AlterField(
model_name='space',
name='use_plural',
field=models.BooleanField(default=True),
),
]

View File

@@ -4,6 +4,8 @@ import re
import uuid
from datetime import date, timedelta
import oauth2_provider.models
from PIL import Image
from annoying.fields import AutoOneToOneField
from django.contrib import auth
from django.contrib.auth.models import Group, User
@@ -12,7 +14,7 @@ from django.contrib.postgres.search import SearchVectorField
from django.core.files.uploadedfile import InMemoryUploadedFile, UploadedFile
from django.core.validators import MinLengthValidator
from django.db import IntegrityError, models
from django.db.models import Index, ProtectedError, Q
from django.db.models import Index, ProtectedError, Q, Avg, Max
from django.db.models.fields.related import ManyToManyField
from django.db.models.functions import Substr
from django.utils import timezone
@@ -25,7 +27,7 @@ from recipes.settings import (COMMENT_PREF_DEFAULT, FRACTION_PREF_DEFAULT, KJ_PR
SORT_TREE_BY_NAME, STICKY_NAV_PREF_DEFAULT)
def get_user_name(self):
def get_user_display_name(self):
if not (name := f"{self.first_name} {self.last_name}") == " ":
return name
else:
@@ -57,11 +59,18 @@ def get_shopping_share(self):
]))
auth.models.User.add_to_class('get_user_name', get_user_name)
auth.models.User.add_to_class('get_user_display_name', get_user_display_name)
auth.models.User.add_to_class('get_shopping_share', get_shopping_share)
auth.models.User.add_to_class('get_active_space', get_active_space)
def oauth_token_get_owner(self):
return self.user
oauth2_provider.models.AccessToken.add_to_class('get_owner', oauth_token_get_owner)
def get_model_name(model):
return ('_'.join(re.findall('[A-Z][^A-Z]*', model.__name__))).lower()
@@ -244,12 +253,14 @@ class FoodInheritField(models.Model, PermissionModelMixin):
class Space(ExportModelOperationsMixin('space'), models.Model):
name = models.CharField(max_length=128, default='Default')
image = models.ForeignKey("UserFile", on_delete=models.SET_NULL, null=True, blank=True, related_name='space_image')
created_by = models.ForeignKey(User, on_delete=models.PROTECT, null=True)
created_at = models.DateTimeField(auto_now_add=True)
message = models.CharField(max_length=512, default='', blank=True)
max_recipes = models.IntegerField(default=0)
max_file_storage_mb = models.IntegerField(default=0, help_text=_('Maximum file storage for space in MB. 0 for unlimited, -1 to disable file upload.'))
max_users = models.IntegerField(default=0)
use_plural = models.BooleanField(default=True)
allow_sharing = models.BooleanField(default=True)
demo = models.BooleanField(default=False)
food_inherit = models.ManyToManyField(FoodInheritField, blank=True)
@@ -355,34 +366,16 @@ class UserPreference(models.Model, PermissionModelMixin):
(BOOKS, _('Books')),
)
# Search Style
SMALL = 'SMALL'
LARGE = 'LARGE'
NEW = 'NEW'
SEARCH_STYLE = ((SMALL, _('Small')), (LARGE, _('Large')), (NEW, _('New')))
user = AutoOneToOneField(User, on_delete=models.CASCADE, primary_key=True)
image = models.ForeignKey("UserFile", on_delete=models.SET_NULL, null=True, blank=True, related_name='user_image')
theme = models.CharField(choices=THEMES, max_length=128, default=TANDOOR)
nav_color = models.CharField(
choices=COLORS, max_length=128, default=PRIMARY
)
nav_color = models.CharField(choices=COLORS, max_length=128, default=PRIMARY)
default_unit = models.CharField(max_length=32, default='g')
use_fractions = models.BooleanField(default=FRACTION_PREF_DEFAULT)
use_kj = models.BooleanField(default=KJ_PREF_DEFAULT)
default_page = models.CharField(
choices=PAGES, max_length=64, default=SEARCH
)
search_style = models.CharField(
choices=SEARCH_STYLE, max_length=64, default=NEW
)
show_recent = models.BooleanField(default=True)
plan_share = models.ManyToManyField(
User, blank=True, related_name='plan_share_default'
)
shopping_share = models.ManyToManyField(
User, blank=True, related_name='shopping_share'
)
default_page = models.CharField(choices=PAGES, max_length=64, default=SEARCH)
plan_share = models.ManyToManyField(User, blank=True, related_name='plan_share_default')
shopping_share = models.ManyToManyField(User, blank=True, related_name='shopping_share')
ingredient_decimals = models.IntegerField(default=2)
comments = models.BooleanField(default=COMMENT_PREF_DEFAULT)
shopping_auto_sync = models.IntegerField(default=5)
@@ -538,6 +531,7 @@ class Keyword(ExportModelOperationsMixin('keyword'), TreeModel, PermissionModelM
class Unit(ExportModelOperationsMixin('unit'), models.Model, PermissionModelMixin):
name = models.CharField(max_length=128, validators=[MinLengthValidator(1)])
plural_name = models.CharField(max_length=128, null=True, blank=True, default=None)
description = models.TextField(blank=True, null=True)
space = models.ForeignKey(Space, on_delete=models.CASCADE)
@@ -562,6 +556,7 @@ class Food(ExportModelOperationsMixin('food'), TreeModel, PermissionModelMixin):
if SORT_TREE_BY_NAME:
node_order_by = ['name']
name = models.CharField(max_length=128, validators=[MinLengthValidator(1)])
plural_name = models.CharField(max_length=128, null=True, blank=True, default=None)
recipe = models.ForeignKey('Recipe', null=True, blank=True, on_delete=models.SET_NULL)
supermarket_category = models.ForeignKey(SupermarketCategory, null=True, blank=True, on_delete=models.SET_NULL) # inherited field
ignore_shopping = models.BooleanField(default=False) # inherited field
@@ -612,7 +607,7 @@ class Food(ExportModelOperationsMixin('food'), TreeModel, PermissionModelMixin):
# remove all inherited fields from food
trough = Food.inherit_fields.through
trough.objects.all().delete()
# food is going to inherit attributes
if len(inherit) > 0:
# ManyToMany cannot be updated through an UPDATE operation
@@ -662,6 +657,8 @@ class Ingredient(ExportModelOperationsMixin('ingredient'), models.Model, Permiss
note = models.CharField(max_length=256, null=True, blank=True)
is_header = models.BooleanField(default=False)
no_amount = models.BooleanField(default=False)
always_use_plural_unit = models.BooleanField(default=False)
always_use_plural_food = models.BooleanField(default=False)
order = models.IntegerField(default=0)
original_text = models.CharField(max_length=512, null=True, blank=True, default=None)
@@ -671,7 +668,23 @@ class Ingredient(ExportModelOperationsMixin('ingredient'), models.Model, Permiss
objects = ScopedManager(space='space')
def __str__(self):
return str(self.amount) + ' ' + str(self.unit) + ' ' + str(self.food)
food = ""
unit = ""
if self.always_use_plural_food and self.food.plural_name not in (None, "") and not self.no_amount:
food = self.food.plural_name
else:
if self.amount > 1 and self.food.plural_name not in (None, "") and not self.no_amount:
food = self.food.plural_name
else:
food = str(self.food)
if self.always_use_plural_unit and self.unit.plural_name not in (None, "") and not self.no_amount:
unit = self.unit.plural_name
else:
if self.amount > 1 and self.unit.plural_name not in (None, "") and not self.no_amount:
unit = self.unit.plural_name
else:
unit = str(self.unit)
return str(self.amount) + ' ' + str(unit) + ' ' + str(food)
class Meta:
ordering = ['order', 'pk']
@@ -730,6 +743,10 @@ class NutritionInformation(models.Model, PermissionModelMixin):
# space = models.ForeignKey(Space, on_delete=models.CASCADE)
# objects = ScopedManager(space='space')
class RecipeManager(models.Manager.from_queryset(models.QuerySet)):
def get_queryset(self):
return super(RecipeManager, self).get_queryset().annotate(rating=Avg('cooklog__rating')).annotate(last_cooked=Max('cooklog__created_at'))
class Recipe(ExportModelOperationsMixin('recipe'), models.Model, PermissionModelMixin):
name = models.CharField(max_length=128)
@@ -749,6 +766,8 @@ class Recipe(ExportModelOperationsMixin('recipe'), models.Model, PermissionModel
internal = models.BooleanField(default=False)
nutrition = models.ForeignKey(NutritionInformation, blank=True, null=True, on_delete=models.CASCADE)
show_ingredient_overview = models.BooleanField(default=True)
private = models.BooleanField(default=False)
shared = models.ManyToManyField(User, blank=True, related_name='recipe_shared_with')
source_url = models.CharField(max_length=1024, default=None, blank=True, null=True)
created_by = models.ForeignKey(User, on_delete=models.PROTECT)
@@ -759,7 +778,7 @@ class Recipe(ExportModelOperationsMixin('recipe'), models.Model, PermissionModel
desc_search_vector = SearchVectorField(null=True)
space = models.ForeignKey(Space, on_delete=models.CASCADE)
objects = ScopedManager(space='space')
objects = ScopedManager(space='space', _manager_class=RecipeManager)
def __str__(self):
return self.name
@@ -1017,9 +1036,8 @@ class InviteLink(ExportModelOperationsMixin('invite_link'), models.Model, Permis
email = models.EmailField(blank=True)
group = models.ForeignKey(Group, on_delete=models.CASCADE)
valid_until = models.DateField(default=default_valid_until)
used_by = models.ForeignKey(
User, null=True, on_delete=models.CASCADE, related_name='used_by'
)
used_by = models.ForeignKey(User, null=True, on_delete=models.CASCADE, related_name='used_by')
reusable = models.BooleanField(default=False)
created_by = models.ForeignKey(User, on_delete=models.CASCADE)
created_at = models.DateTimeField(auto_now_add=True)
@@ -1187,6 +1205,13 @@ class UserFile(ExportModelOperationsMixin('user_files'), models.Model, Permissio
objects = ScopedManager(space='space')
space = models.ForeignKey(Space, on_delete=models.CASCADE)
def is_image(self):
try:
img = Image.open(self.file.file.file)
return True
except Exception:
return False
def save(self, *args, **kwargs):
if hasattr(self.file, 'file') and isinstance(self.file.file, UploadedFile) or isinstance(self.file.file, InMemoryUploadedFile):
self.file.name = f'{uuid.uuid4()}' + pathlib.Path(self.file.name).suffix
@@ -1198,9 +1223,12 @@ class Automation(ExportModelOperationsMixin('automations'), models.Model, Permis
FOOD_ALIAS = 'FOOD_ALIAS'
UNIT_ALIAS = 'UNIT_ALIAS'
KEYWORD_ALIAS = 'KEYWORD_ALIAS'
DESCRIPTION_REPLACE = 'DESCRIPTION_REPLACE'
INSTRUCTION_REPLACE = 'INSTRUCTION_REPLACE'
type = models.CharField(max_length=128,
choices=((FOOD_ALIAS, _('Food Alias')), (UNIT_ALIAS, _('Unit Alias')), (KEYWORD_ALIAS, _('Keyword Alias')),))
choices=((FOOD_ALIAS, _('Food Alias')), (UNIT_ALIAS, _('Unit Alias')), (KEYWORD_ALIAS, _('Keyword Alias')),
(DESCRIPTION_REPLACE, _('Description Replace')), (INSTRUCTION_REPLACE, _('Instruction Replace')),))
name = models.CharField(max_length=128, default='')
description = models.TextField(blank=True, null=True)
@@ -1208,6 +1236,8 @@ class Automation(ExportModelOperationsMixin('automations'), models.Model, Permis
param_2 = models.CharField(max_length=128, blank=True, null=True)
param_3 = models.CharField(max_length=128, blank=True, null=True)
order = models.IntegerField(default=1000)
disabled = models.BooleanField(default=False)
updated_at = models.DateTimeField(auto_now=True)

View File

@@ -1,12 +1,12 @@
import traceback
from datetime import timedelta, datetime
import uuid
from datetime import datetime, timedelta
from decimal import Decimal
from gettext import gettext as _
from html import escape
from smtplib import SMTPException
from PIL import Image
from django.contrib.auth.models import User, Group
from django.contrib.auth.models import Group, User, AnonymousUser
from django.core.mail import send_mail
from django.db.models import Avg, Q, QuerySet, Sum
from django.http import BadHeaderError
@@ -14,6 +14,8 @@ from django.urls import reverse
from django.utils import timezone
from django_scopes import scopes_disabled
from drf_writable_nested import UniqueFieldsMixin, WritableNestedModelSerializer
from PIL import Image
from oauth2_provider.models import AccessToken
from rest_framework import serializers
from rest_framework.exceptions import NotFound, ValidationError
@@ -22,14 +24,14 @@ from cookbook.helper.HelperFunctions import str2bool
from cookbook.helper.permission_helper import above_space_limit
from cookbook.helper.shopping_helper import RecipeShoppingEditor
from cookbook.models import (Automation, BookmarkletImport, Comment, CookLog, CustomFilter,
ExportLog, Food, FoodInheritField, ImportLog, Ingredient, Keyword,
MealPlan, MealType, NutritionInformation, Recipe, RecipeBook,
ExportLog, Food, FoodInheritField, ImportLog, Ingredient, InviteLink,
Keyword, MealPlan, MealType, NutritionInformation, Recipe, RecipeBook,
RecipeBookEntry, RecipeImport, ShareLink, ShoppingList,
ShoppingListEntry, ShoppingListRecipe, Step, Storage, Supermarket,
SupermarketCategory, SupermarketCategoryRelation, Sync, SyncLog, Unit,
UserFile, UserPreference, ViewLog, Space, UserSpace, InviteLink)
ShoppingListEntry, ShoppingListRecipe, Space, Step, Storage,
Supermarket, SupermarketCategory, SupermarketCategoryRelation, Sync,
SyncLog, Unit, UserFile, UserPreference, UserSpace, ViewLog)
from cookbook.templatetags.custom_tags import markdown
from recipes.settings import MEDIA_URL, AWS_ENABLED
from recipes.settings import AWS_ENABLED, MEDIA_URL
class ExtendedRecipeMixin(serializers.ModelSerializer):
@@ -124,22 +126,26 @@ class SpaceFilterSerializer(serializers.ListSerializer):
# if query is sliced it came from api request not nested serializer
return super().to_representation(data)
if self.child.Meta.model == User:
data = data.filter(userspace__space=self.context['request'].user.get_active_space()).all()
if type(self.context['request'].user) == AnonymousUser:
data = []
else:
data = data.filter(userspace__space=self.context['request'].user.get_active_space()).all()
else:
data = data.filter(**{'__'.join(data.model.get_space_key()): self.context['request'].space})
return super().to_representation(data)
class UserNameSerializer(WritableNestedModelSerializer):
username = serializers.SerializerMethodField('get_user_label')
class UserSerializer(WritableNestedModelSerializer):
display_name = serializers.SerializerMethodField('get_user_label')
def get_user_label(self, obj):
return obj.get_user_name()
return obj.get_user_display_name()
class Meta:
list_serializer_class = SpaceFilterSerializer
model = User
fields = ('id', 'username')
fields = ('id', 'username', 'first_name', 'last_name', 'display_name')
read_only_fields = ('username',)
class GroupSerializer(UniqueFieldsMixin, WritableNestedModelSerializer):
@@ -170,103 +176,6 @@ class FoodInheritFieldSerializer(UniqueFieldsMixin, WritableNestedModelSerialize
read_only_fields = ['id']
class SpaceSerializer(WritableNestedModelSerializer):
user_count = serializers.SerializerMethodField('get_user_count')
recipe_count = serializers.SerializerMethodField('get_recipe_count')
file_size_mb = serializers.SerializerMethodField('get_file_size_mb')
food_inherit = FoodInheritFieldSerializer(many=True)
def get_user_count(self, obj):
return UserSpace.objects.filter(space=obj).count()
def get_recipe_count(self, obj):
return Recipe.objects.filter(space=obj).count()
def get_file_size_mb(self, obj):
try:
return UserFile.objects.filter(space=obj).aggregate(Sum('file_size_kb'))['file_size_kb__sum'] / 1000
except TypeError:
return 0
def create(self, validated_data):
raise ValidationError('Cannot create using this endpoint')
class Meta:
model = Space
fields = ('id', 'name', 'created_by', 'created_at', 'message', 'max_recipes', 'max_file_storage_mb', 'max_users', 'allow_sharing', 'demo', 'food_inherit', 'show_facet_count', 'user_count', 'recipe_count', 'file_size_mb',)
read_only_fields = ('id', 'created_by', 'created_at', 'max_recipes', 'max_file_storage_mb', 'max_users', 'allow_sharing', 'demo',)
class UserSpaceSerializer(WritableNestedModelSerializer):
user = UserNameSerializer(read_only=True)
groups = GroupSerializer(many=True)
def validate(self, data):
if self.instance.user == self.context['request'].space.created_by: # can't change space owner permission
raise serializers.ValidationError(_('Cannot modify Space owner permission.'))
return super().validate(data)
def create(self, validated_data):
raise ValidationError('Cannot create using this endpoint')
class Meta:
model = UserSpace
fields = ('id', 'user', 'space', 'groups', 'active', 'created_at', 'updated_at',)
read_only_fields = ('id', 'created_at', 'updated_at', 'space')
class SpacedModelSerializer(serializers.ModelSerializer):
def create(self, validated_data):
validated_data['space'] = self.context['request'].space
return super().create(validated_data)
class MealTypeSerializer(SpacedModelSerializer, WritableNestedModelSerializer):
def create(self, validated_data):
validated_data['created_by'] = self.context['request'].user
return super().create(validated_data)
class Meta:
list_serializer_class = SpaceFilterSerializer
model = MealType
fields = ('id', 'name', 'order', 'icon', 'color', 'default', 'created_by')
read_only_fields = ('created_by',)
class UserPreferenceSerializer(WritableNestedModelSerializer):
food_inherit_default = serializers.SerializerMethodField('get_food_inherit_defaults')
plan_share = UserNameSerializer(many=True, allow_null=True, required=False)
shopping_share = UserNameSerializer(many=True, allow_null=True, required=False)
food_children_exist = serializers.SerializerMethodField('get_food_children_exist')
def get_food_inherit_defaults(self, obj):
return FoodInheritFieldSerializer(obj.user.get_active_space().food_inherit.all(), many=True).data
def get_food_children_exist(self, obj):
space = getattr(self.context.get('request', None), 'space', None)
return Food.objects.filter(depth__gt=0, space=space).exists()
def update(self, instance, validated_data):
with scopes_disabled():
return super().update(instance, validated_data)
def create(self, validated_data):
raise ValidationError('Cannot create using this endpoint')
class Meta:
model = UserPreference
fields = (
'user', 'theme', 'nav_color', 'default_unit', 'default_page', 'use_fractions', 'use_kj', 'search_style',
'show_recent', 'plan_share',
'ingredient_decimals', 'comments', 'shopping_auto_sync', 'mealplan_autoadd_shopping',
'food_inherit_default', 'default_delay',
'mealplan_autoinclude_related', 'mealplan_autoexclude_onhand', 'shopping_share', 'shopping_recent_days',
'csv_delim', 'csv_prefix',
'filter_to_supermarket', 'shopping_add_onhand', 'left_handed', 'food_children_exist'
)
class UserFileSerializer(serializers.ModelSerializer):
file = serializers.FileField(write_only=True)
file_download = serializers.SerializerMethodField('get_download_link')
@@ -343,6 +252,107 @@ class UserFileViewSerializer(serializers.ModelSerializer):
read_only_fields = ('id', 'file')
class SpaceSerializer(WritableNestedModelSerializer):
user_count = serializers.SerializerMethodField('get_user_count')
recipe_count = serializers.SerializerMethodField('get_recipe_count')
file_size_mb = serializers.SerializerMethodField('get_file_size_mb')
food_inherit = FoodInheritFieldSerializer(many=True)
image = UserFileViewSerializer(required=False, many=False, allow_null=True)
def get_user_count(self, obj):
return UserSpace.objects.filter(space=obj).count()
def get_recipe_count(self, obj):
return Recipe.objects.filter(space=obj).count()
def get_file_size_mb(self, obj):
try:
return UserFile.objects.filter(space=obj).aggregate(Sum('file_size_kb'))['file_size_kb__sum'] / 1000
except TypeError:
return 0
def create(self, validated_data):
raise ValidationError('Cannot create using this endpoint')
class Meta:
model = Space
fields = ('id', 'name', 'created_by', 'created_at', 'message', 'max_recipes', 'max_file_storage_mb', 'max_users',
'allow_sharing', 'demo', 'food_inherit', 'show_facet_count', 'user_count', 'recipe_count', 'file_size_mb',
'image', 'use_plural',)
read_only_fields = ('id', 'created_by', 'created_at', 'max_recipes', 'max_file_storage_mb', 'max_users', 'allow_sharing', 'demo',)
class UserSpaceSerializer(WritableNestedModelSerializer):
user = UserSerializer(read_only=True)
groups = GroupSerializer(many=True)
def validate(self, data):
if self.instance.user == self.context['request'].space.created_by: # can't change space owner permission
raise serializers.ValidationError(_('Cannot modify Space owner permission.'))
return super().validate(data)
def create(self, validated_data):
raise ValidationError('Cannot create using this endpoint')
class Meta:
model = UserSpace
fields = ('id', 'user', 'space', 'groups', 'active', 'created_at', 'updated_at',)
read_only_fields = ('id', 'created_at', 'updated_at', 'space')
class SpacedModelSerializer(serializers.ModelSerializer):
def create(self, validated_data):
validated_data['space'] = self.context['request'].space
return super().create(validated_data)
class MealTypeSerializer(SpacedModelSerializer, WritableNestedModelSerializer):
def create(self, validated_data):
validated_data['created_by'] = self.context['request'].user
return super().create(validated_data)
class Meta:
list_serializer_class = SpaceFilterSerializer
model = MealType
fields = ('id', 'name', 'order', 'icon', 'color', 'default', 'created_by')
read_only_fields = ('created_by',)
class UserPreferenceSerializer(WritableNestedModelSerializer):
food_inherit_default = serializers.SerializerMethodField('get_food_inherit_defaults')
plan_share = UserSerializer(many=True, allow_null=True, required=False)
shopping_share = UserSerializer(many=True, allow_null=True, required=False)
food_children_exist = serializers.SerializerMethodField('get_food_children_exist')
image = UserFileViewSerializer(required=False, allow_null=True, many=False)
def get_food_inherit_defaults(self, obj):
return FoodInheritFieldSerializer(obj.user.get_active_space().food_inherit.all(), many=True).data
def get_food_children_exist(self, obj):
space = getattr(self.context.get('request', None), 'space', None)
return Food.objects.filter(depth__gt=0, space=space).exists()
def update(self, instance, validated_data):
with scopes_disabled():
return super().update(instance, validated_data)
def create(self, validated_data):
raise ValidationError('Cannot create using this endpoint')
class Meta:
model = UserPreference
fields = (
'user', 'image', 'theme', 'nav_color', 'default_unit', 'default_page', 'use_fractions', 'use_kj',
'plan_share', 'sticky_navbar',
'ingredient_decimals', 'comments', 'shopping_auto_sync', 'mealplan_autoadd_shopping',
'food_inherit_default', 'default_delay',
'mealplan_autoinclude_related', 'mealplan_autoexclude_onhand', 'shopping_share', 'shopping_recent_days',
'csv_delim', 'csv_prefix',
'filter_to_supermarket', 'shopping_add_onhand', 'left_handed', 'food_children_exist'
)
class StorageSerializer(SpacedModelSerializer):
def create(self, validated_data):
@@ -422,17 +432,22 @@ class UnitSerializer(UniqueFieldsMixin, ExtendedRecipeMixin):
def create(self, validated_data):
name = validated_data.pop('name').strip()
plural_name = validated_data.pop('plural_name', None)
if plural_name:
plural_name = plural_name.strip()
space = validated_data.pop('space', self.context['request'].space)
obj, created = Unit.objects.get_or_create(name=name, space=space, defaults=validated_data)
obj, created = Unit.objects.get_or_create(name=name, plural_name=plural_name, space=space, defaults=validated_data)
return obj
def update(self, instance, validated_data):
validated_data['name'] = validated_data['name'].strip()
if plural_name := validated_data.get('plural_name', None):
validated_data['plural_name'] = plural_name.strip()
return super(UnitSerializer, self).update(instance, validated_data)
class Meta:
model = Unit
fields = ('id', 'name', 'description', 'numrecipe', 'image')
fields = ('id', 'name', 'plural_name', 'description', 'numrecipe', 'image')
read_only_fields = ('id', 'numrecipe', 'image')
@@ -490,7 +505,7 @@ class RecipeSimpleSerializer(WritableNestedModelSerializer):
class FoodSimpleSerializer(serializers.ModelSerializer):
class Meta:
model = Food
fields = ('id', 'name')
fields = ('id', 'name', 'plural_name')
class FoodSerializer(UniqueFieldsMixin, WritableNestedModelSerializer, ExtendedRecipeMixin):
@@ -529,6 +544,9 @@ class FoodSerializer(UniqueFieldsMixin, WritableNestedModelSerializer, ExtendedR
def create(self, validated_data):
name = validated_data.pop('name').strip()
plural_name = validated_data.pop('plural_name', None)
if plural_name:
plural_name = plural_name.strip()
space = validated_data.pop('space', self.context['request'].space)
# supermarket category needs to be handled manually as food.get or create does not create nested serializers unlike a super.create of serializer
if 'supermarket_category' in validated_data and validated_data['supermarket_category']:
@@ -553,12 +571,14 @@ class FoodSerializer(UniqueFieldsMixin, WritableNestedModelSerializer, ExtendedR
else:
validated_data['onhand_users'] = list(set(onhand_users) - set(shared_users))
obj, created = Food.objects.get_or_create(name=name, space=space, defaults=validated_data)
obj, created = Food.objects.get_or_create(name=name, plural_name=plural_name, space=space, defaults=validated_data)
return obj
def update(self, instance, validated_data):
if name := validated_data.get('name', None):
validated_data['name'] = name.strip()
if plural_name := validated_data.get('plural_name', None):
validated_data['plural_name'] = plural_name.strip()
# assuming if on hand for user also onhand for shopping_share users
onhand = validated_data.get('food_onhand', None)
reset_inherit = self.initial_data.get('reset_inherit', False)
@@ -578,7 +598,7 @@ class FoodSerializer(UniqueFieldsMixin, WritableNestedModelSerializer, ExtendedR
class Meta:
model = Food
fields = (
'id', 'name', 'description', 'shopping', 'recipe', 'food_onhand', 'supermarket_category',
'id', 'name', 'plural_name', 'description', 'shopping', 'recipe', 'food_onhand', 'supermarket_category',
'image', 'parent', 'numchild', 'numrecipe', 'inherit_fields', 'full_name', 'ignore_shopping',
'substitute', 'substitute_siblings', 'substitute_children', 'substitute_onhand', 'child_inherit_fields'
)
@@ -607,6 +627,7 @@ class IngredientSimpleSerializer(WritableNestedModelSerializer):
fields = (
'id', 'food', 'unit', 'amount', 'note', 'order',
'is_header', 'no_amount', 'original_text', 'used_in_recipes',
'always_use_plural_unit', 'always_use_plural_food',
)
@@ -675,25 +696,6 @@ class NutritionInformationSerializer(serializers.ModelSerializer):
class RecipeBaseSerializer(WritableNestedModelSerializer):
def get_recipe_rating(self, obj):
try:
rating = obj.cooklog_set.filter(created_by=self.context['request'].user, rating__gt=0).aggregate(
Avg('rating'))
if rating['rating__avg']:
return rating['rating__avg']
except TypeError:
pass
return 0
def get_recipe_last_cooked(self, obj):
try:
last = obj.cooklog_set.filter(created_by=self.context['request'].user).order_by('created_at').last()
if last:
return last.created_at
except TypeError:
pass
return None
# TODO make days of new recipe a setting
def is_recipe_new(self, obj):
if getattr(obj, 'new_recipe', None) or obj.created_at > (timezone.now() - timedelta(days=7)):
@@ -704,11 +706,12 @@ class RecipeBaseSerializer(WritableNestedModelSerializer):
class RecipeOverviewSerializer(RecipeBaseSerializer):
keywords = KeywordLabelSerializer(many=True)
rating = serializers.SerializerMethodField('get_recipe_rating')
last_cooked = serializers.SerializerMethodField('get_recipe_last_cooked')
new = serializers.SerializerMethodField('is_recipe_new')
recent = serializers.ReadOnlyField()
rating = CustomDecimalField(required=False, allow_null=True)
last_cooked = serializers.DateTimeField(required=False, allow_null=True)
def create(self, validated_data):
pass
@@ -729,8 +732,9 @@ class RecipeSerializer(RecipeBaseSerializer):
nutrition = NutritionInformationSerializer(allow_null=True, required=False)
steps = StepSerializer(many=True)
keywords = KeywordSerializer(many=True)
rating = serializers.SerializerMethodField('get_recipe_rating')
last_cooked = serializers.SerializerMethodField('get_recipe_last_cooked')
shared = UserSerializer(many=True, required=False)
rating = CustomDecimalField(required=False, allow_null=True, read_only=True)
last_cooked = serializers.DateTimeField(required=False, allow_null=True, read_only=True)
class Meta:
model = Recipe
@@ -738,6 +742,7 @@ class RecipeSerializer(RecipeBaseSerializer):
'id', 'name', 'description', 'image', 'keywords', 'steps', 'working_time',
'waiting_time', 'created_by', 'created_at', 'updated_at', 'source_url',
'internal', 'show_ingredient_overview', 'nutrition', 'servings', 'file_path', 'servings_text', 'rating', 'last_cooked',
'private', 'shared',
)
read_only_fields = ['image', 'created_by', 'created_at']
@@ -775,7 +780,7 @@ class CommentSerializer(serializers.ModelSerializer):
class CustomFilterSerializer(SpacedModelSerializer, WritableNestedModelSerializer):
shared = UserNameSerializer(many=True, required=False)
shared = UserSerializer(many=True, required=False)
def create(self, validated_data):
validated_data['created_by'] = self.context['request'].user
@@ -788,7 +793,7 @@ class CustomFilterSerializer(SpacedModelSerializer, WritableNestedModelSerialize
class RecipeBookSerializer(SpacedModelSerializer, WritableNestedModelSerializer):
shared = UserNameSerializer(many=True)
shared = UserSerializer(many=True)
filter = CustomFilterSerializer(allow_null=True, required=False)
def create(self, validated_data):
@@ -832,7 +837,7 @@ class MealPlanSerializer(SpacedModelSerializer, WritableNestedModelSerializer):
meal_type_name = serializers.ReadOnlyField(source='meal_type.name') # TODO deprecate once old meal plan was removed
note_markdown = serializers.SerializerMethodField('get_note_markdown')
servings = CustomDecimalField()
shared = UserNameSerializer(many=True, required=False, allow_null=True)
shared = UserSerializer(many=True, required=False, allow_null=True)
shopping = serializers.SerializerMethodField('in_shopping')
def get_note_markdown(self, obj):
@@ -871,11 +876,11 @@ class ShoppingListRecipeSerializer(serializers.ModelSerializer):
value = value.quantize(
Decimal(1)) if value == value.to_integral() else value.normalize() # strips trailing zero
return (
obj.name
or getattr(obj.mealplan, 'title', None)
or (d := getattr(obj.mealplan, 'date', None)) and ': '.join([obj.mealplan.recipe.name, str(d)])
or obj.recipe.name
) + f' ({value:.2g})'
obj.name
or getattr(obj.mealplan, 'title', None)
or (d := getattr(obj.mealplan, 'date', None)) and ': '.join([obj.mealplan.recipe.name, str(d)])
or obj.recipe.name
) + f' ({value:.2g})'
def update(self, instance, validated_data):
# TODO remove once old shopping list
@@ -896,7 +901,7 @@ class ShoppingListEntrySerializer(WritableNestedModelSerializer):
ingredient_note = serializers.ReadOnlyField(source='ingredient.note')
recipe_mealplan = ShoppingListRecipeSerializer(source='list_recipe', read_only=True)
amount = CustomDecimalField()
created_by = UserNameSerializer(read_only=True)
created_by = UserSerializer(read_only=True)
completed_at = serializers.DateTimeField(allow_null=True, required=False)
def get_fields(self, *args, **kwargs):
@@ -964,7 +969,7 @@ class ShoppingListEntryCheckedSerializer(serializers.ModelSerializer):
class ShoppingListSerializer(WritableNestedModelSerializer):
recipes = ShoppingListRecipeSerializer(many=True, allow_null=True)
entries = ShoppingListEntrySerializer(many=True, allow_null=True)
shared = UserNameSerializer(many=True)
shared = UserSerializer(many=True)
supermarket = SupermarketSerializer(allow_null=True)
def create(self, validated_data):
@@ -1062,7 +1067,7 @@ class AutomationSerializer(serializers.ModelSerializer):
class Meta:
model = Automation
fields = (
'id', 'type', 'name', 'description', 'param_1', 'param_2', 'param_3', 'disabled', 'created_by',)
'id', 'type', 'name', 'description', 'param_1', 'param_2', 'param_3', 'order', 'disabled', 'created_by',)
read_only_fields = ('created_by',)
@@ -1077,7 +1082,7 @@ class InviteLinkSerializer(WritableNestedModelSerializer):
if obj.email:
try:
if InviteLink.objects.filter(space=self.context['request'].space, created_at__gte=datetime.now() - timedelta(hours=4)).count() < 20:
message = _('Hello') + '!\n\n' + _('You have been invited by ') + escape(self.context['request'].user.username)
message = _('Hello') + '!\n\n' + _('You have been invited by ') + escape(self.context['request'].user.get_user_display_name())
message += _(' to join their Tandoor Recipes space ') + escape(self.context['request'].space.name) + '.\n\n'
message += _('Click the following link to activate your account: ') + self.context['request'].build_absolute_uri(reverse('view_invite', args=[str(obj.uuid)])) + '\n\n'
message += _('If the link does not work use the following code to manually join the space: ') + str(obj.uuid) + '\n\n'
@@ -1099,7 +1104,7 @@ class InviteLinkSerializer(WritableNestedModelSerializer):
class Meta:
model = InviteLink
fields = (
'id', 'uuid', 'email', 'group', 'valid_until', 'used_by', 'created_by', 'created_at',)
'id', 'uuid', 'email', 'group', 'valid_until', 'used_by', 'reusable', 'created_by', 'created_at',)
read_only_fields = ('id', 'uuid', 'created_by', 'created_at',)
@@ -1125,6 +1130,27 @@ class BookmarkletImportSerializer(BookmarkletImportListSerializer):
read_only_fields = ('created_by', 'space')
# OAuth / Auth Token related Serializers
class AccessTokenSerializer(serializers.ModelSerializer):
token = serializers.SerializerMethodField('get_token')
def create(self, validated_data):
validated_data['token'] = f'tda_{str(uuid.uuid4()).replace("-", "_")}'
validated_data['user'] = self.context['request'].user
return super().create(validated_data)
def get_token(self, obj):
if (timezone.now() - obj.created).seconds < 15:
return obj.token
return f'tda_************_******_***********{obj.token[len(obj.token) - 4:]}'
class Meta:
model = AccessToken
fields = ('id', 'token', 'expires', 'scope', 'created', 'updated')
read_only_fields = ('id', 'token',)
# Export/Import Serializers
class KeywordExportSerializer(KeywordSerializer):
@@ -1148,7 +1174,7 @@ class SupermarketCategoryExportSerializer(SupermarketCategorySerializer):
class UnitExportSerializer(UnitSerializer):
class Meta:
model = Unit
fields = ('name', 'description')
fields = ('name', 'plural_name', 'description')
class FoodExportSerializer(FoodSerializer):
@@ -1156,7 +1182,7 @@ class FoodExportSerializer(FoodSerializer):
class Meta:
model = Food
fields = ('name', 'ignore_shopping', 'supermarket_category',)
fields = ('name', 'plural_name', 'ignore_shopping', 'supermarket_category',)
class IngredientExportSerializer(WritableNestedModelSerializer):
@@ -1170,7 +1196,7 @@ class IngredientExportSerializer(WritableNestedModelSerializer):
class Meta:
model = Ingredient
fields = ('food', 'unit', 'amount', 'note', 'order', 'is_header', 'no_amount')
fields = ('food', 'unit', 'amount', 'note', 'order', 'is_header', 'no_amount', 'always_use_plural_unit', 'always_use_plural_food')
class StepExportSerializer(WritableNestedModelSerializer):
@@ -1232,6 +1258,6 @@ class FoodShoppingUpdateSerializer(serializers.ModelSerializer):
# non model serializers
class RecipeFromSourceSerializer(serializers.Serializer):
url = serializers.CharField(max_length=4096, required=False, allow_null=True)
url = serializers.CharField(max_length=4096, required=False, allow_null=True, allow_blank=True)
data = serializers.CharField(required=False, allow_null=True, allow_blank=True)
bookmarklet = serializers.IntegerField(required=False, allow_null=True, )

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

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

View File

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

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -2815,6 +2815,323 @@ input[type=button].btn-block, input[type=reset].btn-block, input[type=submit].bt
width: 100%
}
.btn {
font-size: .875rem;
font-family: Poppins, sans-serif;
padding: .625rem 1.25rem;
outline: none;
line-height: 1.5;
}
.btn.btn-rounded {
border-radius: 50px
}
.btn.btn-white {
background: #fff;
transition: all .5s ease-in-out
}
.btn.btn-white:hover {
background: #a7240e;
color: #fff
}
.btn:focus {
box-shadow: none
}
.btn-primary {
transition: all .5s ease-in-out;
color: #fff
}
.btn-primary:hover {
background: transparent;
color: #b98766;
border: 1px solid #b98766
}
.btn-secondary {
transition: all .5s ease-in-out;
color: #fff
}
.btn-secondary:hover {
background: transparent;
color: #b55e4f;
border: 1px solid #b55e4f
}
.btn-success {
transition: all .5s ease-in-out;
color: #fff
}
.btn-success:hover {
background: transparent;
color: #82aa8b;
border: 1px solid #82aa8b
}
.btn-info {
transition: all .5s ease-in-out;
color: #fff
}
.btn-info:hover {
background: transparent;
color: #385f84;
border: 1px solid #385f84
}
.btn-warning {
transition: all .5s ease-in-out;
color: #fff
}
.btn-warning:hover {
background: transparent;
color: #eaaa21;
border: 1px solid #eaaa21
}
.btn-danger {
transition: all .5s ease-in-out;
color: #fff
}
.btn-danger:hover {
background: transparent;
color: #a7240e;
border: 1px solid #a7240e
}
.btn-light {
transition: all .5s ease-in-out;
color: #fff
}
.btn-light:hover {
background-color: hsla(0, 0%, 18%, .5);
color: #cfd5cd;
border: 1px solid hsla(0, 0%, 18%, .5)
}
.btn-dark {
transition: all .5s ease-in-out;
color: #fff
}
.btn-dark:hover {
background: transparent;
color: #221e1e;
border: 1px solid #221e1e
}
.btn-opacity-primary {
color: #b98766;
background-color: #0012a7;
border: 2px solid transparent;
transition: all .5s ease-in-out
}
.btn-opacity-primary:hover {
color: #b98766;
background-color: #fff;
border: 2px solid #b98766
}
.btn-opacity-secondary {
color: #b55e4f;
background-color: #fff;
border: 2px solid transparent;
transition: all .5s ease-in-out
}
.btn-opacity-secondary:hover {
color: #b55e4f;
background-color: #fff;
border: 2px solid #b55e4f
}
.btn-opacity-success {
color: #82aa8b;
background-color: #b7eddd;
border: 2px solid transparent;
transition: all .5s ease-in-out
}
.btn-opacity-success:hover {
color: #82aa8b;
background-color: #fff;
border: 2px solid #82aa8b
}
.btn-opacity-info {
color: #385f84;
background-color: #89caff;
border: 2px solid transparent;
transition: all .5s ease-in-out
}
.btn-opacity-info:hover {
color: #385f84;
background-color: #fff;
border: 2px solid #385f84
}
.btn-opacity-warning {
color: #eaaa21;
background-color: #ffd170;
border: 2px solid transparent;
transition: all .5s ease-in-out
}
.btn-opacity-warning:hover {
color: #eaaa21;
background-color: #fff;
border: 2px solid #eaaa21
}
.btn-opacity-danger {
color: #a7240e;
background-color: #ff7070;
border: 2px solid transparent;
transition: all .5s ease-in-out
}
.btn-opacity-danger:hover {
color: #a7240e;
background-color: #fff;
border: 2px solid #a7240e
}
.btn-opacity-light {
color: #cfd5cd;
background-color: #fec4af;
border: 2px solid transparent;
transition: all .5s ease-in-out
}
.btn-opacity-light:hover {
color: #cfd5cd;
background-color: #fff;
border: 2px solid #cfd5cd
}
.btn-opacity-dark {
color: #221e1e;
background-color: #5e5353;
border: 2px solid transparent;
transition: all .5s ease-in-out
}
.btn-opacity-dark:hover {
color: #221e1e;
background-color: #fff;
border: 2px solid #221e1e
}
.btn-outline-primary {
color: #b98766;
background-color: #fff;
border: 2px solid #b98766;
transition: all .5s ease-in-out
}
.btn-outline-primary:hover {
color: #fff;
background-color: #b98766
}
.btn-outline-secondary {
color: #b55e4f;
background-color: #fff;
border: 2px solid #b55e4f;
transition: all .5s ease-in-out
}
.btn-outline-secondary:hover {
color: #fff;
background-color: #b55e4f
}
.btn-outline-success {
color: #82aa8b;
background-color: #fff;
border: 2px solid #82aa8b;
transition: all .5s ease-in-out
}
.btn-outline-success:hover {
color: #fff;
background-color: #82aa8b
}
.btn-outline-info {
color: #385f84;
background-color: #fff;
border: 2px solid #385f84;
transition: all .5s ease-in-out
}
.btn-outline-info:hover {
color: #fff;
background-color: #385f84
}
.btn-outline-warning {
color: #eaaa21;
background-color: #fff;
border: 2px solid #eaaa21;
transition: all .5s ease-in-out
}
.btn-outline-warning:hover {
color: #fff;
background-color: #eaaa21
}
.btn-outline-danger {
color: #a7240e;
background-color: #fff;
border: 2px solid #a7240e;
transition: all .5s ease-in-out
}
.btn-outline-danger:hover {
color: #fff;
background-color: #a7240e
}
.btn-outline-light {
color: #cfd5cd;
background-color: #fff;
border: 2px solid #cfd5cd;
transition: all .5s ease-in-out
}
.btn-outline-light:hover {
color: #fff;
background-color: #cfd5cd
}
.btn-outline-dark {
color: #221e1e;
background-color: #fff;
border: 2px solid #221e1e;
transition: all .5s ease-in-out
}
.btn-outline-dark:hover {
color: #fff;
background-color: #221e1e
}
.fade {
transition: opacity .15s linear
}
@@ -3148,6 +3465,13 @@ input[type=button].btn-block, input[type=reset].btn-block, input[type=submit].bt
margin-right: 0
}
.btn-sm, .btn-group-sm > .btn {
padding: 0.25rem 0.5rem;
font-size: 0.8203125rem;
line-height: 1.5;
border-radius: 0.2rem
}
.btn-group-sm > .btn + .dropdown-toggle-split, .btn-sm + .dropdown-toggle-split {
padding-right: .375rem;
padding-left: .375rem
@@ -4611,7 +4935,7 @@ a.badge:focus, a.badge:hover {
a.badge-primary:focus, a.badge-primary:hover {
color: #fff;
background-color: #000004
background-color: var(--primary);
}
a.badge-primary.focus, a.badge-primary:focus {
@@ -6114,8 +6438,11 @@ a.close.disabled {
vertical-align: text-top !important
}
/*!
* technically the wrong color but not used anywhere besides nav and this way changing nav color is supported
*/
.bg-primary {
background-color: #b98766 !important
background-color: rgb(221, 191, 134) !important;
}
a.bg-primary:focus, a.bg-primary:hover, button.bg-primary:focus, button.bg-primary:hover {
@@ -10063,319 +10390,6 @@ footer a:hover {
min-width: 100%
}
.btn {
font-size: .875rem;
font-family: Poppins, sans-serif;
padding: .625rem 1.25rem;
outline: none
}
.btn.btn-rounded {
border-radius: 50px
}
.btn.btn-white {
background: #fff;
transition: all .5s ease-in-out
}
.btn.btn-white:hover {
background: #a7240e;
color: #fff
}
.btn:focus {
box-shadow: none
}
.btn-primary {
transition: all .5s ease-in-out;
color: #fff
}
.btn-primary:hover {
background: transparent;
color: #b98766;
border: 1px solid #b98766
}
.btn-secondary {
transition: all .5s ease-in-out;
color: #fff
}
.btn-secondary:hover {
background: transparent;
color: #b55e4f;
border: 1px solid #b55e4f
}
.btn-success {
transition: all .5s ease-in-out;
color: #fff
}
.btn-success:hover {
background: transparent;
color: #82aa8b;
border: 1px solid #82aa8b
}
.btn-info {
transition: all .5s ease-in-out;
color: #fff
}
.btn-info:hover {
background: transparent;
color: #385f84;
border: 1px solid #385f84
}
.btn-warning {
transition: all .5s ease-in-out;
color: #fff
}
.btn-warning:hover {
background: transparent;
color: #eaaa21;
border: 1px solid #eaaa21
}
.btn-danger {
transition: all .5s ease-in-out;
color: #fff
}
.btn-danger:hover {
background: transparent;
color: #a7240e;
border: 1px solid #a7240e
}
.btn-light {
transition: all .5s ease-in-out;
color: #fff
}
.btn-light:hover {
background-color: hsla(0, 0%, 18%, .5);
color: #cfd5cd;
border: 1px solid hsla(0, 0%, 18%, .5)
}
.btn-dark {
transition: all .5s ease-in-out;
color: #fff
}
.btn-dark:hover {
background: transparent;
color: #221e1e;
border: 1px solid #221e1e
}
.btn-opacity-primary {
color: #b98766;
background-color: #0012a7;
border: 2px solid transparent;
transition: all .5s ease-in-out
}
.btn-opacity-primary:hover {
color: #b98766;
background-color: #fff;
border: 2px solid #b98766
}
.btn-opacity-secondary {
color: #b55e4f;
background-color: #fff;
border: 2px solid transparent;
transition: all .5s ease-in-out
}
.btn-opacity-secondary:hover {
color: #b55e4f;
background-color: #fff;
border: 2px solid #b55e4f
}
.btn-opacity-success {
color: #82aa8b;
background-color: #b7eddd;
border: 2px solid transparent;
transition: all .5s ease-in-out
}
.btn-opacity-success:hover {
color: #82aa8b;
background-color: #fff;
border: 2px solid #82aa8b
}
.btn-opacity-info {
color: #385f84;
background-color: #89caff;
border: 2px solid transparent;
transition: all .5s ease-in-out
}
.btn-opacity-info:hover {
color: #385f84;
background-color: #fff;
border: 2px solid #385f84
}
.btn-opacity-warning {
color: #eaaa21;
background-color: #ffd170;
border: 2px solid transparent;
transition: all .5s ease-in-out
}
.btn-opacity-warning:hover {
color: #eaaa21;
background-color: #fff;
border: 2px solid #eaaa21
}
.btn-opacity-danger {
color: #a7240e;
background-color: #ff7070;
border: 2px solid transparent;
transition: all .5s ease-in-out
}
.btn-opacity-danger:hover {
color: #a7240e;
background-color: #fff;
border: 2px solid #a7240e
}
.btn-opacity-light {
color: #cfd5cd;
background-color: #fec4af;
border: 2px solid transparent;
transition: all .5s ease-in-out
}
.btn-opacity-light:hover {
color: #cfd5cd;
background-color: #fff;
border: 2px solid #cfd5cd
}
.btn-opacity-dark {
color: #221e1e;
background-color: #5e5353;
border: 2px solid transparent;
transition: all .5s ease-in-out
}
.btn-opacity-dark:hover {
color: #221e1e;
background-color: #fff;
border: 2px solid #221e1e
}
.btn-outline-primary {
color: #b98766;
background-color: #fff;
border: 2px solid #b98766;
transition: all .5s ease-in-out
}
.btn-outline-primary:hover {
color: #fff;
background-color: #b98766
}
.btn-outline-secondary {
color: #b55e4f;
background-color: #fff;
border: 2px solid #b55e4f;
transition: all .5s ease-in-out
}
.btn-outline-secondary:hover {
color: #fff;
background-color: #b55e4f
}
.btn-outline-success {
color: #82aa8b;
background-color: #fff;
border: 2px solid #82aa8b;
transition: all .5s ease-in-out
}
.btn-outline-success:hover {
color: #fff;
background-color: #82aa8b
}
.btn-outline-info {
color: #385f84;
background-color: #fff;
border: 2px solid #385f84;
transition: all .5s ease-in-out
}
.btn-outline-info:hover {
color: #fff;
background-color: #385f84
}
.btn-outline-warning {
color: #eaaa21;
background-color: #fff;
border: 2px solid #eaaa21;
transition: all .5s ease-in-out
}
.btn-outline-warning:hover {
color: #fff;
background-color: #eaaa21
}
.btn-outline-danger {
color: #a7240e;
background-color: #fff;
border: 2px solid #a7240e;
transition: all .5s ease-in-out
}
.btn-outline-danger:hover {
color: #fff;
background-color: #a7240e
}
.btn-outline-light {
color: #cfd5cd;
background-color: #fff;
border: 2px solid #cfd5cd;
transition: all .5s ease-in-out
}
.btn-outline-light:hover {
color: #fff;
background-color: #cfd5cd
}
.btn-outline-dark {
color: #221e1e;
background-color: #fff;
border: 2px solid #221e1e;
transition: all .5s ease-in-out
}
.btn-outline-dark:hover {
color: #fff;
background-color: #221e1e
}
.card {
border: none;
border-radius: 6px
@@ -10424,8 +10438,6 @@ footer a:hover {
padding: 5px 0 20px 39px
}
/*# sourceMappingURL=maps/style.min.css.map */
.bg-header {
background-color: rgb(221, 191, 134) !important;
}
@@ -10441,7 +10453,7 @@ footer a:hover {
background-color: transparent !important;
}
textarea, input:not([type="submit"]):not([class="multiselect__input"]):not([class="select2-search__field"]):not([class="vue-treeselect__input"]), select {
textarea, input:not([type="submit"]):not([class="multiselect__input"]):not([class="select2-search__field"]):not([class="vue-treeselect__input"]), select {
background-color: white !important;
border-radius: .25rem !important;
border: 1px solid #ced4da !important;
@@ -10465,6 +10477,6 @@ textarea, input:not([type="submit"]):not([class="multiselect__input"]):not([clas
}
.ghost {
opacity: 0.5 !important;
background: #b98766 !important;
opacity: 0.5 !important;
background: #b98766 !important;
}

View File

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

View File

@@ -35,9 +35,7 @@
{% endif %}
{% if EMAIL_ENABLED %}
<a class="btn btn-warning float-right d-none d-xl-block d-lg-block"
href="{% url 'account_reset_password' %}">{% trans "Reset My Password" %}</a>
<p class="d-xl-none d-lg-none">{% trans 'Lost your password?' %} <a
<p>{% trans 'Lost your password?' %} <a
href="{% url 'account_reset_password' %}">{% trans "Reset My Password" %}</a></p>
{% endif %}
</form>

View File

@@ -19,7 +19,7 @@
<link rel="mask-icon" href="{% static 'assets/safari-pinned-tab.svg' %}" color="#161616">
<link rel="apple-touch-icon" href="{% static 'assets/apple-touch-icon.png' %}" sizes="180x180">
<link rel="manifest" href="{% url 'web_manifest' %}">
<link rel="manifest" crossorigin="use-credentials" href="{% url 'web_manifest' %}">
<meta name="msapplication-TileColor" content="#ffffff">
<meta name="msapplication-TileImage" content="/mstile-144x144.png">
@@ -48,6 +48,9 @@
<script type="text/javascript">
$.fn.select2.defaults.set("theme", "bootstrap");
{% if request.user.is_authenticated %}
window.ACTIVE_SPACE_ID = '{{request.space.id}}';
{% endif %}
</script>
<!-- Fontawesome icons -->
@@ -69,7 +72,7 @@
</head>
<body>
<nav class="navbar navbar-expand-lg navbar-dark bg-{% nav_color request %} bg-header"
<nav class="navbar navbar-expand-lg navbar-dark bg-{% nav_color request %}"
id="id_main_nav"
style="{% sticky_nav request %}">
@@ -285,7 +288,7 @@
<li class="nav-item dropdown {% if request.resolver_match.url_name in 'view_space,view_settings,view_history,view_system,docs_markdown' %}active{% endif %}">
<a class="nav-link dropdown-toggle" href="#" id="navbarDropdownMenuLink" data-toggle="dropdown"
aria-haspopup="true" aria-expanded="false"><i
class="fas fa-fw fa-user-alt"></i> {{ user.get_user_name }}
class="fas fa-fw fa-user-alt"></i> {{ user.get_user_display_name }}
</a>
<div class="dropdown-menu dropdown-menu-right" aria-labelledby="navbarDropdownMenuLink">
@@ -408,6 +411,8 @@
localStorage.setItem('BASE_PATH', "{% base_path request 'base' %}")
localStorage.setItem('STATIC_URL', "{% base_path request 'static_base' %}")
localStorage.setItem('DEBUG', "{% is_debug %}")
localStorage.setItem('USER_ID', "{{request.user.pk}}")
window.addEventListener("load", () => {
if ("serviceWorker" in navigator) {
navigator.serviceWorker.register("{% url 'service_worker' %}", {scope: "{% base_path request 'base' %}" + '/'}).then(function (reg) {

View File

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

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