mirror of
https://github.com/TandoorRecipes/recipes.git
synced 2026-01-03 13:19:16 -05:00
big cleanup
This commit is contained in:
@@ -1,21 +1,10 @@
|
||||
import cookbook.views.api
|
||||
import cookbook.views.data
|
||||
import cookbook.views.delete
|
||||
import cookbook.views.edit
|
||||
import cookbook.views.import_export
|
||||
import cookbook.views.lists
|
||||
import cookbook.views.new
|
||||
|
||||
import cookbook.views.views
|
||||
import cookbook.views.telegram
|
||||
|
||||
__all__ = [
|
||||
'api',
|
||||
'data',
|
||||
'delete',
|
||||
'edit',
|
||||
'import_export',
|
||||
'lists',
|
||||
'new',
|
||||
'views',
|
||||
'telegram',
|
||||
]
|
||||
|
||||
@@ -1,136 +0,0 @@
|
||||
import uuid
|
||||
from datetime import datetime
|
||||
|
||||
from django.contrib import messages
|
||||
from django.http import HttpResponseRedirect
|
||||
from django.shortcuts import redirect, render
|
||||
from django.urls import reverse
|
||||
from django.utils import timezone
|
||||
from django.utils.translation import gettext as _
|
||||
from django.utils.translation import ngettext
|
||||
from django_tables2 import RequestConfig
|
||||
from oauth2_provider.models import AccessToken
|
||||
|
||||
from cookbook.forms import BatchEditForm, SyncForm
|
||||
from cookbook.helper.permission_helper import (above_space_limit, group_required,
|
||||
has_group_permission)
|
||||
from cookbook.models import BookmarkletImport, Recipe, RecipeImport, Sync
|
||||
from cookbook.tables import SyncTable
|
||||
from recipes import settings
|
||||
|
||||
|
||||
@group_required('user')
|
||||
def sync(request):
|
||||
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:
|
||||
messages.add_message(request, messages.ERROR, _('This feature is not yet available in the hosted version of tandoor!'))
|
||||
return redirect('index')
|
||||
|
||||
if request.method == "POST":
|
||||
if not has_group_permission(request.user, ['admin']):
|
||||
messages.add_message(request, messages.ERROR, _('You do not have the required permissions to view this page!'))
|
||||
return HttpResponseRedirect(reverse('data_sync'))
|
||||
form = SyncForm(request.POST, space=request.space)
|
||||
if form.is_valid():
|
||||
new_path = Sync()
|
||||
new_path.path = form.cleaned_data['path']
|
||||
new_path.storage = form.cleaned_data['storage']
|
||||
new_path.last_checked = datetime.now()
|
||||
new_path.space = request.space
|
||||
new_path.save()
|
||||
return redirect('data_sync')
|
||||
else:
|
||||
form = SyncForm(space=request.space)
|
||||
|
||||
monitored_paths = SyncTable(Sync.objects.filter(space=request.space).all())
|
||||
RequestConfig(request, paginate={'per_page': 25}).configure(monitored_paths)
|
||||
|
||||
return render(request, 'batch/monitor.html', {'form': form, 'monitored_paths': monitored_paths})
|
||||
|
||||
|
||||
@group_required('user')
|
||||
def sync_wait(request):
|
||||
return render(request, 'batch/waiting.html')
|
||||
|
||||
|
||||
@group_required('user')
|
||||
def batch_import(request):
|
||||
imports = RecipeImport.objects.filter(space=request.space).all()
|
||||
for new_recipe in imports:
|
||||
recipe = Recipe(
|
||||
name=new_recipe.name,
|
||||
file_path=new_recipe.file_path,
|
||||
storage=new_recipe.storage,
|
||||
file_uid=new_recipe.file_uid,
|
||||
created_by=request.user,
|
||||
space=request.space
|
||||
)
|
||||
recipe.save()
|
||||
new_recipe.delete()
|
||||
|
||||
return redirect('list_recipe_import')
|
||||
|
||||
|
||||
@group_required('user')
|
||||
def batch_edit(request):
|
||||
if request.method == "POST":
|
||||
form = BatchEditForm(request.POST, space=request.space)
|
||||
if form.is_valid():
|
||||
word = form.cleaned_data['search']
|
||||
keywords = form.cleaned_data['keywords']
|
||||
|
||||
recipes = Recipe.objects.filter(name__icontains=word, space=request.space)
|
||||
count = 0
|
||||
for recipe in recipes:
|
||||
edit = False
|
||||
if keywords.__sizeof__() > 0:
|
||||
recipe.keywords.add(*list(keywords))
|
||||
edit = True
|
||||
if edit:
|
||||
count = count + 1
|
||||
|
||||
recipe.save()
|
||||
|
||||
msg = ngettext(
|
||||
'Batch edit done. %(count)d recipe was updated.',
|
||||
'Batch edit done. %(count)d Recipes where updated.',
|
||||
count) % {
|
||||
'count': count,
|
||||
}
|
||||
messages.add_message(request, messages.SUCCESS, msg)
|
||||
|
||||
return redirect('data_batch_edit')
|
||||
else:
|
||||
form = BatchEditForm(space=request.space)
|
||||
|
||||
return render(request, 'batch/edit.html', {'form': form})
|
||||
|
||||
|
||||
@group_required('user')
|
||||
def import_url(request):
|
||||
limit, msg = above_space_limit(request.space)
|
||||
if limit:
|
||||
messages.add_message(request, messages.WARNING, msg)
|
||||
return HttpResponseRedirect(reverse('index'))
|
||||
|
||||
if (api_token := AccessToken.objects.filter(user=request.user, scope='bookmarklet').first()) is None:
|
||||
api_token = AccessToken.objects.create(
|
||||
user=request.user,
|
||||
scope='bookmarklet',
|
||||
expires=(
|
||||
timezone.now() +
|
||||
timezone.timedelta(
|
||||
days=365 *
|
||||
10)),
|
||||
token=f'tda_{str(uuid.uuid4()).replace("-","_")}')
|
||||
|
||||
bookmarklet_import_id = -1
|
||||
if 'id' in request.GET:
|
||||
if bookmarklet_import := BookmarkletImport.objects.filter(id=request.GET['id']).first():
|
||||
bookmarklet_import_id = bookmarklet_import.pk
|
||||
|
||||
return render(request, 'url_import.html', {'api_token': api_token, 'bookmarklet_import_id': bookmarklet_import_id})
|
||||
@@ -1,185 +0,0 @@
|
||||
from django.contrib import messages
|
||||
from django.db import models
|
||||
from django.db.models import ProtectedError
|
||||
from django.http import HttpResponseRedirect
|
||||
from django.shortcuts import get_object_or_404, render
|
||||
from django.urls import reverse, reverse_lazy
|
||||
from django.utils.translation import gettext as _
|
||||
from django.views.generic import DeleteView
|
||||
|
||||
from cookbook.helper.permission_helper import GroupRequiredMixin, OwnerRequiredMixin, group_required
|
||||
|
||||
from cookbook.models import Comment, InviteLink, Recipe, RecipeImport, Space, Storage, Sync, UserSpace
|
||||
|
||||
from cookbook.models import (Comment, InviteLink, MealPlan, Recipe, RecipeBook, RecipeBookEntry,
|
||||
RecipeImport, Space, Storage, Sync, UserSpace, ConnectorConfig)
|
||||
|
||||
from cookbook.provider.dropbox import Dropbox
|
||||
from cookbook.provider.local import Local
|
||||
from cookbook.provider.nextcloud import Nextcloud
|
||||
|
||||
|
||||
class RecipeDelete(GroupRequiredMixin, DeleteView):
|
||||
groups_required = ['user']
|
||||
template_name = "generic/delete_template.html"
|
||||
model = Recipe
|
||||
success_url = reverse_lazy('index')
|
||||
|
||||
def delete(self, request, *args, **kwargs):
|
||||
self.object = self.get_object()
|
||||
# TODO make this more generic so that all delete functions benefit from this
|
||||
if self.get_context_data()['protected_objects']:
|
||||
return render(request, template_name=self.template_name, context=self.get_context_data())
|
||||
|
||||
success_url = self.get_success_url()
|
||||
self.object.delete()
|
||||
return HttpResponseRedirect(success_url)
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super(RecipeDelete, self).get_context_data(**kwargs)
|
||||
context['title'] = _("Recipe")
|
||||
|
||||
# TODO make this more generic so that all delete functions benefit from this
|
||||
self.object = self.get_object()
|
||||
context['protected_objects'] = []
|
||||
context['cascading_objects'] = []
|
||||
context['set_null_objects'] = []
|
||||
for x in self.object._meta.get_fields():
|
||||
try:
|
||||
related = x.related_model.objects.filter(**{x.field.name: self.object})
|
||||
if related.exists() and x.on_delete == models.PROTECT:
|
||||
context['protected_objects'].append(related)
|
||||
if related.exists() and x.on_delete == models.CASCADE:
|
||||
context['cascading_objects'].append(related)
|
||||
if related.exists() and x.on_delete == models.SET_NULL:
|
||||
context['set_null_objects'].append(related)
|
||||
except AttributeError:
|
||||
pass
|
||||
|
||||
return context
|
||||
|
||||
|
||||
@group_required('user')
|
||||
def delete_recipe_source(request, pk):
|
||||
recipe = get_object_or_404(Recipe, pk=pk, space=request.space)
|
||||
|
||||
if recipe.storage.method == Storage.DROPBOX:
|
||||
# TODO central location to handle storage type switches
|
||||
Dropbox.delete_file(recipe)
|
||||
if recipe.storage.method == Storage.NEXTCLOUD:
|
||||
Nextcloud.delete_file(recipe)
|
||||
if recipe.storage.method == Storage.LOCAL:
|
||||
Local.delete_file(recipe)
|
||||
|
||||
recipe.storage = None
|
||||
recipe.file_path = ''
|
||||
recipe.file_uid = ''
|
||||
recipe.save()
|
||||
|
||||
return HttpResponseRedirect(reverse('edit_recipe', args=[recipe.pk]))
|
||||
|
||||
|
||||
class RecipeImportDelete(GroupRequiredMixin, DeleteView):
|
||||
groups_required = ['user']
|
||||
template_name = "generic/delete_template.html"
|
||||
model = RecipeImport
|
||||
success_url = reverse_lazy('list_recipe_import')
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super(RecipeImportDelete, self).get_context_data(**kwargs)
|
||||
context['title'] = _("Import")
|
||||
return context
|
||||
|
||||
|
||||
class SyncDelete(GroupRequiredMixin, DeleteView):
|
||||
groups_required = ['admin']
|
||||
template_name = "generic/delete_template.html"
|
||||
model = Sync
|
||||
success_url = reverse_lazy('data_sync')
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super(SyncDelete, self).get_context_data(**kwargs)
|
||||
context['title'] = _("Monitor")
|
||||
return context
|
||||
|
||||
|
||||
class StorageDelete(GroupRequiredMixin, DeleteView):
|
||||
groups_required = ['admin']
|
||||
template_name = "generic/delete_template.html"
|
||||
model = Storage
|
||||
success_url = reverse_lazy('list_storage')
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super(StorageDelete, self).get_context_data(**kwargs)
|
||||
context['title'] = _("Storage Backend")
|
||||
return context
|
||||
|
||||
def post(self, request, *args, **kwargs):
|
||||
try:
|
||||
return self.delete(request, *args, **kwargs)
|
||||
except ProtectedError:
|
||||
messages.add_message(request, messages.WARNING,
|
||||
_('Could not delete this storage backend as it is used in at least one monitor.') # noqa: E501
|
||||
)
|
||||
return HttpResponseRedirect(reverse('list_storage'))
|
||||
|
||||
|
||||
class ConnectorConfigDelete(GroupRequiredMixin, DeleteView):
|
||||
groups_required = ['admin']
|
||||
template_name = "generic/delete_template.html"
|
||||
model = ConnectorConfig
|
||||
success_url = reverse_lazy('list_connector_config')
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super().get_context_data(**kwargs)
|
||||
context['title'] = _("Connectors Config Backend")
|
||||
return context
|
||||
|
||||
|
||||
class CommentDelete(OwnerRequiredMixin, DeleteView):
|
||||
template_name = "generic/delete_template.html"
|
||||
model = Comment
|
||||
success_url = reverse_lazy('index')
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super(CommentDelete, self).get_context_data(**kwargs)
|
||||
context['title'] = _("Comment")
|
||||
return context
|
||||
|
||||
|
||||
class InviteLinkDelete(OwnerRequiredMixin, DeleteView):
|
||||
template_name = "generic/delete_template.html"
|
||||
model = InviteLink
|
||||
success_url = reverse_lazy('list_invite_link')
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super(InviteLinkDelete, self).get_context_data(**kwargs)
|
||||
context['title'] = _("Invite Link")
|
||||
return context
|
||||
|
||||
|
||||
class UserSpaceDelete(OwnerRequiredMixin, DeleteView):
|
||||
template_name = "generic/delete_template.html"
|
||||
model = UserSpace
|
||||
success_url = reverse_lazy('view_space_overview')
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super(UserSpaceDelete, self).get_context_data(**kwargs)
|
||||
context['title'] = _("Space Membership")
|
||||
return context
|
||||
|
||||
|
||||
class SpaceDelete(OwnerRequiredMixin, DeleteView):
|
||||
template_name = "generic/delete_template.html"
|
||||
model = Space
|
||||
success_url = reverse_lazy('view_space_overview')
|
||||
|
||||
def delete(self, request, *args, **kwargs):
|
||||
self.object = self.get_object()
|
||||
self.object.safe_delete()
|
||||
return HttpResponseRedirect(self.get_success_url())
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super(SpaceDelete, self).get_context_data(**kwargs)
|
||||
context['title'] = _("Space")
|
||||
return context
|
||||
@@ -1,214 +0,0 @@
|
||||
import copy
|
||||
import os
|
||||
|
||||
from django.contrib import messages
|
||||
from django.http import HttpResponseRedirect
|
||||
from django.shortcuts import get_object_or_404, redirect, render
|
||||
from django.urls import reverse
|
||||
from django.utils.translation import gettext as _
|
||||
from django.views.generic import UpdateView
|
||||
from django.views.generic.edit import FormMixin
|
||||
|
||||
from cookbook.forms import CommentForm, ExternalRecipeForm, StorageForm, SyncForm, ConnectorConfigForm
|
||||
from cookbook.helper.permission_helper import GroupRequiredMixin, OwnerRequiredMixin, above_space_limit, group_required
|
||||
from cookbook.models import Comment, Recipe, RecipeImport, Storage, Sync, ConnectorConfig
|
||||
from cookbook.provider.dropbox import Dropbox
|
||||
from cookbook.provider.local import Local
|
||||
from cookbook.provider.nextcloud import Nextcloud
|
||||
from recipes import settings
|
||||
|
||||
VALUE_NOT_CHANGED = '__NO__CHANGE__'
|
||||
|
||||
|
||||
@group_required('guest')
|
||||
def switch_recipe(request, pk):
|
||||
recipe = get_object_or_404(Recipe, pk=pk, space=request.space)
|
||||
if recipe.internal:
|
||||
return HttpResponseRedirect(reverse('edit_internal_recipe', args=[pk]))
|
||||
else:
|
||||
return HttpResponseRedirect(reverse('edit_external_recipe', args=[pk]))
|
||||
|
||||
|
||||
@group_required('user')
|
||||
def convert_recipe(request, pk):
|
||||
recipe = get_object_or_404(Recipe, pk=pk, space=request.space)
|
||||
if not recipe.internal:
|
||||
recipe.internal = True
|
||||
recipe.save()
|
||||
|
||||
return HttpResponseRedirect(reverse('edit_internal_recipe', args=[pk]))
|
||||
|
||||
|
||||
@group_required('user')
|
||||
def internal_recipe_update(request, pk):
|
||||
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)
|
||||
|
||||
return render(request, 'forms/edit_internal_recipe.html', {'recipe': recipe_instance})
|
||||
|
||||
|
||||
class SpaceFormMixing(FormMixin):
|
||||
|
||||
def get_form_kwargs(self):
|
||||
kwargs = super().get_form_kwargs()
|
||||
kwargs.update({'space': self.request.space})
|
||||
return kwargs
|
||||
|
||||
|
||||
class SyncUpdate(GroupRequiredMixin, UpdateView, SpaceFormMixing):
|
||||
groups_required = ['admin']
|
||||
template_name = "generic/edit_template.html"
|
||||
model = Sync
|
||||
form_class = SyncForm
|
||||
|
||||
# TODO add msg box
|
||||
|
||||
def get_success_url(self):
|
||||
return reverse('edit_sync', kwargs={'pk': self.object.pk})
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super().get_context_data(**kwargs)
|
||||
context['title'] = _("Sync")
|
||||
return context
|
||||
|
||||
|
||||
@group_required('admin')
|
||||
def edit_storage(request, pk):
|
||||
instance: Storage = get_object_or_404(Storage, pk=pk, space=request.space)
|
||||
|
||||
if not request.user.is_superuser:
|
||||
messages.add_message(request, messages.ERROR, _('You cannot edit this storage!'))
|
||||
return HttpResponseRedirect(reverse('list_storage'))
|
||||
|
||||
if request.space.demo or settings.HOSTED:
|
||||
messages.add_message(request, messages.ERROR, _('This feature is not yet available in the hosted version of tandoor!'))
|
||||
return redirect('index')
|
||||
|
||||
if request.method == "POST":
|
||||
form = StorageForm(request.POST, instance=copy.deepcopy(instance))
|
||||
if form.is_valid():
|
||||
instance.name = form.cleaned_data['name']
|
||||
instance.method = form.cleaned_data['method']
|
||||
instance.username = form.cleaned_data['username']
|
||||
instance.url = form.cleaned_data['url']
|
||||
instance.path = form.cleaned_data['path']
|
||||
|
||||
if form.cleaned_data['password'] != VALUE_NOT_CHANGED:
|
||||
instance.password = form.cleaned_data['password']
|
||||
|
||||
if form.cleaned_data['token'] != VALUE_NOT_CHANGED:
|
||||
instance.token = form.cleaned_data['token']
|
||||
|
||||
instance.save()
|
||||
|
||||
messages.add_message(request, messages.SUCCESS, _('Storage saved!'))
|
||||
else:
|
||||
messages.add_message(request, messages.ERROR, _('There was an error updating this storage backend!'))
|
||||
else:
|
||||
pseudo_instance = instance
|
||||
pseudo_instance.password = VALUE_NOT_CHANGED
|
||||
pseudo_instance.token = VALUE_NOT_CHANGED
|
||||
form = StorageForm(instance=pseudo_instance)
|
||||
|
||||
return render(request, 'generic/edit_template.html', {'form': form, 'title': _('Storage')})
|
||||
|
||||
|
||||
class ConnectorConfigUpdate(GroupRequiredMixin, UpdateView):
|
||||
groups_required = ['admin']
|
||||
template_name = "generic/edit_template.html"
|
||||
model = ConnectorConfig
|
||||
form_class = ConnectorConfigForm
|
||||
|
||||
def get_form_kwargs(self):
|
||||
kwargs = super().get_form_kwargs()
|
||||
kwargs['initial']['update_token'] = VALUE_NOT_CHANGED
|
||||
return kwargs
|
||||
|
||||
def form_valid(self, form):
|
||||
if form.cleaned_data['update_token'] != VALUE_NOT_CHANGED and form.cleaned_data['update_token'] != "":
|
||||
form.instance.token = form.cleaned_data['update_token']
|
||||
messages.add_message(self.request, messages.SUCCESS, _('Config saved!'))
|
||||
return super(ConnectorConfigUpdate, self).form_valid(form)
|
||||
|
||||
def get_success_url(self):
|
||||
return reverse('edit_connector_config', kwargs={'pk': self.object.pk})
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super().get_context_data(**kwargs)
|
||||
context['title'] = _("ConnectorConfig")
|
||||
return context
|
||||
|
||||
|
||||
class CommentUpdate(OwnerRequiredMixin, UpdateView):
|
||||
template_name = "generic/edit_template.html"
|
||||
model = Comment
|
||||
form_class = CommentForm
|
||||
|
||||
def get_success_url(self):
|
||||
return reverse('edit_comment', kwargs={'pk': self.object.pk})
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super(CommentUpdate, self).get_context_data(**kwargs)
|
||||
context['title'] = _("Comment")
|
||||
context['view_url'] = reverse('view_recipe', args=[self.object.recipe.pk])
|
||||
return context
|
||||
|
||||
|
||||
class ImportUpdate(GroupRequiredMixin, UpdateView):
|
||||
groups_required = ['user']
|
||||
template_name = "generic/edit_template.html"
|
||||
model = RecipeImport
|
||||
fields = ['name', 'path']
|
||||
|
||||
# TODO add msg box
|
||||
|
||||
def get_success_url(self):
|
||||
return reverse('edit_import', kwargs={'pk': self.object.pk})
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super(ImportUpdate, self).get_context_data(**kwargs)
|
||||
context['title'] = _("Import")
|
||||
return context
|
||||
|
||||
|
||||
class ExternalRecipeUpdate(GroupRequiredMixin, UpdateView, SpaceFormMixing):
|
||||
groups_required = ['user']
|
||||
model = Recipe
|
||||
form_class = ExternalRecipeForm
|
||||
template_name = "generic/edit_template.html"
|
||||
|
||||
def form_valid(self, form):
|
||||
self.object = form.save(commit=False)
|
||||
old_recipe = Recipe.objects.get(pk=self.object.pk, space=self.request.space)
|
||||
if not old_recipe.name == self.object.name:
|
||||
# TODO central location to handle storage type switches
|
||||
if self.object.storage.method == Storage.DROPBOX:
|
||||
Dropbox.rename_file(old_recipe, self.object.name)
|
||||
if self.object.storage.method == Storage.NEXTCLOUD:
|
||||
Nextcloud.rename_file(old_recipe, self.object.name)
|
||||
if self.object.storage.method == Storage.LOCAL:
|
||||
Local.rename_file(old_recipe, self.object.name)
|
||||
|
||||
self.object.file_path = "%s/%s%s" % (os.path.dirname(self.object.file_path), self.object.name, os.path.splitext(self.object.file_path)[1])
|
||||
|
||||
messages.add_message(self.request, messages.SUCCESS, _('Changes saved!'))
|
||||
return super(ExternalRecipeUpdate, self).form_valid(form)
|
||||
|
||||
def form_invalid(self, form):
|
||||
messages.add_message(self.request, messages.ERROR, _('Error saving changes!'))
|
||||
return super(ExternalRecipeUpdate, self).form_valid(form)
|
||||
|
||||
def get_success_url(self):
|
||||
return reverse('edit_recipe', kwargs={'pk': self.object.pk})
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super().get_context_data(**kwargs)
|
||||
context['title'] = _("Recipe")
|
||||
context['view_url'] = reverse('view_recipe', args=[self.object.pk])
|
||||
if self.object.storage:
|
||||
context['delete_external_url'] = reverse('delete_recipe_source', args=[self.object.pk])
|
||||
return context
|
||||
@@ -83,61 +83,6 @@ def get_integration(request, export_type):
|
||||
return Rezeptsuitede(request, export_type)
|
||||
if export_type == ImportExportBase.GOURMET:
|
||||
return Gourmet(request, export_type)
|
||||
|
||||
|
||||
@group_required('user')
|
||||
def export_recipe(request):
|
||||
if request.method == "POST":
|
||||
form = ExportForm(request.POST, space=request.space)
|
||||
if form.is_valid():
|
||||
try:
|
||||
recipes = form.cleaned_data['recipes']
|
||||
if form.cleaned_data['all']:
|
||||
recipes = Recipe.objects.filter(space=request.space, internal=True).all()
|
||||
elif custom_filter := form.cleaned_data['custom_filter']:
|
||||
search = RecipeSearch(request, filter=custom_filter)
|
||||
recipes = search.get_queryset(Recipe.objects.filter(space=request.space, internal=True))
|
||||
|
||||
integration = get_integration(request, form.cleaned_data['type'])
|
||||
|
||||
if form.cleaned_data['type'] == ImportExportBase.PDF and not settings.ENABLE_PDF_EXPORT:
|
||||
return JsonResponse({'error': _('The PDF Exporter is not enabled on this instance as it is still in an experimental state.')})
|
||||
|
||||
el = ExportLog.objects.create(type=form.cleaned_data['type'], created_by=request.user, space=request.space)
|
||||
|
||||
t = threading.Thread(target=integration.do_export, args=[recipes, el])
|
||||
t.setDaemon(True)
|
||||
t.start()
|
||||
|
||||
return JsonResponse({'export_id': el.pk})
|
||||
except NotImplementedError:
|
||||
return JsonResponse(
|
||||
{
|
||||
'error': True,
|
||||
'msg': _('Importing is not implemented for this provider')
|
||||
},
|
||||
status=400
|
||||
)
|
||||
else:
|
||||
pk = ''
|
||||
recipe = request.GET.get('r')
|
||||
if recipe:
|
||||
if re.match(r'^([0-9])+$', recipe):
|
||||
pk = Recipe.objects.filter(pk=int(recipe), space=request.space).first().pk
|
||||
|
||||
return render(request, 'export.html', {'pk': pk})
|
||||
|
||||
|
||||
@group_required('user')
|
||||
def import_response(request, pk):
|
||||
return render(request, 'import_response.html', {'pk': pk})
|
||||
|
||||
|
||||
@group_required('user')
|
||||
def export_response(request, pk):
|
||||
return render(request, 'export_response.html', {'pk': pk})
|
||||
|
||||
|
||||
@group_required('user')
|
||||
def export_file(request, pk):
|
||||
el = get_object_or_404(ExportLog, pk=pk, space=request.space)
|
||||
|
||||
@@ -1,275 +0,0 @@
|
||||
from datetime import datetime
|
||||
|
||||
from django.db.models import Sum
|
||||
from django.shortcuts import render
|
||||
from django.utils.translation import gettext as _
|
||||
from django_tables2 import RequestConfig
|
||||
|
||||
from cookbook.helper.permission_helper import group_required
|
||||
from cookbook.models import InviteLink, RecipeImport, Storage, SyncLog, UserFile, ConnectorConfig
|
||||
from cookbook.tables import ImportLogTable, InviteLinkTable, RecipeImportTable, StorageTable, ConnectorConfigTable
|
||||
|
||||
|
||||
@group_required('admin')
|
||||
def sync_log(request):
|
||||
table = ImportLogTable(
|
||||
SyncLog.objects.filter(sync__space=request.space).all().order_by('-created_at')
|
||||
)
|
||||
RequestConfig(request, paginate={'per_page': 25}).configure(table)
|
||||
|
||||
return render(
|
||||
request,
|
||||
'generic/list_template.html',
|
||||
{'title': _("Import Log"), 'table': table}
|
||||
)
|
||||
|
||||
|
||||
@group_required('user')
|
||||
def recipe_import(request):
|
||||
table = RecipeImportTable(RecipeImport.objects.filter(space=request.space).all())
|
||||
|
||||
RequestConfig(request, paginate={'per_page': 25}).configure(table)
|
||||
|
||||
return render(
|
||||
request,
|
||||
'generic/list_template.html',
|
||||
{'title': _("Discovery"), 'table': table, 'import_btn': True}
|
||||
)
|
||||
|
||||
|
||||
@group_required('user')
|
||||
def shopping_list(request):
|
||||
return render(
|
||||
request,
|
||||
'shoppinglist_template.html',
|
||||
{
|
||||
"title": _("Shopping List"),
|
||||
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
@group_required('admin')
|
||||
def storage(request):
|
||||
table = StorageTable(Storage.objects.filter(space=request.space).all())
|
||||
RequestConfig(request, paginate={'per_page': 25}).configure(table)
|
||||
|
||||
return render(
|
||||
request,
|
||||
'generic/list_template.html',
|
||||
{
|
||||
'title': _("Storage Backend"),
|
||||
'table': table,
|
||||
'create_url': 'new_storage'
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
@group_required('admin')
|
||||
def connector_config(request):
|
||||
table = ConnectorConfigTable(ConnectorConfig.objects.filter(space=request.space).all())
|
||||
RequestConfig(request, paginate={'per_page': 25}).configure(table)
|
||||
|
||||
return render(
|
||||
request,
|
||||
'generic/list_template.html',
|
||||
{
|
||||
'title': _("Connector Config Backend"),
|
||||
'table': table,
|
||||
'create_url': 'new_connector_config'
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
@group_required('admin')
|
||||
def invite_link(request):
|
||||
table = InviteLinkTable(
|
||||
InviteLink.objects.filter(valid_until__gte=datetime.today(), used_by=None, space=request.space).all())
|
||||
RequestConfig(request, paginate={'per_page': 25}).configure(table)
|
||||
|
||||
return render(request, 'generic/list_template.html', {
|
||||
'title': _("Invite Links"),
|
||||
'table': table,
|
||||
'create_url': 'new_invite_link'
|
||||
})
|
||||
|
||||
|
||||
@group_required('user')
|
||||
def keyword(request):
|
||||
return render(
|
||||
request,
|
||||
'generic/model_template.html',
|
||||
{
|
||||
"title": _("Keywords"),
|
||||
"config": {
|
||||
'model': "KEYWORD",
|
||||
'recipe_param': 'keywords'
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
@group_required('user')
|
||||
def food(request):
|
||||
# recipe-param is the name of the parameters used when filtering recipes by this attribute
|
||||
# model-name is the models.js name of the model, probably ALL-CAPS
|
||||
return render(
|
||||
request,
|
||||
'generic/model_template.html',
|
||||
{
|
||||
"title": _("Foods"),
|
||||
"config": {
|
||||
'model': "FOOD", # *REQUIRED* name of the model in models.js
|
||||
'recipe_param': 'foods' # *OPTIONAL* name of the listRecipes parameter if filtering on this attribute
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
@group_required('user')
|
||||
def unit(request):
|
||||
# recipe-param is the name of the parameters used when filtering recipes by this attribute
|
||||
# model-name is the models.js name of the model, probably ALL-CAPS
|
||||
return render(
|
||||
request,
|
||||
'generic/model_template.html',
|
||||
{
|
||||
"title": _("Units"),
|
||||
"config": {
|
||||
'model': "UNIT", # *REQUIRED* name of the model in models.js
|
||||
'recipe_param': 'units', # *OPTIONAL* name of the listRecipes parameter if filtering on this attribute
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
@group_required('user')
|
||||
def supermarket(request):
|
||||
# recipe-param is the name of the parameters used when filtering recipes by this attribute
|
||||
# model-name is the models.js name of the model, probably ALL-CAPS
|
||||
return render(
|
||||
request,
|
||||
'generic/model_template.html',
|
||||
{
|
||||
"title": _("Supermarkets"),
|
||||
"config": {
|
||||
'model': "SUPERMARKET", # *REQUIRED* name of the model in models.js
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
@group_required('user')
|
||||
def supermarket_category(request):
|
||||
# recipe-param is the name of the parameters used when filtering recipes by this attribute
|
||||
# model-name is the models.js name of the model, probably ALL-CAPS
|
||||
return render(
|
||||
request,
|
||||
'generic/model_template.html',
|
||||
{
|
||||
"title": _("Shopping Categories"),
|
||||
"config": {
|
||||
'model': "SHOPPING_CATEGORY", # *REQUIRED* name of the model in models.js
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
@group_required('user')
|
||||
def automation(request):
|
||||
# recipe-param is the name of the parameters used when filtering recipes by this attribute
|
||||
# model-name is the models.js name of the model, probably ALL-CAPS
|
||||
return render(
|
||||
request,
|
||||
'generic/model_template.html',
|
||||
{
|
||||
"title": _("Automations"),
|
||||
"config": {
|
||||
'model': "AUTOMATION", # *REQUIRED* name of the model in models.js
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
@group_required('user')
|
||||
def custom_filter(request):
|
||||
# recipe-param is the name of the parameters used when filtering recipes by this attribute
|
||||
# model-name is the models.js name of the model, probably ALL-CAPS
|
||||
return render(
|
||||
request,
|
||||
'generic/model_template.html',
|
||||
{
|
||||
"title": _("Custom Filters"),
|
||||
"config": {
|
||||
'model': "CUSTOM_FILTER", # *REQUIRED* name of the model in models.js
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
@group_required('user')
|
||||
def user_file(request):
|
||||
try:
|
||||
current_file_size_mb = UserFile.objects.filter(space=request.space).aggregate(Sum('file_size_kb'))[
|
||||
'file_size_kb__sum'] / 1000
|
||||
except TypeError:
|
||||
current_file_size_mb = 0
|
||||
|
||||
return render(
|
||||
request,
|
||||
'generic/model_template.html',
|
||||
{
|
||||
"title": _("Files"),
|
||||
"config": {
|
||||
'model': "USERFILE", # *REQUIRED* name of the model in models.js
|
||||
},
|
||||
'current_file_size_mb': current_file_size_mb, 'max_file_size_mb': request.space.max_file_storage_mb
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
@group_required('user')
|
||||
def step(request):
|
||||
# recipe-param is the name of the parameters used when filtering recipes by this attribute
|
||||
# model-name is the models.js name of the model, probably ALL-CAPS
|
||||
return render(
|
||||
request,
|
||||
'generic/model_template.html',
|
||||
{
|
||||
"title": _("Steps"),
|
||||
"config": {
|
||||
'model': "STEP", # *REQUIRED* name of the model in models.js
|
||||
'recipe_param': 'steps',
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
@group_required('user')
|
||||
def unit_conversion(request):
|
||||
# model-name is the models.js name of the model, probably ALL-CAPS
|
||||
return render(
|
||||
request,
|
||||
'generic/model_template.html',
|
||||
{
|
||||
"title": _("Unit Conversions"),
|
||||
"config": {
|
||||
'model': "UNIT_CONVERSION", # *REQUIRED* name of the model in models.js
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
@group_required('user')
|
||||
def property_type(request):
|
||||
# model-name is the models.js name of the model, probably ALL-CAPS
|
||||
return render(
|
||||
request,
|
||||
'generic/model_template.html',
|
||||
{
|
||||
"title": _("Property Types"),
|
||||
"config": {
|
||||
'model': "PROPERTY_TYPE", # *REQUIRED* name of the model in models.js
|
||||
}
|
||||
}
|
||||
)
|
||||
@@ -1,137 +0,0 @@
|
||||
from django.contrib import messages
|
||||
from django.http import HttpResponseRedirect
|
||||
from django.shortcuts import get_object_or_404, redirect, render
|
||||
from django.urls import reverse, reverse_lazy
|
||||
from django.utils.translation import gettext as _
|
||||
from django.views.generic import CreateView
|
||||
|
||||
from cookbook.forms import ImportRecipeForm, Storage, StorageForm, ConnectorConfigForm
|
||||
from cookbook.helper.permission_helper import GroupRequiredMixin, above_space_limit, group_required
|
||||
from cookbook.models import Recipe, RecipeImport, ShareLink, Step, ConnectorConfig
|
||||
from recipes import settings
|
||||
|
||||
|
||||
class RecipeCreate(GroupRequiredMixin, CreateView):
|
||||
groups_required = ['user']
|
||||
template_name = "generic/new_template.html"
|
||||
model = Recipe
|
||||
fields = ('name', )
|
||||
|
||||
def form_valid(self, form):
|
||||
limit, msg = above_space_limit(self.request.space)
|
||||
if limit:
|
||||
messages.add_message(self.request, messages.WARNING, msg)
|
||||
return HttpResponseRedirect(reverse('index'))
|
||||
|
||||
obj = form.save(commit=False)
|
||||
obj.created_by = self.request.user
|
||||
obj.space = self.request.space
|
||||
obj.internal = True
|
||||
obj.save()
|
||||
obj.steps.add(Step.objects.create(space=self.request.space, show_as_header=False, show_ingredients_table=self.request.user.userpreference.show_step_ingredients))
|
||||
return HttpResponseRedirect(reverse('edit_recipe', kwargs={'pk': obj.pk}))
|
||||
|
||||
def get_success_url(self):
|
||||
return reverse('edit_recipe', kwargs={'pk': self.object.pk})
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super(RecipeCreate, self).get_context_data(**kwargs)
|
||||
context['title'] = _("Recipe")
|
||||
return context
|
||||
|
||||
|
||||
@group_required('user')
|
||||
def share_link(request, pk):
|
||||
recipe = get_object_or_404(Recipe, pk=pk, space=request.space)
|
||||
link = ShareLink.objects.create(recipe=recipe, created_by=request.user, space=request.space)
|
||||
return HttpResponseRedirect(reverse('view_recipe', kwargs={'pk': pk, 'share': link.uuid}))
|
||||
|
||||
|
||||
class StorageCreate(GroupRequiredMixin, CreateView):
|
||||
groups_required = ['admin']
|
||||
template_name = "generic/new_template.html"
|
||||
model = Storage
|
||||
form_class = StorageForm
|
||||
success_url = reverse_lazy('list_storage')
|
||||
|
||||
def form_valid(self, form):
|
||||
obj = form.save(commit=False)
|
||||
obj.created_by = self.request.user
|
||||
obj.space = self.request.space
|
||||
|
||||
if self.request.space.demo or settings.HOSTED:
|
||||
messages.add_message(self.request, messages.ERROR, _('This feature is not yet available in the hosted version of tandoor!'))
|
||||
return redirect('index')
|
||||
|
||||
if not self.request.user.is_superuser:
|
||||
messages.add_message(self.request, messages.ERROR, _('This feature is only available for the instance administrator (superuser)'))
|
||||
return redirect('index')
|
||||
|
||||
obj.save()
|
||||
return HttpResponseRedirect(reverse('edit_storage', kwargs={'pk': obj.pk}))
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super(StorageCreate, self).get_context_data(**kwargs)
|
||||
context['title'] = _("Storage Backend")
|
||||
return context
|
||||
|
||||
|
||||
class ConnectorConfigCreate(GroupRequiredMixin, CreateView):
|
||||
groups_required = ['admin']
|
||||
template_name = "generic/new_template.html"
|
||||
model = ConnectorConfig
|
||||
form_class = ConnectorConfigForm
|
||||
success_url = reverse_lazy('list_connector_config')
|
||||
|
||||
def form_valid(self, form):
|
||||
if self.request.space.demo:
|
||||
messages.add_message(self.request, messages.ERROR, _('This feature is not yet available in the hosted version of tandoor!'))
|
||||
return redirect('index')
|
||||
|
||||
if settings.DISABLE_EXTERNAL_CONNECTORS:
|
||||
messages.add_message(self.request, messages.ERROR, _('This feature is not enabled by the server admin!'))
|
||||
return redirect('index')
|
||||
|
||||
obj = form.save(commit=False)
|
||||
obj.token = form.cleaned_data['update_token']
|
||||
obj.created_by = self.request.user
|
||||
obj.space = self.request.space
|
||||
obj.save()
|
||||
return HttpResponseRedirect(reverse('edit_connector_config', kwargs={'pk': obj.pk}))
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super().get_context_data(**kwargs)
|
||||
context['title'] = _("Connector Config Backend")
|
||||
return context
|
||||
|
||||
|
||||
@group_required('user')
|
||||
def create_new_external_recipe(request, import_id):
|
||||
if request.method == "POST":
|
||||
form = ImportRecipeForm(request.POST, space=request.space)
|
||||
if form.is_valid():
|
||||
new_recipe = get_object_or_404(RecipeImport, pk=import_id, space=request.space)
|
||||
recipe = Recipe()
|
||||
recipe.space = request.space
|
||||
recipe.storage = new_recipe.storage
|
||||
recipe.name = form.cleaned_data['name']
|
||||
recipe.file_path = form.cleaned_data['file_path']
|
||||
recipe.file_uid = form.cleaned_data['file_uid']
|
||||
recipe.created_by = request.user
|
||||
|
||||
recipe.save()
|
||||
|
||||
if form.cleaned_data['keywords']:
|
||||
recipe.keywords.set(form.cleaned_data['keywords'])
|
||||
|
||||
new_recipe.delete()
|
||||
|
||||
messages.add_message(request, messages.SUCCESS, _('Imported new recipe!'))
|
||||
return redirect('list_recipe_import')
|
||||
else:
|
||||
messages.add_message(request, messages.ERROR, _('There was an error importing this recipe!'))
|
||||
else:
|
||||
new_recipe = get_object_or_404(RecipeImport, pk=import_id, space=request.space)
|
||||
form = ImportRecipeForm(initial={'file_path': new_recipe.file_path, 'name': new_recipe.name, 'file_uid': new_recipe.file_uid}, space=request.space)
|
||||
|
||||
return render(request, 'forms/edit_import_recipe.html', {'form': form})
|
||||
@@ -16,24 +16,20 @@ from django.core.exceptions import ValidationError
|
||||
from django.core.management import call_command
|
||||
from django.db import models
|
||||
from django.http import HttpResponseRedirect, JsonResponse
|
||||
from django.shortcuts import get_object_or_404, redirect, render
|
||||
from django.shortcuts import get_object_or_404, render
|
||||
from django.templatetags.static import static
|
||||
from django.urls import reverse, reverse_lazy
|
||||
from django.utils import timezone
|
||||
from django.utils.datetime_safe import date
|
||||
from django.utils.translation import gettext as _
|
||||
from django_scopes import scopes_disabled
|
||||
from drf_spectacular.views import SpectacularRedocView, SpectacularSwaggerView
|
||||
from rest_framework.response import Response
|
||||
|
||||
from cookbook.forms import CommentForm, Recipe, SearchPreferenceForm, SpaceCreateForm, SpaceJoinForm, User, UserCreateForm, UserPreference
|
||||
from cookbook.forms import Recipe, SpaceCreateForm, SpaceJoinForm, User, UserCreateForm
|
||||
from cookbook.helper.HelperFunctions import str2bool
|
||||
from cookbook.helper.permission_helper import CustomIsGuest, GroupRequiredMixin, group_required, has_group_permission, share_link_valid, switch_user_active_space
|
||||
from cookbook.models import Comment, CookLog, InviteLink, SearchFields, SearchPreference, ShareLink, Space, UserSpace, ViewLog
|
||||
from cookbook.tables import CookLogTable, ViewLogTable
|
||||
from cookbook.helper.permission_helper import CustomIsGuest, GroupRequiredMixin, has_group_permission, share_link_valid, switch_user_active_space
|
||||
from cookbook.models import InviteLink, ShareLink, Space, UserSpace
|
||||
from cookbook.templatetags.theming_tags import get_theming_values
|
||||
from cookbook.version_info import VERSION_INFO
|
||||
from cookbook.views.api import get_recipe_provider
|
||||
from recipes.settings import PLUGINS
|
||||
|
||||
|
||||
@@ -146,58 +142,6 @@ def no_perm(request):
|
||||
return HttpResponseRedirect(reverse('account_login') + '?next=' + request.GET.get('next', '/search/'))
|
||||
return render(request, 'no_perm_info.html')
|
||||
|
||||
|
||||
def recipe_view(request, pk, share=None):
|
||||
with scopes_disabled():
|
||||
recipe = get_object_or_404(Recipe, pk=pk)
|
||||
|
||||
if not request.user.is_authenticated and not share_link_valid(recipe, share):
|
||||
messages.add_message(request, messages.ERROR, _('You do not have the required permissions to view this page!'))
|
||||
return HttpResponseRedirect(reverse('account_login') + '?next=' + request.path)
|
||||
|
||||
if not (has_group_permission(request.user, ('guest',)) and recipe.space == request.space) and not share_link_valid(recipe, share):
|
||||
messages.add_message(request, messages.ERROR, _('You do not have the required permissions to view this page!'))
|
||||
return HttpResponseRedirect(reverse('index'))
|
||||
|
||||
comments = Comment.objects.filter(recipe__space=request.space, recipe=recipe)
|
||||
|
||||
if request.method == "POST":
|
||||
if not request.user.is_authenticated:
|
||||
messages.add_message(request, messages.ERROR, _('You do not have the required permissions to perform this action!'))
|
||||
return HttpResponseRedirect(reverse('view_recipe', kwargs={'pk': recipe.pk, 'share': share}))
|
||||
|
||||
comment_form = CommentForm(request.POST, prefix='comment')
|
||||
if comment_form.is_valid():
|
||||
comment = Comment()
|
||||
comment.recipe = recipe
|
||||
comment.text = comment_form.cleaned_data['text']
|
||||
comment.created_by = request.user
|
||||
comment.save()
|
||||
|
||||
messages.add_message(request, messages.SUCCESS, _('Comment saved!'))
|
||||
|
||||
comment_form = CommentForm()
|
||||
|
||||
if request.user.is_authenticated:
|
||||
if not ViewLog.objects.filter(recipe=recipe, created_by=request.user, created_at__gt=(timezone.now() - timezone.timedelta(minutes=5)), space=request.space).exists():
|
||||
ViewLog.objects.create(recipe=recipe, created_by=request.user, space=request.space)
|
||||
|
||||
servings = recipe.servings
|
||||
if request.method == "GET" and 'servings' in request.GET:
|
||||
servings = request.GET.get("servings")
|
||||
return render(request, 'recipe_view.html', {'recipe': recipe, 'comments': comments, 'comment_form': comment_form, 'share': share, 'servings': servings})
|
||||
|
||||
|
||||
@group_required('user')
|
||||
def books(request):
|
||||
return render(request, 'books.html', {})
|
||||
|
||||
|
||||
@group_required('user')
|
||||
def meal_plan(request):
|
||||
return render(request, 'meal_plan.html', {})
|
||||
|
||||
|
||||
def recipe_pdf_viewer(request, pk):
|
||||
with scopes_disabled():
|
||||
recipe = get_object_or_404(Recipe, pk=pk)
|
||||
@@ -207,120 +151,6 @@ def recipe_pdf_viewer(request, pk):
|
||||
return render(request, 'pdf_viewer.html', {'recipe_id': pk, 'share': request.GET.get('share', None)})
|
||||
return HttpResponseRedirect(reverse('index'))
|
||||
|
||||
|
||||
@group_required('guest')
|
||||
def user_settings(request):
|
||||
if request.space.demo:
|
||||
messages.add_message(request, messages.ERROR, _('This feature is not available in the demo version!'))
|
||||
return redirect('index')
|
||||
|
||||
return render(request, 'user_settings.html', {})
|
||||
|
||||
|
||||
@group_required('user')
|
||||
def ingredient_editor(request):
|
||||
template_vars = {'food_id': -1, 'unit_id': -1}
|
||||
food_id = request.GET.get('food_id', None)
|
||||
if food_id and re.match(r'^(\d)+$', food_id):
|
||||
template_vars['food_id'] = food_id
|
||||
|
||||
unit_id = request.GET.get('unit_id', None)
|
||||
if unit_id and re.match(r'^(\d)+$', unit_id):
|
||||
template_vars['unit_id'] = unit_id
|
||||
return render(request, 'ingredient_editor.html', template_vars)
|
||||
|
||||
|
||||
@group_required('user')
|
||||
def property_editor(request, pk):
|
||||
return render(request, 'property_editor.html', {'recipe_id': pk})
|
||||
|
||||
|
||||
@group_required('guest')
|
||||
def shopping_settings(request):
|
||||
if request.space.demo:
|
||||
messages.add_message(request, messages.ERROR, _('This feature is not available in the demo version!'))
|
||||
return redirect('index')
|
||||
|
||||
sp = request.user.searchpreference
|
||||
search_error = False
|
||||
|
||||
if request.method == "POST":
|
||||
if 'search_form' in request.POST:
|
||||
search_form = SearchPreferenceForm(request.POST, prefix='search')
|
||||
if search_form.is_valid():
|
||||
if not sp:
|
||||
sp = SearchPreferenceForm(user=request.user)
|
||||
fields_searched = (len(search_form.cleaned_data['icontains']) + len(search_form.cleaned_data['istartswith']) + len(search_form.cleaned_data['trigram'])
|
||||
+ len(search_form.cleaned_data['fulltext']))
|
||||
if search_form.cleaned_data['preset'] == 'fuzzy':
|
||||
sp.search = SearchPreference.SIMPLE
|
||||
sp.lookup = True
|
||||
sp.unaccent.set([SearchFields.objects.get(name='Name')])
|
||||
sp.icontains.set([SearchFields.objects.get(name='Name')])
|
||||
sp.istartswith.clear()
|
||||
sp.trigram.set([SearchFields.objects.get(name='Name')])
|
||||
sp.fulltext.clear()
|
||||
sp.trigram_threshold = 0.2
|
||||
sp.save()
|
||||
elif search_form.cleaned_data['preset'] == 'precise':
|
||||
sp.search = SearchPreference.WEB
|
||||
sp.lookup = True
|
||||
sp.unaccent.set(SearchFields.objects.all())
|
||||
# full text on food is very slow, add search_vector field and index it (including Admin functions and postsave signal to rebuild index)
|
||||
sp.icontains.set([SearchFields.objects.get(name='Name')])
|
||||
sp.istartswith.set([SearchFields.objects.get(name='Name')])
|
||||
sp.trigram.clear()
|
||||
sp.fulltext.set(SearchFields.objects.filter(name__in=['Ingredients']))
|
||||
sp.trigram_threshold = 0.2
|
||||
sp.save()
|
||||
elif fields_searched == 0:
|
||||
search_form.add_error(None, _('You must select at least one field to search!'))
|
||||
search_error = True
|
||||
elif search_form.cleaned_data['search'] in ['websearch', 'raw'] and len(search_form.cleaned_data['fulltext']) == 0:
|
||||
search_form.add_error('search', _('To use this search method you must select at least one full text search field!'))
|
||||
search_error = True
|
||||
elif search_form.cleaned_data['search'] in ['websearch', 'raw'] and len(search_form.cleaned_data['trigram']) > 0:
|
||||
search_form.add_error(None, _('Fuzzy search is not compatible with this search method!'))
|
||||
search_error = True
|
||||
else:
|
||||
sp.search = search_form.cleaned_data['search']
|
||||
sp.lookup = search_form.cleaned_data['lookup']
|
||||
sp.unaccent.set(search_form.cleaned_data['unaccent'])
|
||||
sp.icontains.set(search_form.cleaned_data['icontains'])
|
||||
sp.istartswith.set(search_form.cleaned_data['istartswith'])
|
||||
sp.trigram.set(search_form.cleaned_data['trigram'])
|
||||
sp.fulltext.set(search_form.cleaned_data['fulltext'])
|
||||
sp.trigram_threshold = search_form.cleaned_data['trigram_threshold']
|
||||
sp.save()
|
||||
else:
|
||||
search_error = True
|
||||
|
||||
fields_searched = len(sp.icontains.all()) + len(sp.istartswith.all()) + len(sp.trigram.all()) + len(sp.fulltext.all())
|
||||
if sp and not search_error and fields_searched > 0:
|
||||
search_form = SearchPreferenceForm(instance=sp)
|
||||
elif not search_error:
|
||||
search_form = SearchPreferenceForm()
|
||||
|
||||
# these fields require postgresql - just disable them if postgresql isn't available
|
||||
if not settings.DATABASES['default']['ENGINE'] == 'django.db.backends.postgresql':
|
||||
sp.search = SearchPreference.SIMPLE
|
||||
sp.trigram.clear()
|
||||
sp.fulltext.clear()
|
||||
sp.save()
|
||||
|
||||
return render(request, 'settings.html', {'search_form': search_form, })
|
||||
|
||||
|
||||
@group_required('guest')
|
||||
def history(request):
|
||||
view_log = ViewLogTable(ViewLog.objects.filter(created_by=request.user, space=request.space).order_by('-created_at').all(), prefix="viewlog-")
|
||||
view_log.paginate(page=request.GET.get("viewlog-page", 1), per_page=25)
|
||||
|
||||
cook_log = CookLogTable(CookLog.objects.filter(created_by=request.user).order_by('-created_at').all(), prefix="cooklog-")
|
||||
cook_log.paginate(page=request.GET.get("cooklog-page", 1), per_page=25)
|
||||
return render(request, 'history.html', {'view_log': view_log, 'cook_log': cook_log})
|
||||
|
||||
|
||||
def system(request):
|
||||
if not request.user.is_superuser:
|
||||
return HttpResponseRedirect(reverse('index'))
|
||||
@@ -502,16 +332,6 @@ def invite_link(request, token):
|
||||
return HttpResponseRedirect(reverse('view_space_overview'))
|
||||
|
||||
|
||||
@group_required('admin')
|
||||
def space_manage(request, space_id):
|
||||
if request.space.demo:
|
||||
messages.add_message(request, messages.ERROR, _('This feature is not available in the demo version!'))
|
||||
return redirect('index')
|
||||
space = get_object_or_404(Space, id=space_id)
|
||||
switch_user_active_space(request.user, space)
|
||||
return render(request, 'space_manage.html', {})
|
||||
|
||||
|
||||
def report_share_abuse(request, token):
|
||||
if not settings.SHARING_ABUSE:
|
||||
messages.add_message(request, messages.WARNING, _('Reporting share links is not enabled for this instance. Please notify the page administrator to report problems.'))
|
||||
@@ -523,9 +343,6 @@ def report_share_abuse(request, token):
|
||||
return HttpResponseRedirect(reverse('index'))
|
||||
|
||||
|
||||
def service_worker(request):
|
||||
return
|
||||
|
||||
def web_manifest(request):
|
||||
theme_values = get_theming_values(request)
|
||||
|
||||
@@ -703,6 +520,3 @@ def get_orphan_files(delete_orphans=False):
|
||||
|
||||
return [img[1] for img in orphans]
|
||||
|
||||
|
||||
def vue3(request):
|
||||
return HttpResponseRedirect(reverse('index'))
|
||||
|
||||
Reference in New Issue
Block a user