c2cgeoportal-admin 2.6.0__py3-none-any.whl → 2.9rc45__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.
Files changed (80) hide show
  1. c2cgeoportal_admin/__init__.py +42 -12
  2. c2cgeoportal_admin/lib/lingva_extractor.py +77 -0
  3. c2cgeoportal_admin/lib/ogcserver_synchronizer.py +170 -57
  4. c2cgeoportal_admin/py.typed +0 -0
  5. c2cgeoportal_admin/routes.py +18 -6
  6. c2cgeoportal_admin/schemas/dimensions.py +16 -10
  7. c2cgeoportal_admin/schemas/functionalities.py +59 -21
  8. c2cgeoportal_admin/schemas/interfaces.py +26 -18
  9. c2cgeoportal_admin/schemas/metadata.py +101 -48
  10. c2cgeoportal_admin/schemas/restriction_areas.py +25 -19
  11. c2cgeoportal_admin/schemas/roles.py +12 -6
  12. c2cgeoportal_admin/schemas/treegroup.py +46 -21
  13. c2cgeoportal_admin/schemas/treeitem.py +3 -4
  14. c2cgeoportal_admin/static/layertree.css +3 -4
  15. c2cgeoportal_admin/static/navbar.css +36 -35
  16. c2cgeoportal_admin/static/theme.css +19 -9
  17. c2cgeoportal_admin/subscribers.py +3 -3
  18. c2cgeoportal_admin/templates/404.jinja2 +18 -2
  19. c2cgeoportal_admin/templates/layertree.jinja2 +31 -9
  20. c2cgeoportal_admin/templates/navigation_navbar.jinja2 +33 -0
  21. c2cgeoportal_admin/templates/ogcserver_synchronize.jinja2 +12 -0
  22. c2cgeoportal_admin/templates/widgets/functionality_fields.pt +51 -0
  23. c2cgeoportal_admin/templates/widgets/metadata.pt +7 -1
  24. c2cgeoportal_admin/views/__init__.py +29 -0
  25. c2cgeoportal_admin/views/dimension_layers.py +14 -9
  26. c2cgeoportal_admin/views/functionalities.py +52 -18
  27. c2cgeoportal_admin/views/home.py +5 -5
  28. c2cgeoportal_admin/views/interfaces.py +26 -20
  29. c2cgeoportal_admin/views/layer_groups.py +36 -25
  30. c2cgeoportal_admin/views/layers.py +17 -13
  31. c2cgeoportal_admin/views/layers_cog.py +135 -0
  32. c2cgeoportal_admin/views/layers_vectortiles.py +62 -27
  33. c2cgeoportal_admin/views/layers_wms.py +55 -34
  34. c2cgeoportal_admin/views/layers_wmts.py +54 -34
  35. c2cgeoportal_admin/views/layertree.py +38 -29
  36. c2cgeoportal_admin/views/logged_views.py +83 -0
  37. c2cgeoportal_admin/views/logs.py +91 -0
  38. c2cgeoportal_admin/views/oauth2_clients.py +30 -18
  39. c2cgeoportal_admin/views/ogc_servers.py +132 -36
  40. c2cgeoportal_admin/views/restriction_areas.py +39 -27
  41. c2cgeoportal_admin/views/roles.py +42 -28
  42. c2cgeoportal_admin/views/themes.py +47 -35
  43. c2cgeoportal_admin/views/themes_ordering.py +19 -14
  44. c2cgeoportal_admin/views/treeitems.py +21 -17
  45. c2cgeoportal_admin/views/users.py +46 -26
  46. c2cgeoportal_admin/widgets.py +17 -14
  47. {c2cgeoportal_admin-2.6.0.dist-info → c2cgeoportal_admin-2.9rc45.dist-info}/METADATA +12 -12
  48. c2cgeoportal_admin-2.9rc45.dist-info/RECORD +97 -0
  49. {c2cgeoportal_admin-2.6.0.dist-info → c2cgeoportal_admin-2.9rc45.dist-info}/WHEEL +1 -1
  50. c2cgeoportal_admin-2.9rc45.dist-info/entry_points.txt +5 -0
  51. tests/__init__.py +24 -20
  52. tests/conftest.py +22 -11
  53. tests/test_edit_url.py +11 -14
  54. tests/test_functionalities.py +52 -14
  55. tests/test_home.py +0 -1
  56. tests/test_interface.py +34 -11
  57. tests/test_layer_groups.py +57 -27
  58. tests/test_layers_cog.py +243 -0
  59. tests/test_layers_vectortiles.py +43 -25
  60. tests/test_layers_wms.py +67 -45
  61. tests/test_layers_wmts.py +47 -26
  62. tests/test_layertree.py +99 -16
  63. tests/test_left_menu.py +0 -1
  64. tests/test_lingva_extractor_config.py +64 -0
  65. tests/test_logs.py +102 -0
  66. tests/test_main.py +3 -1
  67. tests/test_metadatas.py +34 -21
  68. tests/test_oauth2_clients.py +40 -11
  69. tests/test_ogc_servers.py +84 -35
  70. tests/test_restriction_areas.py +38 -15
  71. tests/test_role.py +71 -43
  72. tests/test_themes.py +71 -37
  73. tests/test_themes_ordering.py +1 -2
  74. tests/test_treegroup.py +2 -2
  75. tests/test_user.py +56 -19
  76. tests/themes_ordering.py +1 -2
  77. c2cgeoportal_admin/templates/navigation_vertical.jinja2 +0 -33
  78. c2cgeoportal_admin-2.6.0.dist-info/RECORD +0 -89
  79. c2cgeoportal_admin-2.6.0.dist-info/entry_points.txt +0 -3
  80. {c2cgeoportal_admin-2.6.0.dist-info → c2cgeoportal_admin-2.9rc45.dist-info}/top_level.txt +0 -0
