API pour l'applification (#60773) #119
|
@ -0,0 +1,165 @@
|
|||
# combo - content management system
|
||||
# Copyright (C) 2017-2023 Entr'ouvert
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify it
|
||||
# under the terms of the GNU Affero General Public License as published
|
||||
# by the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import io
|
||||
import json
|
||||
import tarfile
|
||||
|
||||
from django.contrib.auth.models import Group
|
||||
from django.shortcuts import get_object_or_404, redirect
|
||||
from django.urls import reverse
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from rest_framework import permissions
|
||||
from rest_framework.generics import GenericAPIView
|
||||
from rest_framework.response import Response
|
||||
|
||||
from combo.data.models import Page
|
||||
from combo.data.utils import import_site
|
||||
from combo.utils.misc import is_portal_agent
|
||||
|
||||
|
||||
class Index(GenericAPIView):
|
||||
permission_classes = (permissions.IsAuthenticated,)
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
if is_portal_agent():
|
||||
response = [
|
||||
{
|
||||
'id': 'portal-agent-pages',
|
||||
'text': _('Pages (agent portal)'),
|
||||
'singular': _('Page (agent portal)'),
|
||||
},
|
||||
]
|
||||
else:
|
||||
response = [
|
||||
{'id': 'pages', 'text': _('Pages'), 'singular': _('Page')},
|
||||
]
|
||||
|
||||
response[0]['urls'] = {
|
||||
'list': request.build_absolute_uri(reverse('api-export-import-pages-list')),
|
||||
}
|
||||
|
||||
return Response({'data': response})
|
||||
|
||||
|
||||
index = Index.as_view()
|
||||
|
||||
|
||||
def get_page_bundle_entry(request, page, order):
|
||||
return {
|
||||
'id': str(page.uuid),
|
||||
'text': page.title,
|
||||
'indent': getattr(page, 'level', 0),
|
||||
'type': 'portal-agent-pages' if is_portal_agent() else 'pages',
|
||||
|
||||
'order': order,
|
||||
'urls': {
|
||||
'export': request.build_absolute_uri(
|
||||
reverse('api-export-import-page-export', kwargs={'uuid': str(page.uuid)})
|
||||
lguerin
commented
Plutôt utiliser page.uuid partout, non ? Plutôt utiliser page.uuid partout, non ?
fpeters
commented
Oui en effet, mis dans un commit supplémentaire. Oui en effet, mis dans un commit supplémentaire.
|
||||
),
|
||||
'dependencies': request.build_absolute_uri(
|
||||
reverse('api-export-import-page-dependencies', kwargs={'uuid': str(page.uuid)})
|
||||
),
|
||||
'redirect': request.build_absolute_uri(
|
||||
reverse('api-export-import-page-redirect', kwargs={'uuid': str(page.uuid)})
|
||||
),
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
class ListPages(GenericAPIView):
|
||||
permission_classes = (permissions.IsAuthenticated,)
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
pages = Page.get_as_reordered_flat_hierarchy(Page.objects.all())
|
||||
response = [get_page_bundle_entry(request, x, i) for i, x in enumerate(pages)]
|
||||
return Response({'data': response})
|
||||
|
||||
|
||||
list_pages = ListPages.as_view()
|
||||
|
||||
|
||||
class ExportPage(GenericAPIView):
|
||||
permission_classes = (permissions.IsAuthenticated,)
|
||||
|
||||
def get(self, request, uuid, *args, **kwargs):
|
||||
serialisation = Page.objects.get(uuid=uuid).get_serialized_page()
|
||||
return Response({'data': serialisation})
|
||||
|
||||
|
||||
export_page = ExportPage.as_view()
|
||||
|
||||
|
||||
class PageDependencies(GenericAPIView):
|
||||
permission_classes = (permissions.IsAuthenticated,)
|
||||
|
||||
def get(self, request, uuid, *args, **kwargs):
|
||||
page = Page.objects.get(uuid=uuid)
|
||||
|
||||
def dependency_dict(element):
|
||||
if isinstance(element, Group):
|
||||
return {
|
||||
'id': element.role.slug if hasattr(element, 'role') else element.id,
|
||||
'text': element.name,
|
||||
'type': 'roles',
|
||||
'urls': {},
|
||||
}
|
||||
elif isinstance(element, Page):
|
||||
return get_page_bundle_entry(request, element, 0)
|
||||
return element
|
||||
|
||||
dependencies = [dependency_dict(x) for x in page.get_dependencies() if x]
|
||||
return Response({'err': 0, 'data': dependencies})
|
||||
|
||||
|
||||
page_dependencies = PageDependencies.as_view()
|
||||
|
||||
|
||||
def page_redirect(request, uuid):
|
||||
page = get_object_or_404(Page, uuid=uuid)
|
||||
return redirect(reverse('combo-manager-page-view', kwargs={'pk': page.pk}))
|
||||
|
||||
|
||||
class BundleDeclare(GenericAPIView):
|
||||
permission_classes = (permissions.IsAuthenticated,)
|
||||
|
||||
def put(self, request, *args, **kwargs):
|
||||
return Response({'err': 0})
|
||||
fpeters
commented
Je laisse bundle-declare de côté. Je laisse bundle-declare de côté.
|
||||
|
||||
|
||||
bundle_declare = BundleDeclare.as_view()
|
||||
|
||||
|
||||
class BundleImport(GenericAPIView):
|
||||
permission_classes = (permissions.IsAuthenticated,)
|
||||
|
||||
def put(self, request, *args, **kwargs):
|
||||
tar_io = io.BytesIO(request.read())
|
||||
page_type = 'portal-agent-pages' if is_portal_agent() else 'pages'
|
||||
pages = []
|
||||
with tarfile.open(fileobj=tar_io) as tar:
|
||||
manifest = json.loads(tar.extractfile('manifest.json').read().decode())
|
||||
for element in manifest.get('elements'):
|
||||
if element.get('type') != page_type:
|
||||
continue
|
||||
pages.append(
|
||||
json.loads(tar.extractfile(f'{page_type}/{element["slug"]}').read().decode()).get('data')
|
||||
)
|
||||
if pages:
|
||||
import_site({'pages': pages})
|
||||
fpeters
commented
On prend toutes les pages et on importe tout ça; ça devrait s'étendre facilement pour y ajouter les cartes etc. On prend toutes les pages et on importe tout ça; ça devrait s'étendre facilement pour y ajouter les cartes etc.
|
||||
return Response({'err': 0})
|
||||
|
||||
|
||||
bundle_import = BundleImport.as_view()
|
|
@ -0,0 +1,28 @@
|
|||
# combo - content management system
|
||||
# Copyright (C) 2017-2023 Entr'ouvert
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify it
|
||||
# under the terms of the GNU Affero General Public License as published
|
||||
# by the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import django.apps
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
|
||||
class AppConfig(django.apps.AppConfig):
|
||||
name = 'combo.apps.export_import'
|
||||
verbose_name = _('Export/Import')
|
||||
|
||||
def get_before_urls(self):
|
||||
from . import urls
|
||||
|
||||
return urls.urlpatterns
|
|
@ -0,0 +1,37 @@
|
|||
# combo - content management system
|
||||
# Copyright (C) 2017-2023 Entr'ouvert
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify it
|
||||
# under the terms of the GNU Affero General Public License as published
|
||||
# by the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from django.urls import path
|
||||
|
||||
from . import api_views
|
||||
|
||||
urlpatterns = [
|
||||
path('api/export-import/', api_views.index, name='api-export-import'),
|
||||
path('api/export-import/pages/', api_views.list_pages, name='api-export-import-pages-list'),
|
||||
path('api/export-import/pages/<uuid:uuid>/', api_views.export_page, name='api-export-import-page-export'),
|
||||
path(
|
||||
'api/export-import/pages/<uuid:uuid>/dependencies/',
|
||||
api_views.page_dependencies,
|
||||
name='api-export-import-page-dependencies',
|
||||
),
|
||||
path(
|
||||
'api/export-import/pages/<uuid:uuid>/redirect/',
|
||||
api_views.page_redirect,
|
||||
name='api-export-import-page-redirect',
|
||||
),
|
||||
path('api/export-import/bundle-declare/', api_views.bundle_declare),
|
||||
path('api/export-import/bundle-import/', api_views.bundle_import),
|
||||
]
|
|
@ -171,6 +171,18 @@ class WcsFormCell(CellBase):
|
|||
def render_for_search(self):
|
||||
return ''
|
||||
|
||||
def get_dependencies(self):
|
||||
yield from super().get_dependencies()
|
||||
if self.formdef_reference:
|
||||
wcs_key, form_slug = self.formdef_reference.split(':')
|
||||
wcs_site_url = get_wcs_services().get(wcs_key)['url']
|
||||
urls = {
|
||||
'export': f'{wcs_site_url}api/export-import/forms/{form_slug}/',
|
||||
'dependencies': f'{wcs_site_url}api/export-import/forms/{form_slug}/dependencies/',
|
||||
fpeters
commented
Détection des dépendances inter-applications; si on a une cellule démarche on a la démarche en dépendance, et pareil pour les catégories de démarches, et les modèles de fiches. Détection des dépendances inter-applications; si on a une cellule démarche on a la démarche en dépendance, et pareil pour les catégories de démarches, et les modèles de fiches.
|
||||
'redirect': f'{wcs_site_url}api/export-import/forms/{form_slug}/redirect/',
|
||||
}
|
||||
yield {'type': 'forms', 'id': form_slug, 'text': self.cached_title, 'urls': urls}
|
||||
|
||||
def get_external_links_data(self):
|
||||
if not (self.cached_url and self.cached_title):
|
||||
return []
|
||||
|
@ -278,6 +290,18 @@ class WcsCommonCategoryCell(CellBase):
|
|||
def get_inspect_keys(self):
|
||||
return [k for k in super().get_inspect_keys() if not k.startswith('cached_')]
|
||||
|
||||
def get_dependencies(self):
|
||||
yield from super().get_dependencies()
|
||||
if self.category_reference:
|
||||
wcs_key, category_slug = self.category_reference.split(':')
|
||||
wcs_site_url = get_wcs_services().get(wcs_key)['url']
|
||||
urls = {
|
||||
'export': f'{wcs_site_url}api/export-import/forms-categories/{category_slug}/',
|
||||
'dependencies': f'{wcs_site_url}api/export-import/forms-categories/{category_slug}/dependencies/',
|
||||
'redirect': f'{wcs_site_url}api/export-import/forms-xategories/{category_slug}/redirect/',
|
||||
}
|
||||
yield {'type': 'forms-categories', 'id': category_slug, 'text': self.cached_title, 'urls': urls}
|
||||
|
||||
|
||||
@register_cell_class
|
||||
class WcsCategoryCell(WcsCommonCategoryCell):
|
||||
|
@ -1017,6 +1041,19 @@ class WcsCardCell(CardMixin, CellBase):
|
|||
return False
|
||||
return super().is_visible(request, **kwargs)
|
||||
|
||||
def get_dependencies(self):
|
||||
yield from super().get_dependencies()
|
||||
if self.carddef_reference:
|
||||
parts = self.carddef_reference.split(':')
|
||||
wcs_key, card_slug = parts[:2]
|
||||
wcs_site_url = get_wcs_services().get(wcs_key)['url']
|
||||
urls = {
|
||||
'export': f'{wcs_site_url}api/export-import/cards/{card_slug}/',
|
||||
'dependencies': f'{wcs_site_url}api/export-import/cards/{card_slug}/dependencies/',
|
||||
'redirect': f'{wcs_site_url}api/export-import/cards/{card_slug}/redirect/',
|
||||
}
|
||||
lguerin
commented
Il va manquer, dans le cas d'un cellule avec custo, qui contient un champ de type lien, la dépendance à la page qui peut être paramétrée en target (mais on peut en faire un ticket) (je pose ça ici, parce que l'app search n'a pas été modifiée dans cette PR: Il manquera aussi, pour les cellules de recherche, les dépendances à des fiches ou pages etc, mais là aussi on peut en faire un ticket) Il va manquer, dans le cas d'un cellule avec custo, qui contient un champ de type lien, la dépendance à la page qui peut être paramétrée en target (mais on peut en faire un ticket)
(je pose ça ici, parce que l'app search n'a pas été modifiée dans cette PR: Il manquera aussi, pour les cellules de recherche, les dépendances à des fiches ou pages etc, mais là aussi on peut en faire un ticket)
fpeters
commented
Ça me va bien de passer par des tickets supplémentaires pour affiner/compléter tout ça (évidemment…). Ça me va bien de passer par des tickets supplémentaires pour affiner/compléter tout ça (évidemment…).
|
||||
yield {'type': 'cards', 'id': card_slug, 'text': self.cached_title, 'urls': urls}
|
||||
|
||||
def check_validity(self):
|
||||
if self.get_related_card_path():
|
||||
relations = [r[0] for r in self.get_related_card_paths()]
|
||||
|
|
|
@ -377,6 +377,14 @@ class Page(models.Model):
|
|||
def get_descendants_and_me(self):
|
||||
return self.get_descendants(include_myself=True)
|
||||
|
||||
def get_dependencies(self):
|
||||
yield from self.get_children()
|
||||
yield self.edit_role
|
||||
yield self.subpages_edit_role
|
||||
fpeters
commented
En dépendances on a aussi les rôles. En dépendances on a aussi les rôles.
|
||||
yield from self.groups.all()
|
||||
for cell in self.get_cells():
|
||||
yield from cell.get_dependencies()
|
||||
|
||||
def get_template_display_name(self):
|
||||
try:
|
||||
return settings.COMBO_PUBLIC_TEMPLATES[self.template_name]['name']
|
||||
|
@ -1203,6 +1211,9 @@ class CellBase(models.Model, metaclass=CellMeta):
|
|||
def get_label(self):
|
||||
return self.get_verbose_name()
|
||||
|
||||
def get_dependencies(self):
|
||||
yield from self.groups.all()
|
||||
|
||||
def get_manager_tabs(self):
|
||||
from combo.manager.forms import CellVisibilityForm
|
||||
|
||||
|
@ -1875,6 +1886,10 @@ class LinkCell(CellBase):
|
|||
def render_for_search(self):
|
||||
return ''
|
||||
|
||||
def get_dependencies(self):
|
||||
yield from super().get_dependencies()
|
||||
yield self.link_page
|
||||
|
||||
def get_external_links_data(self):
|
||||
if not self.url:
|
||||
return []
|
||||
|
|
|
@ -68,6 +68,7 @@ INSTALLED_APPS = (
|
|||
'combo.apps.dashboard',
|
||||
'combo.apps.wcs',
|
||||
'combo.apps.publik',
|
||||
'combo.apps.export_import',
|
||||
'combo.apps.family',
|
||||
'combo.apps.dataviz',
|
||||
'combo.apps.lingo',
|
||||
|
|
|
@ -0,0 +1,308 @@
|
|||
import io
|
||||
import json
|
||||
import tarfile
|
||||
from unittest import mock
|
||||
|
||||
import pytest
|
||||
from django.contrib.auth.models import Group
|
||||
|
||||
from combo.apps.wcs.models import WcsCardCell, WcsCategoryCell, WcsFormCell
|
||||
from combo.data.models import LinkCell, LinkListCell, Page, TextCell
|
||||
|
||||
from .wcs.utils import mocked_requests_send
|
||||
|
||||
pytestmark = pytest.mark.django_db
|
||||
|
||||
|
||||
def test_object_types(settings, app, john_doe):
|
||||
app.authorization = ('Basic', (john_doe.username, john_doe.username))
|
||||
resp = app.get('/api/export-import/')
|
||||
assert resp.json == {
|
||||
'data': [
|
||||
{
|
||||
'id': 'pages',
|
||||
'text': 'Pages',
|
||||
'singular': 'Page',
|
||||
'urls': {'list': 'http://testserver/api/export-import/pages/'},
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
with mock.patch('combo.apps.export_import.api_views.is_portal_agent') as is_portal_agent:
|
||||
is_portal_agent.return_value = True
|
||||
resp = app.get('/api/export-import/')
|
||||
assert resp.json == {
|
||||
'data': [
|
||||
{
|
||||
'id': 'portal-agent-pages',
|
||||
'text': 'Pages (agent portal)',
|
||||
'singular': 'Page (agent portal)',
|
||||
'urls': {'list': 'http://testserver/api/export-import/pages/'},
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
def test_list_pages(app, john_doe):
|
||||
app.authorization = ('Basic', (john_doe.username, john_doe.username))
|
||||
Page.objects.all().delete()
|
||||
page = Page(title='Test', slug='test', template_name='standard')
|
||||
page.save()
|
||||
page2 = Page(title='Child', slug='child', template_name='standard', parent=page)
|
||||
page2.save()
|
||||
resp = app.get('/api/export-import/pages/')
|
||||
assert resp.json == {
|
||||
'data': [
|
||||
{
|
||||
'id': f'{page.uuid}',
|
||||
'text': 'Test',
|
||||
'type': 'pages',
|
||||
'indent': 0,
|
||||
'order': 0,
|
||||
'urls': {
|
||||
'export': f'http://testserver/api/export-import/pages/{page.uuid}/',
|
||||
'dependencies': f'http://testserver/api/export-import/pages/{page.uuid}/dependencies/',
|
||||
'redirect': f'http://testserver/api/export-import/pages/{page.uuid}/redirect/',
|
||||
},
|
||||
},
|
||||
{
|
||||
'id': f'{page2.uuid}',
|
||||
'text': 'Child',
|
||||
'type': 'pages',
|
||||
'indent': 1,
|
||||
'order': 1,
|
||||
'urls': {
|
||||
'export': f'http://testserver/api/export-import/pages/{page2.uuid}/',
|
||||
'dependencies': f'http://testserver/api/export-import/pages/{page2.uuid}/dependencies/',
|
||||
'redirect': f'http://testserver/api/export-import/pages/{page2.uuid}/redirect/',
|
||||
},
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
def test_export_page(app, john_doe):
|
||||
app.authorization = ('Basic', (john_doe.username, john_doe.username))
|
||||
Page.objects.all().delete()
|
||||
page = Page(title='Test', slug='test', template_name='standard')
|
||||
page.save()
|
||||
resp = app.get(f'/api/export-import/pages/{page.uuid}/')
|
||||
assert resp.json['data']['fields']['title'] == 'Test'
|
||||
|
||||
|
||||
def test_export_page_with_role(app, john_doe):
|
||||
app.authorization = ('Basic', (john_doe.username, john_doe.username))
|
||||
group = Group(name='plop')
|
||||
group.save()
|
||||
Page.objects.all().delete()
|
||||
page = Page(title='Test', slug='test', template_name='standard')
|
||||
page.save()
|
||||
page.groups.set([group])
|
||||
resp = app.get(f'/api/export-import/pages/{page.uuid}/')
|
||||
assert resp.json['data']['fields']['groups'] == ['plop']
|
||||
|
||||
|
||||
def test_page_dependencies_groups(app, john_doe):
|
||||
app.authorization = ('Basic', (john_doe.username, john_doe.username))
|
||||
group1 = Group(name='plop1')
|
||||
group1.save()
|
||||
group2 = Group(name='plop2')
|
||||
group2.save()
|
||||
Page.objects.all().delete()
|
||||
page = Page(title='Test', slug='test', template_name='standard')
|
||||
page.save()
|
||||
page.groups.set([group1])
|
||||
cell = TextCell(page=page, placeholder='content', text='Foobar', order=0)
|
||||
cell.save()
|
||||
cell.groups.set([group2])
|
||||
resp = app.get(f'/api/export-import/pages/{page.uuid}/dependencies/')
|
||||
# note: with hobo.agent.common installed, 'groups' will contain group slugs,
|
||||
# not group id
|
||||
assert resp.json == {
|
||||
'data': [
|
||||
{'id': group1.id, 'text': group1.name, 'type': 'roles', 'urls': {}},
|
||||
{'id': group2.id, 'text': group2.name, 'type': 'roles', 'urls': {}},
|
||||
],
|
||||
'err': 0,
|
||||
}
|
||||
|
||||
|
||||
def test_page_dependencies_children(app, john_doe):
|
||||
app.authorization = ('Basic', (john_doe.username, john_doe.username))
|
||||
page = Page(title='Test', slug='test', template_name='standard')
|
||||
page.save()
|
||||
page2 = Page(title='Child', slug='child', template_name='standard', parent=page)
|
||||
page2.save()
|
||||
resp = app.get(f'/api/export-import/pages/{page.uuid}/dependencies/')
|
||||
assert resp.json == {
|
||||
'data': [
|
||||
{
|
||||
'id': f'{page2.uuid}',
|
||||
'indent': 0,
|
||||
'order': 0,
|
||||
'text': 'Child',
|
||||
'type': 'pages',
|
||||
'urls': {
|
||||
'dependencies': f'http://testserver/api/export-import/pages/{page2.uuid}/dependencies/',
|
||||
'export': f'http://testserver/api/export-import/pages/{page2.uuid}/',
|
||||
'redirect': f'http://testserver/api/export-import/pages/{page2.uuid}/redirect/',
|
||||
},
|
||||
},
|
||||
],
|
||||
'err': 0,
|
||||
}
|
||||
|
||||
|
||||
def test_page_redirect(app, john_doe):
|
||||
app.authorization = ('Basic', (john_doe.username, john_doe.username))
|
||||
page = Page(title='Test', slug='test', template_name='standard')
|
||||
page.save()
|
||||
resp = app.get(f'/api/export-import/pages/{page.uuid}/redirect/')
|
||||
assert resp.location == f'/manage/pages/{page.id}/'
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def bundle(app, john_doe):
|
||||
app.authorization = ('Basic', (john_doe.username, john_doe.username))
|
||||
|
||||
group = Group(name='plop')
|
||||
group.save()
|
||||
|
||||
page = Page(title='Test Page', slug='test', template_name='standard')
|
||||
page.save()
|
||||
page.groups.set([group])
|
||||
page_export = app.get(f'/api/export-import/pages/{page.uuid}/').content
|
||||
|
||||
tar_io = io.BytesIO()
|
||||
with tarfile.open(mode='w', fileobj=tar_io) as tar:
|
||||
manifest_json = {
|
||||
'application': 'Test',
|
||||
'slug': 'test',
|
||||
'icon': 'foo.png',
|
||||
'description': 'Foo Bar',
|
||||
'documentation_url': 'http://foo.bar',
|
||||
'visible': True,
|
||||
'version_number': '42.0',
|
||||
'version_notes': 'foo bar blah',
|
||||
'elements': [
|
||||
{'type': 'pages', 'slug': f'{page.uuid}', 'name': 'Test Page', 'auto-dependency': False},
|
||||
{'type': 'form', 'slug': 'xxx', 'name': 'Xxx'},
|
||||
],
|
||||
}
|
||||
manifest_fd = io.BytesIO(json.dumps(manifest_json, indent=2).encode())
|
||||
tarinfo = tarfile.TarInfo('manifest.json')
|
||||
tarinfo.size = len(manifest_fd.getvalue())
|
||||
tar.addfile(tarinfo, fileobj=manifest_fd)
|
||||
|
||||
tarinfo = tarfile.TarInfo(f'pages/{page.uuid}')
|
||||
tarinfo.size = len(page_export)
|
||||
tar.addfile(tarinfo, fileobj=io.BytesIO(page_export))
|
||||
bundle = tar_io.getvalue()
|
||||
Page.objects.all().delete()
|
||||
return bundle
|
||||
|
||||
|
||||
def test_bundle_import(app, john_doe, bundle):
|
||||
app.authorization = ('Basic', (john_doe.username, john_doe.username))
|
||||
|
||||
resp = app.put('/api/export-import/bundle-import/', bundle)
|
||||
assert Page.objects.all().count() == 1
|
||||
assert resp.json['err'] == 0
|
||||
|
||||
# check update
|
||||
resp = app.put('/api/export-import/bundle-import/', bundle)
|
||||
assert Page.objects.all().count() == 1
|
||||
assert resp.json['err'] == 0
|
||||
|
||||
|
||||
def test_bundle_declare(app, john_doe, bundle):
|
||||
app.authorization = ('Basic', (john_doe.username, john_doe.username))
|
||||
|
||||
resp = app.put('/api/export-import/bundle-declare/', bundle)
|
||||
assert Page.objects.all().count() == 0
|
||||
assert resp.json['err'] == 0
|
||||
|
||||
|
||||
def test_page_dependencies_link_cell():
|
||||
page1 = Page.objects.create(title='Test', slug='test', template_name='standard')
|
||||
page2 = Page.objects.create(title='Other page', slug='other', template_name='standard')
|
||||
LinkCell.objects.create(page=page1, placeholder='content', link_page=page2, order=0)
|
||||
assert page2 in page1.get_dependencies()
|
||||
|
||||
|
||||
def test_page_dependencies_linkslist_cell():
|
||||
page1 = Page.objects.create(title='Test', slug='test', template_name='standard')
|
||||
page2 = Page.objects.create(title='Other page', slug='other', template_name='standard')
|
||||
|
||||
links = LinkListCell.objects.create(order=1, page=page1, placeholder='content')
|
||||
LinkCell.objects.create(page=page1, placeholder=links.link_placeholder, link_page=page2, order=0)
|
||||
assert page2 in page1.get_dependencies()
|
||||
|
||||
|
||||
@mock.patch('requests.Session.send', side_effect=mocked_requests_send)
|
||||
def test_page_dependencies_form_cell(mock_send, app, john_doe):
|
||||
page = Page.objects.create(title='Test', slug='test', template_name='standard')
|
||||
cell = WcsFormCell(page=page, placeholder='content', order=0, formdef_reference='default:form-title')
|
||||
cell.save()
|
||||
assert {
|
||||
'type': 'forms',
|
||||
'id': 'form-title',
|
||||
'text': cell.cached_title,
|
||||
'urls': {
|
||||
'export': 'http://127.0.0.1:8999/api/export-import/forms/form-title/',
|
||||
'dependencies': 'http://127.0.0.1:8999/api/export-import/forms/form-title/dependencies/',
|
||||
'redirect': 'http://127.0.0.1:8999/api/export-import/forms/form-title/redirect/',
|
||||
},
|
||||
} in page.get_dependencies()
|
||||
app.authorization = ('Basic', (john_doe.username, john_doe.username))
|
||||
resp = app.get(f'/api/export-import/pages/{page.uuid}/dependencies/')
|
||||
assert resp.json['data'][0]['type'] == 'forms'
|
||||
|
||||
|
||||
@mock.patch('requests.Session.send', side_effect=mocked_requests_send)
|
||||
def test_page_dependencies_card_cell(mock_send):
|
||||
page = Page.objects.create(title='Test', slug='test', template_name='standard')
|
||||
cell = WcsCardCell(page=page, placeholder='content', order=0, carddef_reference='default:card_model_1')
|
||||
cell.save()
|
||||
assert {
|
||||
'type': 'cards',
|
||||
'id': 'card_model_1',
|
||||
'text': 'Card Model 1',
|
||||
'urls': {
|
||||
'export': 'http://127.0.0.1:8999/api/export-import/cards/card_model_1/',
|
||||
'dependencies': 'http://127.0.0.1:8999/api/export-import/cards/card_model_1/dependencies/',
|
||||
'redirect': 'http://127.0.0.1:8999/api/export-import/cards/card_model_1/redirect/',
|
||||
},
|
||||
} in page.get_dependencies()
|
||||
|
||||
cell = WcsCardCell(
|
||||
page=page, placeholder='content', order=0, carddef_reference='default:card_model_1:custom_view'
|
||||
)
|
||||
cell.save()
|
||||
assert {
|
||||
'type': 'cards',
|
||||
'id': 'card_model_1',
|
||||
'text': 'Card Model 1',
|
||||
'urls': {
|
||||
'export': 'http://127.0.0.1:8999/api/export-import/cards/card_model_1/',
|
||||
'dependencies': 'http://127.0.0.1:8999/api/export-import/cards/card_model_1/dependencies/',
|
||||
'redirect': 'http://127.0.0.1:8999/api/export-import/cards/card_model_1/redirect/',
|
||||
},
|
||||
} in page.get_dependencies()
|
||||
|
||||
|
||||
@mock.patch('requests.Session.send', side_effect=mocked_requests_send)
|
||||
def test_page_dependencies_category_cell(mock_send):
|
||||
page = Page.objects.create(title='Test', slug='test', template_name='standard')
|
||||
cell = WcsCategoryCell(page=page, placeholder='content', order=0, category_reference='default:test-3')
|
||||
cell.save()
|
||||
assert {
|
||||
'type': 'forms-categories',
|
||||
'id': 'test-3',
|
||||
'text': 'Test 3',
|
||||
'urls': {
|
||||
'export': 'http://127.0.0.1:8999/api/export-import/forms-categories/test-3/',
|
||||
'dependencies': 'http://127.0.0.1:8999/api/export-import/forms-categories/test-3/dependencies/',
|
||||
'redirect': 'http://127.0.0.1:8999/api/export-import/forms-xategories/test-3/redirect/',
|
||||
},
|
||||
} in page.get_dependencies()
|
On distingue deux types de pages pour les applications, selon qu'on est le portail agent ou pas. (et ça devra être la même affaire quand on exportera davantage d'objets type les couches carto).
Ça permet de rester dans le modèle hobo actuel, sans devoir ajouter une notion de type de service qui distinguerait à ce niveau les pages du portail agent des pages du portail usager.