Merge branch 'develop' into fuzzy_search

This commit is contained in:
vabene1111
2022-01-17 15:51:23 +01:00
committed by GitHub
22 changed files with 862 additions and 274 deletions

64
.github/ISSUE_TEMPLATE/bug_report.yml vendored Normal file
View File

@ -0,0 +1,64 @@
name: Bug Report
description: "Create a report to help us improve"
#title: ""
#labels: ["Bug"]
body:
- type: markdown
attributes:
value: |
Thanks for taking the time to fill out this bug report!
- type: input
id: version
attributes:
label: Tandoor Version
description: "What version of Tandoor are you using? (can be found on the system page since v0.8.4)"
validations:
required: true
- type: dropdown
id: setup
attributes:
label: Setup
description: "How is your Tandoor instance set up?"
options:
- Docker / Docker-Compose
- Unraid
- Synology
- Kubernetes
- Manual Setup
- Others (please state below)
validations:
required: true
- type: dropdown
id: reverse-proxy
attributes:
label: "Reverse Proxy"
description: "What reverse proxy do you use with Tandoor?"
options:
- No reverse proxy
- jwilder's nginx proxy
- Nginx Proxy Manager (NPM)
- SWAG
- Caddy
- Traefik
- Apache2
- Others (please state below)
validations:
required: true
- type: input
id: other
attributes:
label: Other
description: "In case you chose 'Others' above, please provide more info here."
- type: textarea
id: bug-descr
attributes:
label: Bug description
description: "Please accurately describe the bug you encountered."
validations:
required: true
- type: textarea
id: logs
attributes:
label: Relevant logs
description: Please copy and paste any relevant logs. This will be automatically formatted into code, so no need for backticks.
render: shell

5
.github/ISSUE_TEMPLATE/config.yml vendored Normal file
View File

@ -0,0 +1,5 @@
blank_issues_enabled: true
contact_links:
- name: FAQs
url: https://docs.tandoor.dev/faq/
about: Please take a look at the FAQs before creating a bug ticket.

View File

@ -0,0 +1,39 @@
name: Feature Request
description: "Suggest an idea for this project"
#title: ""
labels: ["enhancement"]
body:
- type: markdown
attributes:
value: |
Thanks for taking the time to fill out this feature request!
- type: textarea
id: problem
attributes:
label: "Is your feature request related to a problem? Please describe."
description: "A clear and concise description of what the problem is. Ex. I'm always frustrated when..."
- type: textarea
id: solution
attributes:
label: "Describe the solution you'd like"
description: "A clear and concise description of what you want to happen."
validations:
required: true
- type: textarea
id: alternatives
attributes:
label: "Describe alternatives you've considered"
description: "A clear and concise description of any alternative solutions or features you've considered."
- type: textarea
id: additional
attributes:
label: "Additional context"
description: "Add any other context or screenshots about the feature request here."
- type: checkboxes
attributes:
label: "Contribute"
description: "Are you willing and able to help develop this feature?"
options:
- label: "Yes"
- label: "Partly"
- label: "No"

82
.github/ISSUE_TEMPLATE/help_request.yml vendored Normal file
View File

@ -0,0 +1,82 @@
name: Help request
description: "If there is anything wrong with your setup"
#title: ""
labels: ["setup issue"]
body:
- type: markdown
attributes:
value: |
Thanks for taking the time to fill out this help request!
- type: textarea
id: issue
attributes:
label: Issue
description: "Please describe your problem here."
validations:
required: true
- type: input
id: version
attributes:
label: Tandoor Version
description: "What version of Tandoor are you using? (can be found on the system page since v0.8.4)"
validations:
required: true
- type: input
id: os
attributes:
label: OS Version
description: "E.g. Ubuntu 20.02"
validations:
required: true
- type: dropdown
id: setup
attributes:
label: Setup
description: "How is your Tandoor instance set up?"
options:
- Docker / Docker-Compose
- Unraid
- Synology
- Kubernetes
- Manual Setup
- Others (please state below)
validations:
required: true
- type: dropdown
id: reverse-proxy
attributes:
label: "Reverse Proxy"
description: "What reverse proxy do you use with Tandoor?"
options:
- No reverse proxy
- jwilder's nginx proxy
- Nginx Proxy Manager (NPM)
- SWAG
- Caddy
- Traefik
- Others (please state below)
validations:
required: true
- type: input
id: other
attributes:
label: Other
description: "In case you chose 'Others' above or have more info, please provide additional details here."
- type: textarea
id: env
attributes:
label: Environment file
description: "Please include your `.env` config file (**make sure to remove/replace all secrets**)"
render: shell
- type: textarea
id: docker-compose
attributes:
label: Docker-Compose file
description: "When running with docker compose please provide your `docker-compose.yml`"
render: shell
- type: textarea
id: logs
attributes:
label: Relevant logs
description: "If you feel like there is anything interesting please post the output of `docker-compose logs` at container startup and when the issue happens."
render: shell

View File

@ -0,0 +1,36 @@
name: Website Import
description: "Anything related to website imports"
#title: ""
#labels: ["enhancement"]
body:
- type: markdown
attributes:
value: |
Thanks for taking the time to fill out this website import form!
- type: input
id: version
attributes:
label: Tandoor Version
description: "What version of Tandoor are you using? (can be found on the system page since v0.8.4)"
validations:
required: true
- type: input
id: url
attributes:
label: Import URL
description: "Exact URL you are trying to import from."
validations:
required: true
- type: textarea
id: bug-descr
attributes:
label: "When did the issue happen?"
description: "When pressing the search button with the url / when importing after the page has loaded / ..."
validations:
required: true
- type: textarea
id: logs
attributes:
label: Response / message shown
description: Please copy and paste any relevant logs or responses / messages which are shown in Tandoor. This will be automatically formatted into code, so no need for backticks.
render: shell

View File