@@ -1,6 +1,4 @@
1
- # -*- coding: utf-8 -*-
2
-
3
- # Copyright (c) 2018-2020, Camptocamp SA
1
+ # Copyright (c) 2018-2024, Camptocamp SA
4
2
  # All rights reserved.
5
3
 
6
4
  # Redistribution and use in source and binary forms, with or without
@@ -28,16 +26,24 @@
28
26
  # either expressed or implied, of the FreeBSD Project.
29
27
 
30
28
 
29
+ from typing import Any
30
+
31
31
  import colander
32
32
  from c2cgeoform.schema import GeoFormSchemaNode
33
33
  from deform.widget import MappingWidget, SequenceWidget
34
+ from sqlalchemy.orm.attributes import InstrumentedAttribute
34
35
 
35
- from c2cgeoportal_admin import _
36
36
  from c2cgeoportal_commons.models.main import Dimension
37
37
 
38
- dimensions_schema_node = colander.SequenceSchema(
39
- GeoFormSchemaNode(Dimension, name="dimension", widget=MappingWidget(template="dimension")),
40
- name="dimensions",
41
- title=_("Dimensions"),
42
- widget=SequenceWidget(category="structural", template="dimensions"),
43
- )
38
+
39
+ def dimensions_schema_node(
40
+ prop: InstrumentedAttribute[Any], # pylint: disable=unsubscriptable-object
41
+ ) -> colander.SequenceSchema:
42
+ """Get the scheme of the dimensions."""
43
+ return colander.SequenceSchema(
44
+ GeoFormSchemaNode(Dimension, name="dimension", widget=MappingWidget(template="dimension")),
45
+ name=prop.key,
46
+ title=prop.info["colanderalchemy"]["title"],
47
+ description=prop.info["colanderalchemy"]["description"],
48
+ widget=SequenceWidget(category="structural", template="dimensions"),
49
+ )
@@ -1,6 +1,4 @@
1
- # -*- coding: utf-8 -*-
2
-
3
- # Copyright (c) 2018-2020, Camptocamp SA
1
+ # Copyright (c) 2018-2024, Camptocamp SA
4
2
  # All rights reserved.
5
3
 
