Skip to content

Commit e1b5d4e

Browse files
authored
Merge pull request #667 from meyerlor/feature/dxf-export+access-control
Feature/dxf export+access control
2 parents a562b7a + 82b1b76 commit e1b5d4e

File tree

16 files changed

+1059
-17
lines changed

16 files changed

+1059
-17
lines changed

lizmap/config/global_options.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,8 @@ class GlobalOptionsDefinitions(TypedDict):
7474
atlasShowAtStartup: _Item
7575
atlasAutoPlay: _Item
7676
fixed_scale_overview_map: _Item
77+
dxfExportEnabled: _Item
78+
allowedGroups: _Item
7779

7880

7981
globalOptionDefinitions = {
@@ -252,4 +254,21 @@ class GlobalOptionsDefinitions(TypedDict):
252254
+ tr("New in Lizmap Web Client 3.5.3"),
253255
"use_proper_boolean": True,
254256
},
257+
"dxfExportEnabled": {
258+
"wType": "checkbox",
259+
"type": "boolean",
260+
"default": False,
261+
"tooltip": tr("Enable or disable the DXF export functionality globally."),
262+
"use_proper_boolean": True,
263+
},
264+
"allowedGroups": {
265+
"wType": "text",
266+
"type": "string",
267+
"default": "",
268+
"tooltip": tr(
269+
"Comma-separated list of Lizmap group IDs allowed to export DXF. "
270+
"If empty, all users can export."
271+
),
272+
"always_export": True,
273+
},
255274
}

lizmap/definitions/definitions.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ def latest() -> "LwcVersions":
4646
@staticmethod
4747
def oldest() -> "LwcVersions":
4848
""" Oldest version definition in the Python file, like LWC 3.1 """
49-
next(iter(LwcVersions))
49+
return next(iter(LwcVersions))
5050

5151
@classmethod
5252
def find(cls, version_string: str) -> Optional["LwcVersions"]:

lizmap/definitions/dxf_export.py

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
"""Definitions for DXF export."""
2+
from lizmap.definitions.base import BaseDefinitions, InputType
3+
from lizmap.definitions.definitions import LwcVersions
4+
from lizmap.toolbelt.i18n import tr
5+
6+
__copyright__ = 'Copyright 2025, 3Liz'
7+
__license__ = 'GPL version 3'
8+
__email__ = 'info@3liz.org'
9+
10+
11+
class DxfExportDefinitions(BaseDefinitions):
12+
13+
def __init__(self):
14+
super().__init__()
15+
16+
# Layer-specific configuration
17+
self._layer_config['layerId'] = {
18+
'type': InputType.Layer,
19+
'wfs_required': True,
20+
'header': tr('Layer'),
21+
'default': None,
22+
'tooltip': tr('The vector layer for DXF export. Only WFS-enabled layers can be exported to DXF.')
23+
}
24+
self._layer_config['enabled'] = {
25+
'type': InputType.CheckBox,
26+
'header': tr('Enabled'),
27+
'default': True,
28+
'tooltip': tr('If the DXF export is enabled for this layer.'),
29+
'version': LwcVersions.Lizmap_3_9,
30+
'use_json': True,
31+
}
32+
33+
# Global configuration
34+
self._general_config['dxfExportEnabled'] = {
35+
'type': InputType.CheckBox,
36+
'header': tr('Allow DXF export'),
37+
'default': False,
38+
'tooltip': tr('Enable or disable the DXF export functionality globally.'),
39+
'version': LwcVersions.Lizmap_3_9,
40+
}
41+
self._general_config['allowedGroups'] = {
42+
'type': InputType.Text,
43+
'header': tr('Allowed groups'),
44+
'default': '',
45+
'tooltip': tr('Comma-separated list of Lizmap group IDs allowed to export DXF. If empty, all users can export.'),
46+
'version': LwcVersions.Lizmap_3_9,
47+
}
48+
49+
@staticmethod
50+
def primary_keys() -> tuple:
51+
return ('layerId',)
52+
53+
def key(self) -> str:
54+
return 'dxfExport'
55+
56+
def help_path(self) -> str:
57+
return 'publish/lizmap_plugin/dxf_export.html'

lizmap/definitions/online_help.py