@ -358,8 +358,8 @@ class InviteLinkForm(forms.ModelForm):
def clean(self):
space = self.cleaned_data['space']
if space.max_users != 0 and (UserPreference.objects.filter(space=space).count() + InviteLink.objects.filter(
space=space).filter(valid_until__gte=datetime.today()).count()) >= space.max_users:
if space.max_users != 0 and (UserPreference.objects.filter(space=space).count() +
InviteLink.objects.filter(valid_until__gte=datetime.today(), used_by=None, space=space).count()) >= space.max_users:
raise ValidationError(_('Maximum number of users for this space reached.'))
def clean_email(self):

File diff suppressed because it is too large Load Diff

View File

@ -12,7 +12,7 @@ msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-11-07 17:31+0100\n"
"PO-Revision-Date: 2021-11-12 20:06+0000\n"
"PO-Revision-Date: 2022-01-17 07:56+0000\n"
"Last-Translator: Oliver Cervera <olivercervera@yahoo.it>\n"
"Language-Team: Italian <http://translate.tandoor.dev/projects/tandoor/"
"recipes-backend/it/>\n"
@ -260,10 +260,6 @@ msgid "Email address already taken!"
msgstr "Questo indirizzo email è già in uso!"
#: .\cookbook\forms.py:367
#, fuzzy
#| msgid ""
#| "An email address is not required but if present the invite link will be "
#| "send to the user."
msgid ""
"An email address is not required but if present the invite link will be sent "
"to the user."
@ -840,6 +836,10 @@ msgid ""
" Please request a <a href=\"%(passwd_reset_url)s\">new "
"password reset</a>."
msgstr ""
"Il link per il reset della password non è corretto, probabilmente perché è "
"stato già utilizzato.\n"
" Puoi richiedere un <a href=\"%(passwd_reset_url)s\""
">nuovo reset della password</a>."
#: .\cookbook\templates\account\password_reset_from_key.html:33
msgid "change password"
@ -932,10 +932,8 @@ msgid "Supermarket Category"
msgstr "Categoria Supermercato"
#: .\cookbook\templates\base.html:175 .\cookbook\views\lists.py:195
#, fuzzy
#| msgid "Information"
msgid "Automations"
msgstr "Informazioni"
msgstr "Automazioni"
#: .\cookbook\templates\base.html:189 .\cookbook\views\lists.py:215
msgid "Files"
@ -1842,6 +1840,9 @@ msgid ""
"return more results than needed to make sure you find what you are looking "
"for."
msgstr ""
"Cerca quello che ti serve anche se la ricerca o la ricetta contengono "
"errori. Potrebbe mostrare più risultati di quelli necessari per mostrarti "
"quello che stai cercando."
#: .\cookbook\templates\settings.html:175
msgid "This is the default behavior"
@ -1864,7 +1865,7 @@ msgstr ""
#: .\cookbook\templates\settings.html:183
msgid "Perfect for large Databases"
msgstr ""
msgstr "Perfetto per database grandi"
#: .\cookbook\templates\setup.html:6 .\cookbook\templates\system.html:5
msgid "Cookbook Setup"

View File

@ -8,8 +8,8 @@ msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-11-08 16:27+0100\n"
"PO-Revision-Date: 2021-08-20 19:28+0000\n"
"Last-Translator: Danny Tsui <dannytszho@gmail.com>\n"
"PO-Revision-Date: 2022-01-16 07:06+0000\n"
"Last-Translator: 糖多 <1365143958@qq.com>\n"
"Language-Team: Chinese (Simplified) <http://translate.tandoor.dev/projects/"
"tandoor/recipes-backend/zh_Hans/>\n"
"Language: zh_CN\n"
@ -17,7 +17,7 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=1; plural=0;\n"
"X-Generator: Weblate 4.7.2\n"
"X-Generator: Weblate 4.8\n"
#: .\cookbook\filters.py:23 .\cookbook\templates\base.html:125
#: .\cookbook\templates\forms\ingredients.html:34
@ -1173,12 +1173,12 @@ msgstr ""
#: .\cookbook\templates\include\log_cooking.html:19
msgid "Rating"
msgstr ""
msgstr "评分"
#: .\cookbook\templates\include\log_cooking.html:27
#: .\cookbook\templates\include\recipe_open_modal.html:18
msgid "Close"
msgstr ""
msgstr "关闭"
#: .\cookbook\templates\include\recipe_open_modal.html:32
msgid "Open Recipe"

View File