6
4
  # Redistribution and use in source and binary forms, with or without
@@ -28,28 +26,68 @@
28
26
  # either expressed or implied, of the FreeBSD Project.
29
27
 
30
28
 
29
+ from typing import Any
30
+
31
31
  import colander
32
32
  from c2cgeoform.ext.deform_ext import RelationCheckBoxListWidget
33
33
  from c2cgeoform.schema import GeoFormManyToManySchemaNode, manytomany_validator
34
- from sqlalchemy import select
34
+ from sqlalchemy import inspect, select
35
+ from sqlalchemy.orm.attributes import InstrumentedAttribute
35
36
  from sqlalchemy.sql.functions import concat
36
37
 
37
38
  from c2cgeoportal_commons.models.main import Functionality
38
39
 
39
- functionalities_schema_node = colander.SequenceSchema(
40
- GeoFormManyToManySchemaNode(Functionality),
41
- name="functionalities",
42
- widget=RelationCheckBoxListWidget(
43
- select([Functionality.id, concat(Functionality.name, "=", Functionality.value).label("label")]).alias(
44
- "functionality_labels"
45
- ),
46
- "id",
47
- "label",
48
- order_by="label",
49
- edit_url=lambda request, value: request.route_url(
50
- "c2cgeoform_item", table="functionalities", id=value
51
- ),
52
- ),
53
- validator=manytomany_validator,
54
- missing=colander.drop,
55
- )
40
+
41
+ def available_functionalities_for(settings: dict[str, Any], model: type[Any]) -> list[dict[str, Any]]:
42
+ """Return filtered list of functionality definitions."""
43
+ mapper = inspect(model)
44
+ relevant_for = {mapper.local_table.name}
45
+ return [
46
+ f
47
+ for f in settings["admin_interface"]["available_functionalities"]
48
+ if relevant_for & set(f.get("relevant_for", relevant_for))
49
+ ]
50
+
51
+
52
+ def functionalities_widget(model: type[Any]) -> colander.deferred:
53
+ """Return a colander deferred which itself returns a widget for the functionalities field."""
54
+
55
+ def create_widget(node, kw):
56
+ del node
57
+
58
+ return RelationCheckBoxListWidget(
59
+ select( # type: ignore[arg-type]
60
+ Functionality.id,
61
+ concat(Functionality.name, "=", Functionality.value).label("label"),
62
+ )
63
+ .where(
64
+ Functionality.name.in_(
65
+ [f["name"] for f in available_functionalities_for(kw["request"].registry.settings, model)]
66
+ )
67
+ )
68
+ .alias("functionality_labels"),
69
+ "id",
70
+ "label",
71
+ order_by="label",
72
+ edit_url=lambda request, value: request.route_url(
73
+ "c2cgeoform_item", table="functionalities", id=value
74
+ ),
75
+ )
76
+
77
+ return colander.deferred(create_widget)
78
+
79
+
80
+ def functionalities_schema_node(
81
+ prop: InstrumentedAttribute[Any], model: type[Any]
82
+ ) -> colander.SequenceSchema:
83
+ """Get the schema of the functionalities."""
84
+
85
+ return colander.SequenceSchema(
86
+ GeoFormManyToManySchemaNode(Functionality, None),
87
+ name=prop.key,
88
+ title=prop.info["colanderalchemy"]["title"],
89
+ description=prop.info["colanderalchemy"].get("description"),
90
+ widget=functionalities_widget(model),
91
+ validator=manytomany_validator,
92
+ missing=colander.drop,
93
+ )
@@ -1,6 +1,4 @@
1
- # -*- coding: utf-8 -*-
2
-
3
- # Copyright (c) 2018-2020, Camptocamp SA
1
+ # Copyright (c) 2018-2024, Camptocamp SA
4
2
  # All rights reserved.
5
3
 
6
4
  # Redistribution and use in source and binary forms, with or without
@@ -28,24 +26,34 @@
28
26
  # either expressed or implied, of the FreeBSD Project.
29
27
 
30
28
 
