This commit is contained in:
Mikhail Epifanov
2024-02-17 00:34:16 +05:30
3 changed files with 198 additions and 365 deletions

View File

@@ -10,8 +10,7 @@ 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.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
@@ -106,26 +105,16 @@ def edit_storage(request, pk):
instance.save()
messages.add_message(
request, messages.SUCCESS, _('Storage saved!')
)
messages.add_message(request, messages.SUCCESS, _('Storage saved!'))
else:
messages.add_message(
request,
messages.ERROR,
_('There was an error updating this storage backend!')
)
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')}
)
return render(request, 'generic/edit_template.html', {'form': form, 'title': _('Storage')})
class ConnectorConfigUpdate(GroupRequiredMixin, UpdateView):
@@ -165,9 +154,7 @@ class CommentUpdate(OwnerRequiredMixin, UpdateView):
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]
)
context['view_url'] = reverse('view_recipe', args=[self.object.recipe.pk])
return context
@@ -206,11 +193,7 @@ class ExternalRecipeUpdate(GroupRequiredMixin, UpdateView, SpaceFormMixing):
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]
)
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)
@@ -227,7 +210,5 @@ class ExternalRecipeUpdate(GroupRequiredMixin, UpdateView, SpaceFormMixing):
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]
)
context['delete_external_url'] = reverse('delete_recipe_source', args=[self.object.pk])
return context

View File