@ -2,7 +2,23 @@ There are several questions and issues that come up from time to time. Here are
Please note that the existence of some questions is due the application not being perfect in some parts.
Many of those shortcomings are planned to be fixed in future release but simply could not be addressed yet due to time limits.
## CSRF Errors
## Is there a Tandoor app?
Tandoor can be installed as a progressive web app (PWA) on mobile and desktop devices. The PWA stores recently accessed recipes locally for offline use.
### Mobile browsers
#### Safari (iPhone/iPad)
Open Tandoor, click Safari's share button, select `Add to Home Screen`
### Desktop browsers
#### Google Chrome
Open Tandoor, open the menu behind the three vertical dots at the top right, select `Install Tandoor Recipes...`
#### Microsoft Edge
Open Tandoor, open the menu behind the three horizontal dots at the top right, select `Apps > Install Tandoor Recipes`
## Why am I getting CSRF Errors?
If you are getting CSRF Errors this is most likely due to a reverse proxy not passing the correct headers.
If you are using swag by linuxserver you might need `proxy_set_header X-Forwarded-Proto $scheme;` in your nginx config.
@ -10,15 +26,15 @@ If you are using a plain ngix you might need `proxy_set_header Host $http_host;`
Further discussions can be found in this [Issue #518](https://github.com/vabene1111/recipes/issues/518)
## Images not loading
If images are not loading this might be related to the same issue as the CSRF Errors.
A discussion about that can be found [Issue #452](https://github.com/vabene1111/recipes/issues/452)
## Why are images not loading?
If images are not loading this might be related to the same issue as the CSRF errors (see above).
A discussion about that can be found at [Issue #452](https://github.com/vabene1111/recipes/issues/452)
The other common issue is that the recommended nginx container is removed from the deployment stack.
If removed, the nginx webserver needs to be replaced by something else that servers the /mediafiles/ directory or
`GUNICORN_MEDIA` needs to be enabled to allow media serving by the application container itself.
## User Creation
## How can I create users?
To create a new user click on your name (top right corner) and select system. There click on invite links and create a new invite link.
It is not possible to create users through the admin because users must be assigned a default group and space.
@ -28,7 +44,7 @@ To change a users space you need to go to the admin and select User Infos.
If you use an external auth provider or proxy authentication make sure to specify a default group and space in the
environment configuration.
## Spaces
## What are spaces?
Spaces are a feature used to separate one installation of Tandoor into several parts.
In technical terms it is a multi tenant system.
@ -39,11 +55,16 @@ If you want to host the collection of your friends family or your neighbor you c
Sharing between spaces is currently not possible but is planned for future releases.
## Create Admin user / reset passwords
To create a superuser or reset a lost password if access to the container is lost you need to
## How can I reset passwords?
To reset a lost password if access to the container is lost you need to
1. execute into the container using `docker-compose exec web_recipes sh`
2. activate the virtual environment `source venv/bin/activate`
3. run `python manage.py changepassword <username>` and follow the steps shown.
## How can I add an admin user?
To create a superuser you need to
1. execute into the container using `docker-compose exec web_recipes sh`
2. activate the virtual environment `source venv/bin/activate`
3. run `python manage.py createsuperuser` and follow the steps shown.
To change a password enter `python manage.py changepassword <username>` in step 3.

View File

@ -32,7 +32,7 @@ as environment files loaded by docker compose don't support multiple lines for a
Take the example configuration from the allauth docs, fill in your settings and then inline the whole object
(you can use a service like [www.freeformatter.com](https://www.freeformatter.com/json-formatter.html) for formatting).
Assign it to the `SOCIALACCOUNT_PROVIDERS` variable.
Assign it to the additional `SOCIALACCOUNT_PROVIDERS` variable.
```ini
SOCIALACCOUNT_PROVIDERS={"nextcloud":{"SERVER":"https://nextcloud.example.org"}}
@ -56,6 +56,25 @@ Use the superuser account to grant permissions to the newly created users.
I do not have a ton of experience with using various single signon providers and also cannot test all of them.
If you have any Feedback or issues let me know.
### Third-party authentication example
Keycloak is a popular IAM solution and integration is straight forward thanks to Django Allauth. This example can also be used as reference for other third-party authentication solutions, as documented by Allauth.
At Keycloak, create a new client and assign a `Client-ID`, this client comes with a `Secret-Key`. Both values are required later on. Make sure to define the correct Redirection-URL for the service, for example `https://tandoor.example.com/*`. Depending on your Keycloak setup, you need to assign roles and groups to grant access to the service.
To enable Keycloak as a sign in option, set those variables to define the social provider and specify its configuration:
```ini
SOCIAL_PROVIDERS=allauth.socialaccount.providers.keycloak
SOCIALACCOUNT_PROVIDERS='{ "keycloak": { "KEYCLOAK_URL": "https://auth.example.com/", "KEYCLOAK_REALM": "master" } }'
```
1. Restart the service, login as superuser and open the `Admin` page.
2. Make sure that the correct `Domain Name` is defined at `Sites`.
3. Select `Social Application` and chose `Keycloak` from the provider list.
4. Provide an arbitrary name for your authentication provider, and enter the `Client-ID` and `Secret Key` values obtained from Keycloak earlier.
5. Make sure to add your `Site` to the list of available sites and save the new `Social Application`.
You are now able to sign in using Keycloak.
### Linking accounts
To link an account to an already existing normal user go to the settings page of the user and link it.
Here you can also unlink your account if you no longer want to use a social login method.

View File

@ -54,14 +54,20 @@
<div class="col-12 col-md-3 calender-options">
<h5>{{ $t("Planner_Settings") }}</h5>
<b-form>
<b-form-group id="UomInput" :label="$t('Period')" :description="$t('Plan_Period_To_Show')" label-for="UomInput">
<b-form-select id="UomInput" v-model="settings.displayPeriodUom" :options="options.displayPeriodUom"></b-form-select>
<b-form-group id="UomInput" :label="$t('Period')" :description="$t('Plan_Period_To_Show')"
label-for="UomInput">
<b-form-select id="UomInput" v-model="settings.displayPeriodUom"
:options="options.displayPeriodUom"></b-form-select>
</b-form-group>
<b-form-group id="PeriodInput" :label="$t('Periods')" :description="$t('Plan_Show_How_Many_Periods')" label-for="PeriodInput">
<b-form-select id="PeriodInput" v-model="settings.displayPeriodCount" :options="options.displayPeriodCount"></b-form-select>
<b-form-group id="PeriodInput" :label="$t('Periods')"
:description="$t('Plan_Show_How_Many_Periods')" label-for="PeriodInput">
<b-form-select id="PeriodInput" v-model="settings.displayPeriodCount"
:options="options.displayPeriodCount"></b-form-select>
</b-form-group>
<b-form-group id="DaysInput" :label="$t('Starting_Day')" :description="$t('Starting_Day')" label-for="DaysInput">
<b-form-select id="DaysInput" v-model="settings.startingDayOfWeek" :options="dayNames"></b-form-select>
<b-form-group id="DaysInput" :label="$t('Starting_Day')" :description="$t('Starting_Day')"
label-for="DaysInput">
<b-form-select id="DaysInput" v-model="settings.startingDayOfWeek"
:options="dayNames"></b-form-select>
</b-form-group>
<b-form-group id="WeekNumInput" :label="$t('Week_Numbers')">
<b-form-checkbox v-model="settings.displayWeekNumbers" name="week_num">
@ -73,19 +79,25 @@
<div class="col-12 col-md-9 col-lg-6">
<h5>{{ $t("Meal_Types") }}</h5>
<div>
<draggable :list="meal_types" group="meal_types" :empty-insert-threshold="10" handle=".handle" @sort="sortMealTypes()">
<b-card no-body class="mt-1" v-for="(meal_type, index) in meal_types" v-hover :key="meal_type.id">
<draggable :list="meal_types" group="meal_types" :empty-insert-threshold="10"
handle=".handle" @sort="sortMealTypes()">
<b-card no-body class="mt-1" v-for="(meal_type, index) in meal_types" v-hover
:key="meal_type.id">
<b-card-header class="p-4">
<div class="row">
<div class="col-2 handle">
<button type="button" class="btn btn-lg shadow-none"><i class="fas fa-arrows-alt-v"></i></button>
<button type="button" class="btn btn-lg shadow-none"><i
class="fas fa-arrows-alt-v"></i></button>
</div>
<div class="col-10">
<h5>
{{ meal_type.icon }} {{ meal_type.name
{{ meal_type.icon }} {{
meal_type.name
}}<span class="float-right text-primary"
><i class="fa" v-bind:class="{ 'fa-pen': !meal_type.editing, 'fa-save': meal_type.editing }" @click="editOrSaveMealType(index)" aria-hidden="true"></i
></span>
><i class="fa"
v-bind:class="{ 'fa-pen': !meal_type.editing, 'fa-save': meal_type.editing }"
@click="editOrSaveMealType(index)" aria-hidden="true"></i
></span>
</h5>
</div>
</div>
@ -93,20 +105,29 @@
<b-card-body class="p-4" v-if="meal_type.editing">
<div class="form-group">
<label>{{ $t("Name") }}</label>
<input class="form-control" placeholder="Name" v-model="meal_type.name" />
<input class="form-control" placeholder="Name" v-model="meal_type.name"/>
</div>
<div class="form-group">
<emoji-input :field="'icon'" :label="$t('Icon')" :value="meal_type.icon"></emoji-input>
<emoji-input :field="'icon'" :label="$t('Icon')"
:value="meal_type.icon"></emoji-input>
</div>
<div class="form-group">
<label>{{ $t("Color") }}</label>
<input class="form-control" type="color" name="Name" :value="meal_type.color" @change="meal_type.color = $event.target.value" />
<input class="form-control" type="color" name="Name"
:value="meal_type.color"
@change="meal_type.color = $event.target.value"/>
</div>
<b-form-checkbox id="checkbox-1" v-model="meal_type.default" name="default_checkbox" class="mb-2">
<b-form-checkbox id="checkbox-1" v-model="meal_type.default"
name="default_checkbox" class="mb-2">
{{ $t("Default") }}
</b-form-checkbox>
<button class="btn btn-danger" @click="deleteMealType(index)">{{ $t("Delete") }}</button>
<button class="btn btn-primary float-right" @click="editOrSaveMealType(index)">{{ $t("Save") }}</button>
<button class="btn btn-danger" @click="deleteMealType(index)">{{
$t("Delete")
}}
</button>
<button class="btn btn-primary float-right" @click="editOrSaveMealType(index)">
{{ $t("Save") }}
</button>
</b-card-body>
</b-card>
</draggable>
@ -127,7 +148,15 @@
openEntryEdit(contextData.originalItem.entry)
"
>
<a class="dropdown-item p-2" href="javascript:void(0)"><i class="fas fa-pen"></i> {{ $t("Edit") }}</a>
<a class="dropdown-item p-2" href="javascript:void(0)"><i class="fas fa-pen"></i> {{
$t("Edit")
}}</a>
</ContextMenuItem>
<ContextMenuItem
v-if="contextData.originalItem.entry.recipe != null"
@click="$refs.menu.close();openRecipe(contextData.originalItem.entry.recipe)">
<a class="dropdown-item p-2" href="javascript:void(0)"><i class="fas fa-pizza-slice"></i>
{{ $t("Recipe") }}</a>
</ContextMenuItem>
<ContextMenuItem
@click="
@ -135,7 +164,8 @@
moveEntryLeft(contextData)
"
>
<a class="dropdown-item p-2" href="javascript:void(0)"><i class="fas fa-arrow-left"></i> {{ $t("Move") }}</a>
<a class="dropdown-item p-2" href="javascript:void(0)"><i class="fas fa-arrow-left"></i>
{{ $t("Move") }}</a>
</ContextMenuItem>
<ContextMenuItem
@click="
@ -143,7 +173,8 @@
moveEntryRight(contextData)
"
>
<a class="dropdown-item p-2" href="javascript:void(0)"><i class="fas fa-arrow-right"></i> {{ $t("Move") }}</a>
<a class="dropdown-item p-2" href="javascript:void(0)"><i class="fas fa-arrow-right"></i>
{{ $t("Move") }}</a>
</ContextMenuItem>
<ContextMenuItem
@click="
@ -159,7 +190,8 @@
addToShopping(contextData)
"
>
<a class="dropdown-item p-2" href="javascript:void(0)"><i class="fas fa-shopping-cart"></i> {{ $t("Add_to_Shopping") }}</a>
<a class="dropdown-item p-2" href="javascript:void(0)"><i class="fas fa-shopping-cart"></i>
{{ $t("Add_to_Shopping") }}</a>
</ContextMenuItem>
<ContextMenuItem
@click="
@ -167,7 +199,8 @@
deleteEntry(contextData)
"
>
<a class="dropdown-item p-2 text-danger" href="javascript:void(0)"><i class="fas fa-trash"></i> {{ $t("Delete") }}</a>
<a class="dropdown-item p-2 text-danger" href="javascript:void(0)"><i class="fas fa-trash"></i>
{{ $t("Delete") }}</a>
</ContextMenuItem>
</template>
</ContextMenu>
@ -198,10 +231,12 @@
<div class="col-12 mt-1" v-if="shopping_list.length > 0">
<b-button-group>
<b-button variant="success" @click="saveShoppingList"
><i class="fas fa-external-link-alt"></i>
><i class="fas fa-external-link-alt"></i>
{{ $t("Open") }}
</b-button>
<b-button variant="danger" @click="shopping_list = []"><i class="fa fa-trash"></i> {{ $t("Clear") }} </b-button>
<b-button variant="danger" @click="shopping_list = []"><i class="fa fa-trash"></i>
{{ $t("Clear") }}
</b-button>
</b-button-group>
</div>
</div>
@ -209,37 +244,46 @@
</div>
</template>
<transition name="slide-fade">
<div class="row fixed-bottom p-2 b-1 border-top text-center" style="background: rgba(255, 255, 255, 0.6)" v-if="current_tab === 0">
<div class="row fixed-bottom p-2 b-1 border-top text-center" style="background: rgba(255, 255, 255, 0.6)"
v-if="current_tab === 0">
<div class="col-md-3 col-6">
<button class="btn btn-block btn-success shadow-none" @click="createEntryClick(new Date())"><i class="fas fa-calendar-plus"></i> {{ $t("Create") }}</button>
<button class="btn btn-block btn-success shadow-none" @click="createEntryClick(new Date())"><i
class="fas fa-calendar-plus"></i> {{ $t("Create") }}
</button>
</div>
<div class="col-md-3 col-6">
<button class="btn btn-block btn-primary shadow-none" v-b-toggle.sidebar-shopping><i class="fas fa-shopping-cart"></i> {{ $t("Shopping_list") }}</button>
<button class="btn btn-block btn-primary shadow-none" v-b-toggle.sidebar-shopping><i
class="fas fa-shopping-cart"></i> {{ $t("Shopping_list") }}
</button>
</div>
<div class="col-md-3 col-6">
<a class="btn btn-block btn-primary shadow-none" :href="iCalUrl"
><i class="fas fa-download"></i>
><i class="fas fa-download"></i>
{{ $t("Export_To_ICal") }}
</a>
</div>
<div class="col-md-3 col-6">
<button class="btn btn-block btn-primary shadow-none disabled" v-b-tooltip.focus.top :title="$t('Coming_Soon')">
<button class="btn btn-block btn-primary shadow-none disabled" v-b-tooltip.focus.top
:title="$t('Coming_Soon')">
{{ $t("Auto_Planner") }}
</button>
</div>
<div class="col-12 d-flex justify-content-center mt-2 d-block d-md-none">
<b-button-toolbar key-nav aria-label="Toolbar with button groups">
<b-button-group class="mx-1">
<b-button v-html="'<<'" @click="setShowDate($refs.header.headerProps.previousPeriod)"></b-button>
<b-button v-html="'<<'"
@click="setShowDate($refs.header.headerProps.previousPeriod)"></b-button>
<b-button v-html="'<'" @click="setStartingDay(-1)"></b-button>
</b-button-group>
<b-button-group class="mx-1">
<b-button @click="setShowDate($refs.header.headerProps.currentPeriod)"><i class="fas fa-home"></i> </b-button>
<b-button @click="setShowDate($refs.header.headerProps.currentPeriod)"><i
class="fas fa-home"></i></b-button>
<b-form-datepicker button-only button-variant="secondary"></b-form-datepicker>
</b-button-group>
<b-button-group class="mx-1">
<b-button v-html="'>'" @click="setStartingDay(1)"></b-button>
<b-button v-html="'>>'" @click="setShowDate($refs.header.headerProps.nextPeriod)"></b-button>
<b-button v-html="'>>'"
@click="setShowDate($refs.header.headerProps.nextPeriod)"></b-button>
</b-button-group>
</b-button-toolbar>
</div>
@ -250,7 +294,7 @@
<script>
import Vue from "vue"
import { BootstrapVue } from "bootstrap-vue"
import {BootstrapVue} from "bootstrap-vue"
import "bootstrap-vue/dist/bootstrap-vue.css"
import ContextMenu from "@/components/ContextMenu/ContextMenu"
@ -264,11 +308,11 @@ import moment from "moment"
import draggable from "vuedraggable"
import VueCookies from "vue-cookies"
import { ApiMixin, StandardToasts } from "@/utils/utils"
import { CalendarView, CalendarMathMixin } from "vue-simple-calendar/src/components/bundle"
import { ApiApiFactory } from "@/utils/openapi/api"
import {ApiMixin, StandardToasts, ResolveUrlMixin} from "@/utils/utils"
import {CalendarView, CalendarMathMixin} from "vue-simple-calendar/src/components/bundle"
import {ApiApiFactory} from "@/utils/openapi/api"
const { makeToast } = require("@/utils/utils")
const {makeToast} = require("@/utils/utils")
Vue.prototype.moment = moment
Vue.use(BootstrapVue)
@ -288,7 +332,7 @@ export default {
EmojiInput,
draggable,
},
mixins: [CalendarMathMixin, ApiMixin],
mixins: [CalendarMathMixin, ApiMixin, ResolveUrlMixin],
data: function () {
return {
showDate: new Date(),
@ -306,12 +350,12 @@ export default {
current_context_menu_item: null,
options: {
displayPeriodUom: [
{ text: this.$t("Week"), value: "week" },
{text: this.$t("Week"), value: "week"},
{
text: this.$t("Month"),
value: "month",
},
{ text: this.$t("Year"), value: "year" },
{text: this.$t("Year"), value: "year"},
],
displayPeriodCount: [1, 2, 3],
entryEditing: {
@ -369,7 +413,7 @@ export default {
dayNames: function () {
let options = []
this.getFormattedWeekdayNames(this.userLocale, "long", 0).forEach((day, index) => {
options.push({ text: day, value: index })
options.push({text: day, value: index})
})
return options
},
@ -411,6 +455,9 @@ export default {
},
},
methods: {
openRecipe: function (recipe) {
window.open(this.resolveDjangoUrl('view_recipe', recipe.id))
},
addToShopping(entry) {
if (entry.originalItem.entry.recipe !== null) {
this.shopping_list.push(entry.originalItem.entry)
@ -445,7 +492,7 @@ export default {
let apiClient = new ApiApiFactory()
apiClient
.createMealType({ name: this.$t("Meal_Type") })
.createMealType({name: this.$t("Meal_Type")})
.then((e) => {
this.periodChangedCallback(this.current_period)
})

View File

@ -238,7 +238,6 @@ export default {
})
popper.update()
this.over = false
// this.$emit({ action: "drop", target: this.item, source: this.source })
} else {
this.isError = true
}

View File

@ -37,7 +37,7 @@
"Carbohydrates": "Glucides",
"Calories": "Calories",
"Energy": "Energie",
"Nutrition": "Informations nutritionnelles",
"Nutrition": "Valeurs nutritionnelles",
"Date": "Date",
"Share": "Partager",
"Export": "Exporter",
@ -121,7 +121,7 @@
"del_confirmation_tree": "Êtes-vous sûr de vouloir supprimer {source} et tous ses enfants ?",
"warning_feature_beta": "Cette fonctionnalité est actuellement en phase BETA (test). Veuillez vous attendre à des bugs et éventuellement à des changements avenir (éventuellement la perte de données liées aux fonctionnalités) lorsque vous utilisez cette fonctionnalité.",
"confirm_delete": "Voulez-vous vraiment supprimer {objet} ?",
"Note": "Noter",
"Note": "Notes",
"Add_Step": "Ajouter une étape",
"Step_Name": "Nom de l'étape",
"Parameter": "Paramètre",
@ -192,5 +192,96 @@
"Edit_Recipe": "Modifier une Recette",
"Move_Up": "Monter",
"Time": "Temps",
"Coming_Soon": "Bientôt disponible"
"Coming_Soon": "Bientôt disponible",
"Create_New_Shopping Category": "Ajouter une catégorie de courses",
"success_moving_resource": "Ressource correctement déplacée !",
"err_moving_resource": "Il y a eu une erreur pour déplacer une ressource !",
"err_merging_resource": "Il y a eu une erreur pour fusionner une ressource !",
"success_merging_resource": "Ressource correctement fusionnée !",
"Added_by": "Ajouter par",
"Added_on": "Ajouter le",
"Shopping_Categories": "Catégories de courses",
"Add_Servings_to_Shopping": "Ajouter {servings} partions aux courses",
"CountMore": "...+ {count} en plus",
"NoCategory": "Pas de catégorie sélectionnée.",
"OfflineAlert": "Vous êtes déconnecté, votre liste de courses peut ne pas être synchronisée.",
"shopping_share_desc": "Les utilisateurs verront tous les articles que vous ajoutez à votre liste de courses. Ils doivent vous ajouter pour que vous voyez les articles de leur liste.",
"shopping_auto_sync_desc": "Le réglage sur 0 désactive la synchronisation automatique. Lorsque vous consultez une liste de courses, celle-ci est mise à jour toutes les secondes pour synchroniser les modifications apportées par une autre personne. Cette fonction est utile lorsque vous faites des achats avec plusieurs personnes, mais elle consomme des données mobiles.",
"mealplan_autoinclude_related_desc": "Lorsque vous ajoutez un plan de repas à la liste de courses (manuellement ou automatiquement), incluez toutes les recettes associées.",
"err_move_self": "Impossible de déplacer un élément vers lui-même",
"show_sql": "Montrer le SQL",
"filter_to_supermarket_desc": "Par défaut, la liste de courses est filtrée pour n'inclure que les catégories du supermarché sélectionné.",
"CategoryInstruction": "Faites glisser les catégories pour modifier l'ordre dans lequel elles apparaissent dans la liste des courses.",
"in_shopping": "Dans la liste de courses",
"and_up": "&Au-dessus",
"Plan_Show_How_Many_Periods": "Combien de périodes montrer",
"Edit_Meal_Plan_Entry": "Modifier le plan de repas",
"Periods": "Périodes",
"Period": "Période",
"Plan_Period_To_Show": "Montrer les semaines, mois ou années",
"Auto_Planner": "Planning Auto",
"New_Cookbook": "Nouveau livre de recettes",
"Hide_Keyword": "masquer les mots clefs",
"Clear": "Supprimer",
"AddToShopping": "Ajouter à la liste de courses",
"IngredientInShopping": "Cet ingrédient est dans votre liste de courses.",
"NotInShopping": "{food} n'est pas dans votre liste de courses.",
"OnHand": "Disponible actuellement",
"FoodNotOnHand": "L'ingrédient {food} n'est pas disponible.",
"Planner": "Planificateur",
"Planner_Settings": "Paramètres du planificateur",
"AddFoodToShopping": "Ajouter l'ingrédient {food} à votre liste de courses",
"DeleteShoppingConfirm": "Etes-vous sûr que vous souhaitez retirer tous les ingrédients {food} de votre liste de courses ?",
"IgnoredFood": "L'ingrédient {food} est paramétré pour ignorer les courses.",
"Inherit": "Hériter",
"InheritFields": "Hériter les valeurs des champs",
"FoodInherit": "Ingrédient hérité",
"ShowUncategorizedFood": "Montrer ce qui est indéfini",
"GroupBy": "Grouper par",
"SupermarketCategoriesOnly": "Catégories de supermarché uniquement",
"MoveCategory": "Déplacer vers : ",
"IgnoreThis": "Ne jamais ajouter l'ingrédient {food} aux courses",
"DelayFor": "Retard de {hours} heures",
"Warning": "Avertissement",
"InheritWarning": "L'ingrédient {food} est un héritage, les changements pourraient ne pas être conservés.",
"ShowDelayed": "Afficher les éléments retardés",
"Completed": "Achevé",
"shopping_share": "Partager la liste de courses",
"shopping_auto_sync": "Autosynchronisation",
"mealplan_autoadd_shopping": "Ajout automatique d'un plan de repas",
"mealplan_autoexclude_onhand": "Exclure les aliments disponibles",
"mealplan_autoinclude_related": "Ajouter les recettes connexes",
"default_delay": "Heures de retard par défaut",
"mealplan_autoadd_shopping_desc": "Ajouter automatiquement les ingrédients du plan de repas à la liste de courses.",
"mealplan_autoexclude_onhand_desc": "Lorsque vous ajoutez un plan de repas à la liste de courses (manuellement ou automatiquement), excluez les ingrédients que vous avez déjà.",
"default_delay_desc": "Nombre d'heures par défaut pour retarder l'ajoût d'un article à la liste de courses.",
"filter_to_supermarket": "Limiter au supermarché",
"nothing": "Rien à effectuer",
"err_merge_self": "Impossible de fusionner un élément avec lui-même",
"CategoryName": "Intitulé de la catégorie",
"SupermarketName": "Nom du supermarché",
"shopping_recent_days_desc": "Jours des entrées récentes de la liste de courses à afficher.",
"shopping_recent_days": "Jours récents",
"create_shopping_new": "Ajouter à la NOUVELLE liste de courses",
"download_pdf": "Télécharger le PDF",
"download_csv": "Télécharger le CSV",
"csv_delim_help": "Délimiteur à utiliser pour les exports CSV.",
"csv_delim_label": "Délimiteur CSV",
"SuccessClipboard": "Liste de courses copiée dans le presse-papiers",
"copy_to_clipboard": "Copier dans le presse-papiers",
"csv_prefix_help": "Préfixe à ajouter lors de la copie de la liste dans le presse-papiers.",
"csv_prefix_label": "Lister les préfixes",
"copy_markdown_table": "Copier en tant que tableau Markdown",
"DelayUntil": "Retard jusqu'à",
"mark_complete": "Marque comme terminé",
"QuickEntry": "Entrée rapide",
"shopping_add_onhand_desc": "Marquer les aliments comme \"disponibles\" lorsqu'ils sont cochés sur la liste des courses.",
"shopping_add_onhand": "Disponible par défaut",
"related_recipes": "Recettes connexes",
"today_recipes": "Recettes du jour",
"Search Settings": "Paramètres de recherche",
"FoodOnHand": "L'ingrédient {food} est disponible.",
"Undefined": "Indéfini",
"Create_Meal_Plan_Entry": "Création d'un plan de repas",
"RemoveFoodFromShopping": "Retirer l'ingrédient {food} de votre liste de courses"
}

View File

@ -1,8 +1,8 @@
{
"err_fetching_resource": "Si è verificato un errore nel recupero della risorsa!",
"err_fetching_resource": "Si è verificato un errore durante il recupero di una risorsa!",
"err_creating_resource": "Si è verificato un errore durante la creazione di una risorsa!",
"err_updating_resource": "Si è verificato un errore durante l'aggiornamento della risorsa!",
"err_deleting_resource": "Si è verificato un errore durante la cancellazione della risorsa!",
"err_updating_resource": "Si è verificato un errore durante l'aggiornamento di una risorsa!",
"err_deleting_resource": "Si è verificato un errore durante la cancellazione di una risorsa!",
"success_fetching_resource": "Risorsa recuperata con successo!",
"success_creating_resource": "Risorsa creata con successo!",
"success_updating_resource": "Risorsa aggiornata con successo!",
@ -208,5 +208,37 @@
"New_Cookbook": "Nuovo libro di ricette",
"Hide_Keyword": "Nascondi parole chiave",
"Clear": "Pulisci",
"Shopping_List_Empty": "La tua lista della spesa è vuota, puoi aggiungere elementi dal menù contestuale di una voce nel piano alimentare (clicca con il tasto destro sulla scheda o clicca con il tasto sinistro sull'icona del menù)"
"Shopping_List_Empty": "La tua lista della spesa è vuota, puoi aggiungere elementi dal menù contestuale di una voce nel piano alimentare (clicca con il tasto destro sulla scheda o clicca con il tasto sinistro sull'icona del menù)",
"success_moving_resource": "Risorsa spostata con successo!",
"Shopping_Categories": "Categorie di spesa",
"IngredientInShopping": "Questo ingrediente è nella tua lista della spesa.",
"RemoveFoodFromShopping": "Rimuovi {food} dalla tua lista della spesa",
"DelayFor": "Ritarda per {hours} ore",
"OfflineAlert": "Sei offline, le liste della spesa potrebbero non sincronizzarsi.",
"err_moving_resource": "Si è verificato un errore durante lo spostamento di una risorsa!",
"err_merging_resource": "Si è verificato un errore durante l'unione di una risorsa!",
"success_merging_resource": "Risorsa unita con successo!",
"Added_by": "Aggiunto da",
"Added_on": "Aggiunto il",
"AddToShopping": "Aggiungi a lista della spesa",
"NotInShopping": "{food} non è nella tua lista della spesa.",
"Undefined": "Non definito",
"AddFoodToShopping": "Aggiungi {food} alla tua lista della spesa",
"DeleteShoppingConfirm": "Sei sicuro di voler rimuovere tutto {food} dalla lista della spesa?",
"Add_Servings_to_Shopping": "Aggiungi {servings} porzioni alla spesa",
"Inherit": "Eredita",
"InheritFields": "Eredita i valori dei campi",
"ShowUncategorizedFood": "Mostra non definiti",
"GroupBy": "Raggruppa per",
"MoveCategory": "Sposta in: ",
"Warning": "Attenzione",
"NoCategory": "Nessuna categoria selezionata.",
"ShowDelayed": "Mostra elementi ritardati",
"Completed": "Completato",
"shopping_share": "Condividi lista della spesa",
"shopping_auto_sync": "Sincronizzazione automatica",
"err_move_self": "Non è possibile muovere un elemento in sé stesso",
"nothing": "Nulla da fare",
"show_sql": "Mostra SQL",
"Search Settings": "Impostazioni di ricerca"
}

View File

@ -1,5 +1,5 @@
{
"warning_feature_beta": "Ta funkcja jest obecnie w wersji BETA (testowej). Podczas korzystania z tej funkcji należy spodziewać się błędów i ewentualnych zmian w przyszłości (prawdopodobna utrata danych powiązanych z tą funkcją).",
"warning_feature_beta": "Ta funkcja jest obecnie w wersji BETA (testowej). Podczas korzystania z tej funkcji mogą wystąpić błędy, a w przyszłości zmiany funkcjonalności (możliwa utrata danych powiązanych z tą funkcją).",
"err_fetching_resource": "Wystąpił błąd podczas pobierania zasobu!",
"err_creating_resource": "Wystąpił błąd podczas tworzenia zasobu!",
"err_updating_resource": "Wystąpił błąd podczas aktualizowania zasobu!",
@ -207,5 +207,22 @@
"Auto_Planner": "Plan automatyczny",
"New_Cookbook": "Nowa książka kucharska",
"Hide_Keyword": "Ukryj słowa kluczowe",
"Clear": "Wyczyść"
"Clear": "Wyczyść",
"err_moving_resource": "Wystąpił błąd podczas przenoszenia zasobu!",
"err_merging_resource": "Wystąpił błąd podczas scalania zasobu!",
"success_moving_resource": "Pomyślnie przeniesiono zasób!",
"success_merging_resource": "Pomyślnie scalono zasób!",
"Added_by": "Dodane przez",
"Added_on": "Dodano dnia",
"IngredientInShopping": "Ten składnik znajduje się na Twojej liście zakupów.",
"NotInShopping": "{food} nie ma na Twojej liście zakupów.",
"OnHand": "Obecnie pod ręką",
"FoodNotOnHand": "Nie masz pod ręką {food}.",
"Undefined": "Nieokreślony",
"AddFoodToShopping": "Dodaj {food} do swojej listy zakupów",
"RemoveFoodFromShopping": "Usuń {food} z listy zakupów",
"Shopping_Categories": "Kategorie zakupów",
"AddToShopping": "Dodaj do listy zakupów",
"FoodOnHand": "Masz pod ręką {food}.",
"DeleteShoppingConfirm": "Czy na pewno chcesz usunąć wszystkie {food} z listy zakupów?"
}

View File

@ -7,41 +7,41 @@
"success_creating_resource": "",
"success_updating_resource": "",
"success_deleting_resource": "",
"import_running": "",
"all_fields_optional": "",
"convert_internal": "",
"show_only_internal": "",
"import_running": "正在导入,请稍候!",
"all_fields_optional": "所有字段都是可选的,可以留空。",
"convert_internal": "转换为内部菜谱",
"show_only_internal": "仅显示内部菜谱",
"Log_Recipe_Cooking": "",
"External_Recipe_Image": "外部菜谱图像",
"Add_to_Shopping": "添加到购物",
"Add_to_Plan": "添加到计划",
"Step_start_time": "",
"Sort_by_new": "",
"Sort_by_new": "按新旧排序",
"Recipes_per_page": "",
"Manage_Books": "管理书籍",
"Meal_Plan": "",
"Select_Book": "",
"Meal_Plan": "用餐计划",
"Select_Book": "选择书籍",
"Recipe_Image": "菜谱图像",
"Import_finished": "导入完成",
"View_Recipes": "",
"View_Recipes": "查看菜谱",
"Log_Cooking": "",
"New_Recipe": "新菜谱",
"Url_Import": "导入网址",
"Reset_Search": "重置搜索",
"Recently_Viewed": "最近浏览",
"Load_More": "加载更多",
"Keywords": "关键",
"Keywords": "关键",
"Books": "书籍",
"Proteins": "蛋白质",
"Fats": "脂肪",
"Carbohydrates": "碳水化合物",
"Calories": "卡路里",
"Energy": "",
"Energy": "能量",
"Nutrition": "营养",
"Date": "日期",
"Share": "分享",
"Export": "导出",
"Copy": "拷贝",
"Copy": "复制",
"Rating": "评分",
"Close": "关闭",
"Link": "链接",
@ -66,8 +66,8 @@
"Cancel": "取消",
"Delete": "删除",
"Open": "打开",
"Ok": "打开",
"Save": "存",
"Ok": "",
"Save": "存",
"Step": "步骤",
"Search": "搜索",
"Import": "导入",
@ -75,7 +75,33 @@
"Settings": "设置",
"or": "或",
"and": "与",
"Information": "更多资讯",
"Information": "更多信息",
"Download": "下载",
"Create": "创"
"Create": "创",
"Table_of_Contents": "目录",
"Delete_Keyword": "删除关键词",
"Edit_Keyword": "编辑关键词",
"New_Keyword": "新关键词",
"Select_File": "选择文件",
"Merge_Keyword": "合并关键词",
"Hide_Keywords": "隐藏关键词",
"Image": "图片",
"Recipes": "菜谱",
"Move": "移动",
"Merge": "合并",
"confirm_delete": "您确定要删除 {object} 吗?",
"Save_and_View": "保存并查看",
"Edit_Recipe": "编辑菜谱",
"Move_Up": "上移",
"show_split_screen": "拆分视图",
"Move_Keyword": "移动关键词",
"Hide_Recipes": "隐藏菜谱",
"Move_Down": "下移",
"Step_Name": "步骤名称",
"Step_Type": "步骤类型",
"Enable_Amount": "启用金额",
"Disable_Amount": "禁用金额",
"Add_Step": "添加步骤",
"delete_confirmation": "你确定要删除 {source} 吗?",
"Search Settings": "搜索设置"
}

View File

@ -5369,9 +5369,9 @@ flush-write-stream@^1.0.0:
readable-stream "^2.3.6"
follow-redirects@^1.0.0, follow-redirects@^1.14.4:
version "1.14.6"
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.14.6.tgz#8cfb281bbc035b3c067d6cd975b0f6ade6e855cd"
integrity sha512-fhUl5EwSJbbl8AR+uYL2KQDxLkdSjZGR36xy46AO7cOMTrCMON6Sa28FmAnC2tRTDbd/Uuzz3aJBv7EBN7JH8A==
version "1.14.7"
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.14.7.tgz#2004c02eb9436eee9a21446a6477debf17e81685"
integrity sha512-+hbxoLbFMbRKDwohX8GkTataGqO6Jb7jGwpAlwgy2bIz25XtRm7KEzJM76R1WiNT5SwZkX4Y75SwBolkpmE7iQ==
for-in@^1.0.2:
version "1.0.2"