29
+ from typing import Any
30
+
31
31
  import colander
32
32
  from c2cgeoform.ext.deform_ext import RelationCheckBoxListWidget
33
33
  from c2cgeoform.schema import GeoFormManyToManySchemaNode, manytomany_validator
34
+ from sqlalchemy.orm.attributes import InstrumentedAttribute
34
35
 
35
- from c2cgeoportal_admin import _
36
36
  from c2cgeoportal_commons.models.main import Interface
37
37
 
38
- interfaces_schema_node = colander.SequenceSchema(
39
- GeoFormManyToManySchemaNode(Interface),
40
- name="interfaces",
41
- title=_("Interfaces"),
42
- widget=RelationCheckBoxListWidget(
43
- Interface,
44
- "id",
45
- "name",
46
- order_by="name",
47
- edit_url=lambda request, value: request.route_url("c2cgeoform_item", table="interfaces", id=value),
48
- ),
49
- validator=manytomany_validator,
50
- missing=colander.drop,
51
- )
38
+
39
+ def interfaces_schema_node(
40
+ prop: InstrumentedAttribute[Any], # pylint: disable=unsubscriptable-object
41
+ ) -> colander.SequenceSchema:
42
+ """Get the serializable representation of an interface."""
43
+ return colander.SequenceSchema(
44
+ GeoFormManyToManySchemaNode(Interface, None),
45
+ name=prop.key,
46
+ title=prop.info["colanderalchemy"]["title"],
47
+ description=prop.info["colanderalchemy"]["description"],
48
+ widget=RelationCheckBoxListWidget(
49
+ Interface,
50
+ "id",
51
+ "name",
52
+ order_by="name",
53
+ edit_url=lambda request, value: request.route_url(
54
+ "c2cgeoform_item", table="interfaces", id=value
55
+ ),
56
+ ),
57
+ validator=manytomany_validator,
58
+ missing=colander.drop,
59
+ )
@@ -1,6 +1,4 @@
1
- # -*- coding: utf-8 -*-
2
-
3
- # Copyright (c) 2018-2021, Camptocamp SA
1
+ # Copyright (c) 2018-2024, Camptocamp SA
4
2
  # All rights reserved.
5
3
 
6
4
  # Redistribution and use in source and binary forms, with or without
@@ -27,32 +25,49 @@
27
25
  # of the authors and should not be interpreted as representing official policies,
28
26
  # either expressed or implied, of the FreeBSD Project.
29
27
 
30
-
31
28
  import json
32
- from typing import Any, Dict, List, Optional, cast
29
+ from typing import Any, cast
33
30
 
34
31
  import colander
32
+ import pyramid.request
35
33
  from c2cgeoform.schema import GeoFormSchemaNode
36
34
  from deform.widget import MappingWidget, SelectWidget, SequenceWidget, TextAreaWidget
35
+ from sqlalchemy import inspect
36
+ from sqlalchemy.orm.attributes import InstrumentedAttribute
37
+ from sqlalchemy.orm.mapper import Mapper
37
38
 
38
39
  from c2cgeoportal_admin import _
39
40
  from c2cgeoportal_commons.lib.validators import url
40
41
  from c2cgeoportal_commons.models.main import Metadata
41
42
 
42
43
 
43
- @colander.deferred
44
- def metadata_definitions(node, kw):
45
- del node
46
- return {m["name"]: m for m in kw["request"].registry.settings["admin_interface"]["available_metadata"]}
44
+ def get_relevant_for(model: type[Any] | Mapper[Any]) -> set[str]:
45
+ """Return list of relevant_for values for passed class."""
46
+ mapper = inspect(model)
47
+ assert mapper is not None
48
+ relevant_for = {mapper.local_table.name} # or mapper.polymorphic_identity
49
+ if mapper.inherits:
50
+ relevant_for |= get_relevant_for(mapper.inherits)
51
+ return relevant_for
52
+
53
+
54
+ def metadata_definitions(settings: dict[str, Any], model: type[Any]) -> list[dict[str, Any]]:
55
+ """Return filtered list metadata definitions."""
56
+ return [
57
+ m
58
+ for m in settings["admin_interface"]["available_metadata"]
59
+ if get_relevant_for(model) & set(m.get("relevant_for", ["treeitem"]))
60
+ ]
47
61
 