@@ -1,10 +1,10 @@
import json
import os
import re
import subprocess
from datetime import datetime
from io import StringIO
from uuid import UUID
import subprocess
from django.apps import apps
from django.conf import settings
@@ -23,17 +23,14 @@ from django.utils import timezone
from django.utils.translation import gettext as _
from django_scopes import scopes_disabled
from cookbook.forms import (CommentForm, Recipe, SearchPreferenceForm, SpaceCreateForm,
SpaceJoinForm, User, UserCreateForm, UserPreference)
from cookbook.forms import CommentForm, Recipe, SearchPreferenceForm, SpaceCreateForm, SpaceJoinForm, User, UserCreateForm, UserPreference
from cookbook.helper.HelperFunctions import str2bool
from cookbook.helper.permission_helper import (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.helper.permission_helper import 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.templatetags.theming_tags import get_theming_values
from cookbook.version_info import VERSION_INFO
from recipes.settings import PLUGINS, BASE_DIR
from recipes.settings import BASE_DIR, PLUGINS
def index(request):
@@ -44,11 +41,7 @@ def index(request):
return HttpResponseRedirect(reverse_lazy('view_search'))
try:
page_map = {
UserPreference.SEARCH: reverse_lazy('view_search'),
UserPreference.PLAN: reverse_lazy('view_plan'),
UserPreference.BOOKS: reverse_lazy('view_books'),
}
page_map = {UserPreference.SEARCH: reverse_lazy('view_search'), UserPreference.PLAN: reverse_lazy('view_plan'), UserPreference.BOOKS: reverse_lazy('view_books'), }
return HttpResponseRedirect(page_map.get(request.user.userpreference.default_page))
except UserPreference.DoesNotExist:
@@ -57,7 +50,7 @@ def index(request):
# TODO need to deprecate
def search(request):
if has_group_permission(request.user, ('guest',)):
if has_group_permission(request.user, ('guest', )):
return render(request, 'search.html', {})
else:
if request.user.is_authenticated:
@@ -84,14 +77,13 @@ def space_overview(request):
_('You have the reached the maximum amount of spaces that can be owned by you.') + f' ({request.user.userpreference.max_owned_spaces})')
return HttpResponseRedirect(reverse('view_space_overview'))
created_space = Space.objects.create(
name=create_form.cleaned_data['name'],
created_by=request.user,
max_file_storage_mb=settings.SPACE_DEFAULT_MAX_FILES,
max_recipes=settings.SPACE_DEFAULT_MAX_RECIPES,
max_users=settings.SPACE_DEFAULT_MAX_USERS,
allow_sharing=settings.SPACE_DEFAULT_ALLOW_SHARING,
)
created_space = Space.objects.create(name=create_form.cleaned_data['name'],
created_by=request.user,
max_file_storage_mb=settings.SPACE_DEFAULT_MAX_FILES,
max_recipes=settings.SPACE_DEFAULT_MAX_RECIPES,
max_users=settings.SPACE_DEFAULT_MAX_USERS,
allow_sharing=settings.SPACE_DEFAULT_ALLOW_SHARING,
)
user_space = UserSpace.objects.create(space=created_space, user=request.user, active=False)
user_space.groups.add(Group.objects.filter(name='admin').get())
@@ -135,23 +127,18 @@ def recipe_view(request, pk, share=None):
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!'))
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!'))
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!'))
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')
@@ -167,16 +154,16 @@ def recipe_view(request, pk, share=None):
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():
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)
if request.method == "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', {})
@@ -239,12 +226,8 @@ def shopping_settings(request):
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'])
)
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
@@ -269,13 +252,10 @@ def shopping_settings(request):
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!'))
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:
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:
@@ -291,8 +271,7 @@ def shopping_settings(request):
else:
search_error = True
fields_searched = len(sp.icontains.all()) + len(sp.istartswith.all()) + len(sp.trigram.all()) + len(
sp.fulltext.all())
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:
@@ -305,23 +284,13 @@ def shopping_settings(request):
sp.fulltext.clear()
sp.save()
return render(request, 'settings.html', {
'search_form': search_form,
})
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()
)
cook_log = CookLogTable(
CookLog.objects.filter(
created_by=request.user
).order_by('-created_at').all()
)
view_log = ViewLogTable(ViewLog.objects.filter(created_by=request.user, space=request.space).order_by('-created_at').all())
cook_log = CookLogTable(CookLog.objects.filter(created_by=request.user).order_by('-created_at').all())
return render(request, 'history.html', {'view_log': view_log, 'cook_log': cook_log})
@@ -344,12 +313,10 @@ def system(request):
database_message = _('Everything is fine!')
elif postgres_ver < postgres_current - 2:
database_status = 'danger'
database_message = _('PostgreSQL %(v)s is deprecated. Upgrade to a fully supported version!') % {
'v': postgres_ver}
database_message = _('PostgreSQL %(v)s is deprecated. Upgrade to a fully supported version!') % {'v': postgres_ver}
else:
database_status = 'info'
database_message = _('You are running PostgreSQL %(v1)s. PostgreSQL %(v2)s is recommended') % {
'v1': postgres_ver, 'v2': postgres_current}
database_message = _('You are running PostgreSQL %(v1)s. PostgreSQL %(v2)s is recommended') % {'v1': postgres_ver, 'v2': postgres_current}
else:
database_status = 'info'
database_message = _(
@@ -378,34 +345,26 @@ def system(request):
pass
else:
current_app = row
migration_info[current_app] = {'app': current_app, 'unapplied_migrations': [], 'applied_migrations': [],
'total': 0}
migration_info[current_app] = {'app': current_app, 'unapplied_migrations': [], 'applied_migrations': [], 'total': 0}
for key in migration_info.keys():
migration_info[key]['total'] = len(migration_info[key]['unapplied_migrations']) + len(
migration_info[key]['applied_migrations'])
migration_info[key]['total'] = len(migration_info[key]['unapplied_migrations']) + len(migration_info[key]['applied_migrations'])
return render(request, 'system.html', {
'gunicorn_media': settings.GUNICORN_MEDIA,
'debug': settings.DEBUG,
'postgres': postgres,
'postgres_version': postgres_ver,
'postgres_status': database_status,
'postgres_message': database_message,
'version_info': VERSION_INFO,
'plugins': PLUGINS,
'secret_key': secret_key,
'orphans': orphans,
'migration_info': migration_info,
'missing_migration': missing_migration,
})
return render(
request, 'system.html', {
'gunicorn_media': settings.GUNICORN_MEDIA, 'debug': settings.DEBUG, 'postgres': postgres, 'postgres_version': postgres_ver, 'postgres_status': database_status,
'postgres_message': database_message, 'version_info': VERSION_INFO, 'plugins': PLUGINS, 'secret_key': secret_key, 'orphans': orphans, 'migration_info': migration_info,
'missing_migration': missing_migration,
})
def setup(request):
with scopes_disabled():
if User.objects.count() > 0 or 'django.contrib.auth.backends.RemoteUserBackend' in settings.AUTHENTICATION_BACKENDS:
messages.add_message(request, messages.ERROR,
_('The setup page can only be used to create the first user! If you have forgotten your superuser credentials please consult the django documentation on how to reset passwords.'))
messages.add_message(
request, messages.ERROR,
_('The setup page can only be used to create the first user! If you have forgotten your superuser credentials please consult the django documentation on how to reset passwords.'
))
return HttpResponseRedirect(reverse('account_login'))
if request.method == 'POST':
@@ -445,8 +404,7 @@ def invite_link(request, token):
link.used_by = request.user
link.save()
user_space = UserSpace.objects.create(user=request.user, space=link.space,
internal_note=link.internal_note, invite_link=link, active=False)
user_space = UserSpace.objects.create(user=request.user, space=link.space, internal_note=link.internal_note, invite_link=link, active=False)
if request.user.userspace_set.count() == 1:
user_space.active = True
@@ -476,66 +434,36 @@ def space_manage(request, space_id):
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.'))
messages.add_message(request, messages.WARNING, _('Reporting share links is not enabled for this instance. Please notify the page administrator to report problems.'))
else:
if link := ShareLink.objects.filter(uuid=token).first():
link.abuse_blocked = True
link.save()
messages.add_message(request, messages.WARNING,
_('Recipe sharing link has been disabled! For additional information please contact the page administrator.'))
messages.add_message(request, messages.WARNING, _('Recipe sharing link has been disabled! For additional information please contact the page administrator.'))
return HttpResponseRedirect(reverse('index'))
def web_manifest(request):
theme_values = get_theming_values(request)
icons = [
{"src": theme_values['logo_color_svg'], "sizes": "any"},
{"src": theme_values['logo_color_144'], "type": "image/png", "sizes": "144x144"},
{"src": theme_values['logo_color_512'], "type": "image/png", "sizes": "512x512"}
]
icons = [{"src": theme_values['logo_color_svg'], "sizes": "any"}, {"src": theme_values['logo_color_144'], "type": "image/png", "sizes": "144x144"},
{"src": theme_values['logo_color_512'], "type": "image/png", "sizes": "512x512"}]
manifest_info = {
"name": theme_values['app_name'],
"short_name": theme_values['app_name'],
"description": _("Manage recipes, shopping list, meal plans and more."),
"icons": icons,
"start_url": "./search",
"background_color": theme_values['nav_bg_color'],
"display": "standalone",
"scope": ".",
"theme_color": theme_values['nav_bg_color'],
"shortcuts": [
{
"name": _("Plan"),
"short_name": _("Plan"),
"description": _("View your meal Plan"),
"url": "./plan"
},
{
"name": _("Books"),
"short_name": _("Books"),
"description": _("View your cookbooks"),
"url": "./books"
},
{
"name": _("Shopping"),
"short_name": _("Shopping"),
"description": _("View your shopping lists"),
"url": "./list/shopping-list/"
}
],
"share_target": {
"action": "/data/import/url",
"method": "GET",
"params": {
"title": "title",
"url": "url",
"text": "text"
}
}
"name":
theme_values['app_name'], "short_name":
theme_values['app_name'], "description":
_("Manage recipes, shopping list, meal plans and more."), "icons":
icons, "start_url":
"./search", "background_color":
theme_values['nav_bg_color'], "display":
"standalone", "scope":
".", "theme_color":
theme_values['nav_bg_color'], "shortcuts":
[{"name": _("Plan"), "short_name": _("Plan"), "description": _("View your meal Plan"), "url":
"./plan"}, {"name": _("Books"), "short_name": _("Books"), "description": _("View your cookbooks"), "url": "./books"},
{"name": _("Shopping"), "short_name": _("Shopping"), "description": _("View your shopping lists"), "url":
"./list/shopping-list/"}], "share_target": {"action": "/data/import/url", "method": "GET", "params": {"title": "title", "url": "url", "text": "text"}}
}
return JsonResponse(manifest_info, json_dumps_params={'indent': 4})
@@ -565,9 +493,7 @@ def test(request):
from cookbook.helper.ingredient_parser import IngredientParser
parser = IngredientParser(request, False)
data = {
'original': '90g golden syrup'
}
data = {'original': '90g golden syrup'}
data['parsed'] = parser.parse(data['original'])
return render(request, 'test.html', {'data': data})