lots of small fixes
This commit is contained in:
@ -1,6 +1,3 @@
|
||||
"""
|
||||
Source: https://djangosnippets.org/snippets/1703/
|
||||
"""
|
||||
from django.conf import settings
|
||||
from django.contrib import messages
|
||||
from django.contrib.auth.decorators import user_passes_test
|
||||
@ -12,7 +9,7 @@ from django.utils.translation import gettext as _
|
||||
from rest_framework import permissions
|
||||
from rest_framework.permissions import SAFE_METHODS
|
||||
|
||||
from cookbook.models import ShareLink
|
||||
from cookbook.models import ShareLink, Recipe, UserPreference
|
||||
|
||||
|
||||
def get_allowed_groups(groups_required):
|
||||
@ -262,3 +259,38 @@ class CustomIsShare(permissions.BasePermission):
|
||||
if share:
|
||||
return share_link_valid(obj, share)
|
||||
return False
|
||||
|
||||
|
||||
def above_space_limit(space): # TODO add file storage limit
|
||||
"""
|
||||
Test if the space has reached any limit (e.g. max recipes, users, ..)
|
||||
:param space: Space to test for limits
|
||||
:return: Tuple (True if above or equal any limit else false, message)
|
||||
"""
|
||||
r_limit, r_msg = above_space_recipe_limit(space)
|
||||
u_limit, u_msg = above_space_user_limit(space)
|
||||
return r_limit or u_limit, (r_msg + ' ' + u_msg).strip()
|
||||
|
||||
|
||||
def above_space_recipe_limit(space):
|
||||
"""
|
||||
Test if a space has reached its recipe limit
|
||||
:param space: Space to test for limits
|
||||
:return: Tuple (True if above or equal limit else false, message)
|
||||
"""
|
||||
limit = space.max_recipes != 0 and Recipe.objects.filter(space=space).count() >= space.max_recipes
|
||||
if limit:
|
||||
return True, _('You have reached the maximum number of recipes for your space.')
|
||||
return False, ''
|
||||
|
||||
|
||||
def above_space_user_limit(space):
|
||||
"""
|
||||
Test if a space has reached its user limit
|
||||
:param space: Space to test for limits
|
||||
:return: Tuple (True if above or equal limit else false, message)
|
||||
"""
|
||||
limit = space.max_users != 0 and UserPreference.objects.filter(space=space).count() > space.max_users
|
||||
if limit:
|
||||
return True, _('You have more users than allowed in your space.')
|
||||
return False, ''
|
||||
|
@ -113,6 +113,14 @@ def get_from_scraper(scrape, request):
|
||||
keywords += listify_keywords(scrape.schema.data.get("recipeCuisine"))
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
if source_url := scrape.canonical_url():
|
||||
recipe_json['source_url'] = source_url
|
||||
try:
|
||||
keywords.append(source_url.replace('http://', '').replace('https://', '').split('/')[0])
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
try:
|
||||
recipe_json['keywords'] = parse_keywords(list(set(map(str.casefold, keywords))), request.space)
|
||||
except AttributeError:
|
||||
@ -136,19 +144,18 @@ def get_from_scraper(scrape, request):
|
||||
for x in scrape.ingredients():
|
||||
try:
|
||||
amount, unit, ingredient, note = ingredient_parser.parse(x)
|
||||
recipe_json['steps'][0]['ingredients'].append(
|
||||
{
|
||||
ingredient = {
|
||||
'amount': amount,
|
||||
'unit': {
|
||||
'name': unit,
|
||||
},
|
||||
'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(
|
||||
{
|
||||
@ -164,8 +171,6 @@ def get_from_scraper(scrape, request):
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
if scrape.canonical_url():
|
||||
recipe_json['source_url'] = scrape.canonical_url()
|
||||
return recipe_json
|
||||
|
||||
|
||||
|
@ -12,6 +12,7 @@ from rest_framework.exceptions import NotFound, ValidationError
|
||||
|
||||
from cookbook.helper.CustomStorageClass import CachedS3Boto3Storage
|
||||
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,
|
||||
@ -649,6 +650,12 @@ class RecipeSerializer(RecipeBaseSerializer):
|
||||
)
|
||||
read_only_fields = ['image', 'created_by', 'created_at']
|
||||
|
||||
def validate(self, data):
|
||||
above_limit, msg = above_space_limit(self.context['request'].space)
|
||||
if above_limit:
|
||||
raise serializers.ValidationError(msg)
|
||||
return super().validate(data)
|
||||
|
||||
def create(self, validated_data):
|
||||
validated_data['created_by'] = self.context['request'].user
|
||||
validated_data['space'] = self.context['request'].space
|
||||
@ -971,12 +978,19 @@ class AutomationSerializer(serializers.ModelSerializer):
|
||||
# CORS, REST and Scopes aren't currently working
|
||||
# Scopes are evaluating before REST has authenticated the user assigning a None space
|
||||
# I've made the change below to fix the bookmarklet, other serializers likely need a similar/better fix
|
||||
class BookmarkletImportSerializer(serializers.ModelSerializer):
|
||||
class BookmarkletImportListSerializer(serializers.ModelSerializer):
|
||||
def create(self, validated_data):
|
||||
validated_data['created_by'] = self.context['request'].user
|
||||
validated_data['space'] = self.context['request'].user.userpreference.space
|
||||
return super().create(validated_data)
|
||||
|
||||
class Meta:
|
||||
model = BookmarkletImport
|
||||
fields = ('id', 'url', 'created_by', 'created_at')
|
||||
read_only_fields = ('created_by', 'space')
|
||||
|
||||
|
||||
class BookmarkletImportSerializer(BookmarkletImportListSerializer):
|
||||
class Meta:
|
||||
model = BookmarkletImport
|
||||
fields = ('id', 'url', 'html', 'created_by', 'created_at')
|
||||
|
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 it is too large
Load Diff
@ -70,7 +70,7 @@ from cookbook.serializer import (AutomationSerializer, BookmarkletImportSerializ
|
||||
SupermarketCategorySerializer, SupermarketSerializer,
|
||||
SyncLogSerializer, SyncSerializer, UnitSerializer,
|
||||
UserFileSerializer, UserNameSerializer, UserPreferenceSerializer,
|
||||
ViewLogSerializer, IngredientSimpleSerializer)
|
||||
ViewLogSerializer, IngredientSimpleSerializer, BookmarkletImportListSerializer)
|
||||
from recipes import settings
|
||||
|
||||
|
||||
@ -974,6 +974,11 @@ class BookmarkletImportViewSet(viewsets.ModelViewSet):
|
||||
serializer_class = BookmarkletImportSerializer
|
||||
permission_classes = [CustomIsUser]
|
||||
|
||||
def get_serializer_class(self):
|
||||
if self.action == 'list':
|
||||
return BookmarkletImportListSerializer
|
||||
return self.serializer_class
|
||||
|
||||
def get_queryset(self):
|
||||
return self.queryset.filter(space=self.request.space).all()
|
||||
|
||||
|
@ -10,7 +10,7 @@ from django_tables2 import RequestConfig
|
||||
from rest_framework.authtoken.models import Token
|
||||
|
||||
from cookbook.forms import BatchEditForm, SyncForm
|
||||
from cookbook.helper.permission_helper import group_required, has_group_permission
|
||||
from cookbook.helper.permission_helper import group_required, has_group_permission, above_space_limit
|
||||
from cookbook.models import (Comment, Food, Keyword, Recipe, RecipeImport, Sync,
|
||||
Unit, UserPreference, BookmarkletImport)
|
||||
from cookbook.tables import SyncTable
|
||||
@ -19,12 +19,9 @@ from recipes import settings
|
||||
|
||||
@group_required('user')
|
||||
def sync(request):
|
||||
if request.space.max_recipes != 0 and Recipe.objects.filter(space=request.space).count() >= request.space.max_recipes: # TODO move to central helper function
|
||||
messages.add_message(request, messages.WARNING, _('You have reached the maximum number of recipes for your space.'))
|
||||
return HttpResponseRedirect(reverse('index'))
|
||||
|
||||
if request.space.max_users != 0 and UserPreference.objects.filter(space=request.space).count() > request.space.max_users:
|
||||
messages.add_message(request, messages.WARNING, _('You have more users than allowed in your space.'))
|
||||
limit, msg = above_space_limit(request.space)
|
||||
if limit:
|
||||
messages.add_message(request, messages.WARNING, msg)
|
||||
return HttpResponseRedirect(reverse('index'))
|
||||
|
||||
if request.space.demo or settings.HOSTED:
|
||||
@ -113,12 +110,9 @@ def batch_edit(request):
|
||||
|
||||
@group_required('user')
|
||||
def import_url(request):
|
||||
if request.space.max_recipes != 0 and Recipe.objects.filter(space=request.space).count() >= request.space.max_recipes: # TODO move to central helper function
|
||||
messages.add_message(request, messages.WARNING, _('You have reached the maximum number of recipes for your space.'))
|
||||
return HttpResponseRedirect(reverse('index'))
|
||||
|
||||
if request.space.max_users != 0 and UserPreference.objects.filter(space=request.space).count() > request.space.max_users:
|
||||
messages.add_message(request, messages.WARNING, _('You have more users than allowed in your space.'))
|
||||
limit, msg = above_space_limit(request.space)
|
||||
if limit:
|
||||
messages.add_message(request, messages.WARNING, msg)
|
||||
return HttpResponseRedirect(reverse('index'))
|
||||
|
||||
if (api_token := Token.objects.filter(user=request.user).first()) is None:
|
||||
@ -129,7 +123,7 @@ def import_url(request):
|
||||
if bookmarklet_import := BookmarkletImport.objects.filter(id=request.GET['id']).first():
|
||||
bookmarklet_import_id = bookmarklet_import.pk
|
||||
|
||||
return render(request, 'test.html', {'api_token': api_token, 'bookmarklet_import_id': bookmarklet_import_id})
|
||||
return render(request, 'url_import.html', {'api_token': api_token, 'bookmarklet_import_id': bookmarklet_import_id})
|
||||
|
||||
|
||||
class Object(object):
|
||||
|
@ -9,7 +9,7 @@ from django.views.generic import UpdateView
|
||||
from django.views.generic.edit import FormMixin
|
||||
|
||||
from cookbook.forms import CommentForm, ExternalRecipeForm, MealPlanForm, StorageForm, SyncForm
|
||||
from cookbook.helper.permission_helper import GroupRequiredMixin, OwnerRequiredMixin, group_required
|
||||
from cookbook.helper.permission_helper import GroupRequiredMixin, OwnerRequiredMixin, group_required, above_space_limit
|
||||
from cookbook.models import (Comment, MealPlan, MealType, Recipe, RecipeImport, Storage, Sync,
|
||||
UserPreference)
|
||||
from cookbook.provider.dropbox import Dropbox
|
||||
@ -39,12 +39,9 @@ def convert_recipe(request, pk):
|
||||
|
||||
@group_required('user')
|
||||
def internal_recipe_update(request, pk):
|
||||
if request.space.max_recipes != 0 and Recipe.objects.filter(space=request.space).count() > request.space.max_recipes: # TODO move to central helper function
|
||||
messages.add_message(request, messages.WARNING, _('You have reached the maximum number of recipes for your space.'))
|
||||
return HttpResponseRedirect(reverse('view_recipe', args=[pk]))
|
||||
|
||||
if request.space.max_users != 0 and UserPreference.objects.filter(space=request.space).count() > request.space.max_users:
|
||||
messages.add_message(request, messages.WARNING, _('You have more users than allowed in your space.'))
|
||||
limit, msg = above_space_limit(request.space)
|
||||
if limit:
|
||||
messages.add_message(request, messages.WARNING, msg)
|
||||
return HttpResponseRedirect(reverse('view_recipe', args=[pk]))
|
||||
|
||||
recipe_instance = get_object_or_404(Recipe, pk=pk, space=request.space)
|
||||
|
@ -10,7 +10,7 @@ from django.urls import reverse
|
||||
from django.utils.translation import gettext as _
|
||||
|
||||
from cookbook.forms import ExportForm, ImportExportBase, ImportForm
|
||||
from cookbook.helper.permission_helper import group_required
|
||||
from cookbook.helper.permission_helper import group_required, above_space_limit
|
||||
from cookbook.helper.recipe_search import RecipeSearch
|
||||
from cookbook.integration.cheftap import ChefTap
|
||||
from cookbook.integration.chowdown import Chowdown
|
||||
@ -84,12 +84,9 @@ def get_integration(request, export_type):
|
||||
|
||||
@group_required('user')
|
||||
def import_recipe(request):
|
||||
if request.space.max_recipes != 0 and Recipe.objects.filter(space=request.space).count() >= request.space.max_recipes: # TODO move to central helper function
|
||||
messages.add_message(request, messages.WARNING, _('You have reached the maximum number of recipes for your space.'))
|
||||
return HttpResponseRedirect(reverse('index'))
|
||||
|
||||
if request.space.max_users != 0 and UserPreference.objects.filter(space=request.space).count() > request.space.max_users:
|
||||
messages.add_message(request, messages.WARNING, _('You have more users than allowed in your space.'))
|
||||
limit, msg = above_space_limit(request.space)
|
||||
if limit:
|
||||
messages.add_message(request, messages.WARNING, msg)
|
||||
return HttpResponseRedirect(reverse('index'))
|
||||
|
||||
if request.method == "POST":
|
||||
|
@ -106,10 +106,12 @@
|
||||
|
||||
<div class="row">
|
||||
<div class="col-12 col-md-8 offset-0 offset-md-2">
|
||||
<h4 class="text-center flex-grow-1" v-b-tooltip.hover.bottom :title="$t('Click_To_Edit')" v-if="!edit_name"
|
||||
<h4 class="text-center flex-grow-1" v-b-tooltip.hover.bottom
|
||||
:title="$t('Click_To_Edit')" v-if="!edit_name"
|
||||
@click="edit_name = true">{{
|
||||
recipe_json.name
|
||||
}} <span class="text-primary"><i class="fa fa-edit"></i> </span> </h4>
|
||||
}} <span class="text-primary"><i class="fa fa-edit"></i> </span>
|
||||
</h4>
|
||||
<b-input-group v-if="edit_name" class="mb-2">
|
||||
<b-input
|
||||
class="form-control form-control-borderless form-control-search"
|
||||
@ -235,6 +237,11 @@
|
||||
{{ $t('import_duplicates') }}
|
||||
</b-form-checkbox>
|
||||
|
||||
<a href="recipe_app_info.help_url"
|
||||
v-if="recipe_app_info !== undefined && recipe_app_info.help_url !== ''">{{
|
||||
$t('Help')
|
||||
}}</a>
|
||||
|
||||
<b-form-file
|
||||
class="my-2"
|
||||
multiple
|
||||
@ -312,6 +319,11 @@ export default {
|
||||
RecipeCard,
|
||||
ImportViewStepEditor
|
||||
},
|
||||
computed: {
|
||||
recipe_app_info: function () {
|
||||
return this.INTEGRATIONS.filter(x => x.id === this.recipe_app)[0]
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
tab_index: 0,
|
||||
@ -343,7 +355,8 @@ export default {
|
||||
recipe_files: [],
|
||||
loading: false,
|
||||
empty_input: false,
|
||||
edit_name: false,// Bookmarklet
|
||||
edit_name: false,
|
||||
// Bookmarklet
|
||||
BOOKMARKLET_CODE: window.BOOKMARKLET_CODE
|
||||
}
|
||||
},
|
||||
@ -422,7 +435,7 @@ export default {
|
||||
window.localStorage.setItem(this.LS_IMPORT_RECENT, JSON.stringify(this.recent_urls))
|
||||
}
|
||||
|
||||
if (this.website_url === '') {
|
||||
if (this.website_url === '' && bookmarklet === undefined) {
|
||||
this.empty_input = true
|
||||
setTimeout(() => {
|
||||
this.empty_input = false
|
||||
@ -541,7 +554,7 @@ export default {
|
||||
`localStorage.setItem("token", "${window.API_TOKEN}");` +
|
||||
`document.body.appendChild(document.createElement("script")).src="${localStorage.getItem('BASE_PATH')}${resolveDjangoStatic('/js/bookmarklet.js')}?r="+Math.floor(Math.random()*999999999)}` +
|
||||
`})()`
|
||||
}
|
||||
},
|
||||
},
|
||||
directives: {
|
||||
hover: {
|
||||
|
@ -1,24 +1,24 @@
|
||||
// containing all data and functions regarding the different integrations
|
||||
|
||||
export const INTEGRATIONS = [
|
||||
{id: 'DEFAULT', name: "Tandoor", import: true, export: true},
|
||||
{id: 'CHEFTAP', name: "Cheftap", import: true, export: false},
|
||||
{id: 'CHOWDOWN', name: "Chowdown", import: true, export: false},
|
||||
{id: 'COOKBOOKAPP', name: "CookBookApp", import: true, export: false},
|
||||
{id: 'COOKMATE', name: "Cookmate", import: true, export: false},
|
||||
{id: 'COPYMETHAT', name: "CopyMeThat", import: true, export: false},
|
||||
{id: 'DOMESTICA', name: "Domestica", import: true, export: false},
|
||||
{id: 'MEALIE', name: "Mealie", import: true, export: false},
|
||||
{id: 'MEALMASTER', name: "Mealmaster", import: true, export: false},
|
||||
{id: 'MELARECIPES', name: "Melarecipes", import: true, export: false},
|
||||
{id: 'NEXTCLOUD', name: "Nextcloud Cookbook", import: true, export: false},
|
||||
{id: 'OPENEATS', name: "Openeats", import: true, export: false},
|
||||
{id: 'PAPRIKA', name: "Paprika", import: true, export: false},
|
||||
{id: 'PEPPERPLATE', name: "Pepperplate", import: true, export: false},
|
||||
{id: 'PLANTOEAT', name: "Plantoeat", import: true, export: false},
|
||||
{id: 'RECETTETEK', name: "RecetteTek", import: true, export: false},
|
||||
{id: 'RECIPEKEEPER', name: "Recipekeeper", import: true, export: false},
|
||||
{id: 'RECIPESAGE', name: "Recipesage", import: true, export: true},
|
||||
{id: 'REZKONV', name: "Rezkonv", import: true, export: false},
|
||||
{id: 'SAFRON', name: "Safron", import: true, export: true},
|
||||
{id: 'DEFAULT', name: "Tandoor", import: true, export: true, help_url: 'https://docs.tandoor.dev/features/import_export/#default'},
|
||||
{id: 'CHEFTAP', name: "Cheftap", import: true, export: false, help_url: 'https://docs.tandoor.dev/features/import_export/#cheftap'},
|
||||
{id: 'CHOWDOWN', name: "Chowdown", import: true, export: false, help_url: 'https://docs.tandoor.dev/features/import_export/#chowdown'},
|
||||
{id: 'COOKBOOKAPP', name: "CookBookApp", import: true, export: false, help_url: 'https://docs.tandoor.dev/features/import_export/#cookbookapp'},
|
||||
{id: 'COOKMATE', name: "Cookmate", import: true, export: false, help_url: ''},
|
||||
{id: 'COPYMETHAT', name: "CopyMeThat", import: true, export: false, help_url: 'https://docs.tandoor.dev/features/import_export/#copymethat'},
|
||||
{id: 'DOMESTICA', name: "Domestica", import: true, export: false, help_url: 'https://docs.tandoor.dev/features/import_export/#domestica'},
|
||||
{id: 'MEALIE', name: "Mealie", import: true, export: false, help_url: 'https://docs.tandoor.dev/features/import_export/#mealie'},
|
||||
{id: 'MEALMASTER', name: "Mealmaster", import: true, export: false, help_url: 'https://docs.tandoor.dev/features/import_export/#mealmaster'},
|
||||
{id: 'MELARECIPES', name: "Melarecipes", import: true, export: false, help_url: ''},
|
||||
{id: 'NEXTCLOUD', name: "Nextcloud Cookbook", import: true, export: false, help_url: 'https://docs.tandoor.dev/features/import_export/#nextcloud'},
|
||||
{id: 'OPENEATS', name: "Openeats", import: true, export: false, help_url: 'https://docs.tandoor.dev/features/import_export/#openeats'},
|
||||
{id: 'PAPRIKA', name: "Paprika", import: true, export: false, help_url: 'https://docs.tandoor.dev/features/import_export/#paprika'},
|
||||
{id: 'PEPPERPLATE', name: "Pepperplate", import: true, export: false, help_url: 'https://docs.tandoor.dev/features/import_export/#pepperplate'},
|
||||
{id: 'PLANTOEAT', name: "Plantoeat", import: true, export: false, help_url: 'https://docs.tandoor.dev/features/import_export/#plantoeat'},
|
||||
{id: 'RECETTETEK', name: "RecetteTek", import: true, export: false, help_url: ''},
|
||||
{id: 'RECIPEKEEPER', name: "Recipekeeper", import: true, export: false, help_url: 'https://docs.tandoor.dev/features/import_export/#recipekeeper'},
|
||||
{id: 'RECIPESAGE', name: "Recipesage", import: true, export: true, help_url: 'https://docs.tandoor.dev/features/import_export/#recipesage'},
|
||||
{id: 'REZKONV', name: "Rezkonv", import: true, export: false, help_url: 'https://docs.tandoor.dev/features/import_export/#rezkonv'},
|
||||
{id: 'SAFRON', name: "Safron", import: true, export: true, help_url: 'https://docs.tandoor.dev/features/import_export/#safron'},
|
||||
]
|
||||
|
Reference in New Issue
Block a user