48
62
 
49
- class MetadataSelectWidget(SelectWidget):
50
- """Extends class SelectWidget to support undefined metadatas.
63
+ class MetadataSelectWidget(SelectWidget): # type: ignore
64
+ """
65
+ Extends class SelectWidget to support undefined metadata.
51
66
 
52
67
  Override serialize to add option in values for current cstruct when needed.
53
68
  """
54
69
 
55
- def serialize(self, field, cstruct, **kw):
70
+ def serialize(self, field: Any, cstruct: Any, **kw: Any) -> Any:
56
71
  values = kw.get("values", self.values)
57
72
  if isinstance(cstruct, str) and (cstruct, cstruct) not in values:
58
73
  values = values.copy()
@@ -61,21 +76,26 @@ class MetadataSelectWidget(SelectWidget):
61
76
  return super().serialize(field, cstruct, **kw)
62
77
 
63
78
 
64
- @colander.deferred
65
- def metadata_name_widget(node, kw):
66
- del node
67
- return MetadataSelectWidget(
68
- values=[
69
- (m["name"], m["name"])
70
- for m in sorted(
71
- kw["request"].registry.settings["admin_interface"]["available_metadata"],
72
- key=lambda m: m["name"],
73
- )
74
- ]
75
- )
79
+ def metadata_name_widget(model: type[Any]) -> colander.deferred:
80
+ """Return a colander deferred which itself returns a widget for the metadata name field."""
81
+
82
+ def create_widget(node, kw):
83
+ del node
84
+ return MetadataSelectWidget(
85
+ values=[
86
+ (m["name"], m["name"])
87
+ for m in sorted(
88
+ metadata_definitions(kw["request"].registry.settings, model),
89
+ key=lambda m: cast(str, m["name"]),
90
+ )
91
+ ]
92
+ )
93
+
94
+ return colander.deferred(create_widget)
76
95
 
77
96
 
78
97
  def json_validator(node, value):
98
+ """Validate the value to be a valid JSON."""
79
99
  try:
80
100
  json.loads(value)
81
101
  except ValueError as e:
@@ -83,6 +103,7 @@ def json_validator(node, value):
83
103
 
84
104
 
85
105
  def regex_validator(node, value):
106
+ """Validate the value with a regexp."""
86
107
  definition = node.metadata_definitions.get(value["name"], {})
87
108
  if definition.get("type", "string") == "regex":
88
109
  validator = colander.Regex(definition["regex"], msg=_(definition["error_message"]))
@@ -91,11 +112,11 @@ def regex_validator(node, value):
91
112
  except colander.Invalid as e:
92
113
  error = colander.Invalid(node)
93
114
  error.add(e, node.children.index(node["string"]))
94
- raise error
115
+ raise error from e
95
116
 
96
117
 
97
- class BooleanMetadata(colander.Boolean):
98
- """Boolean metadata values are stored as string in database"""
118
+ class BooleanMetadata(colander.Boolean): # type: ignore
119
+ """Boolean metadata values are stored as string in database."""
99
120
 
100
121
  def serialize(self, node, appstruct):
101
122
  if appstruct == "true":
@@ -116,13 +137,14 @@ class BooleanMetadata(colander.Boolean):
116
137
 
117
138
 
118
139
  class MetadataSchemaNode(GeoFormSchemaNode): # pylint: disable=abstract-method
140
+ """The metadata schema."""
119
141
 
120
- metadata_definitions: Optional[Dict[str, Any]] = None
142
+ metadata_definitions: dict[str, Any] | None = None
121
143
 
122
- def __init__(self, *args, **kw):
144
+ def __init__(self, *args: Any, **kw: Any):
123
145
  super().__init__(*args, **kw)
124
146
 
125
- self.available_types: List[str] = []
147
+ self.available_types: list[str] = []
126
148
 
127
149
  self._add_value_node("string", colander.String())
128
150
  self._add_value_node("liste", colander.String())
@@ -134,10 +156,17 @@ class MetadataSchemaNode(GeoFormSchemaNode): # pylint: disable=abstract-method
134
156
  "json", colander.String(), widget=TextAreaWidget(rows=10), validator=json_validator
135
157
  )
136
158
 
137
- def _add_value_node(self, type_name, colander_type, **kw):
159
+ def _add_value_node(self, type_name: str, colander_type: colander.SchemaType, **kw: Any) -> None:
138
160
  self.add_before(
139
161
  "description",
140
- colander.SchemaNode(colander_type, name=type_name, title=_("Value"), missing=colander.null, **kw),
162
+ colander.SchemaNode(
163
+ colander_type,
164
+ name=type_name,
165
+ title=Metadata.value.info["colanderalchemy"]["title"],
166
+ description=Metadata.value.info["colanderalchemy"]["description"],
167
+ missing=colander.null,
168
+ **kw,
169
+ ),
141
170
  )
142
171
  self.available_types.append(type_name)
143
172
 
@@ -153,24 +182,48 @@ class MetadataSchemaNode(GeoFormSchemaNode): # pylint: disable=abstract-method
153
182
  dict_[self._ui_type(obj.name)] = value
154
183
  return dict_
155
184
 
156
- def _ui_type(self, metadata_name: str):
185
+ def _ui_type(self, metadata_name: str) -> str:
157
186
  metadata_type = (
158
- cast(Dict[str, Any], self.metadata_definitions).get(metadata_name, {}).get("type", "string")
187
+ cast(dict[str, dict[str, str]], self.metadata_definitions)
188
+ .get(metadata_name, {})
189
+ .get("type", "string")
159
190
  )
160
191
  return metadata_type if metadata_type in self.available_types else "string"
161
192
 
162
193
 
163
- metadatas_schema_node = colander.SequenceSchema(
164
- MetadataSchemaNode(
165
- Metadata,
166
- name="metadata",
167
- metadata_definitions=metadata_definitions,
168
- validator=regex_validator,
169
- widget=MappingWidget(template="metadata"),
170
- overrides={"name": {"widget": metadata_name_widget}},
171
- ),
172
- name="metadatas",
173
- title=_("Metadatas"),
174
- metadata_definitions=metadata_definitions,
175
- widget=SequenceWidget(template="metadatas", category="structural"),
176
- )
194
+ def _translate_available_metadata(
195
+ available_metadata: dict[str, Any], request: pyramid.request.Request
196
+ ) -> dict[str, Any]:
197
+ result = {}
198
+ result.update(available_metadata)
199
+ result["description"] = request.localizer.translate(_(available_metadata.get("description", "").strip()))
200
+ return result
201
+
202
+
203
+ def metadata_schema_node(prop: InstrumentedAttribute[Any], model: type[Any]) -> colander.SequenceSchema:
204
+ """Get the schema of a collection of metadata."""
205
+
206
+ # Deferred which returns a dictionary with metadata name as key and metadata definition as value.
207
+ # Needed to get the metadata types on UI side.
208
+ metadata_definitions_dict = colander.deferred(
209
+ lambda node, kw: {
210
+ m["name"]: _translate_available_metadata(m, kw["request"])
211
+ for m in metadata_definitions(kw["request"].registry.settings, model)
212
+ }
213
+ )
214
+
215
+ return colander.SequenceSchema(
216
+ MetadataSchemaNode(
217
+ Metadata,
218
+ name="metadata",
219
+ metadata_definitions=metadata_definitions_dict,
220
+ validator=regex_validator,
221
+ widget=MappingWidget(template="metadata"),
222
+ overrides={"name": {"widget": metadata_name_widget(model)}},
223
+ ),
224
+ name=prop.key,
225
+ title=prop.info["colanderalchemy"]["title"],
226
+ description=prop.info["colanderalchemy"]["description"],
227
+ metadata_definitions=metadata_definitions_dict,
228
+ widget=SequenceWidget(template="metadatas", category="structural"),
229
+ )
@@ -1,6 +1,4 @@
1
- # -*- coding: utf-8 -*-
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,26 +26,34 @@
28
26
  # either expressed or implied, of the FreeBSD Project.
29
27
 
30
28
 
29
+ from typing import Any
30
+
31
31
  import colander
32
32
  from c2cgeoform.ext.deform_ext import RelationCheckBoxListWidget
33
33
  from c2cgeoform.schema import GeoFormManyToManySchemaNode, manytomany_validator
34
+ from sqlalchemy.orm.attributes import InstrumentedAttribute
34
35
 
35
- from c2cgeoportal_admin import _
36
36
  from c2cgeoportal_commons.models.main import RestrictionArea
37
37
 
38
- restrictionareas_schema_node = colander.SequenceSchema(
39
- GeoFormManyToManySchemaNode(RestrictionArea),
40
- name="restrictionareas",
41
- title=_("Restriction areas"),
42
- widget=RelationCheckBoxListWidget(
43
- RestrictionArea,
44
- "id",
45
- "name",
46
- order_by="name",
47
- edit_url=lambda request, value: request.route_url(
48
- "c2cgeoform_item", table="restriction_areas", id=value
38
+
39
+ def restrictionareas_schema_node(
40
+ prop: InstrumentedAttribute[Any], # pylint: disable=unsubscriptable-object
41
+ ) -> colander.SequenceSchema:
42
+ """Get the schema of a restriction area."""
43
+ return colander.SequenceSchema(
44
+ GeoFormManyToManySchemaNode(RestrictionArea, None),
45
+ name=prop.key,
46
+ title=prop.info["colanderalchemy"]["title"],
47
+ description=prop.info["colanderalchemy"].get("description"),
48
+ widget=RelationCheckBoxListWidget(
49
+ RestrictionArea,
50
+ "id",
51
+ "name",
52
+ order_by="name",
53
+ edit_url=lambda request, value: request.route_url(
54
+ "c2cgeoform_item", table="restriction_areas", id=value
55
+ ),
49
56
  ),
50
- ),
51
- validator=manytomany_validator,
52
- missing=colander.drop,
53
- )
57
+ validator=manytomany_validator,
58
+ missing=colander.drop,
59
+ )
@@ -1,6 +1,4 @@
1
- # -*- coding: utf-8 -*-
2
-
3
- # Copyright (c) 2018-2020, Camptocamp SA
1
+ # Copyright (c) 2018-2024, Camptocamp SA
4
2
  # All rights reserved.
5
3
 
6
4
  # Redistribution and use in source and binary forms, with or without
@@ -28,17 +26,25 @@
28
26
  # either expressed or implied, of the FreeBSD Project.
29
27
 
30
28
 
29
+ from typing import Any
30
+
31
31
  import colander
32
32
  from c2cgeoform.ext.deform_ext import RelationCheckBoxListWidget
33
33
  from c2cgeoform.schema import GeoFormManyToManySchemaNode, manytomany_validator
34
+ from sqlalchemy.orm.attributes import InstrumentedAttribute
34
35
 
35
36
  from c2cgeoportal_commons.models.main import Role
36
37
 
37
38
 
38
- def roles_schema_node(name):
39
+ def roles_schema_node(
40
+ prop: InstrumentedAttribute[Any], # pylint: disable=unsubscriptable-object
41
+ ) -> colander.SequenceSchema:
42
+ """Get the schema of all the items."""
39
43
  return colander.SequenceSchema(
40
- GeoFormManyToManySchemaNode(Role),
41
- name=name,
44
+ GeoFormManyToManySchemaNode(Role, None),
45
+ name=prop.key,
46
+ title=prop.info["colanderalchemy"]["title"],
47
+ description=prop.info["colanderalchemy"].get("description"),
42
48
  widget=RelationCheckBoxListWidget(
43
49
  Role,
44
50
  "id",