Merge branch 'develop' into feature/shopping-ui

# Conflicts:
#	cookbook/serializer.py
This commit is contained in:
vabene1111 2024-01-28 11:05:56 +01:00
commit 3e51bdc7f0
15 changed files with 61 additions and 27 deletions

View File

@ -450,7 +450,7 @@ class SpacePreferenceForm(forms.ModelForm):
class Meta: class Meta:
model = Space model = Space
fields = ('food_inherit', 'reset_food_inherit', 'use_plural') fields = ('food_inherit', 'reset_food_inherit',)
help_texts = { help_texts = {
'food_inherit': _('Fields on food that should be inherited by default.'), 'food_inherit': _('Fields on food that should be inherited by default.'),

View File

@ -14,12 +14,14 @@ class IngredientObject(object):
unit = "" unit = ""
food = "" food = ""
note = "" note = ""
numeric_amount = 0
def __init__(self, ingredient): def __init__(self, ingredient):
if ingredient.no_amount: if ingredient.no_amount:
self.amount = "" self.amount = ""
else: else:
self.amount = f"<scalable-number v-bind:number='{bleach.clean(str(ingredient.amount))}' v-bind:factor='ingredient_factor'></scalable-number>" self.amount = f"<scalable-number v-bind:number='{bleach.clean(str(ingredient.amount))}' v-bind:factor='ingredient_factor'></scalable-number>"
self.numeric_amount = float(ingredient.amount)
if ingredient.unit: if ingredient.unit:
if ingredient.unit.plural_name in (None, ""): if ingredient.unit.plural_name in (None, ""):
self.unit = bleach.clean(str(ingredient.unit)) self.unit = bleach.clean(str(ingredient.unit))
@ -83,9 +85,12 @@ def render_instructions(step): # TODO deduplicate markdown cleanup code
for i in step.ingredients.all(): for i in step.ingredients.all():
ingredients.append(IngredientObject(i)) ingredients.append(IngredientObject(i))
def scale(number):
return f"<scalable-number v-bind:number='{bleach.clean(str(number))}' v-bind:factor='ingredient_factor'></scalable-number>"
try: try:
template = Template(instructions) template = Template(instructions)
instructions = template.render(ingredients=ingredients) instructions = template.render(ingredients=ingredients, scale=scale)
except TemplateSyntaxError: except TemplateSyntaxError:
return _('Could not parse template code.') + ' Error: Template Syntax broken' return _('Could not parse template code.') + ' Error: Template Syntax broken'
except UndefinedError: except UndefinedError:

View File

@ -0,0 +1,17 @@
# Generated by Django 4.2.7 on 2024-01-28 07:42
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('cookbook', '0208_space_app_name_userpreference_max_owned_spaces'),
]
operations = [
migrations.RemoveField(
model_name='space',
name='use_plural',
),
]

View File

@ -303,7 +303,6 @@ class Space(ExportModelOperationsMixin('space'), models.Model):
max_recipes = models.IntegerField(default=0) 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_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) max_users = models.IntegerField(default=0)
use_plural = models.BooleanField(default=True)
allow_sharing = models.BooleanField(default=True) allow_sharing = models.BooleanField(default=True)
no_sharing_limit = models.BooleanField(default=False) no_sharing_limit = models.BooleanField(default=False)
demo = models.BooleanField(default=False) demo = models.BooleanField(default=False)

View File

@ -315,9 +315,8 @@ class SpaceSerializer(WritableNestedModelSerializer):
fields = ( fields = (
'id', 'name', 'created_by', 'created_at', 'message', 'max_recipes', 'max_file_storage_mb', 'max_users', 'id', 'name', 'created_by', 'created_at', 'message', 'max_recipes', 'max_file_storage_mb', 'max_users',
'allow_sharing', 'demo', 'food_inherit', 'user_count', 'recipe_count', 'file_size_mb', 'allow_sharing', 'demo', 'food_inherit', 'user_count', 'recipe_count', 'file_size_mb',
'image', 'nav_logo', 'space_theme', 'custom_space_theme', 'nav_bg_color', 'nav_text_color', 'use_plural', 'image', 'nav_logo', 'space_theme', 'custom_space_theme', 'nav_bg_color', 'nav_text_color',
'logo_color_32', 'logo_color_128', 'logo_color_144', 'logo_color_180', 'logo_color_192', 'logo_color_512', 'logo_color_32', 'logo_color_128', 'logo_color_144', 'logo_color_180', 'logo_color_192', 'logo_color_512', 'logo_color_svg',)
'logo_color_svg',)
read_only_fields = ( read_only_fields = (
'id', 'created_by', 'created_at', 'max_recipes', 'max_file_storage_mb', 'max_users', 'allow_sharing', 'id', 'created_by', 'created_at', 'max_recipes', 'max_file_storage_mb', 'max_users', 'allow_sharing',
'demo',) 'demo',)

View File

