c2cgeoportal-admin 2.5.0.100__py3-none-any.whl → 2.9rc44__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- c2cgeoportal_admin/__init__.py +44 -14
- c2cgeoportal_admin/lib/__init__.py +0 -0
- c2cgeoportal_admin/lib/lingva_extractor.py +77 -0
- c2cgeoportal_admin/lib/ogcserver_synchronizer.py +410 -0
- c2cgeoportal_admin/py.typed +0 -0
- c2cgeoportal_admin/routes.py +30 -11
- c2cgeoportal_admin/schemas/dimensions.py +17 -11
- c2cgeoportal_admin/schemas/functionalities.py +60 -22
- c2cgeoportal_admin/schemas/interfaces.py +27 -19
- c2cgeoportal_admin/schemas/metadata.py +122 -48
- c2cgeoportal_admin/schemas/restriction_areas.py +26 -20
- c2cgeoportal_admin/schemas/roles.py +13 -7
- c2cgeoportal_admin/schemas/treegroup.py +90 -20
- c2cgeoportal_admin/schemas/treeitem.py +3 -4
- c2cgeoportal_admin/static/layertree.css +26 -4
- c2cgeoportal_admin/static/navbar.css +59 -36
- c2cgeoportal_admin/static/theme.css +51 -11
- c2cgeoportal_admin/subscribers.py +3 -3
- c2cgeoportal_admin/templates/404.jinja2 +41 -2
- c2cgeoportal_admin/templates/edit.jinja2 +23 -0
- c2cgeoportal_admin/templates/home.jinja2 +23 -0
- c2cgeoportal_admin/templates/index.jinja2 +23 -0
- c2cgeoportal_admin/templates/layertree.jinja2 +55 -11
- c2cgeoportal_admin/templates/layout.jinja2 +23 -0
- c2cgeoportal_admin/templates/navigation_navbar.jinja2 +56 -0
- c2cgeoportal_admin/templates/ogcserver_synchronize.jinja2 +90 -0
- c2cgeoportal_admin/templates/widgets/child.pt +35 -3
- c2cgeoportal_admin/templates/widgets/children.pt +121 -92
- c2cgeoportal_admin/templates/widgets/dimension.pt +23 -0
- c2cgeoportal_admin/templates/widgets/dimensions.pt +23 -0
- c2cgeoportal_admin/templates/widgets/functionality_fields.pt +51 -0
- c2cgeoportal_admin/templates/widgets/layer_fields.pt +23 -0
- c2cgeoportal_admin/templates/widgets/layer_group_fields.pt +23 -0
- c2cgeoportal_admin/templates/widgets/layer_v1_fields.pt +23 -0
- c2cgeoportal_admin/templates/widgets/metadata.pt +30 -1
- c2cgeoportal_admin/templates/widgets/metadatas.pt +23 -0
- c2cgeoportal_admin/templates/widgets/ogcserver_fields.pt +23 -0
- c2cgeoportal_admin/templates/widgets/restriction_area_fields.pt +25 -9
- c2cgeoportal_admin/templates/widgets/role_fields.pt +52 -25
- c2cgeoportal_admin/templates/widgets/theme_fields.pt +23 -0
- c2cgeoportal_admin/templates/widgets/user_fields.pt +23 -0
- c2cgeoportal_admin/views/__init__.py +29 -0
- c2cgeoportal_admin/views/dimension_layers.py +14 -9
- c2cgeoportal_admin/views/functionalities.py +52 -18
- c2cgeoportal_admin/views/home.py +5 -5
- c2cgeoportal_admin/views/interfaces.py +29 -21
- c2cgeoportal_admin/views/layer_groups.py +36 -25
- c2cgeoportal_admin/views/layers.py +17 -13
- c2cgeoportal_admin/views/layers_cog.py +135 -0
- c2cgeoportal_admin/views/layers_vectortiles.py +62 -27
- c2cgeoportal_admin/views/layers_wms.py +61 -36
- c2cgeoportal_admin/views/layers_wmts.py +54 -32
- c2cgeoportal_admin/views/layertree.py +37 -28
- c2cgeoportal_admin/views/logged_views.py +83 -0
- c2cgeoportal_admin/views/logs.py +91 -0
- c2cgeoportal_admin/views/oauth2_clients.py +96 -0
- c2cgeoportal_admin/views/ogc_servers.py +192 -21
- c2cgeoportal_admin/views/restriction_areas.py +78 -25
- c2cgeoportal_admin/views/roles.py +88 -25
- c2cgeoportal_admin/views/themes.py +47 -35
- c2cgeoportal_admin/views/themes_ordering.py +44 -24
- c2cgeoportal_admin/views/treeitems.py +21 -17
- c2cgeoportal_admin/views/users.py +46 -26
- c2cgeoportal_admin/widgets.py +79 -28
- {c2cgeoportal_admin-2.5.0.100.dist-info → c2cgeoportal_admin-2.9rc44.dist-info}/METADATA +15 -13
- c2cgeoportal_admin-2.9rc44.dist-info/RECORD +97 -0
- {c2cgeoportal_admin-2.5.0.100.dist-info → c2cgeoportal_admin-2.9rc44.dist-info}/WHEEL +1 -1
- c2cgeoportal_admin-2.9rc44.dist-info/entry_points.txt +5 -0
- tests/__init__.py +36 -27
- tests/conftest.py +23 -24
- tests/test_edit_url.py +16 -19
- tests/test_functionalities.py +52 -14
- tests/test_home.py +0 -1
- tests/test_interface.py +35 -12
- tests/test_layer_groups.py +58 -32
- tests/test_layers_cog.py +243 -0
- tests/test_layers_vectortiles.py +46 -30
- tests/test_layers_wms.py +77 -82
- tests/test_layers_wmts.py +51 -30
- tests/test_layertree.py +107 -101
- tests/test_learn.py +1 -1
- tests/test_left_menu.py +0 -1
- tests/test_lingva_extractor_config.py +64 -0
- tests/test_logs.py +102 -0
- tests/test_main.py +4 -2
- tests/test_metadatas.py +79 -71
- tests/test_oauth2_clients.py +186 -0
- tests/test_ogc_servers.py +110 -28
- tests/test_restriction_areas.py +109 -20
- tests/test_role.py +142 -82
- tests/test_themes.py +75 -41
- tests/test_themes_ordering.py +1 -2
- tests/test_treegroup.py +2 -2
- tests/test_user.py +72 -70
- tests/themes_ordering.py +1 -2
- c2cgeoportal_admin/templates/navigation_vertical.jinja2 +0 -10
- c2cgeoportal_admin-2.5.0.100.dist-info/RECORD +0 -84
- c2cgeoportal_admin-2.5.0.100.dist-info/entry_points.txt +0 -3
- {c2cgeoportal_admin-2.5.0.100.dist-info → c2cgeoportal_admin-2.9rc44.dist-info}/top_level.txt +0 -0
@@ -1,6 +1,4 @@
|
|
1
|
-
#
|
2
|
-
|
3
|
-
# Copyright (c) 2017-2020, Camptocamp SA
|
1
|
+
# Copyright (c) 2017-2024, Camptocamp SA
|
4
2
|
# All rights reserved.
|
5
3
|
|
6
4
|
# Redistribution and use in source and binary forms, with or without
|
@@ -30,8 +28,18 @@
|
|
30
28
|
|
31
29
|
from functools import partial
|
32
30
|
|
31
|
+
import sqlalchemy
|
32
|
+
from c2cgeoform import JSONDict
|
33
33
|
from c2cgeoform.schema import GeoFormSchemaNode
|
34
|
-
from c2cgeoform.views.abstract_views import
|
34
|
+
from c2cgeoform.views.abstract_views import (
|
35
|
+
DeleteResponse,
|
36
|
+
GridResponse,
|
37
|
+
IndexResponse,
|
38
|
+
ItemAction,
|
39
|
+
ListField,
|
40
|
+
ObjectResponse,
|
41
|
+
SaveResponse,
|
42
|
+
)
|
35
43
|
from deform.widget import FormWidget
|
36
44
|
from pyramid.view import view_config, view_defaults
|
37
45
|
from sqlalchemy import delete, insert, inspect, update
|
@@ -40,60 +48,71 @@ from zope.sqlalchemy import mark_changed
|
|
40
48
|
from c2cgeoportal_admin import _
|
41
49
|
from c2cgeoportal_admin.schemas.dimensions import dimensions_schema_node
|
42
50
|
from c2cgeoportal_admin.schemas.interfaces import interfaces_schema_node
|
43
|
-
from c2cgeoportal_admin.schemas.metadata import
|
51
|
+
from c2cgeoportal_admin.schemas.metadata import metadata_schema_node
|
44
52
|
from c2cgeoportal_admin.schemas.restriction_areas import restrictionareas_schema_node
|
45
53
|
from c2cgeoportal_admin.schemas.treeitem import parent_id_node
|
46
54
|
from c2cgeoportal_admin.views.dimension_layers import DimensionLayerViews
|
47
|
-
from c2cgeoportal_commons.models.main import LayerGroup, LayerWMS, LayerWMTS, OGCServer, TreeItem
|
55
|
+
from c2cgeoportal_commons.models.main import LayerGroup, LayerWMS, LayerWMTS, LogAction, OGCServer, TreeItem
|
48
56
|
|
49
57
|
_list_field = partial(ListField, LayerWMS)
|
50
58
|
|
51
59
|
base_schema = GeoFormSchemaNode(LayerWMS, widget=FormWidget(fields_template="layer_fields"))
|
52
|
-
base_schema.add(dimensions_schema_node.
|
53
|
-
base_schema.add(
|
54
|
-
base_schema.add(interfaces_schema_node.
|
55
|
-
base_schema.add(restrictionareas_schema_node.
|
60
|
+
base_schema.add(dimensions_schema_node(LayerWMS.dimensions))
|
61
|
+
base_schema.add(metadata_schema_node(LayerWMS.metadatas, LayerWMS))
|
62
|
+
base_schema.add(interfaces_schema_node(LayerWMS.interfaces))
|
63
|
+
base_schema.add(restrictionareas_schema_node(LayerWMS.restrictionareas))
|
56
64
|
base_schema.add_unique_validator(LayerWMS.name, LayerWMS.id)
|
57
65
|
base_schema.add(parent_id_node(LayerGroup))
|
58
66
|
|
59
67
|
|
60
68
|
@view_defaults(match_param="table=layers_wms")
|
61
|
-
class LayerWmsViews(DimensionLayerViews):
|
69
|
+
class LayerWmsViews(DimensionLayerViews[LayerWMS]):
|
70
|
+
"""The WMS layer administration view."""
|
71
|
+
|
62
72
|
_list_fields = (
|
63
|
-
DimensionLayerViews._list_fields
|
73
|
+
DimensionLayerViews._list_fields # pylint: disable=protected-access
|
64
74
|
+ [
|
65
|
-
_list_field("layer"),
|
66
|
-
_list_field("style"),
|
67
|
-
_list_field("time_mode"),
|
68
|
-
_list_field("time_widget"),
|
69
75
|
_list_field(
|
70
76
|
"ogc_server",
|
71
77
|
renderer=lambda layer_wms: layer_wms.ogc_server.name,
|
72
78
|
sort_column=OGCServer.name,
|
73
79
|
filter_column=OGCServer.name,
|
74
80
|
),
|
81
|
+
_list_field("layer"),
|
82
|
+
_list_field("style"),
|
83
|
+
_list_field("valid", label="Valid"),
|
84
|
+
_list_field("invalid_reason", visible=False),
|
85
|
+
_list_field("time_mode"),
|
86
|
+
_list_field("time_widget"),
|
75
87
|
]
|
76
|
-
+ DimensionLayerViews._extra_list_fields
|
88
|
+
+ DimensionLayerViews._extra_list_fields # pylint: disable=protected-access
|
77
89
|
)
|
78
90
|
_id_field = "id"
|
79
91
|
_model = LayerWMS
|
80
92
|
_base_schema = base_schema
|
81
93
|
|
82
|
-
def _base_query(self
|
94
|
+
def _base_query(self) -> sqlalchemy.orm.query.Query[LayerWMS]:
|
95
|
+
return super()._sub_query(
|
96
|
+
self._request.dbsession.query(LayerWMS, OGCServer.name).distinct().outerjoin(LayerWMS.ogc_server)
|
97
|
+
)
|
98
|
+
|
99
|
+
def _sub_query(
|
100
|
+
self, query: sqlalchemy.orm.query.Query[LayerWMS] | None
|
101
|
+
) -> sqlalchemy.orm.query.Query[LayerWMS]:
|
83
102
|
del query
|
84
|
-
return
|
103
|
+
return self._base_query()
|
85
104
|
|
86
|
-
@view_config(route_name="c2cgeoform_index", renderer="../templates/index.jinja2")
|
87
|
-
def index(self):
|
105
|
+
@view_config(route_name="c2cgeoform_index", renderer="../templates/index.jinja2") # type: ignore[misc]
|
106
|
+
def index(self) -> IndexResponse:
|
88
107
|
return super().index()
|
89
108
|
|
90
|
-
@view_config(route_name="c2cgeoform_grid", renderer="fast_json")
|
91
|
-
def grid(self):
|
109
|
+
@view_config(route_name="c2cgeoform_grid", renderer="fast_json") # type: ignore[misc]
|
110
|
+
def grid(self) -> GridResponse:
|
92
111
|
return super().grid()
|
93
112
|
|
94
|
-
def _item_actions(self, item, readonly=False):
|
95
|
-
actions = super()._item_actions(item, readonly)
|
96
|
-
if inspect(item).persistent:
|
113
|
+
def _item_actions(self, item: LayerWMS, readonly: bool = False) -> list[ItemAction]:
|
114
|
+
actions: list[ItemAction] = super()._item_actions(item, readonly)
|
115
|
+
if inspect(item).persistent: # type: ignore[attr-defined]
|
97
116
|
actions.insert(
|
98
117
|
next((i for i, v in enumerate(actions) if v.name() == "delete")),
|
99
118
|
ItemAction(
|
@@ -107,8 +126,10 @@ class LayerWmsViews(DimensionLayerViews):
|
|
107
126
|
)
|
108
127
|
return actions
|
109
128
|
|
110
|
-
@view_config(
|
111
|
-
|
129
|
+
@view_config( # type: ignore[misc]
|
130
|
+
route_name="c2cgeoform_item", request_method="GET", renderer="../templates/edit.jinja2"
|
131
|
+
)
|
132
|
+
def view(self) -> ObjectResponse:
|
112
133
|
if self._is_new():
|
113
134
|
dbsession = self._request.dbsession
|
114
135
|
default_wms = LayerWMS.get_default(dbsession)
|
@@ -116,22 +137,24 @@ class LayerWmsViews(DimensionLayerViews):
|
|
116
137
|
return self.copy(default_wms, excludes=["name", "layer"])
|
117
138
|
return super().edit()
|
118
139
|
|
119
|
-
@view_config(
|
120
|
-
|
140
|
+
@view_config( # type: ignore[misc]
|
141
|
+
route_name="c2cgeoform_item", request_method="POST", renderer="../templates/edit.jinja2"
|
142
|
+
)
|
143
|
+
def save(self) -> SaveResponse:
|
121
144
|
return super().save()
|
122
145
|
|
123
|
-
@view_config(route_name="c2cgeoform_item", request_method="DELETE", renderer="fast_json")
|
124
|
-
def delete(self):
|
146
|
+
@view_config(route_name="c2cgeoform_item", request_method="DELETE", renderer="fast_json") # type: ignore[misc]
|
147
|
+
def delete(self) -> DeleteResponse:
|
125
148
|
return super().delete()
|
126
149
|
|
127
|
-
@view_config(
|
150
|
+
@view_config( # type: ignore[misc]
|
128
151
|
route_name="c2cgeoform_item_duplicate", request_method="GET", renderer="../templates/edit.jinja2"
|
129
152
|
)
|
130
|
-
def duplicate(self):
|
153
|
+
def duplicate(self) -> ObjectResponse:
|
131
154
|
return super().duplicate()
|
132
155
|
|
133
|
-
@view_config(route_name="convert_to_wmts", request_method="POST", renderer="fast_json")
|
134
|
-
def convert_to_wmts(self):
|
156
|
+
@view_config(route_name="convert_to_wmts", request_method="POST", renderer="fast_json") # type: ignore[misc]
|
157
|
+
def convert_to_wmts(self) -> JSONDict:
|
135
158
|
src = self._get_object()
|
136
159
|
dbsession = self._request.dbsession
|
137
160
|
default_wmts = LayerWMTS.get_default(dbsession)
|
@@ -164,6 +187,8 @@ class LayerWmsViews(DimensionLayerViews):
|
|
164
187
|
dbsession.flush()
|
165
188
|
mark_changed(dbsession)
|
166
189
|
|
190
|
+
self._create_log(LogAction.CONVERT_TO_WMTS, src, element_url_table="layers_wmts")
|
191
|
+
|
167
192
|
return {
|
168
193
|
"success": True,
|
169
194
|
"redirect": self._request.route_url(
|
@@ -1,6 +1,4 @@
|
|
1
|
-
#
|
2
|
-
|
3
|
-
# Copyright (c) 2017-2020, Camptocamp SA
|
1
|
+
# Copyright (c) 2017-2024, Camptocamp SA
|
4
2
|
# All rights reserved.
|
5
3
|
|
6
4
|
# Redistribution and use in source and binary forms, with or without
|
@@ -30,8 +28,18 @@
|
|
30
28
|
|
31
29
|
from functools import partial
|
32
30
|
|
31
|
+
import sqlalchemy
|
32
|
+
from c2cgeoform import JSONDict
|
33
33
|
from c2cgeoform.schema import GeoFormSchemaNode
|
34
|
-
from c2cgeoform.views.abstract_views import
|
34
|
+
from c2cgeoform.views.abstract_views import (
|
35
|
+
DeleteResponse,
|
36
|
+
GridResponse,
|
37
|
+
IndexResponse,
|
38
|
+
ItemAction,
|
39
|
+
ListField,
|
40
|
+
ObjectResponse,
|
41
|
+
SaveResponse,
|
42
|
+
)
|
35
43
|
from deform.widget import FormWidget
|
36
44
|
from pyramid.view import view_config, view_defaults
|
37
45
|
from sqlalchemy import delete, insert, inspect, update
|
@@ -40,27 +48,29 @@ from zope.sqlalchemy import mark_changed
|
|
40
48
|
from c2cgeoportal_admin import _
|
41
49
|
from c2cgeoportal_admin.schemas.dimensions import dimensions_schema_node
|
42
50
|
from c2cgeoportal_admin.schemas.interfaces import interfaces_schema_node
|
43
|
-
from c2cgeoportal_admin.schemas.metadata import
|
51
|
+
from c2cgeoportal_admin.schemas.metadata import metadata_schema_node
|
44
52
|
from c2cgeoportal_admin.schemas.restriction_areas import restrictionareas_schema_node
|
45
53
|
from c2cgeoportal_admin.schemas.treeitem import parent_id_node
|
46
54
|
from c2cgeoportal_admin.views.dimension_layers import DimensionLayerViews
|
47
|
-
from c2cgeoportal_commons.models.main import LayerGroup, LayerWMS, LayerWMTS, OGCServer, TreeItem
|
55
|
+
from c2cgeoportal_commons.models.main import LayerGroup, LayerWMS, LayerWMTS, LogAction, OGCServer, TreeItem
|
48
56
|
|
49
57
|
_list_field = partial(ListField, LayerWMTS)
|
50
58
|
|
51
59
|
base_schema = GeoFormSchemaNode(LayerWMTS, widget=FormWidget(fields_template="layer_fields"))
|
52
|
-
base_schema.add(dimensions_schema_node.
|
53
|
-
base_schema.add(
|
54
|
-
base_schema.add(interfaces_schema_node.
|
55
|
-
base_schema.add(restrictionareas_schema_node.
|
60
|
+
base_schema.add(dimensions_schema_node(LayerWMTS.dimensions))
|
61
|
+
base_schema.add(metadata_schema_node(LayerWMTS.metadatas, LayerWMTS))
|
62
|
+
base_schema.add(interfaces_schema_node(LayerWMTS.interfaces))
|
63
|
+
base_schema.add(restrictionareas_schema_node(LayerWMTS.restrictionareas))
|
56
64
|
base_schema.add_unique_validator(LayerWMTS.name, LayerWMTS.id)
|
57
65
|
base_schema.add(parent_id_node(LayerGroup))
|
58
66
|
|
59
67
|
|
60
68
|
@view_defaults(match_param="table=layers_wmts")
|
61
|
-
class LayerWmtsViews(DimensionLayerViews):
|
69
|
+
class LayerWmtsViews(DimensionLayerViews[LayerWMTS]):
|
70
|
+
"""The WMTS layer administration view."""
|
71
|
+
|
62
72
|
_list_fields = (
|
63
|
-
DimensionLayerViews._list_fields
|
73
|
+
DimensionLayerViews._list_fields # pylint: disable=protected-access
|
64
74
|
+ [
|
65
75
|
_list_field("url"),
|
66
76
|
_list_field("layer"),
|
@@ -68,26 +78,32 @@ class LayerWmtsViews(DimensionLayerViews):
|
|
68
78
|
_list_field("matrix_set"),
|
69
79
|
_list_field("image_type"),
|
70
80
|
]
|
71
|
-
+ DimensionLayerViews._extra_list_fields
|
81
|
+
+ DimensionLayerViews._extra_list_fields # pylint: disable=protected-access
|
72
82
|
)
|
73
83
|
_id_field = "id"
|
74
84
|
_model = LayerWMTS
|
75
85
|
_base_schema = base_schema
|
76
86
|
|
77
|
-
def _base_query(self
|
78
|
-
return super().
|
87
|
+
def _base_query(self) -> sqlalchemy.orm.query.Query[LayerWMTS]:
|
88
|
+
return super()._sub_query(self._request.dbsession.query(LayerWMTS).distinct())
|
89
|
+
|
90
|
+
def _sub_query(
|
91
|
+
self, query: sqlalchemy.orm.query.Query[LayerWMTS]
|
92
|
+
) -> sqlalchemy.orm.query.Query[LayerWMTS]:
|
93
|
+
del query
|
94
|
+
return self._base_query()
|
79
95
|
|
80
|
-
@view_config(route_name="c2cgeoform_index", renderer="../templates/index.jinja2")
|
81
|
-
def index(self):
|
96
|
+
@view_config(route_name="c2cgeoform_index", renderer="../templates/index.jinja2") # type: ignore[misc]
|
97
|
+
def index(self) -> IndexResponse:
|
82
98
|
return super().index()
|
83
99
|
|
84
|
-
@view_config(route_name="c2cgeoform_grid", renderer="fast_json")
|
85
|
-
def grid(self):
|
100
|
+
@view_config(route_name="c2cgeoform_grid", renderer="fast_json") # type: ignore[misc]
|
101
|
+
def grid(self) -> GridResponse:
|
86
102
|
return super().grid()
|
87
103
|
|
88
|
-
def _item_actions(self, item, readonly=False):
|
89
|
-
actions = super()._item_actions(item, readonly)
|
90
|
-
if inspect(item).persistent:
|
104
|
+
def _item_actions(self, item: LayerWMTS, readonly: bool = False) -> list[ItemAction]:
|
105
|
+
actions: list[ItemAction] = super()._item_actions(item, readonly)
|
106
|
+
if inspect(item).persistent: # type: ignore[attr-defined]
|
91
107
|
actions.insert(
|
92
108
|
next((i for i, v in enumerate(actions) if v.name() == "delete")),
|
93
109
|
ItemAction(
|
@@ -101,8 +117,10 @@ class LayerWmtsViews(DimensionLayerViews):
|
|
101
117
|
)
|
102
118
|
return actions
|
103
119
|
|
104
|
-
@view_config(
|
105
|
-
|
120
|
+
@view_config( # type: ignore[misc]
|
121
|
+
route_name="c2cgeoform_item", request_method="GET", renderer="../templates/edit.jinja2"
|
122
|
+
)
|
123
|
+
def view(self) -> ObjectResponse:
|
106
124
|
if self._is_new():
|
107
125
|
dbsession = self._request.dbsession
|
108
126
|
default_wmts = LayerWMTS.get_default(dbsession)
|
@@ -110,22 +128,24 @@ class LayerWmtsViews(DimensionLayerViews):
|
|
110
128
|
return self.copy(default_wmts, excludes=["name", "layer"])
|
111
129
|
return super().edit()
|
112
130
|
|
113
|
-
@view_config(
|
114
|
-
|
131
|
+
@view_config( # type: ignore[misc]
|
132
|
+
route_name="c2cgeoform_item", request_method="POST", renderer="../templates/edit.jinja2"
|
133
|
+
)
|
134
|
+
def save(self) -> SaveResponse:
|
115
135
|
return super().save()
|
116
136
|
|
117
|
-
@view_config(route_name="c2cgeoform_item", request_method="DELETE", renderer="fast_json")
|
118
|
-
def delete(self):
|
137
|
+
@view_config(route_name="c2cgeoform_item", request_method="DELETE", renderer="fast_json") # type: ignore[misc]
|
138
|
+
def delete(self) -> DeleteResponse:
|
119
139
|
return super().delete()
|
120
140
|
|
121
|
-
@view_config(
|
141
|
+
@view_config( # type: ignore[misc]
|
122
142
|
route_name="c2cgeoform_item_duplicate", request_method="GET", renderer="../templates/edit.jinja2"
|
123
143
|
)
|
124
|
-
def duplicate(self):
|
144
|
+
def duplicate(self) -> ObjectResponse:
|
125
145
|
return super().duplicate()
|
126
146
|
|
127
|
-
@view_config(route_name="convert_to_wms", request_method="POST", renderer="fast_json")
|
128
|
-
def convert_to_wms(self):
|
147
|
+
@view_config(route_name="convert_to_wms", request_method="POST", renderer="fast_json") # type: ignore[misc]
|
148
|
+
def convert_to_wms(self) -> JSONDict:
|
129
149
|
src = self._get_object()
|
130
150
|
dbsession = self._request.dbsession
|
131
151
|
default_wms = LayerWMS.get_default(dbsession)
|
@@ -159,6 +179,8 @@ class LayerWmtsViews(DimensionLayerViews):
|
|
159
179
|
dbsession.flush()
|
160
180
|
mark_changed(dbsession)
|
161
181
|
|
182
|
+
self._create_log(LogAction.CONVERT_TO_WMS, src, element_url_table="layers_wms")
|
183
|
+
|
162
184
|
return {
|
163
185
|
"success": True,
|
164
186
|
"redirect": self._request.route_url(
|
@@ -1,6 +1,4 @@
|
|
1
|
-
#
|
2
|
-
|
3
|
-
# Copyright (c) 2017-2020, Camptocamp SA
|
1
|
+
# Copyright (c) 2017-2024, Camptocamp SA
|
4
2
|
# All rights reserved.
|
5
3
|
|
6
4
|
# Redistribution and use in source and binary forms, with or without
|
@@ -28,48 +26,59 @@
|
|
28
26
|
# either expressed or implied, of the FreeBSD Project.
|
29
27
|
|
30
28
|
|
31
|
-
from
|
29
|
+
from typing import Any
|
30
|
+
|
31
|
+
import pyramid.request
|
32
|
+
from c2cgeoform.views.abstract_views import DeleteResponse, ItemAction
|
32
33
|
from pyramid.httpexceptions import HTTPNotFound
|
33
34
|
from pyramid.view import view_config, view_defaults
|
34
35
|
from translationstring import TranslationStringFactory
|
35
36
|
|
36
37
|
from c2cgeoportal_admin import _
|
37
|
-
from c2cgeoportal_commons.models.main import LayergroupTreeitem, Theme, TreeItem
|
38
|
+
from c2cgeoportal_commons.models.main import Interface, Layer, LayergroupTreeitem, Theme, TreeItem
|
38
39
|
|
39
40
|
itemtypes_tables = {
|
40
41
|
"theme": "themes",
|
41
42
|
"group": "layer_groups",
|
42
43
|
"l_wms": "layers_wms",
|
43
44
|
"l_wmts": "layers_wmts",
|
45
|
+
"l_cog": "layers_cog",
|
44
46
|
}
|
45
47
|
|
46
48
|
|
47
|
-
@view_defaults(match_param=
|
49
|
+
@view_defaults(match_param="application=admin")
|
48
50
|
class LayerTreeViews:
|
49
|
-
|
51
|
+
"""The layer tree administration view."""
|
52
|
+
|
53
|
+
def __init__(self, request: pyramid.request.Request):
|
50
54
|
self._request = request
|
51
55
|
self._dbsession = request.dbsession
|
52
56
|
|
53
|
-
@view_config(route_name="layertree", renderer="../templates/layertree.jinja2")
|
54
|
-
def index(self):
|
57
|
+
@view_config(route_name="layertree", renderer="../templates/layertree.jinja2") # type: ignore[misc]
|
58
|
+
def index(self) -> dict[str, int]:
|
55
59
|
node_limit = self._request.registry.settings["admin_interface"].get("layer_tree_max_nodes")
|
56
60
|
limit_exceeded = self._dbsession.query(LayergroupTreeitem).count() < node_limit
|
57
|
-
return {"limit_exceeded": limit_exceeded}
|
61
|
+
return {"limit_exceeded": limit_exceeded, "interfaces": self._dbsession.query(Interface).all()}
|
58
62
|
|
59
|
-
@view_config(route_name="layertree_children", renderer="fast_json")
|
60
|
-
def children(self):
|
63
|
+
@view_config(route_name="layertree_children", renderer="fast_json") # type: ignore[misc]
|
64
|
+
def children(self) -> list[dict[str, Any]]:
|
65
|
+
interface = self._request.params.get("interface", None)
|
61
66
|
group_id = self._request.params.get("group_id", None)
|
62
67
|
path = self._request.params.get("path", "")
|
63
68
|
|
64
|
-
client_tsf = TranslationStringFactory("{
|
69
|
+
client_tsf = TranslationStringFactory(f"{self._request.registry.package_name}-client")
|
65
70
|
|
66
71
|
if group_id is None:
|
67
72
|
items = self._dbsession.query(Theme).order_by(Theme.ordering)
|
73
|
+
if interface is not None:
|
74
|
+
items = items.join(Theme.interfaces).filter(Interface.name == interface)
|
75
|
+
|
68
76
|
else:
|
69
77
|
items = (
|
70
78
|
self._dbsession.query(TreeItem)
|
71
79
|
.join(TreeItem.parents_relation)
|
72
80
|
.filter(LayergroupTreeitem.treegroup_id == group_id)
|
81
|
+
.order_by(LayergroupTreeitem.ordering)
|
73
82
|
)
|
74
83
|
|
75
84
|
return [
|
@@ -79,14 +88,17 @@ class LayerTreeViews:
|
|
79
88
|
"name": item.name,
|
80
89
|
"translated_name": self._request.localizer.translate(client_tsf(item.name)),
|
81
90
|
"description": item.description,
|
82
|
-
"path": "{}_{
|
91
|
+
"path": f"{path}_{item.id}",
|
83
92
|
"parent_path": path,
|
84
93
|
"actions": [action.to_dict(self._request) for action in self._item_actions(item, group_id)],
|
85
94
|
}
|
86
95
|
for item in items
|
96
|
+
if interface is None
|
97
|
+
or not isinstance(item, Layer)
|
98
|
+
or interface in [interface.name for interface in item.interfaces]
|
87
99
|
]
|
88
100
|
|
89
|
-
def _item_actions(self, item, parent_id=None):
|
101
|
+
def _item_actions(self, item: TreeItem, parent_id: int | None = None) -> list[ItemAction]:
|
90
102
|
actions = []
|
91
103
|
actions.append(
|
92
104
|
ItemAction(
|
@@ -105,9 +117,8 @@ class LayerTreeViews:
|
|
105
117
|
name="new_layer_group",
|
106
118
|
label=_("New layer group"),
|
107
119
|
icon="glyphicon glyphicon-plus",
|
108
|
-
url="{}?
|
109
|
-
|
110
|
-
),
|
120
|
+
url=f"{self._request.route_url('c2cgeoform_item', table='layer_groups', id='new')}?"
|
121
|
+
f"parent_id={item.id}",
|
111
122
|
)
|
112
123
|
)
|
113
124
|
|
@@ -117,9 +128,8 @@ class LayerTreeViews:
|
|
117
128
|
name="new_layer_wms",
|
118
129
|
label=_("New WMS layer"),
|
119
130
|
icon="glyphicon glyphicon-plus",
|
120
|
-
url="{}?
|
121
|
-
|
122
|
-
),
|
131
|
+
url=f"{self._request.route_url('c2cgeoform_item', table='layers_wms', id='new')}?"
|
132
|
+
f"parent_id={item.id}",
|
123
133
|
)
|
124
134
|
)
|
125
135
|
|
@@ -128,9 +138,8 @@ class LayerTreeViews:
|
|
128
138
|
name="new_layer_wmts",
|
129
139
|
label=_("New WMTS layer"),
|
130
140
|
icon="glyphicon glyphicon-plus",
|
131
|
-
url="{}?
|
132
|
-
|
133
|
-
),
|
141
|
+
url=f"{self._request.route_url('c2cgeoform_item', table='layers_wmts', id='new')}?"
|
142
|
+
f"parent_id={item.id}",
|
134
143
|
)
|
135
144
|
)
|
136
145
|
|
@@ -170,8 +179,8 @@ class LayerTreeViews:
|
|
170
179
|
|
171
180
|
return actions
|
172
181
|
|
173
|
-
@view_config(route_name="layertree_unlink", request_method="DELETE", renderer="fast_json")
|
174
|
-
def unlink(self):
|
182
|
+
@view_config(route_name="layertree_unlink", request_method="DELETE", renderer="fast_json") # type: ignore[misc]
|
183
|
+
def unlink(self) -> dict[str, Any]:
|
175
184
|
group_id = self._request.matchdict.get("group_id")
|
176
185
|
item_id = self._request.matchdict.get("item_id")
|
177
186
|
link = (
|
@@ -186,8 +195,8 @@ class LayerTreeViews:
|
|
186
195
|
self._request.dbsession.flush()
|
187
196
|
return {"success": True, "redirect": self._request.route_url("layertree")}
|
188
197
|
|
189
|
-
@view_config(route_name="layertree_delete", request_method="DELETE", renderer="fast_json")
|
190
|
-
def delete(self):
|
198
|
+
@view_config(route_name="layertree_delete", request_method="DELETE", renderer="fast_json") # type: ignore[misc]
|
199
|
+
def delete(self) -> DeleteResponse:
|
191
200
|
item_id = self._request.matchdict.get("item_id")
|
192
201
|
item = self._request.dbsession.query(TreeItem).get(item_id)
|
193
202
|
if item is None:
|
@@ -0,0 +1,83 @@
|
|
1
|
+
# Copyright (c) 2023-2024, Camptocamp SA
|
2
|
+
# All rights reserved.
|
3
|
+
|
4
|
+
# Redistribution and use in source and binary forms, with or without
|
5
|
+
# modification, are permitted provided that the following conditions are met:
|
6
|
+
|
7
|
+
# 1. Redistributions of source code must retain the above copyright notice, this
|
8
|
+
# list of conditions and the following disclaimer.
|
9
|
+
# 2. Redistributions in binary form must reproduce the above copyright notice,
|
10
|
+
# this list of conditions and the following disclaimer in the documentation
|
11
|
+
# and/or other materials provided with the distribution.
|
12
|
+
|
13
|
+
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
14
|
+
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
15
|
+
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
16
|
+
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
17
|
+
# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
18
|
+
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
19
|
+
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
20
|
+
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
21
|
+
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
22
|
+
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
23
|
+
|
24
|
+
# The views and conclusions contained in the software and documentation are those
|
25
|
+
# of the authors and should not be interpreted as representing official policies,
|
26
|
+
# either expressed or implied, of the FreeBSD Project.
|
27
|
+
|
28
|
+
import datetime
|
29
|
+
from typing import Generic, TypeVar
|
30
|
+
|
31
|
+
from c2cgeoform.views.abstract_views import AbstractViews, DeleteResponse, SaveResponse
|
32
|
+
from pyramid.httpexceptions import HTTPFound
|
33
|
+
|
34
|
+
from c2cgeoportal_commons.models import Base
|
35
|
+
from c2cgeoportal_commons.models.main import Log, LogAction
|
36
|
+
|
37
|
+
_T = TypeVar("_T", bound=Log)
|
38
|
+
|
39
|
+
|
40
|
+
class LoggedViews(AbstractViews[_T], Generic[_T]):
|
41
|
+
"""Extension of AbstractViews which log actions in a table."""
|
42
|
+
|
43
|
+
_log_model = Log # main.Log or static.Log
|
44
|
+
_name_field = "name"
|
45
|
+
|
46
|
+
def save(self) -> SaveResponse:
|
47
|
+
response = super().save()
|
48
|
+
if isinstance(response, HTTPFound):
|
49
|
+
self._create_log(
|
50
|
+
action=LogAction.INSERT if self._is_new() else LogAction.UPDATE,
|
51
|
+
obj=self._obj,
|
52
|
+
)
|
53
|
+
|
54
|
+
return response
|
55
|
+
|
56
|
+
def delete(self) -> DeleteResponse:
|
57
|
+
obj = self._get_object()
|
58
|
+
|
59
|
+
response = super().delete()
|
60
|
+
|
61
|
+
self._create_log(LogAction.DELETE, obj)
|
62
|
+
|
63
|
+
return response
|
64
|
+
|
65
|
+
def _create_log(
|
66
|
+
self,
|
67
|
+
action: LogAction,
|
68
|
+
obj: Base, # type: ignore[valid-type]
|
69
|
+
element_url_table: str | None = None,
|
70
|
+
) -> None:
|
71
|
+
assert self._model is not None
|
72
|
+
assert self._name_field is not None
|
73
|
+
assert self._id_field is not None
|
74
|
+
log = self._log_model(
|
75
|
+
date=datetime.datetime.now(datetime.timezone.utc),
|
76
|
+
action=action,
|
77
|
+
element_type=self._model.__tablename__,
|
78
|
+
element_id=getattr(obj, self._id_field),
|
79
|
+
element_name=getattr(obj, self._name_field),
|
80
|
+
element_url_table=element_url_table or self._request.matchdict.get("table", None),
|
81
|
+
username=self._request.user.username,
|
82
|
+
)
|
83
|
+
self._request.dbsession.add(log)
|
@@ -0,0 +1,91 @@
|
|
1
|
+
# Copyright (c) 2023-2024, Camptocamp SA
|
2
|
+
# All rights reserved.
|
3
|
+
|
4
|
+
# Redistribution and use in source and binary forms, with or without
|
5
|
+
# modification, are permitted provided that the following conditions are met:
|
6
|
+
|
7
|
+
# 1. Redistributions of source code must retain the above copyright notice, this
|
8
|
+
# list of conditions and the following disclaimer.
|
9
|
+
# 2. Redistributions in binary form must reproduce the above copyright notice,
|
10
|
+
# this list of conditions and the following disclaimer in the documentation
|
11
|
+
# and/or other materials provided with the distribution.
|
12
|
+
|
13
|
+
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
14
|
+
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
15
|
+
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
16
|
+
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
17
|
+
# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
18
|
+
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
19
|
+
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
20
|
+
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
21
|
+
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
22
|
+
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
23
|
+
|
24
|
+
# The views and conclusions contained in the software and documentation are those
|
25
|
+
# of the authors and should not be interpreted as representing official policies,
|
26
|
+
# either expressed or implied, of the FreeBSD Project.
|
27
|
+
|
28
|
+
from functools import partial
|
29
|
+
|
30
|
+
from c2cgeoform import JSONDict
|
31
|
+
from c2cgeoform.views.abstract_views import AbstractViews, GridResponse, IndexResponse, ItemAction, ListField
|
32
|
+
from pyramid.view import view_config, view_defaults
|
33
|
+
|
34
|
+
from c2cgeoportal_commons.models import _
|
35
|
+
from c2cgeoportal_commons.models.main import AbstractLog
|
36
|
+
|
37
|
+
_list_field = partial(ListField, AbstractLog)
|
38
|
+
|
39
|
+
|
40
|
+
@view_defaults(match_param="table=logs")
|
41
|
+
class LogViews(AbstractViews[AbstractLog]):
|
42
|
+
"""The theme administration view."""
|
43
|
+
|
44
|
+
# We pass labels explicitly because actually we are not able to get info
|
45
|
+
# from InstrumentedAttribute created from AbstractConcreteBase.
|
46
|
+
_list_fields = [
|
47
|
+
_list_field("id"),
|
48
|
+
_list_field("date", label=_("Date")),
|
49
|
+
_list_field("username", label=_("Username")),
|
50
|
+
_list_field("action", label=_("Action"), renderer=lambda log: log.action.name),
|
51
|
+
_list_field("element_type", label=_("Element type")),
|
52
|
+
_list_field("element_id", label=_("Element identifier")),
|
53
|
+
_list_field("element_name", label=_("Element name")),
|
54
|
+
]
|
55
|
+
_list_ordered_fields = [AbstractLog.date.desc()]
|
56
|
+
|
57
|
+
_id_field = "id"
|
58
|
+
_model = AbstractLog
|
59
|
+
|
60
|
+
@view_config(route_name="c2cgeoform_index", renderer="../templates/index.jinja2") # type: ignore[misc]
|
61
|
+
def index(self) -> IndexResponse:
|
62
|
+
return super().index()
|
63
|
+
|
64
|
+
@view_config(route_name="c2cgeoform_grid", renderer="fast_json") # type: ignore[misc]
|
65
|
+
def grid(self) -> GridResponse:
|
66
|
+
return super().grid()
|
67
|
+
|
68
|
+
def _grid_actions(self):
|
69
|
+
return []
|
70
|
+
|
71
|
+
def _grid_item_actions(self, item: AbstractLog) -> JSONDict:
|
72
|
+
element_url = self._request.route_url(
|
73
|
+
"c2cgeoform_item",
|
74
|
+
table=item.element_url_table,
|
75
|
+
id=item.element_id,
|
76
|
+
)
|
77
|
+
|
78
|
+
return {
|
79
|
+
"dblclick": element_url,
|
80
|
+
"dropdown": [
|
81
|
+
ItemAction(
|
82
|
+
name="edit_element",
|
83
|
+
label=_("Edit element"),
|
84
|
+
icon="glyphicon glyphicon-pencil",
|
85
|
+
url=element_url,
|
86
|
+
).to_dict(self._request)
|
87
|
+
],
|
88
|
+
}
|
89
|
+
|
90
|
+
def _item_actions(self, item, readonly=False):
|
91
|
+
return []
|