This is an automated email from the ASF dual-hosted git repository.

riemer pushed a commit to branch dev
in repository https://gitbox.apache.org/repos/asf/streampipes.git


The following commit(s) were added to refs/heads/dev by this push:
     new 4ff27c3f40 feat(#3725): Support public dashboards (#3741)
4ff27c3f40 is described below

commit 4ff27c3f40bc347a36c873d1b9a88923e07c325b
Author: Dominik Riemer <[email protected]>
AuthorDate: Mon Aug 25 13:30:20 2025 +0200

    feat(#3725): Support public dashboards (#3741)
    
    * feat(#3725): Add feature to mark dashboards as public
    
    * feat: Add authorization to public dashboards
---
 .../streampipes/model/client/user/Permission.java  |   9 +
 .../management/PermissionResourceManager.java      |   8 +
 .../impl/dashboard/DataLakeDashboardResource.java  |  19 +-
 .../datalake/KioskDashboardDataLakeResource.java   | 116 ++++++++
 .../rest/security/SpPermissionEvaluator.java       | 122 +++++---
 .../service/core/UnauthenticatedInterfaces.java    |   5 +-
 .../management/util/GrantedPermissionsBuilder.java |   2 +-
 .../src/lib/apis/dashboard-kiosk.service.ts        |  52 ++++
 .../src/lib/apis/datalake-rest.service.ts          |  17 +-
 .../src/lib/model/gen/streampipes-model-client.ts  |   4 +-
 .../lib/query/data-view-query-generator.service.ts |  38 ++-
 .../platform-services/src/public-api.ts            |   1 +
 .../existing-adapters.component.html               |   2 +-
 .../object-permission-dialog.component.html        | 325 ++++++++++++---------
 .../object-permission-dialog.component.scss        |   9 +
 .../object-permission-dialog.component.ts          |  40 ++-
 .../kiosk/dashboard-kiosk.component.html           |   2 +-
 .../components/kiosk/dashboard-kiosk.component.ts  |   8 +
 .../chart-view/abstract-chart-view.directive.ts    |   4 +
 .../grid-view/dashboard-grid-view.component.html   |   1 +
 .../slide-view/dashboard-slide-view.component.html |   1 +
 .../slide-view/dashboard-slide-view.component.ts   |   7 +-
 .../dashboard-overview-table.component.html        |   2 +-
 .../dashboard-overview-table.component.ts          |   6 +
 .../overview/dashboard-overview.component.ts       |  18 +-
 .../panel/dashboard-panel.component.html           |   2 +
 .../components/panel/dashboard-panel.component.ts  |   5 +
 .../data-explorer-chart-container.component.html   |   2 -
 .../data-explorer-chart-container.component.ts     |  10 +-
 .../base/base-data-explorer-widget.directive.ts    |  35 +--
 .../models/dataview-dashboard.model.ts             |  12 +
 .../services/data-explorer-shared.service.ts       |  62 +++-
 .../data-explorer-chart-view.component.html        |   1 +
 .../data-explorer-chart-view.component.ts          |  26 +-
 .../data-explorer-overview-table.component.html    |   2 +-
 ui/src/app/pipelines/pipelines.component.html      |   4 +-
 36 files changed, 719 insertions(+), 260 deletions(-)

diff --git 
a/streampipes-model-client/src/main/java/org/apache/streampipes/model/client/user/Permission.java
 
b/streampipes-model-client/src/main/java/org/apache/streampipes/model/client/user/Permission.java
index 4e20ca0daa..6bbec97c34 100644
--- 
a/streampipes-model-client/src/main/java/org/apache/streampipes/model/client/user/Permission.java
+++ 
b/streampipes-model-client/src/main/java/org/apache/streampipes/model/client/user/Permission.java
@@ -40,6 +40,7 @@ public class Permission implements Storable {
   private String objectInstanceId;
   private String objectClassName;
   private boolean publicElement;
+  private boolean readAnonymous;
 
   private String ownerSid;
 
@@ -126,4 +127,12 @@ public class Permission implements Storable {
   public void setPublicElement(boolean publicElement) {
     this.publicElement = publicElement;
   }
+
+  public boolean isReadAnonymous() {
+    return readAnonymous;
+  }
+
+  public void setReadAnonymous(boolean readAnonymous) {
+    this.readAnonymous = readAnonymous;
+  }
 }
diff --git 
a/streampipes-resource-management/src/main/java/org/apache/streampipes/resource/management/PermissionResourceManager.java
 
b/streampipes-resource-management/src/main/java/org/apache/streampipes/resource/management/PermissionResourceManager.java
index 0d48e15949..53b5a36f5c 100644
--- 
a/streampipes-resource-management/src/main/java/org/apache/streampipes/resource/management/PermissionResourceManager.java
+++ 
b/streampipes-resource-management/src/main/java/org/apache/streampipes/resource/management/PermissionResourceManager.java
@@ -19,6 +19,7 @@ package org.apache.streampipes.resource.management;
 
 import org.apache.streampipes.model.client.user.Permission;
 import org.apache.streampipes.model.client.user.PermissionBuilder;
+import org.apache.streampipes.model.dashboard.DashboardModel;
 import org.apache.streampipes.storage.api.IPermissionStorage;
 import org.apache.streampipes.storage.management.StorageDispatcher;
 
@@ -26,6 +27,10 @@ import java.util.List;
 
 public class PermissionResourceManager extends 
AbstractResourceManager<IPermissionStorage> {
 
+  private final List<String> readAnonymousAllowedClasses = List.of(
+      DashboardModel.class.getCanonicalName()
+  );
+
   public PermissionResourceManager() {
     super(StorageDispatcher.INSTANCE.getNoSqlStore().getPermissionStorage());
   }
@@ -59,6 +64,9 @@ public class PermissionResourceManager extends 
AbstractResourceManager<IPermissi
   }
 
   public void update(Permission permission) {
+    if 
(!readAnonymousAllowedClasses.contains(permission.getObjectClassName())) {
+      permission.setReadAnonymous(false);
+    }
     db.updateElement(permission);
   }
 
diff --git 
a/streampipes-rest/src/main/java/org/apache/streampipes/rest/impl/dashboard/DataLakeDashboardResource.java
 
b/streampipes-rest/src/main/java/org/apache/streampipes/rest/impl/dashboard/DataLakeDashboardResource.java
index 0fcb6e2162..ec015f2390 100644
--- 
a/streampipes-rest/src/main/java/org/apache/streampipes/rest/impl/dashboard/DataLakeDashboardResource.java
+++ 
b/streampipes-rest/src/main/java/org/apache/streampipes/rest/impl/dashboard/DataLakeDashboardResource.java
@@ -23,6 +23,7 @@ import 
org.apache.streampipes.model.client.user.DefaultPrivilege;
 import org.apache.streampipes.model.dashboard.DashboardModel;
 import org.apache.streampipes.resource.management.DataExplorerResourceManager;
 import 
org.apache.streampipes.rest.core.base.impl.AbstractAuthGuardedRestResource;
+import org.apache.streampipes.storage.api.IPermissionStorage;
 
 import org.springframework.http.CacheControl;
 import org.springframework.http.HttpStatus;
@@ -47,6 +48,12 @@ import java.util.Objects;
 @RequestMapping("/api/v3/datalake/dashboard")
 public class DataLakeDashboardResource extends AbstractAuthGuardedRestResource 
{
 
+  private final IPermissionStorage permissionStorage;
+
+  public DataLakeDashboardResource() {
+    this.permissionStorage = getNoSqlStorage().getPermissionStorage();
+  }
+
   @GetMapping(produces = MediaType.APPLICATION_JSON_VALUE)
   @PreAuthorize("this.hasReadAuthority()")
   @PostFilter("hasPermission(filterObject.couchDbId, 'READ')")
@@ -61,7 +68,7 @@ public class DataLakeDashboardResource extends 
AbstractAuthGuardedRestResource {
   }
 
   @GetMapping(path = "/{dashboardId}/composite", produces = 
MediaType.APPLICATION_JSON_VALUE)
-  @PreAuthorize("this.hasReadAuthority() and hasPermission(#dashboardId, 
'READ')")
+  @PreAuthorize("this.hasReadAuthorityOrAnonymous(#dashboardId) and 
hasPermission(#dashboardId, 'READ')")
   public ResponseEntity<?> 
getCompositeDashboardModel(@PathVariable("dashboardId") String dashboardId,
                                                       @RequestHeader(value = 
"If-None-Match", required = false) String ifNoneMatch) {
     var dashboard = getResourceManager().getCompositeDashboard(dashboardId);
@@ -113,4 +120,14 @@ public class DataLakeDashboardResource extends 
AbstractAuthGuardedRestResource {
   public boolean hasWriteAuthority() {
     return 
isAdminOrHasAnyAuthority(DefaultPrivilege.Constants.PRIVILEGE_WRITE_DASHBOARD_VALUE);
   }
+
+  public boolean hasReadAuthorityOrAnonymous(String dashboardId) {
+    return hasReadAuthority()
+        || hasAnonymousAccessAuthority(dashboardId);
+  }
+
+  private boolean hasAnonymousAccessAuthority(String dashboardId) {
+    var perms = permissionStorage.getUserPermissionsForObject(dashboardId);
+    return !perms.isEmpty() && perms.get(0).isReadAnonymous();
+  }
 }
diff --git 
a/streampipes-rest/src/main/java/org/apache/streampipes/rest/impl/datalake/KioskDashboardDataLakeResource.java
 
b/streampipes-rest/src/main/java/org/apache/streampipes/rest/impl/datalake/KioskDashboardDataLakeResource.java
new file mode 100644
index 0000000000..d6c0258620
--- /dev/null
+++ 
b/streampipes-rest/src/main/java/org/apache/streampipes/rest/impl/datalake/KioskDashboardDataLakeResource.java
@@ -0,0 +1,116 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package org.apache.streampipes.rest.impl.datalake;
+
+import org.apache.streampipes.dataexplorer.api.IDataExplorerQueryManagement;
+import org.apache.streampipes.dataexplorer.api.IDataExplorerSchemaManagement;
+import org.apache.streampipes.dataexplorer.management.DataExplorerDispatcher;
+import org.apache.streampipes.model.client.user.DefaultPrivilege;
+import org.apache.streampipes.model.dashboard.DashboardModel;
+import org.apache.streampipes.model.datalake.DataExplorerWidgetModel;
+import org.apache.streampipes.model.datalake.SpQueryResult;
+import org.apache.streampipes.model.datalake.param.ProvidedRestQueryParams;
+import org.apache.streampipes.model.monitoring.SpLogMessage;
+import 
org.apache.streampipes.rest.core.base.impl.AbstractAuthGuardedRestResource;
+import org.apache.streampipes.storage.api.CRUDStorage;
+import org.apache.streampipes.storage.management.StorageDispatcher;
+
+import org.springframework.http.MediaType;
+import org.springframework.http.ResponseEntity;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.util.List;
+import java.util.Map;
+
+@RestController
+@RequestMapping("/api/v3/datalake/dashboard/kiosk")
+public class KioskDashboardDataLakeResource extends 
AbstractAuthGuardedRestResource {
+
+  private final IDataExplorerQueryManagement dataExplorerQueryManagement;
+  private final IDataExplorerSchemaManagement dataExplorerSchemaManagement;
+  private final CRUDStorage<DashboardModel> dashboardStorage =
+      
StorageDispatcher.INSTANCE.getNoSqlStore().getDataExplorerDashboardStorage();
+  private final CRUDStorage<DataExplorerWidgetModel> dataExplorerWidgetStorage;
+
+  public KioskDashboardDataLakeResource() {
+    this.dataExplorerSchemaManagement = new DataExplorerDispatcher()
+        .getDataExplorerManager()
+        .getSchemaManagement();
+    this.dataExplorerQueryManagement = new DataExplorerDispatcher()
+        .getDataExplorerManager()
+        .getQueryManagement(this.dataExplorerSchemaManagement);
+    this.dataExplorerWidgetStorage = StorageDispatcher.INSTANCE
+        .getNoSqlStore()
+        .getDataExplorerWidgetStorage();
+  }
+
+  @PostMapping(path = "/{dashboardId}/{widgetId}/data",
+      consumes = MediaType.APPLICATION_JSON_VALUE,
+      produces = MediaType.APPLICATION_JSON_VALUE)
+  @PreAuthorize("this.hasReadAuthority() and hasPermission(#dashboardId, 
'READ')")
+  public ResponseEntity<?> getData(@PathVariable("dashboardId") String 
dashboardId,
+                                   @PathVariable("widgetId") String widgetId,
+                                   @RequestBody Map<String, String> 
queryParams) {
+    var dashboard = dashboardStorage.getElementById(dashboardId);
+    if (dashboard.getWidgets().stream().noneMatch(w -> 
w.getId().equals(widgetId))) {
+      return badRequest(String.format("Widget with id %s not found in 
dashboard", widgetId));
+    }
+    var widget = dataExplorerWidgetStorage.getElementById(widgetId);
+    var measureName = queryParams.get("measureName");
+    if (!checkMeasureNameInWidget(widget, measureName)) {
+     return badRequest("Measure name not found in widget configuration");
+    } else {
+      ProvidedRestQueryParams sanitizedParams = new 
ProvidedRestQueryParams(measureName, queryParams);
+      try {
+        SpQueryResult result =
+            this.dataExplorerQueryManagement.getData(sanitizedParams, true);
+        return ok(result);
+      } catch (RuntimeException e) {
+        return badRequest(SpLogMessage.from(e));
+      }
+    }
+  }
+
+  private boolean checkMeasureNameInWidget(DataExplorerWidgetModel widget,
+                                           String measureName) {
+    var sourceConfigs = widget.getDataConfig().get("sourceConfigs");
+    if (sourceConfigs instanceof List<?>) {
+      return ((List<?>) sourceConfigs)
+          .stream()
+          .anyMatch(config -> {
+            if (!(config instanceof Map<?, ?>)) {
+              return false;
+            } else {
+              return ((Map<?, ?>) 
config).get("measureName").equals(measureName);
+            }
+          });
+    } else {
+      return false;
+    }
+  }
+
+  public boolean hasReadAuthority() {
+    return 
isAdminOrHasAnyAuthority(DefaultPrivilege.Constants.PRIVILEGE_READ_DASHBOARD_VALUE);
+  }
+}
diff --git 
a/streampipes-rest/src/main/java/org/apache/streampipes/rest/security/SpPermissionEvaluator.java
 
b/streampipes-rest/src/main/java/org/apache/streampipes/rest/security/SpPermissionEvaluator.java
index 86c0a74d80..28b43f709a 100644
--- 
a/streampipes-rest/src/main/java/org/apache/streampipes/rest/security/SpPermissionEvaluator.java
+++ 
b/streampipes-rest/src/main/java/org/apache/streampipes/rest/security/SpPermissionEvaluator.java
@@ -21,20 +21,29 @@ import org.apache.streampipes.model.client.user.DefaultRole;
 import org.apache.streampipes.model.client.user.Permission;
 import org.apache.streampipes.model.pipeline.PipelineElementRecommendation;
 import 
org.apache.streampipes.model.pipeline.PipelineElementRecommendationMessage;
+import org.apache.streampipes.storage.api.IPermissionStorage;
 import org.apache.streampipes.storage.management.StorageDispatcher;
 import org.apache.streampipes.user.management.model.PrincipalUserDetails;
 
 import org.springframework.context.annotation.Configuration;
 import org.springframework.security.access.PermissionEvaluator;
+import 
org.springframework.security.authentication.AnonymousAuthenticationToken;
 import org.springframework.security.core.Authentication;
 
 import java.io.Serializable;
 import java.util.List;
+import java.util.Objects;
 import java.util.function.Predicate;
 
 @Configuration
 public class SpPermissionEvaluator implements PermissionEvaluator {
 
+  private final IPermissionStorage permissionStorage;
+
+  public SpPermissionEvaluator() {
+    this.permissionStorage = 
StorageDispatcher.INSTANCE.getNoSqlStore().getPermissionStorage();
+  }
+
   /**
    * Evaluates whether the user has the necessary permissions for a given 
resource.
    *
@@ -50,19 +59,22 @@ public class SpPermissionEvaluator implements 
PermissionEvaluator {
       Object targetDomainObject,
       Object permission
   ) {
-    PrincipalUserDetails<?> userDetails = getUserDetails(authentication);
-    if (targetDomainObject instanceof PipelineElementRecommendationMessage) {
-      return isAdmin(userDetails) || filterRecommendation(
-          authentication,
-          (PipelineElementRecommendationMessage) targetDomainObject
-      );
-    } else {
-      String objectInstanceId = (String) targetDomainObject;
-      if (isAdmin(userDetails)) {
-        return true;
-      }
-      return hasPermission(authentication, objectInstanceId);
+    if (targetDomainObject instanceof PipelineElementRecommendationMessage 
msg) {
+      return handleRecommendationMessage(authentication, msg);
+    }
+
+    String objectId = String.valueOf(targetDomainObject);
+    List<Permission> perms = getObjectPermission(objectId);
+
+    if (isAnonymousAccess(perms)) {
+      return true;
     }
+
+    if (isAdmin(authentication)) {
+      return true;
+    }
+
+    return hasPermissionForId(authentication, perms, objectId);
   }
 
   /**
@@ -81,45 +93,75 @@ public class SpPermissionEvaluator implements 
PermissionEvaluator {
       String targetType,
       Object permission
   ) {
-    PrincipalUserDetails<?> userDetails = getUserDetails(authentication);
-    if (isAdmin(userDetails)) {
-      return true;
-    }
-    return hasPermission(authentication, targetId.toString());
+    // We do not use targetType in this implementation
+    return hasPermission(authentication, targetId, permission);
   }
 
-  private boolean filterRecommendation(Authentication auth, 
PipelineElementRecommendationMessage message) {
-    Predicate<PipelineElementRecommendation> isForbidden = r -> 
!hasPermission(auth, r.getElementId());
-    message.getPossibleElements()
-           .removeIf(isForbidden);
+  private boolean handleRecommendationMessage(Authentication auth,
+                                              
PipelineElementRecommendationMessage message) {
+    Predicate<PipelineElementRecommendation> isForbidden = rec -> {
+      String elementId = rec.getElementId();
+      List<Permission> perms = getObjectPermission(elementId);
 
+      if (isAnonymousAccess(perms)) {
+        return false;
+      }
+      if (isAdmin(auth)) {
+        return false;
+      }
+      return !hasPermissionForId(auth, perms, elementId); // remove if not 
allowed
+    };
+
+    message.getPossibleElements().removeIf(isForbidden);
     return true;
   }
 
-  private boolean hasPermission(Authentication auth, String objectInstanceId) {
-    return isPublicElement(objectInstanceId)
-        || getUserDetails(auth).getAllObjectPermissions()
-                               .contains(objectInstanceId);
+  private boolean hasPermissionForId(Authentication auth,
+                                     List<Permission> permissions,
+                                     String objectInstanceId) {
+    PrincipalUserDetails<?> user = getUserDetailsOrNull(auth);
+    if (user == null) {
+      return false;
+    }
+
+    if (isPublicElement(permissions)) {
+      return true;
+    }
+
+    return user.getAllObjectPermissions().contains(objectInstanceId);
+  }
+
+  private PrincipalUserDetails<?> getUserDetailsOrNull(Authentication 
authentication) {
+    if (authentication == null
+        || authentication instanceof AnonymousAuthenticationToken) {
+      return null;
+    }
+    Object principal = authentication.getPrincipal();
+    return (principal instanceof PrincipalUserDetails) ? 
(PrincipalUserDetails<?>) principal : null;
+  }
+
+  private boolean isAdmin(Authentication authentication) {
+    PrincipalUserDetails<?> userDetails = getUserDetailsOrNull(authentication);
+    if (userDetails == null) {
+      return false;
+    }
+
+    return userDetails.getAuthorities().stream()
+        .anyMatch(a ->
+            Objects.equals(a.getAuthority(), 
DefaultRole.Constants.ROLE_ADMIN_VALUE)
+        );
   }
 
-  private PrincipalUserDetails<?> getUserDetails(Authentication 
authentication) {
-    return (PrincipalUserDetails<?>) authentication.getPrincipal();
+  private boolean isPublicElement(List<Permission> permissions) {
+    return !permissions.isEmpty()
+        && (permissions.get(0).isPublicElement());
   }
 
-  private boolean isPublicElement(String objectInstanceId) {
-    List<Permission> permissions =
-        StorageDispatcher.INSTANCE.getNoSqlStore()
-                                  .getPermissionStorage()
-                                  
.getUserPermissionsForObject(objectInstanceId);
-    return permissions.size() > 0 && permissions.get(0)
-                                                .isPublicElement();
+  private boolean isAnonymousAccess(List<Permission> permissions) {
+    return !permissions.isEmpty() && permissions.get(0).isReadAnonymous();
   }
 
-  private boolean isAdmin(PrincipalUserDetails<?> userDetails) {
-    return userDetails
-        .getAuthorities()
-        .stream()
-        .anyMatch(a -> a.getAuthority()
-                        .equals(DefaultRole.Constants.ROLE_ADMIN_VALUE));
+  private List<Permission> getObjectPermission(String objectInstanceId) {
+    return permissionStorage.getUserPermissionsForObject(objectInstanceId);
   }
 }
diff --git 
a/streampipes-service-core/src/main/java/org/apache/streampipes/service/core/UnauthenticatedInterfaces.java
 
b/streampipes-service-core/src/main/java/org/apache/streampipes/service/core/UnauthenticatedInterfaces.java
index ebadac6489..1653f93e1f 100644
--- 
a/streampipes-service-core/src/main/java/org/apache/streampipes/service/core/UnauthenticatedInterfaces.java
+++ 
b/streampipes-service-core/src/main/java/org/apache/streampipes/service/core/UnauthenticatedInterfaces.java
@@ -42,7 +42,10 @@ public class UnauthenticatedInterfaces {
         "/error",
         "/",
         "/streampipes-backend/",
-        "/streampipes-backend/index.html"
+        "/streampipes-backend/index.html",
+        // anonymous dashboard access is allowed
+        "/api/v3/datalake/dashboard/*/composite",
+        "/api/v3/datalake/dashboard/kiosk/*/*/data"
     );
   }
 }
diff --git 
a/streampipes-user-management/src/main/java/org/apache/streampipes/user/management/util/GrantedPermissionsBuilder.java
 
b/streampipes-user-management/src/main/java/org/apache/streampipes/user/management/util/GrantedPermissionsBuilder.java
index 83fbc85d28..1674849767 100644
--- 
a/streampipes-user-management/src/main/java/org/apache/streampipes/user/management/util/GrantedPermissionsBuilder.java
+++ 
b/streampipes-user-management/src/main/java/org/apache/streampipes/user/management/util/GrantedPermissionsBuilder.java
@@ -25,7 +25,7 @@ import java.util.Set;
 
 public class GrantedPermissionsBuilder {
 
-  private Principal principal;
+  private final Principal principal;
 
   public GrantedPermissionsBuilder(Principal principal) {
     this.principal = principal;
diff --git 
a/ui/projects/streampipes/platform-services/src/lib/apis/dashboard-kiosk.service.ts
 
b/ui/projects/streampipes/platform-services/src/lib/apis/dashboard-kiosk.service.ts
new file mode 100644
index 0000000000..be79b32851
--- /dev/null
+++ 
b/ui/projects/streampipes/platform-services/src/lib/apis/dashboard-kiosk.service.ts
@@ -0,0 +1,52 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+import { inject, Injectable } from '@angular/core';
+import { HttpClient, HttpContext } from '@angular/common/http';
+import { SpQueryResult } from '../model/gen/streampipes-model';
+import { Observable } from 'rxjs';
+import { NGX_LOADING_BAR_IGNORED } from '@ngx-loading-bar/http-client';
+import { PlatformServicesCommons } from './commons.service';
+import { DatalakeQueryParameters } from 
'../model/datalake/DatalakeQueryParameters';
+
+@Injectable({
+    providedIn: 'root',
+})
+export class DashboardKioskRestService {
+    private http = inject(HttpClient);
+    private platformServicesCommons = inject(PlatformServicesCommons);
+
+    getData(
+        dashboardId: string,
+        widgetId: string,
+        queryParams: DatalakeQueryParameters,
+    ): Observable<SpQueryResult> {
+        const context = new HttpContext().set(NGX_LOADING_BAR_IGNORED, true);
+        const url = 
`${this.dashboardKioskBasePath}/${dashboardId}/${widgetId}/data`;
+        return this.http.post<SpQueryResult>(url, queryParams, {
+            context,
+        });
+    }
+
+    private get dashboardKioskBasePath() {
+        return (
+            this.platformServicesCommons.basePath +
+            '/api/v3/datalake/dashboard/kiosk'
+        );
+    }
+}
diff --git 
a/ui/projects/streampipes/platform-services/src/lib/apis/datalake-rest.service.ts
 
b/ui/projects/streampipes/platform-services/src/lib/apis/datalake-rest.service.ts
index 4b54c3e802..9b865257cd 100644
--- 
a/ui/projects/streampipes/platform-services/src/lib/apis/datalake-rest.service.ts
+++ 
b/ui/projects/streampipes/platform-services/src/lib/apis/datalake-rest.service.ts
@@ -17,11 +17,17 @@
  */
 
 import { Injectable } from '@angular/core';
-import { HttpClient, HttpParams, HttpRequest } from '@angular/common/http';
+import {
+    HttpClient,
+    HttpContext,
+    HttpParams,
+    HttpRequest,
+} from '@angular/common/http';
 import { Observable, of } from 'rxjs';
 import { DataLakeMeasure, SpQueryResult } from 
'../model/gen/streampipes-model';
 import { map } from 'rxjs/operators';
 import { DatalakeQueryParameters } from 
'../model/datalake/DatalakeQueryParameters';
+import { NGX_LOADING_BAR_IGNORED } from '@ngx-loading-bar/http-client';
 
 @Injectable({
     providedIn: 'root',
@@ -81,20 +87,21 @@ export class DatalakeRestService {
     getData(
         index: string,
         queryParams: DatalakeQueryParameters,
-        ignoreLoadingBar?: boolean,
+        ignoreLoadingBar = false,
     ): Observable<SpQueryResult> {
         const columns = queryParams.columns;
+        const context = ignoreLoadingBar
+            ? new HttpContext().set(NGX_LOADING_BAR_IGNORED, true)
+            : undefined;
         if (columns === '') {
             const emptyQueryResult = new SpQueryResult();
             emptyQueryResult.total = 0;
             return of(emptyQueryResult);
         } else {
             const url = this.dataLakeUrl + '/measurements/' + index;
-            const headers = ignoreLoadingBar ? { ignoreLoadingBar: '' } : {};
-            // @ts-ignore
             return this.http.get<SpQueryResult>(url, {
                 params: queryParams as unknown as HttpParams,
-                headers,
+                context,
             });
         }
     }
diff --git 
a/ui/projects/streampipes/platform-services/src/lib/model/gen/streampipes-model-client.ts
 
b/ui/projects/streampipes/platform-services/src/lib/model/gen/streampipes-model-client.ts
index 09c378e914..bb58115952 100644
--- 
a/ui/projects/streampipes/platform-services/src/lib/model/gen/streampipes-model-client.ts
+++ 
b/ui/projects/streampipes/platform-services/src/lib/model/gen/streampipes-model-client.ts
@@ -20,7 +20,7 @@
 /* tslint:disable */
 /* eslint-disable */
 // @ts-nocheck
-// Generated using typescript-generator version 3.2.1263 on 2025-08-19 
20:00:50.
+// Generated using typescript-generator version 3.2.1263 on 2025-08-21 
14:22:05.
 
 import { Storable } from './streampipes-model';
 
@@ -83,6 +83,7 @@ export class Permission implements Storable {
     ownerSid: string;
     permissionId: string;
     publicElement: boolean;
+    readAnonymous: boolean;
     rev: string;
 
     static fromData(data: Permission, target?: Permission): Permission {
@@ -99,6 +100,7 @@ export class Permission implements Storable {
         instance.ownerSid = data.ownerSid;
         instance.permissionId = data.permissionId;
         instance.publicElement = data.publicElement;
+        instance.readAnonymous = data.readAnonymous;
         instance.rev = data.rev;
         return instance;
     }
diff --git 
a/ui/projects/streampipes/platform-services/src/lib/query/data-view-query-generator.service.ts
 
b/ui/projects/streampipes/platform-services/src/lib/query/data-view-query-generator.service.ts
index 2c2c28c4f7..deb9c14e1b 100644
--- 
a/ui/projects/streampipes/platform-services/src/lib/query/data-view-query-generator.service.ts
+++ 
b/ui/projects/streampipes/platform-services/src/lib/query/data-view-query-generator.service.ts
@@ -16,7 +16,7 @@
  *
  */
 
-import { Injectable } from '@angular/core';
+import { inject, Injectable } from '@angular/core';
 import { Observable } from 'rxjs';
 import { DatalakeRestService } from '../apis/datalake-rest.service';
 import {
@@ -26,12 +26,14 @@ import {
 import { SpQueryResult } from '../model/gen/streampipes-model';
 import { DatalakeQueryParameters } from 
'../model/datalake/DatalakeQueryParameters';
 import { DatalakeQueryParameterBuilder } from 
'./DatalakeQueryParameterBuilder';
+import { DashboardKioskRestService } from '../apis/dashboard-kiosk.service';
 
 @Injectable({
     providedIn: 'root',
 })
 export class DataViewQueryGeneratorService {
-    constructor(protected dataLakeRestService: DatalakeRestService) {}
+    protected dataLakeRestService = inject(DatalakeRestService);
+    protected dashboardKioskRestService = inject(DashboardKioskRestService);
 
     generateObservables(
         startTime: number,
@@ -56,19 +58,45 @@ export class DataViewQueryGeneratorService {
         });
     }
 
+    generateObservablesForKioskMode(
+        startTime: number,
+        endTime: number,
+        dataConfig: DataExplorerDataConfig,
+        dashboardId: string,
+        widgetId: string,
+        maximumResultingEvents = -1,
+    ): Observable<SpQueryResult>[] {
+        return dataConfig.sourceConfigs.map(sourceConfig => {
+            const dataLakeConfiguration = this.generateQuery(
+                startTime,
+                endTime,
+                sourceConfig,
+                dataConfig.ignoreMissingValues,
+                maximumResultingEvents,
+                true,
+            );
+
+            return this.dashboardKioskRestService.getData(
+                dashboardId,
+                widgetId,
+                dataLakeConfiguration,
+            );
+        });
+    }
+
     generateQuery(
         startTime: number,
         endTime: number,
         sourceConfig: SourceConfig,
         ignoreEventsWithMissingValues: boolean,
         maximumResultingEvents: number = -1,
+        includeMeasureName = false,
     ): DatalakeQueryParameters {
         const queryBuilder = DatalakeQueryParameterBuilder.create(
             startTime,
             endTime,
         );
         const queryConfig = sourceConfig.queryConfig;
-
         queryBuilder.withColumnFilter(
             queryConfig.fields.filter(f => f.selected),
             sourceConfig.queryType === 'aggregated' ||
@@ -122,6 +150,10 @@ export class DataViewQueryGeneratorService {
             queryBuilder.withMaximumAmountOfEvents(maximumResultingEvents);
         }
 
+        if (includeMeasureName) {
+            dataLakeQueryParameter.measureName = sourceConfig.measureName;
+        }
+
         return dataLakeQueryParameter;
     }
 }
diff --git a/ui/projects/streampipes/platform-services/src/public-api.ts 
b/ui/projects/streampipes/platform-services/src/public-api.ts
index ec91f1a39a..583573db3c 100644
--- a/ui/projects/streampipes/platform-services/src/public-api.ts
+++ b/ui/projects/streampipes/platform-services/src/public-api.ts
@@ -30,6 +30,7 @@ export * from './lib/apis/compact-pipeline.service';
 export * from './lib/apis/certificate.service';
 export * from './lib/apis/chart.service';
 export * from './lib/apis/dashboard.service';
+export * from './lib/apis/dashboard-kiosk.service';
 export * from './lib/apis/datalake-rest.service';
 export * from './lib/apis/files.service';
 export * from './lib/apis/functions.service';
diff --git 
a/ui/src/app/connect/components/existing-adapters/existing-adapters.component.html
 
b/ui/src/app/connect/components/existing-adapters/existing-adapters.component.html
index 25d93d4d95..057e54fd02 100644
--- 
a/ui/src/app/connect/components/existing-adapters/existing-adapters.component.html
+++ 
b/ui/src/app/connect/components/existing-adapters/existing-adapters.component.html
@@ -99,7 +99,7 @@
             ></sp-basic-header-title-component>
             <div fxFlex="100" fxLayout="row" fxLayoutAlign="center start">
                 <sp-table
-                    fxFlex="90"
+                    fxFlex="100"
                     [columns]="displayedColumns"
                     [dataSource]="dataSource"
                     data-cy="all-adapters-table"
diff --git 
a/ui/src/app/core-ui/object-permission-dialog/object-permission-dialog.component.html
 
b/ui/src/app/core-ui/object-permission-dialog/object-permission-dialog.component.html
index 460e0d9162..08095d79fc 100644
--- 
a/ui/src/app/core-ui/object-permission-dialog/object-permission-dialog.component.html
+++ 
b/ui/src/app/core-ui/object-permission-dialog/object-permission-dialog.component.html
@@ -17,140 +17,201 @@
   -->
 
 <div class="sp-dialog-container">
-    <div class="sp-dialog-content" *ngIf="usersLoaded">
-        <div fxFlex="100" fxLayout="column" class="p-15">
-            <h4>{{ headerTitle }}</h4>
-            <form [formGroup]="parentForm" fxFlex="100" fxLayout="column">
-                <div class="general-options-panel" fxLayout="column">
-                    <span class="general-options-header">{{
-                        'Basics' | translate
-                    }}</span>
-                    <mat-form-field color="accent">
-                        <mat-label>{{ 'Owner' | translate }}</mat-label>
-                        <mat-select formControlName="owner" fxFlex required>
-                            <mat-option
-                                *ngFor="let user of allUsers"
-                                [value]="user.principalId"
-                                >{{ user.username }}</mat-option
-                            >
-                        </mat-select>
-                    </mat-form-field>
-                    <mat-checkbox
-                        data-cy="permission-public-element"
-                        formControlName="publicElement"
-                    >
-                        {{ 'Public Element' | translate }}
-                    </mat-checkbox>
-                </div>
-                <div
-                    fxLayout="column"
-                    class="general-options-panel"
-                    *ngIf="!permission.publicElement"
-                >
-                    <span class="general-options-header">{{
-                        'Users' | translate
-                    }}</span>
-                    <mat-form-field color="accent">
-                        <mat-label>{{
-                            'Authorized Users' | translate
-                        }}</mat-label>
-                        <mat-chip-grid
-                            #chipList
-                            [attr.aria-label]="'User selection' | translate"
-                        >
-                            <mat-chip-row
-                                *ngFor="let user of grantedUserAuthorities"
-                                selectable="true"
-                                removable="true"
-                                (removed)="removeUser(user)"
-                            >
-                                {{ user.username }}
-                                <button matChipRemove>
-                                    <mat-icon>cancel</mat-icon>
-                                </button>
-                            </mat-chip-row>
-                            <input
-                                [placeholder]="'Add' | translate"
-                                #userInput
-                                [formControl]="userCtrl"
-                                [matAutocomplete]="auto"
-                                [matChipInputFor]="chipList"
-                                [matChipInputSeparatorKeyCodes]="
-                                    separatorKeysCodes
-                                "
-                                data-cy="authorized-user"
-                                (matChipInputTokenEnd)="addUser($event)"
-                            />
-                        </mat-chip-grid>
-                        <mat-autocomplete
-                            #auto="matAutocomplete"
-                            (optionSelected)="userSelected($event)"
-                        >
-                            <mat-option
-                                *ngFor="let user of filteredUsers | async"
-                                [value]="user"
-                                [attr.data-cy]="'user-option-' + user.username"
-                            >
-                                {{ user.username }}
-                            </mat-option>
-                        </mat-autocomplete>
-                    </mat-form-field>
-                </div>
-                <div
-                    fxLayout="column"
-                    class="general-options-panel"
-                    *ngIf="!permission.publicElement"
-                >
-                    <span class="general-options-header">{{
-                        'Groups' | translate
-                    }}</span>
-                    <mat-form-field color="accent">
-                        <mat-label data-cy="authorized-groups-label">{{
-                            'Authorized Groups' | translate
-                        }}</mat-label>
-                        <mat-chip-grid
-                            #chipList
-                            [attr.aria-label]="'Group selection' | translate"
-                        >
-                            <mat-chip-row
-                                *ngFor="let group of grantedGroupAuthorities"
-                                selectable="true"
-                                removable="true"
-                                (removed)="removeGroup(group)"
-                            >
-                                {{ group.groupName }}
-                                <button matChipRemove>
-                                    <mat-icon>cancel</mat-icon>
-                                </button>
-                            </mat-chip-row>
-                            <input
-                                [placeholder]="'Add' | translate"
-                                #groupInput
-                                [formControl]="groupCtrl"
-                                [matAutocomplete]="auto"
-                                [matChipInputFor]="chipList"
-                                [matChipInputSeparatorKeyCodes]="
-                                    separatorKeysCodes
-                                "
-                                (matChipInputTokenEnd)="addGroup($event)"
-                            />
-                        </mat-chip-grid>
-                        <mat-autocomplete
-                            #auto="matAutocomplete"
-                            (optionSelected)="groupSelected($event)"
+    @if (usersLoaded) {
+        <div class="sp-dialog-content">
+            <div fxFlex="100" fxLayout="column" class="p-15">
+                <h4>{{ headerTitle }}</h4>
+                <form [formGroup]="parentForm" fxFlex="100" fxLayout="column">
+                    <div class="general-options-panel" fxLayout="column">
+                        <span class="general-options-header">{{
+                            'Basics' | translate
+                        }}</span>
+                        <mat-form-field color="accent">
+                            <mat-label>{{ 'Owner' | translate }}</mat-label>
+                            <mat-select formControlName="owner" fxFlex 
required>
+                                <mat-option
+                                    *ngFor="let user of allUsers"
+                                    [value]="user.principalId"
+                                    >{{ user.username }}</mat-option
+                                >
+                            </mat-select>
+                        </mat-form-field>
+                        <mat-checkbox
+                            data-cy="permission-public-element"
+                            formControlName="publicElement"
                         >
-                            <mat-option
-                                *ngFor="let group of filteredGroups | async"
-                                [value]="group"
+                            {{ 'Public Element' | translate }} ({{
+                                'visible to registered users' | translate
+                            }})
+                        </mat-checkbox>
+                    </div>
+                    @if (!parentForm.get('publicElement')?.value) {
+                        <div fxLayout="column" class="general-options-panel">
+                            <span class="general-options-header">{{
+                                'Users' | translate
+                            }}</span>
+                            <mat-form-field color="accent">
+                                <mat-label>{{
+                                    'Authorized Users' | translate
+                                }}</mat-label>
+                                <mat-chip-grid
+                                    #chipList
+                                    [attr.aria-label]="
+                                        'User selection' | translate
+                                    "
+                                >
+                                    <mat-chip-row
+                                        *ngFor="
+                                            let user of grantedUserAuthorities
+                                        "
+                                        selectable="true"
+                                        removable="true"
+                                        (removed)="removeUser(user)"
+                                    >
+                                        {{ user.username }}
+                                        <button matChipRemove>
+                                            <mat-icon>cancel</mat-icon>
+                                        </button>
+                                    </mat-chip-row>
+                                    <input
+                                        matInput
+                                        [placeholder]="'Add' | translate"
+                                        #userInput
+                                        [formControl]="userCtrl"
+                                        [matAutocomplete]="userAuto"
+                                        [matChipInputFor]="chipList"
+                                        [matChipInputSeparatorKeyCodes]="
+                                            separatorKeysCodes
+                                        "
+                                        data-cy="authorized-user"
+                                        
(matChipInputTokenEnd)="addUser($event)"
+                                    />
+                                </mat-chip-grid>
+                                <mat-autocomplete
+                                    #userAuto="matAutocomplete"
+                                    (optionSelected)="userSelected($event)"
+                                >
+                                    <mat-option
+                                        *ngFor="
+                                            let user of filteredUsers | async
+                                        "
+                                        [value]="user"
+                                        [attr.data-cy]="
+                                            'user-option-' + user.username
+                                        "
+                                    >
+                                        {{ user.username }}
+                                    </mat-option>
+                                </mat-autocomplete>
+                            </mat-form-field>
+                        </div>
+                    }
+                    @if (!parentForm.get('publicElement')?.value) {
+                        <div fxLayout="column" class="general-options-panel">
+                            <span class="general-options-header">{{
+                                'Groups' | translate
+                            }}</span>
+                            <mat-form-field color="accent">
+                                <mat-label data-cy="authorized-groups-label">{{
+                                    'Authorized Groups' | translate
+                                }}</mat-label>
+                                <mat-chip-grid
+                                    #chipList
+                                    [attr.aria-label]="
+                                        'Group selection' | translate
+                                    "
+                                >
+                                    <mat-chip-row
+                                        *ngFor="
+                                            let group of 
grantedGroupAuthorities
+                                        "
+                                        selectable="true"
+                                        removable="true"
+                                        (removed)="removeGroup(group)"
+                                    >
+                                        {{ group.groupName }}
+                                        <button matChipRemove>
+                                            <mat-icon>cancel</mat-icon>
+                                        </button>
+                                    </mat-chip-row>
+                                    <input
+                                        matInput
+                                        [placeholder]="'Add' | translate"
+                                        #groupInput
+                                        [formControl]="groupCtrl"
+                                        [matAutocomplete]="groupAuto"
+                                        [matChipInputFor]="chipList"
+                                        [matChipInputSeparatorKeyCodes]="
+                                            separatorKeysCodes
+                                        "
+                                        (matChipInputTokenEnd)="
+                                            addGroup($event)
+                                        "
+                                    />
+                                </mat-chip-grid>
+                                <mat-autocomplete
+                                    #groupAuto="matAutocomplete"
+                                    (optionSelected)="groupSelected($event)"
+                                >
+                                    <mat-option
+                                        *ngFor="
+                                            let group of filteredGroups | async
+                                        "
+                                        [value]="group"
+                                    >
+                                        {{ group.groupName }}
+                                    </mat-option>
+                                </mat-autocomplete>
+                            </mat-form-field>
+                        </div>
+                    }
+                    @if (
+                        anonymousReadSupported &&
+                        parentForm.contains('readAnonymous')
+                    ) {
+                        <div fxLayout="column" class="general-options-panel">
+                            <span class="general-options-header">{{
+                                'Public Link' | translate
+                            }}</span>
+                            <mat-checkbox
+                                data-cy="permission-anonymous-read"
+                                formControlName="readAnonymous"
                             >
-                                {{ group.groupName }}
-                            </mat-option>
-                        </mat-autocomplete>
-                    </mat-form-field>
-                </div>
-            </form>
+                                {{
+                                    'Allow anonymous access through public 
link'
+                                        | translate
+                                }}
+                            </mat-checkbox>
+                            @if (parentForm.get('readAnonymous')?.value) {
+                                <div
+                                    fxLayout="row"
+                                    fxFlex="100"
+                                    class="mt-10"
+                                    fxLayoutGap="10px"
+                                    fxLayoutAlign="start center"
+                                >
+                                    <span>
+                                        {{ 'URL' | translate }}
+                                    </span>
+                                    <span class="public-link" fxFlex>
+                                        {{ publicLink }}
+                                    </span>
+                                    <button
+                                        mat-icon-button
+                                        color="accent"
+                                        [matTooltip]="'Copy' | translate"
+                                        [cdkCopyToClipboard]="publicLink"
+                                    >
+                                        <mat-icon>content_copy</mat-icon>
+                                    </button>
+                                </div>
+                            }
+                        </div>
+                    }
+                </form>
+            </div>
         </div>
-    </div>
+    }
     <mat-divider></mat-divider>
     <div class="sp-dialog-actions">
         <div fxLayout="row">
@@ -160,7 +221,7 @@
                 color="accent"
                 (click)="save()"
                 style="margin-right: 10px"
-                [disabled]="!parentForm.valid"
+                [disabled]="parentForm.invalid"
                 data-cy="sp-manage-permissions-save"
             >
                 <i class="material-icons">save</i
diff --git 
a/ui/src/app/core-ui/object-permission-dialog/object-permission-dialog.component.scss
 
b/ui/src/app/core-ui/object-permission-dialog/object-permission-dialog.component.scss
index c38d325ac5..9925783cfb 100644
--- 
a/ui/src/app/core-ui/object-permission-dialog/object-permission-dialog.component.scss
+++ 
b/ui/src/app/core-ui/object-permission-dialog/object-permission-dialog.component.scss
@@ -23,3 +23,12 @@
 .form-field .mat-form-field-infix {
     border-top: 0;
 }
+
+.public-link {
+    background: var(--color-bg-2);
+    border: 1px solid var(--color-bg-1);
+    padding: 10px;
+    color: var(--color-default-text);
+    margin-left: 10px;
+    margin-right: 10px;
+}
diff --git 
a/ui/src/app/core-ui/object-permission-dialog/object-permission-dialog.component.ts
 
b/ui/src/app/core-ui/object-permission-dialog/object-permission-dialog.component.ts
index 02d104a3dc..2dbda6e745 100644
--- 
a/ui/src/app/core-ui/object-permission-dialog/object-permission-dialog.component.ts
+++ 
b/ui/src/app/core-ui/object-permission-dialog/object-permission-dialog.component.ts
@@ -55,6 +55,12 @@ export class ObjectPermissionDialogComponent implements 
OnInit {
     @Input()
     headerTitle: string;
 
+    @Input()
+    anonymousReadSupported = false;
+
+    @Input()
+    publicLink = '';
+
     parentForm: UntypedFormGroup;
 
     permission: Permission;
@@ -90,17 +96,6 @@ export class ObjectPermissionDialogComponent implements 
OnInit {
     ngOnInit(): void {
         this.loadUsersAndGroups();
         this.parentForm = this.fb.group({});
-        this.parentForm.valueChanges.subscribe(v => {
-            this.permission.publicElement = v.publicElement;
-            if (v.publicElement) {
-                this.permission.grantedAuthorities = [];
-                this.grantedGroupAuthorities = [];
-                this.grantedUserAuthorities = [];
-            }
-            if (v.owner) {
-                this.permission.ownerSid = v.owner;
-            }
-        });
     }
 
     loadUsersAndGroups() {
@@ -137,6 +132,12 @@ export class ObjectPermissionDialogComponent implements 
OnInit {
                     Validators.required,
                 ),
             );
+            if (this.anonymousReadSupported) {
+                this.parentForm.addControl(
+                    'readAnonymous',
+                    new UntypedFormControl(this.permission.readAnonymous),
+                );
+            }
             this.filteredUsers = this.userCtrl.valueChanges.pipe(
                 startWith(null),
                 map((username: string | null) => {
@@ -166,12 +167,25 @@ export class ObjectPermissionDialogComponent implements 
OnInit {
                     this.addUserToSelection(authority);
                 }
             });
-        } else {
-            console.log('No permission entry found for item');
         }
     }
 
     save() {
+        const { owner, publicElement, readAnonymous } =
+            this.parentForm.getRawValue();
+        this.permission.publicElement = publicElement;
+        if (this.anonymousReadSupported) {
+            this.permission.readAnonymous = readAnonymous || false;
+        }
+        if (this.permission.publicElement) {
+            this.permission.grantedAuthorities = [];
+            this.grantedGroupAuthorities = [];
+            this.grantedUserAuthorities = [];
+        }
+        if (owner) {
+            this.permission.ownerSid = owner;
+        }
+
         this.permission.grantedAuthorities = this.grantedUserAuthorities
             .map(u => {
                 return { principalType: u.principalType, sid: u.principalId };
diff --git 
a/ui/src/app/dashboard-kiosk/components/kiosk/dashboard-kiosk.component.html 
b/ui/src/app/dashboard-kiosk/components/kiosk/dashboard-kiosk.component.html
index c08275e9c7..13e9c8f850 100644
--- a/ui/src/app/dashboard-kiosk/components/kiosk/dashboard-kiosk.component.html
+++ b/ui/src/app/dashboard-kiosk/components/kiosk/dashboard-kiosk.component.html
@@ -58,11 +58,11 @@
     <div fxLayout="column" style="height: calc(100vh - 40px)">
         @if (dashboard) {
             <sp-dashboard-grid-view
-                #dashboardGrid
                 *ngIf="dashboard?.widgets.length > 0"
                 [editMode]="false"
                 [kioskMode]="true"
                 [dashboard]="dashboard"
+                [observableGenerator]="observableGenerator"
                 [widgets]="widgets"
                 [timeSettings]="dashboard.dashboardTimeSettings"
                 class="dashboard-grid"
diff --git 
a/ui/src/app/dashboard-kiosk/components/kiosk/dashboard-kiosk.component.ts 
b/ui/src/app/dashboard-kiosk/components/kiosk/dashboard-kiosk.component.ts
index 871967aeb2..34b7a86df0 100644
--- a/ui/src/app/dashboard-kiosk/components/kiosk/dashboard-kiosk.component.ts
+++ b/ui/src/app/dashboard-kiosk/components/kiosk/dashboard-kiosk.component.ts
@@ -29,6 +29,8 @@ import { of, Subscription, timer } from 'rxjs';
 import { switchMap } from 'rxjs/operators';
 import { TimeSelectionService } from '@streampipes/shared-ui';
 import { DataExplorerDashboardService } from 
'../../../dashboard-shared/services/dashboard.service';
+import { DataExplorerSharedService } from 
'../../../data-explorer-shared/services/data-explorer-shared.service';
+import { ObservableGenerator } from 
'../../../data-explorer-shared/models/dataview-dashboard.model';
 
 @Component({
     selector: 'sp-dashboard-kiosk',
@@ -41,7 +43,9 @@ export class DashboardKioskComponent implements OnInit, 
OnDestroy {
     private dashboardService = inject(DashboardService);
     private timeSelectionService = inject(TimeSelectionService);
     private dataExplorerDashboardService = 
inject(DataExplorerDashboardService);
+    private dataExplorerSharedService = inject(DataExplorerSharedService);
 
+    observableGenerator: ObservableGenerator;
     dashboard: Dashboard;
     widgets: DataExplorerWidgetModel[] = [];
     refresh$: Subscription;
@@ -49,6 +53,10 @@ export class DashboardKioskComponent implements OnInit, 
OnDestroy {
 
     ngOnInit() {
         const dashboardId = this.route.snapshot.params.dashboardId;
+        this.observableGenerator =
+            this.dataExplorerSharedService.kioskModeObservableGenerator(
+                dashboardId,
+            );
         this.dashboardService
             .getCompositeDashboard(dashboardId)
             .subscribe(res => {
diff --git 
a/ui/src/app/dashboard-shared/components/chart-view/abstract-chart-view.directive.ts
 
b/ui/src/app/dashboard-shared/components/chart-view/abstract-chart-view.directive.ts
index 8a22e809f6..324bae85f1 100644
--- 
a/ui/src/app/dashboard-shared/components/chart-view/abstract-chart-view.directive.ts
+++ 
b/ui/src/app/dashboard-shared/components/chart-view/abstract-chart-view.directive.ts
@@ -26,6 +26,7 @@ import {
 } from '@streampipes/platform-services';
 import { ResizeService } from 
'../../../data-explorer-shared/services/resize.service';
 import { DataExplorerChartRegistry } from 
'../../../data-explorer-shared/registry/data-explorer-chart-registry';
+import { ObservableGenerator } from 
'../../../data-explorer-shared/models/dataview-dashboard.model';
 
 @Directive()
 export abstract class AbstractChartViewDirective {
@@ -45,6 +46,9 @@ export abstract class AbstractChartViewDirective {
     @Input()
     currentlyConfiguredWidgetId: string;
 
+    @Input()
+    observableGenerator: ObservableGenerator;
+
     configuredWidgets: Map<string, DataExplorerWidgetModel> = new Map<
         string,
         DataExplorerWidgetModel
diff --git 
a/ui/src/app/dashboard-shared/components/chart-view/grid-view/dashboard-grid-view.component.html
 
b/ui/src/app/dashboard-shared/components/chart-view/grid-view/dashboard-grid-view.component.html
index 43f50a8dda..cfa89e8de7 100644
--- 
a/ui/src/app/dashboard-shared/components/chart-view/grid-view/dashboard-grid-view.component.html
+++ 
b/ui/src/app/dashboard-shared/components/chart-view/grid-view/dashboard-grid-view.component.html
@@ -48,6 +48,7 @@
                         [dashboardItem]="item"
                         [configuredWidget]="configuredWidgets.get(item.id)"
                         [dataLakeMeasure]="dataLakeMeasures.get(item.id)"
+                        [observableGenerator]="observableGenerator"
                         [editMode]="editMode"
                         [kioskMode]="kioskMode"
                         [gridMode]="true"
diff --git 
a/ui/src/app/dashboard-shared/components/chart-view/slide-view/dashboard-slide-view.component.html
 
b/ui/src/app/dashboard-shared/components/chart-view/slide-view/dashboard-slide-view.component.html
index 1640260e3a..e7fd8d59b2 100644
--- 
a/ui/src/app/dashboard-shared/components/chart-view/slide-view/dashboard-slide-view.component.html
+++ 
b/ui/src/app/dashboard-shared/components/chart-view/slide-view/dashboard-slide-view.component.html
@@ -61,6 +61,7 @@
                     [dashboardItem]="currentDashboardItem"
                     [configuredWidget]="currentWidget"
                     [dataLakeMeasure]="currentMeasure"
+                    [observableGenerator]="observableGenerator"
                     [editMode]="editMode"
                     [gridMode]="false"
                     [widgetIndex]="i"
diff --git 
a/ui/src/app/dashboard-shared/components/chart-view/slide-view/dashboard-slide-view.component.ts
 
b/ui/src/app/dashboard-shared/components/chart-view/slide-view/dashboard-slide-view.component.ts
index 7d6dd7b283..c8c76a1f6e 100644
--- 
a/ui/src/app/dashboard-shared/components/chart-view/slide-view/dashboard-slide-view.component.ts
+++ 
b/ui/src/app/dashboard-shared/components/chart-view/slide-view/dashboard-slide-view.component.ts
@@ -25,6 +25,7 @@ import {
 } from '@angular/core';
 import { AbstractChartViewDirective } from '../abstract-chart-view.directive';
 import {
+    ClientDashboardItem,
     DashboardItem,
     DataExplorerWidgetModel,
     DataLakeMeasure,
@@ -46,7 +47,7 @@ export class DashboardSlideViewComponent
 
     currentWidget: DataExplorerWidgetModel;
     currentMeasure: DataLakeMeasure;
-    currentDashboardItem: DashboardItem;
+    currentDashboardItem: ClientDashboardItem;
 
     displayWidget = false;
 
@@ -62,9 +63,7 @@ export class DashboardSlideViewComponent
             this.selectedWidgetIndex = index;
             this.currentWidget = this.configuredWidgets.get(widgetId);
             this.currentMeasure = this.dataLakeMeasures.get(widgetId);
-            this.currentDashboardItem = this.dashboard.widgets[
-                index
-            ] as unknown as DashboardItem;
+            this.currentDashboardItem = this.dashboard.widgets[index];
             this.currentlyConfiguredWidgetId = widgetId;
             this.displayWidget = true;
         });
diff --git 
a/ui/src/app/dashboard/components/overview/dashboard-overview-table/dashboard-overview-table.component.html
 
b/ui/src/app/dashboard/components/overview/dashboard-overview-table/dashboard-overview-table.component.html
index 112ba222b1..f3117836fe 100644
--- 
a/ui/src/app/dashboard/components/overview/dashboard-overview-table/dashboard-overview-table.component.html
+++ 
b/ui/src/app/dashboard/components/overview/dashboard-overview-table/dashboard-overview-table.component.html
@@ -22,7 +22,7 @@
     ></sp-basic-header-title-component>
     <div fxFlex="100" fxLayout="row" fxLayoutAlign="center start">
         <sp-table
-            fxFlex="90"
+            fxFlex="100"
             [columns]="displayedColumns"
             [dataSource]="dataSource"
         >
diff --git 
a/ui/src/app/dashboard/components/overview/dashboard-overview-table/dashboard-overview-table.component.ts
 
b/ui/src/app/dashboard/components/overview/dashboard-overview-table/dashboard-overview-table.component.ts
index b2c53014aa..43053f6068 100644
--- 
a/ui/src/app/dashboard/components/overview/dashboard-overview-table/dashboard-overview-table.component.ts
+++ 
b/ui/src/app/dashboard/components/overview/dashboard-overview-table/dashboard-overview-table.component.ts
@@ -71,6 +71,8 @@ export class DashboardOverviewTableComponent extends 
SpDataExplorerOverviewDirec
             this.translateService.instant(
                 `Manage permissions for dashboard ${dashboard.name}`,
             ),
+            true,
+            this.makeDashboardKioskUrl(dashboard.elementId),
         );
 
         dialogRef.afterClosed().subscribe(refresh => {
@@ -152,4 +154,8 @@ export class DashboardOverviewTableComponent extends 
SpDataExplorerOverviewDirec
     openDashboardInKioskMode(dashboard: Dashboard) {
         this.router.navigate(['dashboard-kiosk', dashboard.elementId]);
     }
+
+    makeDashboardKioskUrl(dashboardId: string): string {
+        return 
`${window.location.protocol}//${window.location.host}/#/dashboard-kiosk/${dashboardId}`;
+    }
 }
diff --git 
a/ui/src/app/dashboard/components/overview/dashboard-overview.component.ts 
b/ui/src/app/dashboard/components/overview/dashboard-overview.component.ts
index 7fb66fbdec..4d6bfc337e 100644
--- a/ui/src/app/dashboard/components/overview/dashboard-overview.component.ts
+++ b/ui/src/app/dashboard/components/overview/dashboard-overview.component.ts
@@ -16,7 +16,7 @@
  *
  */
 
-import { Component, OnInit, ViewChild } from '@angular/core';
+import { Component, inject, OnInit, ViewChild } from '@angular/core';
 import { MatDialog } from '@angular/material/dialog';
 import {
     CurrentUserService,
@@ -30,6 +30,7 @@ import { Dashboard } from '@streampipes/platform-services';
 import { DataExplorerDashboardService } from 
'../../../dashboard-shared/services/dashboard.service';
 import { DashboardOverviewTableComponent } from 
'./dashboard-overview-table/dashboard-overview-table.component';
 import { TranslateService } from '@ngx-translate/core';
+import { DataExplorerSharedService } from 
'../../../data-explorer-shared/services/data-explorer-shared.service';
 
 @Component({
     selector: 'sp-dashboard-overview',
@@ -47,14 +48,13 @@ export class DashboardOverviewComponent implements OnInit {
     @ViewChild(DashboardOverviewTableComponent)
     dashboardOverview: DashboardOverviewTableComponent;
 
-    constructor(
-        public dialog: MatDialog,
-        private dataExplorerDashboardService: DataExplorerDashboardService,
-        private authService: AuthService,
-        private currentUserService: CurrentUserService,
-        private breadcrumbService: SpBreadcrumbService,
-        private translateService: TranslateService,
-    ) {}
+    public dialog = inject(MatDialog);
+    private dataExplorerDashboardService = 
inject(DataExplorerDashboardService);
+    private dataExplorerSharedService = inject(DataExplorerSharedService);
+    private authService = inject(AuthService);
+    private currentUserService = inject(CurrentUserService);
+    private breadcrumbService = inject(SpBreadcrumbService);
+    private translateService = inject(TranslateService);
 
     ngOnInit(): void {
         this.breadcrumbService.updateBreadcrumb(
diff --git 
a/ui/src/app/dashboard/components/panel/dashboard-panel.component.html 
b/ui/src/app/dashboard/components/panel/dashboard-panel.component.html
index d2627f69c7..fc3d97d855 100644
--- a/ui/src/app/dashboard/components/panel/dashboard-panel.component.html
+++ b/ui/src/app/dashboard/components/panel/dashboard-panel.component.html
@@ -80,6 +80,7 @@
                     [editMode]="editMode"
                     [dashboard]="dashboard"
                     [widgets]="widgets"
+                    [observableGenerator]="observableGenerator"
                     [timeSettings]="timeSettings"
                     (deleteCallback)="removeChartFromDashboard($event)"
                     (startEditModeEmitter)="startEditMode($event)"
@@ -92,6 +93,7 @@
                     [editMode]="editMode"
                     [dashboard]="dashboard"
                     [widgets]="widgets"
+                    [observableGenerator]="observableGenerator"
                     [timeSettings]="timeSettings"
                     (deleteCallback)="removeChartFromDashboard($event)"
                     (startEditModeEmitter)="startEditMode($event)"
diff --git a/ui/src/app/dashboard/components/panel/dashboard-panel.component.ts 
b/ui/src/app/dashboard/components/panel/dashboard-panel.component.ts
index 57aa4e6c35..d56e0f2f9d 100644
--- a/ui/src/app/dashboard/components/panel/dashboard-panel.component.ts
+++ b/ui/src/app/dashboard/components/panel/dashboard-panel.component.ts
@@ -51,6 +51,7 @@ import { DataExplorerDetectChangesService } from 
'../../../data-explorer/service
 import { SupportsUnsavedChangeDialog } from 
'../../../data-explorer-shared/models/dataview-dashboard.model';
 import { TranslateService } from '@ngx-translate/core';
 import { DataExplorerDashboardService } from 
'../../../dashboard-shared/services/dashboard.service';
+import { DataExplorerSharedService } from 
'../../../data-explorer-shared/services/data-explorer-shared.service';
 
 @Component({
     selector: 'sp-dashboard-panel',
@@ -97,6 +98,10 @@ export class DashboardPanelComponent
     private breadcrumbService = inject(SpBreadcrumbService);
     private translateService = inject(TranslateService);
     private dataExplorerDashboardService = 
inject(DataExplorerDashboardService);
+    private dataExplorerSharedService = inject(DataExplorerSharedService);
+
+    observableGenerator =
+        this.dataExplorerSharedService.defaultObservableGenerator();
 
     public ngOnInit() {
         const params = this.route.snapshot.params;
diff --git 
a/ui/src/app/data-explorer-shared/components/chart-container/data-explorer-chart-container.component.html
 
b/ui/src/app/data-explorer-shared/components/chart-container/data-explorer-chart-container.component.html
index 1dc6c0a031..73a2e635c6 100644
--- 
a/ui/src/app/data-explorer-shared/components/chart-container/data-explorer-chart-container.component.html
+++ 
b/ui/src/app/data-explorer-shared/components/chart-container/data-explorer-chart-container.component.html
@@ -64,7 +64,6 @@
                     <button
                         mat-icon-button
                         [matMenuTriggerFor]="menu"
-                        [aria-label]="'More options' | translate"
                         [matTooltip]="'More options' | translate"
                         *ngIf="!dataViewMode"
                         [attr.data-cy]="
@@ -102,7 +101,6 @@
                         mat-icon-button
                         [matMenuTriggerFor]="optMenu"
                         *ngIf="!globalTimeEnabled"
-                        [aria-label]="'Options' | translate"
                         data-cy="options-data-explorer"
                         #menuTrigger="matMenuTrigger"
                         [matTooltip]="tooltipText"
diff --git 
a/ui/src/app/data-explorer-shared/components/chart-container/data-explorer-chart-container.component.ts
 
b/ui/src/app/data-explorer-shared/components/chart-container/data-explorer-chart-container.component.ts
index 91f35ac40c..de5144a8c3 100644
--- 
a/ui/src/app/data-explorer-shared/components/chart-container/data-explorer-chart-container.component.ts
+++ 
b/ui/src/app/data-explorer-shared/components/chart-container/data-explorer-chart-container.component.ts
@@ -53,7 +53,10 @@ import {
     TimeSelectionService,
     TimeSelectorLabel,
 } from '@streampipes/shared-ui';
-import { BaseWidgetData } from '../../models/dataview-dashboard.model';
+import {
+    BaseWidgetData,
+    ObservableGenerator,
+} from '../../models/dataview-dashboard.model';
 import { DataExplorerSharedService } from 
'../../services/data-explorer-shared.service';
 import { MatMenuTrigger } from '@angular/material/menu';
 
@@ -108,6 +111,9 @@ export class DataExplorerChartContainerComponent
     @Input()
     globalTimeEnabled = true;
 
+    @Input()
+    observableGenerator: ObservableGenerator;
+
     @Output() deleteCallback: EventEmitter<number> = new 
EventEmitter<number>();
     @Output() startEditModeEmitter: EventEmitter<DataExplorerWidgetModel> =
         new EventEmitter<DataExplorerWidgetModel>();
@@ -261,6 +267,8 @@ export class DataExplorerChartContainerComponent
         this.componentRef.instance.previewMode = this.previewMode;
         this.componentRef.instance.gridMode = this.gridMode;
         this.componentRef.instance.widgetIndex = this.widgetIndex;
+        this.componentRef.instance.observableGenerator =
+            this.observableGenerator;
         const removeSub =
             this.componentRef.instance.removeWidgetCallback.subscribe(ev =>
                 this.removeWidget(),
diff --git 
a/ui/src/app/data-explorer-shared/components/charts/base/base-data-explorer-widget.directive.ts
 
b/ui/src/app/data-explorer-shared/components/charts/base/base-data-explorer-widget.directive.ts
index 2eebe05707..37d9a719a0 100644
--- 
a/ui/src/app/data-explorer-shared/components/charts/base/base-data-explorer-widget.directive.ts
+++ 
b/ui/src/app/data-explorer-shared/components/charts/base/base-data-explorer-widget.directive.ts
@@ -41,6 +41,7 @@ import { ResizeService } from 
'../../../services/resize.service';
 import {
     BaseWidgetData,
     FieldProvider,
+    ObservableGenerator,
 } from '../../../models/dataview-dashboard.model';
 import { Observable, Subject, Subscription, zip } from 'rxjs';
 import { DataExplorerFieldProviderService } from 
'../../../services/data-explorer-field-provider-service';
@@ -71,6 +72,7 @@ export abstract class BaseDataExplorerWidgetDirective<
     @Input() gridsterItemComponent: GridsterItemComponent;
     @Input() editMode: boolean;
     @Input() kioskMode: boolean;
+    @Input() observableGenerator: ObservableGenerator;
 
     @Input() timeSettings: TimeSettings;
 
@@ -245,29 +247,18 @@ export abstract class BaseDataExplorerWidgetDirective<
     }
 
     private loadData(includeTooMuchEventsParameter: boolean) {
-        let observables: Observable<SpQueryResult>[];
-        if (
+        const returnCompleteResult =
             includeTooMuchEventsParameter &&
-            !this.dataExplorerWidget.dataConfig.ignoreTooMuchDataWarning
-        ) {
-            observables =
-                this.dataViewQueryGeneratorService.generateObservables(
-                    this.timeSettings.startTime,
-                    this.timeSettings.endTime,
-                    this.dataExplorerWidget
-                        .dataConfig as DataExplorerDataConfig,
-                    BaseDataExplorerWidgetDirective.TOO_MUCH_DATA_PARAMETER,
-                );
-        } else {
-            observables =
-                this.dataViewQueryGeneratorService.generateObservables(
-                    this.timeSettings.startTime,
-                    this.timeSettings.endTime,
-                    this.dataExplorerWidget
-                        .dataConfig as DataExplorerDataConfig,
-                );
-        }
-
+            !this.dataExplorerWidget.dataConfig.ignoreTooMuchDataWarning;
+        const observables = this.observableGenerator.generateObservables(
+            this.timeSettings.startTime,
+            this.timeSettings.endTime,
+            this.dataExplorerWidget.dataConfig as DataExplorerDataConfig,
+            this.dataExplorerWidget.elementId,
+            returnCompleteResult
+                ? BaseDataExplorerWidgetDirective.TOO_MUCH_DATA_PARAMETER
+                : undefined,
+        );
         this.timerCallback.emit(true);
         this.requestQueue$.next(observables);
     }
diff --git a/ui/src/app/data-explorer-shared/models/dataview-dashboard.model.ts 
b/ui/src/app/data-explorer-shared/models/dataview-dashboard.model.ts
index 56fd261462..86c996c499 100644
--- a/ui/src/app/data-explorer-shared/models/dataview-dashboard.model.ts
+++ b/ui/src/app/data-explorer-shared/models/dataview-dashboard.model.ts
@@ -23,6 +23,7 @@ import {
 } from 'angular-gridster2';
 import {
     ClientDashboardItem,
+    DataExplorerDataConfig,
     DataExplorerField,
     DataExplorerWidgetModel,
     SpLogMessage,
@@ -48,6 +49,7 @@ export interface BaseWidgetData<T extends 
DataExplorerWidgetModel> {
     gridsterItemComponent: GridsterItemComponent;
     editMode: boolean;
     kioskMode: boolean;
+    observableGenerator: ObservableGenerator;
 
     timeSettings: TimeSettings;
 
@@ -60,6 +62,16 @@ export interface BaseWidgetData<T extends 
DataExplorerWidgetModel> {
     cleanupSubscriptions(): void;
 }
 
+export interface ObservableGenerator {
+    generateObservables(
+        startTime: number,
+        endTime: number,
+        dataConfig: DataExplorerDataConfig,
+        widgetId: string,
+        maxRowCountPerTag: number,
+    ): Observable<SpQueryResult>[];
+}
+
 export interface SpEchartsRenderer<T extends DataExplorerWidgetModel> {
     render(
         queryResult: SpQueryResult[],
diff --git 
a/ui/src/app/data-explorer-shared/services/data-explorer-shared.service.ts 
b/ui/src/app/data-explorer-shared/services/data-explorer-shared.service.ts
index b3d77875c7..12e555ebb4 100644
--- a/ui/src/app/data-explorer-shared/services/data-explorer-shared.service.ts
+++ b/ui/src/app/data-explorer-shared/services/data-explorer-shared.service.ts
@@ -16,10 +16,11 @@
  *
  */
 
-import { Injectable } from '@angular/core';
+import { inject, Injectable } from '@angular/core';
 import {
     DataExplorerDataConfig,
     DataExplorerWidgetModel,
+    DataViewQueryGeneratorService,
     DateRange,
     TimeSettings,
 } from '@streampipes/platform-services';
@@ -30,15 +31,22 @@ import {
 } from '@streampipes/shared-ui';
 import { ObjectPermissionDialogComponent } from 
'../../core-ui/object-permission-dialog/object-permission-dialog.component';
 import { TranslateService } from '@ngx-translate/core';
+import { ObservableGenerator } from '../models/dataview-dashboard.model';
 
 @Injectable({ providedIn: 'root' })
 export class DataExplorerSharedService {
-    constructor(
-        private dialogService: DialogService,
-        private translateService: TranslateService,
-    ) {}
+    private dialogService = inject(DialogService);
+    private translateService = inject(TranslateService);
+    private dataViewQueryGeneratorService = inject(
+        DataViewQueryGeneratorService,
+    );
 
-    openPermissionsDialog(elementId: string, headerTitle: string) {
+    openPermissionsDialog(
+        elementId: string,
+        headerTitle: string,
+        anonymousReadSupported: boolean = false,
+        publicLink: string = '',
+    ) {
         return this.dialogService.open(ObjectPermissionDialogComponent, {
             panelType: PanelType.SLIDE_IN_PANEL,
             title: this.translateService.instant('Manage permissions'),
@@ -46,6 +54,8 @@ export class DataExplorerSharedService {
             data: {
                 objectInstanceId: elementId,
                 headerTitle,
+                anonymousReadSupported,
+                publicLink,
             },
         });
     }
@@ -68,4 +78,44 @@ export class DataExplorerSharedService {
             },
         });
     }
+
+    defaultObservableGenerator(): ObservableGenerator {
+        return {
+            generateObservables: (
+                startTime: number,
+                endTime: number,
+                dataConfig: DataExplorerDataConfig,
+                widgetId: string,
+                maxRowCountPerTag: number,
+            ) => {
+                return this.dataViewQueryGeneratorService.generateObservables(
+                    startTime,
+                    endTime,
+                    dataConfig,
+                    maxRowCountPerTag,
+                );
+            },
+        };
+    }
+
+    kioskModeObservableGenerator(dashboardId: string): ObservableGenerator {
+        return {
+            generateObservables: (
+                startTime: number,
+                endTime: number,
+                dataConfig: DataExplorerDataConfig,
+                widgetId: string,
+                maxRowCountPerTag: number,
+            ) => {
+                return 
this.dataViewQueryGeneratorService.generateObservablesForKioskMode(
+                    startTime,
+                    endTime,
+                    dataConfig,
+                    dashboardId,
+                    widgetId,
+                    maxRowCountPerTag,
+                );
+            },
+        };
+    }
 }
diff --git 
a/ui/src/app/data-explorer/components/chart-view/data-explorer-chart-view.component.html
 
b/ui/src/app/data-explorer/components/chart-view/data-explorer-chart-view.component.html
index 9bb81bf9b0..91f9f7b542 100644
--- 
a/ui/src/app/data-explorer/components/chart-view/data-explorer-chart-view.component.html
+++ 
b/ui/src/app/data-explorer/components/chart-view/data-explorer-chart-view.component.html
@@ -78,6 +78,7 @@
                         [configuredWidget]="dataView"
                         [gridsterItemComponent]="gridsterItemComponent"
                         [timeSettings]="timeSettings"
+                        [observableGenerator]="observableGenerator"
                         [dataLakeMeasure]="
                             dataView.dataConfig.sourceConfigs[0].measure
                         "
diff --git 
a/ui/src/app/data-explorer/components/chart-view/data-explorer-chart-view.component.ts
 
b/ui/src/app/data-explorer/components/chart-view/data-explorer-chart-view.component.ts
index 839a35fa39..0e9c817d9d 100644
--- 
a/ui/src/app/data-explorer/components/chart-view/data-explorer-chart-view.component.ts
+++ 
b/ui/src/app/data-explorer/components/chart-view/data-explorer-chart-view.component.ts
@@ -21,7 +21,6 @@ import {
     ElementRef,
     inject,
     OnInit,
-    signal,
     ViewChild,
 } from '@angular/core';
 import {
@@ -71,18 +70,19 @@ export class DataExplorerChartViewComponent
 
     resizeEchartsService = inject(ResizeEchartsService);
 
-    @ViewChild('panel', { static: false }) outerPanel: ElementRef;
+    private dataExplorerSharedService = inject(DataExplorerSharedService);
+    private detectChangesService = inject(DataExplorerDetectChangesService);
+    private route = inject(ActivatedRoute);
+    private dialog = inject(MatDialog);
+    private routingService = inject(DataExplorerRoutingService);
+    private dataViewService = inject(ChartService);
+    private timeSelectionService = inject(TimeSelectionService);
+    private translateService = inject(TranslateService);
+
+    observableGenerator =
+        this.dataExplorerSharedService.defaultObservableGenerator();
 
-    constructor(
-        private dashboardService: DataExplorerSharedService,
-        private detectChangesService: DataExplorerDetectChangesService,
-        private route: ActivatedRoute,
-        private dialog: MatDialog,
-        private routingService: DataExplorerRoutingService,
-        private dataViewService: ChartService,
-        private timeSelectionService: TimeSelectionService,
-        private translateService: TranslateService,
-    ) {}
+    @ViewChild('panel', { static: false }) outerPanel: ElementRef;
 
     ngOnInit() {
         const dataViewId = this.route.snapshot.params.id;
@@ -233,7 +233,7 @@ export class DataExplorerChartViewComponent
     }
 
     downloadDataAsFile() {
-        this.dashboardService.downloadDataAsFile(
+        this.dataExplorerSharedService.downloadDataAsFile(
             this.timeSettings,
             this.dataView,
         );
diff --git 
a/ui/src/app/data-explorer/components/overview/data-explorer-overview-table/data-explorer-overview-table.component.html
 
b/ui/src/app/data-explorer/components/overview/data-explorer-overview-table/data-explorer-overview-table.component.html
index 65a615065c..6857516236 100644
--- 
a/ui/src/app/data-explorer/components/overview/data-explorer-overview-table/data-explorer-overview-table.component.html
+++ 
b/ui/src/app/data-explorer/components/overview/data-explorer-overview-table/data-explorer-overview-table.component.html
@@ -22,7 +22,7 @@
     ></sp-basic-header-title-component>
     <div fxFlex="100" fxLayout="row" fxLayoutAlign="center start">
         <sp-table
-            fxFlex="90"
+            fxFlex="100"
             [columns]="displayedColumns"
             [dataSource]="dataSource"
         >
diff --git a/ui/src/app/pipelines/pipelines.component.html 
b/ui/src/app/pipelines/pipelines.component.html
index dd7ddf1096..077b3e861e 100644
--- a/ui/src/app/pipelines/pipelines.component.html
+++ b/ui/src/app/pipelines/pipelines.component.html
@@ -89,7 +89,7 @@
                     title="Pipelines"
                 ></sp-basic-header-title-component>
                 <div fxFlex="100" fxLayout="row" fxLayoutAlign="center start">
-                    <div fxFlex="90">
+                    <div fxFlex="100">
                         <sp-pipeline-overview
                             [pipelines]="filteredPipelines"
                             (refreshPipelinesEmitter)="getPipelines()"
@@ -106,7 +106,7 @@
                         fxLayout="row"
                         fxLayoutAlign="center start"
                     >
-                        <div fxFlex="90">
+                        <div fxFlex="100">
                             <sp-functions-overview
                                 [functions]="functions"
                                 *ngIf="functionsReady"

Reply via email to