diff --git a/src/qgis_geonode/apiclient/base.py b/src/qgis_geonode/apiclient/base.py index 229926b..5b5ce6b 100644 --- a/src/qgis_geonode/apiclient/base.py +++ b/src/qgis_geonode/apiclient/base.py @@ -123,28 +123,28 @@ def get_dataset_detail( self, dataset: typing.Union[models.BriefDataset, models.Dataset], get_style_too: bool = False, + authenticated: bool = False, ) -> None: - requests_to_perform = [ - network.RequestToPerform(url=self.get_dataset_detail_url(dataset.pk)) - ] - if get_style_too: - is_vector = ( - dataset.dataset_sub_type == models.GeonodeResourceType.VECTOR_LAYER - ) - should_load_vector_style = ( - models.ApiClientCapability.LOAD_VECTOR_LAYER_STYLE in self.capabilities - ) - if is_vector and should_load_vector_style: - sld_url = QtCore.QUrl(dataset.default_style.sld_url) - requests_to_perform.append(network.RequestToPerform(url=sld_url)) + + auth_manager = qgis.core.QgsApplication.authManager() + auth_provider_name = auth_manager.configAuthMethodKey(self.auth_config).lower() + + if auth_provider_name == "basic": + authenticated = True self.network_fetcher_task = network.NetworkRequestTask( - requests_to_perform, + [network.RequestToPerform(url=self.get_dataset_detail_url(dataset.pk))], self.network_requests_timeout, self.auth_config, description="Get dataset detail", ) - self.network_fetcher_task.task_done.connect(self.handle_dataset_detail) + self.network_fetcher_task.task_done.connect( + partial( + self.handle_dataset_detail, + get_style_too=get_style_too, + authenticated=authenticated, + ) + ) qgis.core.QgsApplication.taskManager().addTask(self.network_fetcher_task) def handle_dataset_detail(self, result: bool): diff --git a/src/qgis_geonode/apiclient/geonode_api_v2.py b/src/qgis_geonode/apiclient/geonode_api_v2.py index 33fc684..d1cc8e6 100644 --- a/src/qgis_geonode/apiclient/geonode_api_v2.py +++ b/src/qgis_geonode/apiclient/geonode_api_v2.py @@ -251,7 +251,12 @@ def handle_dataset_list(self, task_result: bool) -> None: ) self.dataset_list_received.emit(brief_datasets, pagination_info) - def handle_dataset_detail(self, task_result: bool) -> None: + def handle_dataset_detail( + self, + task_result: bool, + get_style_too: bool = False, + authenticated: bool = False, + ) -> None: log("inside the API client's handle_dataset_detail") deserialized_resource = self._retrieve_response( task_result, 0, self.dataset_detail_error_received @@ -267,21 +272,23 @@ def handle_dataset_detail(self, task_result: bool) -> None: debug=False, ) else: - try: - style_response_contents = ( - self.network_fetcher_task.response_contents[1] + # check if the request is from a WFS to see if it will retrieve the style + if get_style_too and authenticated: + is_vector = ( + dataset.dataset_sub_type + == models.GeonodeResourceType.VECTOR_LAYER ) - except IndexError: - pass + should_load_vector_style = ( + models.ApiClientCapability.LOAD_VECTOR_LAYER_STYLE + in self.capabilities + ) + # Check if the layer is vector and if it has the permissions to read the style + if is_vector and should_load_vector_style: + self.get_dataset_style( + dataset, emit_dataset_detail_received=True + ) else: - ( - sld_named_layer, - error_message, - ) = geonode_styles.get_usable_sld(style_response_contents) - if sld_named_layer is None: - raise RuntimeError(error_message) - dataset.default_style.sld = sld_named_layer - self.dataset_detail_received.emit(dataset) + self.dataset_detail_received.emit(dataset) def handle_dataset_style( self, diff --git a/src/qgis_geonode/conf.py b/src/qgis_geonode/conf.py index 366148c..2cbc496 100644 --- a/src/qgis_geonode/conf.py +++ b/src/qgis_geonode/conf.py @@ -4,6 +4,7 @@ import json import typing import uuid +import urllib.parse from configparser import ConfigParser from pathlib import Path @@ -104,8 +105,15 @@ def prepare(self, plugin_dir): self.plugin_metadata = _plugin_metadata["general"] + homepage = urllib.parse.urlparse(self.plugin_metadata.get("homepage")) + self.homepage_root = f"{homepage.scheme}://{homepage.hostname}/" + self.help_page = homepage.path.strip("/") + def get(self, attr): - return self.plugin_metadata.get(attr) + try: + return getattr(self, attr) + except ValueError: + return self.plugin_metadata.get(attr) class SettingsManager(QtCore.QObject): diff --git a/src/qgis_geonode/gui/geonode_map_layer_config_widget.py b/src/qgis_geonode/gui/geonode_map_layer_config_widget.py index d6e9884..96546ec 100644 --- a/src/qgis_geonode/gui/geonode_map_layer_config_widget.py +++ b/src/qgis_geonode/gui/geonode_map_layer_config_widget.py @@ -73,6 +73,7 @@ def connection_settings(self) -> typing.Optional[conf.ConnectionSettings]: def __init__(self, layer, canvas, parent): super().__init__(layer, canvas, parent) self.setupUi(self) + self.setProperty("helpPage", conf.plugin_metadata.get("help_page")) self.open_detail_url_pb.setIcon( QtGui.QIcon(":/plugins/qgis_geonode/mIconGeonode.svg") ) @@ -314,8 +315,8 @@ def _apply_metadata(self) -> None: dataset = self.get_dataset() updated_metadata = populate_metadata(self.layer.metadata(), dataset) self.layer.setMetadata(updated_metadata) - layer_properties_dialog = self._get_layer_properties_dialog() - layer_properties_dialog.syncToLayer() + # sync layer properties with the reloaded SLD and/or Metadata from GeoNode + self.sync_layer_properties() # FIXME: rather use the api_client to perform the metadata upload def upload_metadata(self) -> None: @@ -433,8 +434,7 @@ def _apply_sld(self) -> None: dataset.default_style.sld, sld_load_error_msg ) if sld_load_result: - layer_properties_dialog = self._get_layer_properties_dialog() - layer_properties_dialog.syncToLayer() + self.sync_layer_properties() else: self._show_message( message=f"Could not load GeoNode style: {sld_load_error_msg}", @@ -449,10 +449,33 @@ def _show_message( ) -> None: utils.show_message(self.message_bar, message, level, add_loading_widget) - def _get_layer_properties_dialog(self): - # FIXME: This is a very hacky way to get the layer properties dialog - # but I've not been able to find a more elegant way to retrieve it yet - return self.parent().parent().parent().parent() + def find_parent_by_type(self, obj, target_type): + # Find the desired object by type + # from a structure: self.parent().parent()... + current_obj = obj + while current_obj is not None: + if isinstance(current_obj, target_type): + return current_obj + if hasattr(current_obj, "parent"): + current_obj = current_obj.parent() + else: + break + return None + + def sync_layer_properties(self): + # get layer properties dialog + # We need to find QDialog object from a structure like: + # self.parent().parent()... + properties_dialog = self.find_parent_by_type(self, QtWidgets.QDialog) + + if properties_dialog is not None: + # Sync GeoNode's SLD or / and metadata with the layer properties dialog + properties_dialog.syncToLayer() + else: + self._show_message( + "The corresponding layer properties from GeoNode cannot be loaded correctly...", + level=qgis.core.Qgis.Critical, + ) def _toggle_link_controls(self, enabled: bool) -> None: self.links_gb.setEnabled(enabled) @@ -472,12 +495,17 @@ def _toggle_style_controls(self, enabled: bool) -> None: models.GeonodePermission.CHANGE_DATASET_STYLE in dataset.permissions ) is_service = self.layer.dataProvider().name().lower() in ("wfs", "wcs") - has_style_url = dataset.default_style.sld_url is not None - if can_load_style and has_style_url and is_service: + has_geonode_style = dataset.default_style.sld is not None + if can_load_style and has_geonode_style and is_service: widgets.append(self.download_style_pb) else: self.download_style_pb.setEnabled(False) - if allowed_to_modify and can_modify_style and has_style_url and is_service: + if ( + allowed_to_modify + and can_modify_style + and has_geonode_style + and is_service + ): widgets.append(self.upload_style_pb) else: self.upload_style_pb.setEnabled(False) diff --git a/src/qgis_geonode/gui/search_result_widget.py b/src/qgis_geonode/gui/search_result_widget.py index 7be16c6..02eb17e 100644 --- a/src/qgis_geonode/gui/search_result_widget.py +++ b/src/qgis_geonode/gui/search_result_widget.py @@ -224,11 +224,14 @@ def prepare_loaded_layer(self): self.layer = self.dataset_loader_task.layer self.api_client.dataset_detail_received.connect(self.handle_layer_detail) self.api_client.dataset_detail_error_received.connect(self.handle_loading_error) + self.api_client.style_detail_error_received.connect(self.handle_style_error) self.api_client.get_dataset_detail( self.brief_dataset, get_style_too=self.layer.dataProvider().name() != "wms" ) - def handle_layer_detail(self, dataset: typing.Optional[models.Dataset]): + def handle_layer_detail( + self, dataset: typing.Optional[models.Dataset], retrieved_style: bool = False + ): self.api_client.dataset_detail_received.disconnect(self.handle_layer_detail) self.layer.setCustomProperty( models.DATASET_CUSTOM_PROPERTY_KEY, @@ -245,7 +248,11 @@ def handle_layer_detail(self, dataset: typing.Optional[models.Dataset]): can_load_style = models.loading_style_supported( self.layer.type(), self.api_client.capabilities ) - if can_load_style and dataset.default_style: + + if dataset.default_style.sld is not None: + retrieved_style = True + + if can_load_style and retrieved_style: error_message = "" loaded_sld = self.layer.readSld(dataset.default_style.sld, error_message) if not loaded_sld: @@ -257,10 +264,16 @@ def handle_loading_error(self): self.data_source_widget.show_message(message, level=qgis.core.Qgis.Critical) self.handle_layer_load_end(clear_message_bar=False) + def handle_style_error(self): + message = f"Unable to retrieve the style of {self.brief_dataset.title}" + self.data_source_widget.show_message(message, level=qgis.core.Qgis.Critical) + self.handle_layer_load_end(clear_message_bar=False) + def add_layer_to_project(self): self.api_client.dataset_detail_error_received.disconnect( self.handle_loading_error ) + self.api_client.style_detail_error_received.disconnect(self.handle_style_error) self.project.addMapLayer(self.layer) self.handle_layer_load_end() diff --git a/src/qgis_geonode/main.py b/src/qgis_geonode/main.py index 84994a5..90f08b1 100644 --- a/src/qgis_geonode/main.py +++ b/src/qgis_geonode/main.py @@ -11,6 +11,7 @@ import os.path +from qgis.core import QgsSettings from qgis.gui import QgsGui from qgis.PyQt.QtCore import QSettings, QTranslator, QCoreApplication from qgis.PyQt.QtGui import QIcon @@ -36,6 +37,17 @@ def __init__(self, iface): self.plugin_dir, "i18n", "QgisGeoNode_{}.qm".format(locale) ) + settings = QgsSettings() + help_paths = settings.value("help/helpSearchPath") + homepage_root = plugin_metadata.get("homepage_root") + + if isinstance(help_paths, str): + help_paths = [homepage_root, help_paths] + elif isinstance(help_paths, list) and not homepage_root in help_paths: + help_paths = [homepage_root] + help_paths + + settings.setValue("help/helpSearchPath", help_paths) + if os.path.exists(locale_path): self.translator = QTranslator() self.translator.load(locale_path)