Lines changed: 15 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -50,19 +50,20 @@ class Panels:
5050
AttributeTable = 4
5151
Editing = 5
5252
Layouts = 6
53-
FormFiltering = 7
54-
Dataviz = 8
55-
FilteredLayers = 9
56-
Actions = 10
57-
TimeManager = 11
58-
Atlas = 12
59-
LocateByLayer = 13
60-
ToolTip = 14
61-
Checks = 15
62-
AutoFix = 16
63-
Settings = 17
64-
Upload = 18
65-
Training = 19
53+
DxfExport = 7
54+
FormFiltering = 8
55+
Dataviz = 9
56+
FilteredLayers = 10
57+
Actions = 11
58+
TimeManager = 12
59+
Atlas = 13
60+
LocateByLayer = 14
61+
ToolTip = 15
62+
Checks = 16
63+
AutoFix = 17
64+
Settings = 18
65+
Upload = 19
66+
Training = 20
6667

6768

6869
MAPPING_INDEX_DOC = {
@@ -86,6 +87,7 @@ class Panels:
8687
Panels.Settings: None, # Settings
8788
Panels.Upload: None,
8889
Panels.Training: None,
90+
Panels.DxfExport: 'publish/lizmap_plugin/dxf_export.html',
8991
}
9092

9193

lizmap/dialogs/main.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -954,6 +954,13 @@ def setup_icons(self):
954954
self.mOptionsListWidget.item(Panels.Layouts).setIcon(icon)
955955
self.mOptionsListWidget.item(Panels.Layouts).setData(Qt.ItemDataRole.UserRole, 'layouts')
956956

957+
# DXF Export
958+
icon = QIcon()
959+
icon.addFile(resources_path('icons', '19-dxfexport-white.png'), mode=QIcon.Mode.Normal)
960+
icon.addFile(resources_path('icons', '19-dxfexport-dark.png'), mode=QIcon.Mode.Selected)
961+
self.mOptionsListWidget.item(Panels.DxfExport).setIcon(icon)
962+
self.mOptionsListWidget.item(Panels.DxfExport).setData(Qt.ItemDataRole.UserRole, 'dxf-export')
963+
957964
# Filter data with form
958965
icon = QIcon()
959966
icon.addFile(resources_path('icons', 'filter-icon-white.png'), mode=QIcon.Mode.Normal)

lizmap/forms/dxf_export_edition.py

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
"""Dialog for DXF export edition."""
2+
from typing import Dict, Optional
3+
4+
from qgis.core import QgsMapLayerProxyModel
5+
from qgis.PyQt.QtWidgets import QWidget
6+
7+
from lizmap.definitions.definitions import LwcVersions
8+
from lizmap.definitions.dxf_export import DxfExportDefinitions
9+
from lizmap.forms.base_edition_dialog import BaseEditionDialog
10+
from lizmap.toolbelt.i18n import tr
11+
from lizmap.toolbelt.resources import load_ui
12+
13+
CLASS = load_ui('ui_form_dxf_export.ui')
14+
15+
16+
class DxfExportEditionDialog(BaseEditionDialog, CLASS):
17+
18+
def __init__(
19+
self,
20+
parent: Optional[QWidget] = None,
21+
unicity: Optional[Dict[str, str]] = None,
22+
lwc_version: Optional[LwcVersions] = None,
23+
):
24+
super().__init__(parent, unicity, lwc_version)
25+
self.setupUi(self)
26+
self.config = DxfExportDefinitions()
27+
28+
# Layer configuration
29+
self.config.add_layer_widget('layerId', self.layer)
30+
self.config.add_layer_widget('enabled', self.enabled)
31+
32+
self.config.add_layer_label('layerId', self.label_layer)
33+
self.config.add_layer_label('enabled', self.label_enabled)
34+
35+
# Set layer filter to only show vector layers
36+
self.layer.setFilters(QgsMapLayerProxyModel.Filter.VectorLayer)
37+
self.layer.layerChanged.connect(self.check_layer_wfs)
38+
39+
self.setup_ui()
40+
self.check_layer_wfs()
41+
42+
def check_layer_wfs(self):
43+
"""When the layer has changed in the combobox, check if the layer is published as WFS."""
44+
layer = self.layer.currentLayer()
45+
if not layer:
46+
self.show_error(tr('A layer is mandatory.'))
47+
return
48+
49+
not_in_wfs = self.is_layer_in_wfs(layer)
50+
self.show_error(not_in_wfs)
51+
52+
def validate(self) -> Optional[str]:
53+
upstream = super().validate()
54+
if upstream:
55+
return upstream
56+
57+
layer = self.layer.currentLayer()
58+
not_in_wfs = self.is_layer_in_wfs(layer)
59+
if not_in_wfs:
60+
return not_in_wfs
61+
62+
return None

0 commit comments

Comments
 (0)