From 9349ef24cc0c65008c5ec724159d2979222c1051 Mon Sep 17 00:00:00 2001 From: Francesco Filicetti Date: Fri, 21 May 2021 11:44:05 +0200 Subject: [PATCH] feat: menu cloning --- requirements.txt | 1 + src/cms/api/tests/test_menu.py | 14 ++++++- src/cms/api/urls.py | 1 + src/cms/api/views/menu.py | 41 ++++++++++++++++++- src/cms/contexts/utils.py | 27 ++++++++++++ .../templatetags/unicms_templates.py | 5 +++ 6 files changed, 87 insertions(+), 2 deletions(-) diff --git a/requirements.txt b/requirements.txt index b63d7577..55a944be 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,5 @@ django>=2.0,<4.0 +git+https://github.com/UniversitaDellaCalabria/django-auto-serializer@dev django-filter django-nested-admin>=3.3.2 django-taggit diff --git a/src/cms/api/tests/test_menu.py b/src/cms/api/tests/test_menu.py index 57e49c94..1cf1c9a1 100644 --- a/src/cms/api/tests/test_menu.py +++ b/src/cms/api/tests/test_menu.py @@ -6,7 +6,7 @@ from django.test import Client, TestCase from django.urls import reverse -from cms.menus.models import NavigationBar +from cms.menus.models import NavigationBar, NavigationBarItem from cms.menus.tests import MenuUnitTest from cms.contexts.tests import ContextUnitTest @@ -61,6 +61,18 @@ def test_menu(self): res = req.get(url, content_type='application/json',) assert isinstance(res.json(), dict) + # CLONE + current_menus = NavigationBar.objects.all().count() + url = reverse('unicms_api:editorial-board-menu-clone', kwargs={'pk': menu.pk}) + res = req.get(url, content_type='application/json',) + assert isinstance(res.json(), dict) + new_current_menus = NavigationBar.objects.all().count() + assert new_current_menus == current_menus + 1 + source_items = NavigationBarItem.objects.filter(menu=menu) + new_menu = NavigationBar.objects.last() + dest_items = NavigationBarItem.objects.filter(menu=new_menu) + assert source_items.count() == dest_items.count() + # GET, patch, put, delete url = reverse('unicms_api:editorial-board-menu', kwargs={'pk': menu.pk}) diff --git a/src/cms/api/urls.py b/src/cms/api/urls.py index 34b17b52..8a1379a5 100644 --- a/src/cms/api/urls.py +++ b/src/cms/api/urls.py @@ -329,6 +329,7 @@ urlpatterns += path(f'{menu_prefix}/', menu.MenuList.as_view(), name='editorial-board-menus'), urlpatterns += path(f'{menu_prefix}//', menu.MenuView.as_view(), name='editorial-board-menu'), urlpatterns += path(f'{menu_prefix}//logs/', menu.MenuLogsView.as_view(), name='editorial-board-menu-logs'), +urlpatterns += path(f'{menu_prefix}//clone/', menu.MenuCloneView.as_view(), name='editorial-board-menu-clone'), urlpatterns += path(f'{menu_prefix}/form/', menu.MenuFormView.as_view(), name='editorial-board-menu-form'), # menu items diff --git a/src/cms/api/views/menu.py b/src/cms/api/views/menu.py index eb3593ef..4a92d24c 100644 --- a/src/cms/api/views/menu.py +++ b/src/cms/api/views/menu.py @@ -2,6 +2,7 @@ from django.http import Http404, HttpResponseRedirect from django.shortcuts import get_object_or_404 from django.urls import reverse +from django.utils import timezone from django.utils.decorators import method_decorator from rest_framework.exceptions import NotFound, PermissionDenied @@ -11,13 +12,14 @@ from rest_framework.views import APIView from cms.contexts.decorators import detect_language +from cms.contexts.utils import clone from cms.menus.forms import MenuForm from cms.menus.models import NavigationBar from cms.menus.serializers import MenuSerializer from . generics import UniCMSCachedRetrieveUpdateDestroyAPIView, UniCMSListCreateAPIView from . logs import ObjectLogEntriesList -from .. exceptions import LoggedPermissionDenied +from .. exceptions import LoggedPermissionDenied, LoggedValidationException from .. permissions import MenuGetCreatePermissions from .. serializers import UniCMSFormSerializer from .. utils import check_user_permission_on_object @@ -170,3 +172,40 @@ def get_queryset(self, **kwargs): item = get_object_or_404(NavigationBar, pk=object_id) content_type_id = ContentType.objects.get_for_model(item).pk return super().get_queryset(object_id, content_type_id) + + +class MenuCloneSchema(AutoSchema): + def get_operation_id(self, path, method):# pragma: no cover + return 'cloneMenu' + + +class MenuCloneView(APIView): + + schema = MenuCloneSchema() + description = "" + permission_classes = [MenuGetCreatePermissions] + serializer_class = MenuSerializer + + def get_queryset(self): + """ + """ + menu_id = self.kwargs['pk'] + menus = NavigationBar.objects.filter(pk=menu_id) + return menus + + def get(self, request, *args, **kwargs): + item = self.get_queryset().first() + if not item: raise Http404 + try: + new_menu = clone(item, + excluded_fields=['created', 'modified'], + excluded_childrens=['pagemenu'], + custom_values={'name': f'{item.name} (copy {timezone.localtime()})'}, + recursive_custom_values={'created_by': request.user, + 'modified_by': None}) + except Exception as e: + raise LoggedValidationException(classname=self.__class__.__name__, + resource=request.method, + detail=e) + result = self.serializer_class(new_menu) + return Response(result.data) diff --git a/src/cms/contexts/utils.py b/src/cms/contexts/utils.py index e4b04441..8e3dabbd 100644 --- a/src/cms/contexts/utils.py +++ b/src/cms/contexts/utils.py @@ -15,6 +15,8 @@ from copy import deepcopy +from django_auto_serializer.auto_serializer import * + logger = logging.getLogger(__name__) CMS_PATH_PREFIX = getattr(settings, 'CMS_PATH_PREFIX', '') @@ -176,3 +178,28 @@ def log_obj_event(user, obj, data={}, action_flag=CHANGE): object_repr = obj.__str__(), action_flag = action_flag, change_message = f'{msg}: {data}' or msg) + + +def clone(obj, + excluded_fields=[], + excluded_childrens=[], + custom_values={}, + recursive_custom_values={}): + """ + clone object using django_auto_serializer + """ + try: + si = SerializableInstance(obj, + excluded_fields=excluded_fields, + excluded_childrens=excluded_childrens, + auto_fields = False, + change_uniques = True, + duplicate = True) + si.serialize_tree() + si.remove_duplicates() + isi = ImportableSerializedInstance(si.dict) + new_obj = isi.save(custom_values=custom_values, + recursive_custom_values=recursive_custom_values) + return new_obj + except Exception as e: + raise e diff --git a/src/cms/templates/templatetags/unicms_templates.py b/src/cms/templates/templatetags/unicms_templates.py index 920b0125..6adf31b2 100644 --- a/src/cms/templates/templatetags/unicms_templates.py +++ b/src/cms/templates/templatetags/unicms_templates.py @@ -43,3 +43,8 @@ def settings_value(name, **kwargs): value = getattr(settings, name, None) if value and kwargs: return value.format(**kwargs) return value + + +@register.simple_tag +def installed_app(app_name): + return app_name in settings.INSTALLED_APPS