@ -3,7 +3,7 @@ from django.templatetags.static import static
from django_scopes import scopes_disabled from django_scopes import scopes_disabled
from cookbook.models import UserPreference, UserFile, Space from cookbook.models import UserPreference, UserFile, Space
from recipes.settings import STICKY_NAV_PREF_DEFAULT, UNAUTHENTICATED_THEME_FROM_SPACE from recipes.settings import STICKY_NAV_PREF_DEFAULT, UNAUTHENTICATED_THEME_FROM_SPACE, FORCE_THEME_FROM_SPACE
register = template.Library() register = template.Library()
@ -15,11 +15,14 @@ def theme_values(request):
def get_theming_values(request): def get_theming_values(request):
space = None space = None
if getattr(request,'space',None): if getattr(request, 'space', None):
space = request.space space = request.space
if not request.user.is_authenticated and UNAUTHENTICATED_THEME_FROM_SPACE > 0: if not request.user.is_authenticated and UNAUTHENTICATED_THEME_FROM_SPACE > 0 and FORCE_THEME_FROM_SPACE == 0:
with scopes_disabled(): with scopes_disabled():
space = Space.objects.filter(id=UNAUTHENTICATED_THEME_FROM_SPACE).first() space = Space.objects.filter(id=UNAUTHENTICATED_THEME_FROM_SPACE).first()
if FORCE_THEME_FROM_SPACE:
with scopes_disabled():
space = Space.objects.filter(id=FORCE_THEME_FROM_SPACE).first()
themes = { themes = {
UserPreference.BOOTSTRAP: 'themes/bootstrap.min.css', UserPreference.BOOTSTRAP: 'themes/bootstrap.min.css',

View File

@ -204,6 +204,22 @@ server {
} }
``` ```
Tandoor does not support directly serving of images, as explained in the [Nginx vs Gunicorn"](#nginx-vs-gunicorn) section. If you are already using nginx to serve as a reverse proxy, you can configure it to serve images as well.
Add the following directly after the `location /` context:
```
location /media/ {
root /media/;
index index.html index.htm;
}
```
Make sure you also update your `docker-compose.yml` file to mount the `mediafiles` directory. If you are using the [Plain](#plain) deployment, you do not need to make any changes. If you are using nginx to act as a reverse proxy for other apps, it may not be optimal to have `mediafiles` mounted to `/media`. In that case, adjust the directory declarations as needed, utilizing nginx's [`alias`](https://nginx.org/en/docs/http/ngx_http_core_module.html#alias) if needed.
!!!note
Use `alias` if your mount point directory is not the same as the URL request path. Tandoor media files are requested from `$http_host/media/recipes/xxx.jpg`. This means if you are mounting to a directory that does **NOT** end in `./media`, you will need to use `alias`.
!!!note !!!note
Don't forget to [download and configure](#docker-compose) your ```.env``` file! Don't forget to [download and configure](#docker-compose) your ```.env``` file!

View File

@ -560,6 +560,15 @@ With this setting you can specify the ID of a space of which the appearance sett
UNAUTHENTICATED_THEME_FROM_SPACE= UNAUTHENTICATED_THEME_FROM_SPACE=
``` ```
#### Force Theme
> default `0` - options `1-X` (space ID)
Similar to the Default theme but forces the theme upon all users (authenticated/unauthenticated) and all spaces
```
FORCE_THEME_FROM_SPACE=
```
### Rate Limiting / Performance ### Rate Limiting / Performance
#### Shopping auto sync #### Shopping auto sync

View File

@ -59,6 +59,7 @@ KJ_PREF_DEFAULT = bool(int(os.getenv('KJ_PREF_DEFAULT', False)))
STICKY_NAV_PREF_DEFAULT = bool(int(os.getenv('STICKY_NAV_PREF_DEFAULT', True))) STICKY_NAV_PREF_DEFAULT = bool(int(os.getenv('STICKY_NAV_PREF_DEFAULT', True)))
MAX_OWNED_SPACES_PREF_DEFAULT = int(os.getenv('MAX_OWNED_SPACES_PREF_DEFAULT', 100)) MAX_OWNED_SPACES_PREF_DEFAULT = int(os.getenv('MAX_OWNED_SPACES_PREF_DEFAULT', 100))
UNAUTHENTICATED_THEME_FROM_SPACE = int(os.getenv('UNAUTHENTICATED_THEME_FROM_SPACE', 0)) UNAUTHENTICATED_THEME_FROM_SPACE = int(os.getenv('UNAUTHENTICATED_THEME_FROM_SPACE', 0))
FORCE_THEME_FROM_SPACE = int(os.getenv('FORCE_THEME_FROM_SPACE', 0))
# minimum interval that users can set for automatic sync of shopping lists # minimum interval that users can set for automatic sync of shopping lists
SHOPPING_MIN_AUTOSYNC_INTERVAL = int( SHOPPING_MIN_AUTOSYNC_INTERVAL = int(

View File

@ -20,7 +20,7 @@
<cookbook-toc :recipes="recipes" v-if="current_page === 1" v-on:switchRecipe="switchRecipe($event)"></cookbook-toc> <cookbook-toc :recipes="recipes" v-if="current_page === 1" v-on:switchRecipe="switchRecipe($event)"></cookbook-toc>
</transition> </transition>
<transition name="flip" mode="out-in"> <transition name="flip" mode="out-in">
<recipe-card :recipe="display_recipes[1].recipe_content" v-if="current_page > 1 && display_recipes.length === 2" :key="display_recipes[1].recipe" :use_plural="use_plural"></recipe-card> <recipe-card :recipe="display_recipes[1].recipe_content" v-if="current_page > 1 && display_recipes.length === 2" :key="display_recipes[1].recipe" ></recipe-card>
</transition> </transition>
</div> </div>
<div class="col-md-1" @click="swipeLeft" style="cursor: pointer"></div> <div class="col-md-1" @click="swipeLeft" style="cursor: pointer"></div>
@ -57,10 +57,7 @@ export default {
} }
}, },
mounted(){ mounted(){
let apiClient = new ApiApiFactory()
apiClient.retrieveSpace(window.ACTIVE_SPACE_ID).then(r => {
this.use_plural = r.data.use_plural
})
}, },
data() { data() {
return { return {
@ -69,7 +66,6 @@ export default {
bounce_left: false, bounce_left: false,
bounce_right: false, bounce_right: false,
cookbook_editing: false, cookbook_editing: false,
use_plural: false,
} }
}, },
methods: { methods: {

View File

@ -76,8 +76,7 @@
<div class="col-md-10 offset-md-2"> <div class="col-md-10 offset-md-2">
<generic-horizontal-card v-for="child in item[children]" <generic-horizontal-card v-for="child in item[children]"
v-bind:key="child.id" v-bind:key="child.id"
:item="child" :model="model" :item="child" :model="model"
:use_plural="use_plural"
@item-action="$emit('item-action', $event)"></generic-horizontal-card> @item-action="$emit('item-action', $event)"></generic-horizontal-card>
</div> </div>
</div> </div>
@ -160,7 +159,6 @@ export default {
recipe_count: { type: String, default: "numrecipe" }, recipe_count: { type: String, default: "numrecipe" },
recipes: { type: String, default: "recipes" }, recipes: { type: String, default: "recipes" },
show_context_menu: { type: Boolean, default: true }, show_context_menu: { type: Boolean, default: true },
use_plural: { type: Boolean, default: false},
}, },
data() { data() {
return { return {

View File

@ -24,7 +24,6 @@
<ingredient-component <ingredient-component
:ingredient="i" :ingredient="i"
:ingredient_factor="ingredient_factor" :ingredient_factor="ingredient_factor"
:use_plural="use_plural"
:key="i.id" :key="i.id"
:detailed="detailed" :detailed="detailed"
@checked-state-changed="$emit('checked-state-changed', $event)" @checked-state-changed="$emit('checked-state-changed', $event)"
@ -64,7 +63,6 @@ export default {
recipe: {type: Number}, recipe: {type: Number},
ingredient_factor: {type: Number, default: 1}, ingredient_factor: {type: Number, default: 1},
servings: {type: Number, default: 1}, servings: {type: Number, default: 1},
use_plural: {type: Boolean, default: false},
detailed: {type: Boolean, default: true}, detailed: {type: Boolean, default: true},
header: {type: Boolean, default: false}, header: {type: Boolean, default: false},
recipe_list: {type: Number, default: undefined}, recipe_list: {type: Number, default: undefined},

View File

@ -34,7 +34,6 @@
<div v-for="i in r.steps.flatMap((s) => s.ingredients)" v-bind:key="i.id"> <div v-for="i in r.steps.flatMap((s) => s.ingredients)" v-bind:key="i.id">
<table class="table table-sm mb-0"> <table class="table table-sm mb-0">
<ingredient-component <ingredient-component
:use_plural="true"
:key="i.id" :key="i.id"
:detailed="true" :detailed="true"
:ingredient="i" :ingredient="i"

View File

@ -145,7 +145,6 @@ export default {
props: { props: {
recipe: Object, recipe: Object,
meal_plan: Object, meal_plan: Object,
use_plural: { type: Boolean, default: false },
footer_text: String, footer_text: String,
footer_icon: String, footer_icon: String,
detailed: { type: Boolean, default: true }, detailed: { type: Boolean, default: true },

View File

@ -90,7 +90,6 @@
:index="index" :index="index"
:start_time="start_time" :start_time="start_time"
:force_ingredients="true" :force_ingredients="true"
:use_plural="use_plural"
></step-component> ></step-component>
</div> </div>
</div> </div>
@ -147,10 +146,6 @@ export default {
type: Boolean, type: Boolean,
default: false, default: false,
}, },
use_plural: {
type: Boolean,
default: false,
},
}, },
computed: { computed: {
step_time: function() { step_time: function() {