From 691d3f8b89776b9fd05097017214b812a0058875 Mon Sep 17 00:00:00 2001 From: Charlie Meister Date: Wed, 13 Sep 2023 13:06:14 +0200 Subject: [PATCH 01/18] add meta tags --- .../view/layouting/tracing_layout_view.tsx | 6 ++++++ frontend/javascripts/router.tsx | 6 ++++++ package.json | 1 + yarn.lock | 20 +++++++++++++++++++ 4 files changed, 33 insertions(+) diff --git a/frontend/javascripts/oxalis/view/layouting/tracing_layout_view.tsx b/frontend/javascripts/oxalis/view/layouting/tracing_layout_view.tsx index fa1e180bf0d..27af98b58a5 100644 --- a/frontend/javascripts/oxalis/view/layouting/tracing_layout_view.tsx +++ b/frontend/javascripts/oxalis/view/layouting/tracing_layout_view.tsx @@ -46,6 +46,7 @@ import { determineLayout } from "./default_layout_configs"; import FlexLayoutWrapper from "./flex_layout_wrapper"; import { FloatingMobileControls } from "./floating_mobile_controls"; import app from "app"; +import { Helmet } from "react-helmet"; const { Sider } = Layout; @@ -330,6 +331,11 @@ class TracingLayoutView extends React.PureComponent { return ( + + + + + {this.state.showFloatingMobileButtons && } diff --git a/frontend/javascripts/router.tsx b/frontend/javascripts/router.tsx index 983ac54aea4..e0b7bef35e6 100644 --- a/frontend/javascripts/router.tsx +++ b/frontend/javascripts/router.tsx @@ -67,6 +67,7 @@ import { import ErrorBoundary from "components/error_boundary"; import { Store } from "oxalis/singletons"; import VerifyEmailView from "admin/auth/verify_email_view"; +import { Helmet } from "react-helmet"; const { Content } = Layout; @@ -217,6 +218,11 @@ class ReactRouter extends React.Component { const isAuthenticated = this.props.activeUser !== null; return ( + + + + + diff --git a/package.json b/package.json index 6bfd32d003f..434bd703833 100644 --- a/package.json +++ b/package.json @@ -214,6 +214,7 @@ "react-dropzone": "^11.3.1", "react-flow-renderer": "^10.3.16", "react-google-charts": "^2.0.0", + "react-helmet": "^6.1.0", "react-json-tree": "^0.17.0", "react-redux": "^7.2.0", "react-remarkable": "^1.1.3", diff --git a/yarn.lock b/yarn.lock index 0565ada23f8..2f42a845a4b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -11277,6 +11277,11 @@ react-dropzone@^11.3.1: file-selector "^0.2.2" prop-types "^15.7.2" +react-fast-compare@^3.1.1: + version "3.2.2" + resolved "https://registry.yarnpkg.com/react-fast-compare/-/react-fast-compare-3.2.2.tgz#929a97a532304ce9fee4bcae44234f1ce2c21d49" + integrity sha512-nsO+KSNgo1SbJqJEYRE9ERzo7YtYbou/OqjSQKxV7jcKox7+usiUVZOAC+XnDOABXggQTno0Y1CpVnuWEc1boQ== + react-flow-renderer@^10.3.16: version "10.3.16" resolved "https://registry.yarnpkg.com/react-flow-renderer/-/react-flow-renderer-10.3.16.tgz#7285dfcfa35678c2227ede649c8ed5518bf9f30c" @@ -11298,6 +11303,16 @@ react-google-charts@^2.0.0: dependencies: react-load-script "^0.0.6" +react-helmet@^6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/react-helmet/-/react-helmet-6.1.0.tgz#a750d5165cb13cf213e44747502652e794468726" + integrity sha512-4uMzEY9nlDlgxr61NL3XbKRy1hEkXmKNXhjbAIOVw5vcFrsdYbH2FEwcNyWvWinl103nXgzYNlns9ca+8kFiWw== + dependencies: + object-assign "^4.1.1" + prop-types "^15.7.2" + react-fast-compare "^3.1.1" + react-side-effect "^2.1.0" + react-is@^16.12.0, react-is@^16.13.1, react-is@^16.6.0, react-is@^16.6.3, react-is@^16.7.0, react-is@^16.8.1, react-is@^16.8.6: version "16.13.1" resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4" @@ -11384,6 +11399,11 @@ react-router@5.2.0: tiny-invariant "^1.0.2" tiny-warning "^1.0.0" +react-side-effect@^2.1.0: + version "2.1.2" + resolved "https://registry.yarnpkg.com/react-side-effect/-/react-side-effect-2.1.2.tgz#dc6345b9e8f9906dc2eeb68700b615e0b4fe752a" + integrity sha512-PVjOcvVOyIILrYoyGEpDN3vmYNLdy1CajSFNt4TDsVQC5KpTijDvWVoR+/7Rz2xT978D8/ZtFceXxzsPwZEDvw== + react-sortable-hoc@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/react-sortable-hoc/-/react-sortable-hoc-2.0.0.tgz#f6780d8aa4b922a21f3e754af542f032677078b7" From 1a759552249eb8aecde1c69866900473dbcb9707 Mon Sep 17 00:00:00 2001 From: Florian M Date: Wed, 13 Sep 2023 13:12:41 +0200 Subject: [PATCH 02/18] disable ci checks --- .circleci/not-on-master.sh | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.circleci/not-on-master.sh b/.circleci/not-on-master.sh index 581393ebead..56eeb116ea1 100755 --- a/.circleci/not-on-master.sh +++ b/.circleci/not-on-master.sh @@ -1,8 +1,8 @@ #!/usr/bin/env bash set -Eeuo pipefail -if [ "${CIRCLE_BRANCH}" == "master" ]; then - echo "Skipping this step on master..." -else - exec "$@" -fi +#if [ "${CIRCLE_BRANCH}" == "master" ]; then +echo "Skipping this step on master..." +#else +# exec "$@" +#fi From 8f9e24fae41e09f2bc26cbd9a04d6ce4e188a4ea Mon Sep 17 00:00:00 2001 From: Charlie Meister Date: Wed, 13 Sep 2023 13:34:02 +0200 Subject: [PATCH 03/18] add workflow meta tags --- conf/application.conf | 2 +- frontend/javascripts/admin/voxelytics/workflow_view.tsx | 8 +++++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/conf/application.conf b/conf/application.conf index 056e06ee07d..7b6fc1ddb2e 100644 --- a/conf/application.conf +++ b/conf/application.conf @@ -134,7 +134,7 @@ features { allowDeleteDatasets = true # to enable jobs for local development, use "yarn enable-jobs" to also activate it in the database jobsEnabled = false - voxelyticsEnabled = false + voxelyticsEnabled = true # For new users, the dashboard will show a banner which encourages the user to check out the following dataset. # If isWkorgInstance == true, `/createExplorative/hybrid/true` is appended to the URL so that a new tracing is opened. # If isWkorgInstance == false, `/view` is appended to the URL so that it's opened in view mode (since the user might not diff --git a/frontend/javascripts/admin/voxelytics/workflow_view.tsx b/frontend/javascripts/admin/voxelytics/workflow_view.tsx index f577423f040..6b8243628eb 100644 --- a/frontend/javascripts/admin/voxelytics/workflow_view.tsx +++ b/frontend/javascripts/admin/voxelytics/workflow_view.tsx @@ -23,6 +23,7 @@ import { getVoxelyticsWorkflow, isWorkflowAccessibleBySwitching } from "admin/ad import BrainSpinner, { BrainSpinnerWithError } from "components/brain_spinner"; import TaskListView from "./task_list_view"; import { VX_POLLING_INTERVAL } from "./utils"; +import { Helmet } from "react-helmet"; type LoadingState = | { status: "PENDING" } @@ -419,10 +420,15 @@ export default function WorkflowView() { if (report == null || collapsedReport == null || tasksWithHierarchy == null) { return ; } + const tabTitle = `${collapsedReport.workflow.name} | Voxelytics | WEBKNOSSOS`; return (
- + + + + + Date: Wed, 13 Sep 2023 13:41:21 +0200 Subject: [PATCH 04/18] set static og tags --- app/views/main.scala.html | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/views/main.scala.html b/app/views/main.scala.html index 8bb1c26a14a..99d3c32c422 100755 --- a/app/views/main.scala.html +++ b/app/views/main.scala.html @@ -5,6 +5,9 @@ @(conf.WebKnossos.tabTitle) + + + @if(conf.Features.isWkorgInstance){ Date: Wed, 13 Sep 2023 16:18:43 +0200 Subject: [PATCH 05/18] add og tags in backend --- app/controllers/WkorgProxyController.scala | 20 +++- app/oxalis/opengraph/OpenGraphService.scala | 113 ++++++++++++++++++++ app/views/main.scala.html | 8 +- 3 files changed, 134 insertions(+), 7 deletions(-) create mode 100644 app/oxalis/opengraph/OpenGraphService.scala diff --git a/app/controllers/WkorgProxyController.scala b/app/controllers/WkorgProxyController.scala index b3239f89c5e..cdf26d77e86 100644 --- a/app/controllers/WkorgProxyController.scala +++ b/app/controllers/WkorgProxyController.scala @@ -6,6 +6,7 @@ import com.mohiva.play.silhouette.api.actions.UserAwareRequest import com.scalableminds.util.accesscontext.GlobalAccessContext import com.scalableminds.util.tools.Fox import models.user.{MultiUserDAO, Theme} +import oxalis.opengraph.{OpenGraphService, OpenGraphTags} import oxalis.security.WkEnv import play.api.libs.ws.WSClient import play.api.mvc.{Action, AnyContent} @@ -14,8 +15,11 @@ import utils.WkConf import scala.concurrent.ExecutionContext import scala.util.matching.Regex -class WkorgProxyController @Inject()(ws: WSClient, conf: WkConf, sil: Silhouette[WkEnv], multiUserDAO: MultiUserDAO)( - implicit ec: ExecutionContext) +class WkorgProxyController @Inject()(ws: WSClient, + conf: WkConf, + sil: Silhouette[WkEnv], + multiUserDAO: MultiUserDAO, + openGraphService: OpenGraphService)(implicit ec: ExecutionContext) extends Controller { def proxyPageOrMainView: Action[AnyContent] = sil.UserAwareAction.async { implicit request => @@ -25,7 +29,17 @@ class WkorgProxyController @Inject()(ws: WSClient, conf: WkConf, sil: Silhouette for { multiUserOpt <- Fox.runOptional(request.identity)(user => multiUserDAO.findOne(user._multiUser)(GlobalAccessContext)) - } yield Ok(views.html.main(conf, multiUserOpt.map(_.selectedTheme).getOrElse(Theme.auto).toString)) + openGraphTags <- openGraphService.getOpenGraphTags(request.path) + } yield + Ok( + views.html.main( + conf, + multiUserOpt.map(_.selectedTheme).getOrElse(Theme.auto).toString, + openGraphTags.title, + openGraphTags.description, + openGraphTags.image + ) + ) } } diff --git a/app/oxalis/opengraph/OpenGraphService.scala b/app/oxalis/opengraph/OpenGraphService.scala new file mode 100644 index 00000000000..86d784fa817 --- /dev/null +++ b/app/oxalis/opengraph/OpenGraphService.scala @@ -0,0 +1,113 @@ +package oxalis.opengraph + +import com.google.inject.Inject +import com.scalableminds.util.accesscontext.GlobalAccessContext +import com.scalableminds.util.tools.Fox +import com.scalableminds.webknossos.datastore.models.datasource.Category +import com.typesafe.scalalogging.LazyLogging +import models.binary.{DataSetDAO, DataSetDataLayerDAO} +import models.organization.OrganizationDAO +import models.voxelytics.VoxelyticsDAO +import net.liftweb.common.Full +import utils.WkConf + +import scala.concurrent.ExecutionContext + +case class OpenGraphTags( + title: Option[String], + description: Option[String], + image: Option[String] +) + +object OpenGraphTags { + def default: OpenGraphTags = OpenGraphTags( + Some("WEBKNOSSOS"), + None, + None + ) +} + +class OpenGraphService @Inject()(voxelyticsDAO: VoxelyticsDAO, + dataSetDAO: DataSetDAO, + organizationDAO: OrganizationDAO, + dataSetDataLayerDAO: DataSetDataLayerDAO, + conf: WkConf) + extends LazyLogging { + + def getOpenGraphTags(uriPath: String)(implicit ec: ExecutionContext): Fox[OpenGraphTags] = { + logger.info(s"uriPath is $uriPath") + + val tagsFox = if (isDatasetViewUri(uriPath)) { + datasetOpenGraphTags(uriPath) + } else if (isAnnotationViewUri(uriPath)) { + annotationOpenGraphTags(uriPath) + } else if (isWorkflowViewUri(uriPath)) { + workflowOpenGraphTags(uriPath) + } else Fox.successful(OpenGraphTags.default) + + // In any error case, fall back to default, so the html template does not break + for { + tagsBox <- tagsFox.futureBox + tags = tagsBox match { + case Full(tags) => tags + case _ => OpenGraphTags.default + } + } yield tags + } + + private val datasetRoute1Regex = "^/datasets/([^/^#]+)/([^/^#]+)/view".r + private val datasetRoute2Regex = "^/datasets/([^/^#]+)/([^/^#]+)".r + + private val workflowRouteRegex = "^/workflows/([^/^#]+)".r + + private def isDatasetViewUri(uriPath: String): Boolean = + uriPath match { + case datasetRoute1Regex(_, _) => true + case datasetRoute2Regex(_, _) => true + case _ => false + } + + private def isAnnotationViewUri(uriPath: String): Boolean = false + + private def isWorkflowViewUri(uriPath: String): Boolean = + uriPath match { + case workflowRouteRegex(_) => true + case _ => false + } + + private def datasetOpenGraphTags(uriPath: String)(implicit ec: ExecutionContext): Fox[OpenGraphTags] = + uriPath match { + case datasetRoute1Regex(organizationName, datasetName) => + datasetOpenGraphTagsWithOrganizationName(organizationName, datasetName) + case datasetRoute2Regex(organizationName, datasetName) => + datasetOpenGraphTagsWithOrganizationName(organizationName, datasetName) + case _ => Fox.successful(OpenGraphTags.default) + } + + private def datasetOpenGraphTagsWithOrganizationName(organizationName: String, datasetName: String) = + for { + dataset <- dataSetDAO.findOneByNameAndOrganizationName(datasetName, organizationName)(GlobalAccessContext) + layers <- dataSetDataLayerDAO.findAllForDataSet(dataset._id) + layerOpt = layers.find(_.category == Category.color) + organization <- organizationDAO.findOne(dataset._organization)(GlobalAccessContext) + } yield + OpenGraphTags( + Some(s"${dataset.displayName.getOrElse(datasetName)} | WEBKNOSSOS"), + Some(s"View this dataset of organization ${organization.displayName} in WEBKNOSSOS"), + layerOpt match { + case Some(layer) if dataset.isPublic => + Some(s"${conf.Http.uri}/api/datasets/$organizationName/$datasetName/layers/${layer.name}/thumbnail") + case _ => None + } + ) + + private def annotationOpenGraphTags(uriPath: String): Fox[OpenGraphTags] = ??? + + private def workflowOpenGraphTags(uriPath: String): Fox[OpenGraphTags] = + uriPath match { + case workflowRouteRegex(workflowHash: String) => + for { + workflow <- voxelyticsDAO.findWorkflowByHash(workflowHash) + } yield OpenGraphTags(Some(f"${workflow.name} | WEBKNOSSOS"), Some("Voxelytics Workflow Report"), None) + } +} diff --git a/app/views/main.scala.html b/app/views/main.scala.html index 99d3c32c422..7402f02cc69 100755 --- a/app/views/main.scala.html +++ b/app/views/main.scala.html @@ -1,13 +1,13 @@ -@( conf: utils.WkConf, selectedTheme: String ) +@( conf: utils.WkConf, selectedTheme: String, openGraphTitle: Option[String], openGraphDescription: Option[String], openGraphImage: Option[String] ) @(conf.WebKnossos.tabTitle) - - - + @openGraphTitle.map { ogt => } + @openGraphDescription.map { ogd => } + @openGraphImage.map { ogi => } @if(conf.Features.isWkorgInstance){ Date: Wed, 13 Sep 2023 16:32:37 +0200 Subject: [PATCH 06/18] also for annotations --- app/models/annotation/Annotation.scala | 2 + app/oxalis/opengraph/OpenGraphService.scala | 52 ++++++++++++++++----- 2 files changed, 43 insertions(+), 11 deletions(-) diff --git a/app/models/annotation/Annotation.scala b/app/models/annotation/Annotation.scala index 92e06edeaf2..bc34c1707c2 100755 --- a/app/models/annotation/Annotation.scala +++ b/app/models/annotation/Annotation.scala @@ -43,6 +43,8 @@ case class Annotation( isDeleted: Boolean = false ) extends FoxImplicits { + def nameOpt: Option[String] = if (name.isEmpty) None else Some(name) + lazy val id: String = _id.toString def tracingType: TracingType.Value = { diff --git a/app/oxalis/opengraph/OpenGraphService.scala b/app/oxalis/opengraph/OpenGraphService.scala index 86d784fa817..0a8a0d22789 100644 --- a/app/oxalis/opengraph/OpenGraphService.scala +++ b/app/oxalis/opengraph/OpenGraphService.scala @@ -3,13 +3,14 @@ package oxalis.opengraph import com.google.inject.Inject import com.scalableminds.util.accesscontext.GlobalAccessContext import com.scalableminds.util.tools.Fox -import com.scalableminds.webknossos.datastore.models.datasource.Category +import com.scalableminds.webknossos.datastore.models.datasource.{Category, DataLayer, DataLayerLike} import com.typesafe.scalalogging.LazyLogging -import models.binary.{DataSetDAO, DataSetDataLayerDAO} -import models.organization.OrganizationDAO +import models.annotation.AnnotationDAO +import models.binary.{DataSet, DataSetDAO, DataSetDataLayerDAO} +import models.organization.{Organization, OrganizationDAO} import models.voxelytics.VoxelyticsDAO import net.liftweb.common.Full -import utils.WkConf +import utils.{ObjectId, WkConf} import scala.concurrent.ExecutionContext @@ -31,6 +32,7 @@ class OpenGraphService @Inject()(voxelyticsDAO: VoxelyticsDAO, dataSetDAO: DataSetDAO, organizationDAO: OrganizationDAO, dataSetDataLayerDAO: DataSetDataLayerDAO, + annotationDAO: AnnotationDAO, conf: WkConf) extends LazyLogging { @@ -60,6 +62,8 @@ class OpenGraphService @Inject()(voxelyticsDAO: VoxelyticsDAO, private val workflowRouteRegex = "^/workflows/([^/^#]+)".r + private val annotationRouteRegex = "^/annotations/([^/^#]+)".r + private def isDatasetViewUri(uriPath: String): Boolean = uriPath match { case datasetRoute1Regex(_, _) => true @@ -67,7 +71,11 @@ class OpenGraphService @Inject()(voxelyticsDAO: VoxelyticsDAO, case _ => false } - private def isAnnotationViewUri(uriPath: String): Boolean = false + private def isAnnotationViewUri(uriPath: String): Boolean = + uriPath match { + case annotationRouteRegex(_) => true + case _ => false + } private def isWorkflowViewUri(uriPath: String): Boolean = uriPath match { @@ -94,14 +102,36 @@ class OpenGraphService @Inject()(voxelyticsDAO: VoxelyticsDAO, OpenGraphTags( Some(s"${dataset.displayName.getOrElse(datasetName)} | WEBKNOSSOS"), Some(s"View this dataset of organization ${organization.displayName} in WEBKNOSSOS"), - layerOpt match { - case Some(layer) if dataset.isPublic => - Some(s"${conf.Http.uri}/api/datasets/$organizationName/$datasetName/layers/${layer.name}/thumbnail") - case _ => None - } + thumbnailUri(dataset, layerOpt, organization) ) - private def annotationOpenGraphTags(uriPath: String): Fox[OpenGraphTags] = ??? + private def annotationOpenGraphTags(uriPath: String)(implicit ec: ExecutionContext): Fox[OpenGraphTags] = + uriPath match { + case annotationRouteRegex(annotationId) => + for { + annotationIdValidated <- ObjectId.fromString(annotationId) + annotation <- annotationDAO.findOne(annotationIdValidated)(GlobalAccessContext) + dataset: DataSet <- dataSetDAO.findOne(annotation._dataSet)(GlobalAccessContext) + organization <- organizationDAO.findOne(dataset._organization)(GlobalAccessContext) + layers <- dataSetDataLayerDAO.findAllForDataSet(dataset._id) + layerOpt = layers.find(_.category == Category.color) + } yield + OpenGraphTags( + Some(s"${annotation.nameOpt.orElse(dataset.displayName).getOrElse(dataset.name)} | WEBKNOSSOS"), + Some(s"Annotation on dataset ${dataset.displayName.getOrElse(dataset.name)} in WEBKNOSSOS"), + thumbnailUri(dataset, layerOpt, organization) + ) + case _ => Fox.successful(OpenGraphTags.default) + } + + private def thumbnailUri(dataset: DataSet, + layerOpt: Option[DataLayerLike], + organization: Organization): Option[String] = + layerOpt match { + case Some(layer) if dataset.isPublic => + Some(s"${conf.Http.uri}/api/datasets/${organization.name}/${dataset.name}/layers/${layer.name}/thumbnail") + case _ => None + } private def workflowOpenGraphTags(uriPath: String): Fox[OpenGraphTags] = uriPath match { From f6afd43c6eee1a0dec536ac08678d4fa16a9a2dc Mon Sep 17 00:00:00 2001 From: Florian M Date: Wed, 13 Sep 2023 16:33:16 +0200 Subject: [PATCH 07/18] unused import --- app/oxalis/opengraph/OpenGraphService.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/oxalis/opengraph/OpenGraphService.scala b/app/oxalis/opengraph/OpenGraphService.scala index 0a8a0d22789..4958bf96f70 100644 --- a/app/oxalis/opengraph/OpenGraphService.scala +++ b/app/oxalis/opengraph/OpenGraphService.scala @@ -3,7 +3,7 @@ package oxalis.opengraph import com.google.inject.Inject import com.scalableminds.util.accesscontext.GlobalAccessContext import com.scalableminds.util.tools.Fox -import com.scalableminds.webknossos.datastore.models.datasource.{Category, DataLayer, DataLayerLike} +import com.scalableminds.webknossos.datastore.models.datasource.{Category, DataLayerLike} import com.typesafe.scalalogging.LazyLogging import models.annotation.AnnotationDAO import models.binary.{DataSet, DataSetDAO, DataSetDataLayerDAO} From db9ca366a2d4d4dc48497369276890e8371e7c5f Mon Sep 17 00:00:00 2001 From: Florian M Date: Wed, 13 Sep 2023 17:34:37 +0200 Subject: [PATCH 08/18] unused import --- app/controllers/WkorgProxyController.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/controllers/WkorgProxyController.scala b/app/controllers/WkorgProxyController.scala index cdf26d77e86..5fe291c11c3 100644 --- a/app/controllers/WkorgProxyController.scala +++ b/app/controllers/WkorgProxyController.scala @@ -6,7 +6,7 @@ import com.mohiva.play.silhouette.api.actions.UserAwareRequest import com.scalableminds.util.accesscontext.GlobalAccessContext import com.scalableminds.util.tools.Fox import models.user.{MultiUserDAO, Theme} -import oxalis.opengraph.{OpenGraphService, OpenGraphTags} +import oxalis.opengraph.OpenGraphService import oxalis.security.WkEnv import play.api.libs.ws.WSClient import play.api.mvc.{Action, AnyContent} From d2597596433d98f87dec10e054433e2f53da6a42 Mon Sep 17 00:00:00 2001 From: Florian M Date: Wed, 13 Sep 2023 22:14:41 +0200 Subject: [PATCH 09/18] 1000x300 --- app/oxalis/opengraph/OpenGraphService.scala | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/oxalis/opengraph/OpenGraphService.scala b/app/oxalis/opengraph/OpenGraphService.scala index 4958bf96f70..2f42f5bbc11 100644 --- a/app/oxalis/opengraph/OpenGraphService.scala +++ b/app/oxalis/opengraph/OpenGraphService.scala @@ -129,7 +129,8 @@ class OpenGraphService @Inject()(voxelyticsDAO: VoxelyticsDAO, organization: Organization): Option[String] = layerOpt match { case Some(layer) if dataset.isPublic => - Some(s"${conf.Http.uri}/api/datasets/${organization.name}/${dataset.name}/layers/${layer.name}/thumbnail") + Some( + s"${conf.Http.uri}/api/datasets/${organization.name}/${dataset.name}/layers/${layer.name}/thumbnail?w=1000&h=300") case _ => None } From 14ad45fd74cec6c15b7001e767d8eaffd49e53bb Mon Sep 17 00:00:00 2001 From: Florian M Date: Mon, 25 Sep 2023 15:24:38 +0200 Subject: [PATCH 10/18] remove frontend changes, as clients do not execute javascript --- .../admin/voxelytics/workflow_view.tsx | 8 +------- .../view/layouting/tracing_layout_view.tsx | 6 ------ package.json | 1 - yarn.lock | 20 ------------------- 4 files changed, 1 insertion(+), 34 deletions(-) diff --git a/frontend/javascripts/admin/voxelytics/workflow_view.tsx b/frontend/javascripts/admin/voxelytics/workflow_view.tsx index 6b8243628eb..f577423f040 100644 --- a/frontend/javascripts/admin/voxelytics/workflow_view.tsx +++ b/frontend/javascripts/admin/voxelytics/workflow_view.tsx @@ -23,7 +23,6 @@ import { getVoxelyticsWorkflow, isWorkflowAccessibleBySwitching } from "admin/ad import BrainSpinner, { BrainSpinnerWithError } from "components/brain_spinner"; import TaskListView from "./task_list_view"; import { VX_POLLING_INTERVAL } from "./utils"; -import { Helmet } from "react-helmet"; type LoadingState = | { status: "PENDING" } @@ -420,15 +419,10 @@ export default function WorkflowView() { if (report == null || collapsedReport == null || tasksWithHierarchy == null) { return ; } - const tabTitle = `${collapsedReport.workflow.name} | Voxelytics | WEBKNOSSOS`; return (
- - - - - + { return ( - - - - - {this.state.showFloatingMobileButtons && } diff --git a/package.json b/package.json index 434bd703833..6bfd32d003f 100644 --- a/package.json +++ b/package.json @@ -214,7 +214,6 @@ "react-dropzone": "^11.3.1", "react-flow-renderer": "^10.3.16", "react-google-charts": "^2.0.0", - "react-helmet": "^6.1.0", "react-json-tree": "^0.17.0", "react-redux": "^7.2.0", "react-remarkable": "^1.1.3", diff --git a/yarn.lock b/yarn.lock index 2f42a845a4b..0565ada23f8 100644 --- a/yarn.lock +++ b/yarn.lock @@ -11277,11 +11277,6 @@ react-dropzone@^11.3.1: file-selector "^0.2.2" prop-types "^15.7.2" -react-fast-compare@^3.1.1: - version "3.2.2" - resolved "https://registry.yarnpkg.com/react-fast-compare/-/react-fast-compare-3.2.2.tgz#929a97a532304ce9fee4bcae44234f1ce2c21d49" - integrity sha512-nsO+KSNgo1SbJqJEYRE9ERzo7YtYbou/OqjSQKxV7jcKox7+usiUVZOAC+XnDOABXggQTno0Y1CpVnuWEc1boQ== - react-flow-renderer@^10.3.16: version "10.3.16" resolved "https://registry.yarnpkg.com/react-flow-renderer/-/react-flow-renderer-10.3.16.tgz#7285dfcfa35678c2227ede649c8ed5518bf9f30c" @@ -11303,16 +11298,6 @@ react-google-charts@^2.0.0: dependencies: react-load-script "^0.0.6" -react-helmet@^6.1.0: - version "6.1.0" - resolved "https://registry.yarnpkg.com/react-helmet/-/react-helmet-6.1.0.tgz#a750d5165cb13cf213e44747502652e794468726" - integrity sha512-4uMzEY9nlDlgxr61NL3XbKRy1hEkXmKNXhjbAIOVw5vcFrsdYbH2FEwcNyWvWinl103nXgzYNlns9ca+8kFiWw== - dependencies: - object-assign "^4.1.1" - prop-types "^15.7.2" - react-fast-compare "^3.1.1" - react-side-effect "^2.1.0" - react-is@^16.12.0, react-is@^16.13.1, react-is@^16.6.0, react-is@^16.6.3, react-is@^16.7.0, react-is@^16.8.1, react-is@^16.8.6: version "16.13.1" resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4" @@ -11399,11 +11384,6 @@ react-router@5.2.0: tiny-invariant "^1.0.2" tiny-warning "^1.0.0" -react-side-effect@^2.1.0: - version "2.1.2" - resolved "https://registry.yarnpkg.com/react-side-effect/-/react-side-effect-2.1.2.tgz#dc6345b9e8f9906dc2eeb68700b615e0b4fe752a" - integrity sha512-PVjOcvVOyIILrYoyGEpDN3vmYNLdy1CajSFNt4TDsVQC5KpTijDvWVoR+/7Rz2xT978D8/ZtFceXxzsPwZEDvw== - react-sortable-hoc@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/react-sortable-hoc/-/react-sortable-hoc-2.0.0.tgz#f6780d8aa4b922a21f3e754af542f032677078b7" From 707bf09311954e736369af059dcb3cf92be62c67 Mon Sep 17 00:00:00 2001 From: Florian M Date: Mon, 25 Sep 2023 15:31:27 +0200 Subject: [PATCH 11/18] naming after merge --- app/models/binary/Dataset.scala | 4 ++-- app/oxalis/opengraph/OpenGraphService.scala | 16 ++++++++-------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/app/models/binary/Dataset.scala b/app/models/binary/Dataset.scala index 4e0fb4a807f..ebc2fb5d10d 100755 --- a/app/models/binary/Dataset.scala +++ b/app/models/binary/Dataset.scala @@ -83,7 +83,7 @@ object DatasetCompactInfo { implicit val jsonFormat: Format[DatasetCompactInfo] = Json.format[DatasetCompactInfo] } -class DatasetDAO @Inject()(sqlClient: SqlClient, datasetDataLayerDAO: DatasetLayerDAO, organizationDAO: OrganizationDAO)( +class DatasetDAO @Inject()(sqlClient: SqlClient, datasetLayerDAO: DatasetLayerDAO, organizationDAO: OrganizationDAO)( implicit ec: ExecutionContext) extends SQLDAO[Dataset, DatasetsRow, Datasets](sqlClient) { protected val collection = Datasets @@ -535,7 +535,7 @@ class DatasetDAO @Inject()(sqlClient: SqlClient, datasetDataLayerDAO: DatasetLay scale = ${source.scaleOpt}, status = ${source.statusOpt.getOrElse("").take(1024)} where _id = $id""".asUpdate) - _ <- datasetDataLayerDAO.updateLayers(id, source) + _ <- datasetLayerDAO.updateLayers(id, source) } yield () def deactivateUnreported(existingDataSetIds: List[ObjectId], diff --git a/app/oxalis/opengraph/OpenGraphService.scala b/app/oxalis/opengraph/OpenGraphService.scala index 2f42f5bbc11..3c260663e8b 100644 --- a/app/oxalis/opengraph/OpenGraphService.scala +++ b/app/oxalis/opengraph/OpenGraphService.scala @@ -6,7 +6,7 @@ import com.scalableminds.util.tools.Fox import com.scalableminds.webknossos.datastore.models.datasource.{Category, DataLayerLike} import com.typesafe.scalalogging.LazyLogging import models.annotation.AnnotationDAO -import models.binary.{DataSet, DataSetDAO, DataSetDataLayerDAO} +import models.binary.{Dataset, DatasetDAO, DatasetLayerDAO} import models.organization.{Organization, OrganizationDAO} import models.voxelytics.VoxelyticsDAO import net.liftweb.common.Full @@ -29,9 +29,9 @@ object OpenGraphTags { } class OpenGraphService @Inject()(voxelyticsDAO: VoxelyticsDAO, - dataSetDAO: DataSetDAO, + datasetDAO: DatasetDAO, organizationDAO: OrganizationDAO, - dataSetDataLayerDAO: DataSetDataLayerDAO, + datasetLayerDAO: DatasetLayerDAO, annotationDAO: AnnotationDAO, conf: WkConf) extends LazyLogging { @@ -94,8 +94,8 @@ class OpenGraphService @Inject()(voxelyticsDAO: VoxelyticsDAO, private def datasetOpenGraphTagsWithOrganizationName(organizationName: String, datasetName: String) = for { - dataset <- dataSetDAO.findOneByNameAndOrganizationName(datasetName, organizationName)(GlobalAccessContext) - layers <- dataSetDataLayerDAO.findAllForDataSet(dataset._id) + dataset <- datasetDAO.findOneByNameAndOrganizationName(datasetName, organizationName)(GlobalAccessContext) + layers <- datasetLayerDAO.findAllForDataset(dataset._id) layerOpt = layers.find(_.category == Category.color) organization <- organizationDAO.findOne(dataset._organization)(GlobalAccessContext) } yield @@ -111,9 +111,9 @@ class OpenGraphService @Inject()(voxelyticsDAO: VoxelyticsDAO, for { annotationIdValidated <- ObjectId.fromString(annotationId) annotation <- annotationDAO.findOne(annotationIdValidated)(GlobalAccessContext) - dataset: DataSet <- dataSetDAO.findOne(annotation._dataSet)(GlobalAccessContext) + dataset: Dataset <- datasetDAO.findOne(annotation._dataSet)(GlobalAccessContext) organization <- organizationDAO.findOne(dataset._organization)(GlobalAccessContext) - layers <- dataSetDataLayerDAO.findAllForDataSet(dataset._id) + layers <- datasetLayerDAO.findAllForDataset(dataset._id) layerOpt = layers.find(_.category == Category.color) } yield OpenGraphTags( @@ -124,7 +124,7 @@ class OpenGraphService @Inject()(voxelyticsDAO: VoxelyticsDAO, case _ => Fox.successful(OpenGraphTags.default) } - private def thumbnailUri(dataset: DataSet, + private def thumbnailUri(dataset: Dataset, layerOpt: Option[DataLayerLike], organization: Organization): Option[String] = layerOpt match { From dfba4daee2d554fc663bacb442fe9ab6f2a4db80 Mon Sep 17 00:00:00 2001 From: Florian M Date: Mon, 25 Sep 2023 15:50:40 +0200 Subject: [PATCH 12/18] use token from uri if available for access check --- app/controllers/WkorgProxyController.scala | 2 +- app/oxalis/opengraph/OpenGraphPageType.scala | 8 ++ app/oxalis/opengraph/OpenGraphService.scala | 91 ++++++++++---------- 3 files changed, 53 insertions(+), 48 deletions(-) create mode 100644 app/oxalis/opengraph/OpenGraphPageType.scala diff --git a/app/controllers/WkorgProxyController.scala b/app/controllers/WkorgProxyController.scala index 5fe291c11c3..14f94348c1f 100644 --- a/app/controllers/WkorgProxyController.scala +++ b/app/controllers/WkorgProxyController.scala @@ -29,7 +29,7 @@ class WkorgProxyController @Inject()(ws: WSClient, for { multiUserOpt <- Fox.runOptional(request.identity)(user => multiUserDAO.findOne(user._multiUser)(GlobalAccessContext)) - openGraphTags <- openGraphService.getOpenGraphTags(request.path) + openGraphTags <- openGraphService.getOpenGraphTags(request.path, request.getQueryString("token")) } yield Ok( views.html.main( diff --git a/app/oxalis/opengraph/OpenGraphPageType.scala b/app/oxalis/opengraph/OpenGraphPageType.scala new file mode 100644 index 00000000000..16706a32c4b --- /dev/null +++ b/app/oxalis/opengraph/OpenGraphPageType.scala @@ -0,0 +1,8 @@ +package oxalis.opengraph + +import com.scalableminds.util.enumeration.ExtendedEnumeration + +object OpenGraphPageType extends ExtendedEnumeration { + type OpenGraphPageType = Value + val dataset, annotation, workflow, unknown = Value +} diff --git a/app/oxalis/opengraph/OpenGraphService.scala b/app/oxalis/opengraph/OpenGraphService.scala index 3c260663e8b..8afbefd2cad 100644 --- a/app/oxalis/opengraph/OpenGraphService.scala +++ b/app/oxalis/opengraph/OpenGraphService.scala @@ -1,7 +1,7 @@ package oxalis.opengraph import com.google.inject.Inject -import com.scalableminds.util.accesscontext.GlobalAccessContext +import com.scalableminds.util.accesscontext.DBAccessContext import com.scalableminds.util.tools.Fox import com.scalableminds.webknossos.datastore.models.datasource.{Category, DataLayerLike} import com.typesafe.scalalogging.LazyLogging @@ -10,6 +10,7 @@ import models.binary.{Dataset, DatasetDAO, DatasetLayerDAO} import models.organization.{Organization, OrganizationDAO} import models.voxelytics.VoxelyticsDAO import net.liftweb.common.Full +import oxalis.security.URLSharing import utils.{ObjectId, WkConf} import scala.concurrent.ExecutionContext @@ -36,18 +37,20 @@ class OpenGraphService @Inject()(voxelyticsDAO: VoxelyticsDAO, conf: WkConf) extends LazyLogging { - def getOpenGraphTags(uriPath: String)(implicit ec: ExecutionContext): Fox[OpenGraphTags] = { - logger.info(s"uriPath is $uriPath") + def getOpenGraphTags(uriPath: String, uriToken: Option[String])(implicit ec: ExecutionContext, + ctx: DBAccessContext): Fox[OpenGraphTags] = { + val ctxWithToken = URLSharing.fallbackTokenAccessContext(uriToken) - val tagsFox = if (isDatasetViewUri(uriPath)) { - datasetOpenGraphTags(uriPath) - } else if (isAnnotationViewUri(uriPath)) { - annotationOpenGraphTags(uriPath) - } else if (isWorkflowViewUri(uriPath)) { - workflowOpenGraphTags(uriPath) - } else Fox.successful(OpenGraphTags.default) + val pageType = detectPageType(uriPath) - // In any error case, fall back to default, so the html template does not break + val tagsFox = pageType match { + case OpenGraphPageType.dataset => datasetOpenGraphTags(uriPath, uriToken)(ec, ctxWithToken) + case OpenGraphPageType.annotation => annotationOpenGraphTags(uriPath, uriToken)(ec, ctxWithToken) + case OpenGraphPageType.workflow => workflowOpenGraphTags(uriPath) + case OpenGraphPageType.unknown => Fox.successful(OpenGraphTags.default) + } + + // In error case (probably no access permissions), fall back to default, so the html template does not break for { tagsBox <- tagsFox.futureBox tags = tagsBox match { @@ -57,87 +60,81 @@ class OpenGraphService @Inject()(voxelyticsDAO: VoxelyticsDAO, } yield tags } + private def detectPageType(uriPath: String) = + uriPath match { + case datasetRoute1Regex(_, _) | datasetRoute2Regex(_, _) => OpenGraphPageType.dataset + case annotationRouteRegex(_) => OpenGraphPageType.annotation + case workflowRouteRegex(_) => OpenGraphPageType.workflow + case _ => OpenGraphPageType.unknown + } + private val datasetRoute1Regex = "^/datasets/([^/^#]+)/([^/^#]+)/view".r private val datasetRoute2Regex = "^/datasets/([^/^#]+)/([^/^#]+)".r - private val workflowRouteRegex = "^/workflows/([^/^#]+)".r - private val annotationRouteRegex = "^/annotations/([^/^#]+)".r - private def isDatasetViewUri(uriPath: String): Boolean = - uriPath match { - case datasetRoute1Regex(_, _) => true - case datasetRoute2Regex(_, _) => true - case _ => false - } - - private def isAnnotationViewUri(uriPath: String): Boolean = - uriPath match { - case annotationRouteRegex(_) => true - case _ => false - } - - private def isWorkflowViewUri(uriPath: String): Boolean = - uriPath match { - case workflowRouteRegex(_) => true - case _ => false - } - - private def datasetOpenGraphTags(uriPath: String)(implicit ec: ExecutionContext): Fox[OpenGraphTags] = + private def datasetOpenGraphTags(uriPath: String, token: Option[String])(implicit ec: ExecutionContext, + ctx: DBAccessContext): Fox[OpenGraphTags] = uriPath match { case datasetRoute1Regex(organizationName, datasetName) => - datasetOpenGraphTagsWithOrganizationName(organizationName, datasetName) + datasetOpenGraphTagsWithOrganizationName(organizationName, datasetName, token) case datasetRoute2Regex(organizationName, datasetName) => - datasetOpenGraphTagsWithOrganizationName(organizationName, datasetName) + datasetOpenGraphTagsWithOrganizationName(organizationName, datasetName, token) case _ => Fox.successful(OpenGraphTags.default) } - private def datasetOpenGraphTagsWithOrganizationName(organizationName: String, datasetName: String) = + private def datasetOpenGraphTagsWithOrganizationName(organizationName: String, + datasetName: String, + token: Option[String])(implicit ctx: DBAccessContext) = for { - dataset <- datasetDAO.findOneByNameAndOrganizationName(datasetName, organizationName)(GlobalAccessContext) + dataset <- datasetDAO.findOneByNameAndOrganizationName(datasetName, organizationName) layers <- datasetLayerDAO.findAllForDataset(dataset._id) layerOpt = layers.find(_.category == Category.color) - organization <- organizationDAO.findOne(dataset._organization)(GlobalAccessContext) + organization <- organizationDAO.findOne(dataset._organization) } yield OpenGraphTags( Some(s"${dataset.displayName.getOrElse(datasetName)} | WEBKNOSSOS"), Some(s"View this dataset of organization ${organization.displayName} in WEBKNOSSOS"), - thumbnailUri(dataset, layerOpt, organization) + thumbnailUri(dataset, layerOpt, organization, token) ) - private def annotationOpenGraphTags(uriPath: String)(implicit ec: ExecutionContext): Fox[OpenGraphTags] = + private def annotationOpenGraphTags(uriPath: String, token: Option[String])( + implicit ec: ExecutionContext, + ctx: DBAccessContext): Fox[OpenGraphTags] = uriPath match { case annotationRouteRegex(annotationId) => for { annotationIdValidated <- ObjectId.fromString(annotationId) - annotation <- annotationDAO.findOne(annotationIdValidated)(GlobalAccessContext) - dataset: Dataset <- datasetDAO.findOne(annotation._dataSet)(GlobalAccessContext) - organization <- organizationDAO.findOne(dataset._organization)(GlobalAccessContext) + annotation <- annotationDAO.findOne(annotationIdValidated) + dataset: Dataset <- datasetDAO.findOne(annotation._dataSet) + organization <- organizationDAO.findOne(dataset._organization) layers <- datasetLayerDAO.findAllForDataset(dataset._id) layerOpt = layers.find(_.category == Category.color) } yield OpenGraphTags( Some(s"${annotation.nameOpt.orElse(dataset.displayName).getOrElse(dataset.name)} | WEBKNOSSOS"), Some(s"Annotation on dataset ${dataset.displayName.getOrElse(dataset.name)} in WEBKNOSSOS"), - thumbnailUri(dataset, layerOpt, organization) + thumbnailUri(dataset, layerOpt, organization, token) ) case _ => Fox.successful(OpenGraphTags.default) } private def thumbnailUri(dataset: Dataset, layerOpt: Option[DataLayerLike], - organization: Organization): Option[String] = + organization: Organization, + token: Option[String]): Option[String] = layerOpt match { case Some(layer) if dataset.isPublic => + val tokenParam = token.map(t => s"&token=$t").getOrElse("") Some( - s"${conf.Http.uri}/api/datasets/${organization.name}/${dataset.name}/layers/${layer.name}/thumbnail?w=1000&h=300") + s"${conf.Http.uri}/api/datasets/${organization.name}/${dataset.name}/layers/${layer.name}/thumbnail?w=1000&h=300$tokenParam") case _ => None } private def workflowOpenGraphTags(uriPath: String): Fox[OpenGraphTags] = uriPath match { case workflowRouteRegex(workflowHash: String) => - for { + for { // TODO access check workflow <- voxelyticsDAO.findWorkflowByHash(workflowHash) } yield OpenGraphTags(Some(f"${workflow.name} | WEBKNOSSOS"), Some("Voxelytics Workflow Report"), None) } From df6c0201c9413cf7912ec4c7ca229d47c20338ae Mon Sep 17 00:00:00 2001 From: Florian M Date: Mon, 25 Sep 2023 16:11:07 +0200 Subject: [PATCH 13/18] defaults --- app/oxalis/opengraph/OpenGraphService.scala | 34 +++++++++++++-------- 1 file changed, 22 insertions(+), 12 deletions(-) diff --git a/app/oxalis/opengraph/OpenGraphService.scala b/app/oxalis/opengraph/OpenGraphService.scala index 8afbefd2cad..9caa00c76bb 100644 --- a/app/oxalis/opengraph/OpenGraphService.scala +++ b/app/oxalis/opengraph/OpenGraphService.scala @@ -10,6 +10,7 @@ import models.binary.{Dataset, DatasetDAO, DatasetLayerDAO} import models.organization.{Organization, OrganizationDAO} import models.voxelytics.VoxelyticsDAO import net.liftweb.common.Full +import oxalis.opengraph.OpenGraphPageType.OpenGraphPageType import oxalis.security.URLSharing import utils.{ObjectId, WkConf} @@ -22,11 +23,19 @@ case class OpenGraphTags( ) object OpenGraphTags { - def default: OpenGraphTags = OpenGraphTags( - Some("WEBKNOSSOS"), - None, - None - ) + def default(pageType: OpenGraphPageType = OpenGraphPageType.unknown): OpenGraphTags = { + val description = pageType match { + case OpenGraphPageType.dataset => Some("View this dataset in WEBKNOSSOS") + case OpenGraphPageType.annotation => Some("View this annotation in WEBKNOSSOS") + case OpenGraphPageType.workflow => Some("View this voxelytics workflow report in WEBKNOSSOS") + case _ => None // most clients will fall back to , see template + } + OpenGraphTags( + Some("WEBKNOSSOS"), + description, + None + ) + } } class OpenGraphService @Inject()(voxelyticsDAO: VoxelyticsDAO, @@ -47,7 +56,7 @@ class OpenGraphService @Inject()(voxelyticsDAO: VoxelyticsDAO, case OpenGraphPageType.dataset => datasetOpenGraphTags(uriPath, uriToken)(ec, ctxWithToken) case OpenGraphPageType.annotation => annotationOpenGraphTags(uriPath, uriToken)(ec, ctxWithToken) case OpenGraphPageType.workflow => workflowOpenGraphTags(uriPath) - case OpenGraphPageType.unknown => Fox.successful(OpenGraphTags.default) + case OpenGraphPageType.unknown => Fox.successful(OpenGraphTags.default()) } // In error case (probably no access permissions), fall back to default, so the html template does not break @@ -55,7 +64,7 @@ class OpenGraphService @Inject()(voxelyticsDAO: VoxelyticsDAO, tagsBox <- tagsFox.futureBox tags = tagsBox match { case Full(tags) => tags - case _ => OpenGraphTags.default + case _ => OpenGraphTags.default(pageType) } } yield tags } @@ -80,7 +89,7 @@ class OpenGraphService @Inject()(voxelyticsDAO: VoxelyticsDAO, datasetOpenGraphTagsWithOrganizationName(organizationName, datasetName, token) case datasetRoute2Regex(organizationName, datasetName) => datasetOpenGraphTagsWithOrganizationName(organizationName, datasetName, token) - case _ => Fox.successful(OpenGraphTags.default) + case _ => Fox.failure("not a matching uri") } private def datasetOpenGraphTagsWithOrganizationName(organizationName: String, @@ -94,7 +103,7 @@ class OpenGraphService @Inject()(voxelyticsDAO: VoxelyticsDAO, } yield OpenGraphTags( Some(s"${dataset.displayName.getOrElse(datasetName)} | WEBKNOSSOS"), - Some(s"View this dataset of organization ${organization.displayName} in WEBKNOSSOS"), + Some(s"View this dataset in WEBKNOSSOS"), thumbnailUri(dataset, layerOpt, organization, token) ) @@ -113,10 +122,10 @@ class OpenGraphService @Inject()(voxelyticsDAO: VoxelyticsDAO, } yield OpenGraphTags( Some(s"${annotation.nameOpt.orElse(dataset.displayName).getOrElse(dataset.name)} | WEBKNOSSOS"), - Some(s"Annotation on dataset ${dataset.displayName.getOrElse(dataset.name)} in WEBKNOSSOS"), + Some(s"View this annotation on dataset ${dataset.displayName.getOrElse(dataset.name)} in WEBKNOSSOS"), thumbnailUri(dataset, layerOpt, organization, token) ) - case _ => Fox.successful(OpenGraphTags.default) + case _ => Fox.failure("not a matching uri") } private def thumbnailUri(dataset: Dataset, @@ -131,11 +140,12 @@ class OpenGraphService @Inject()(voxelyticsDAO: VoxelyticsDAO, case _ => None } - private def workflowOpenGraphTags(uriPath: String): Fox[OpenGraphTags] = + private def workflowOpenGraphTags(uriPath: String)(implicit ec: ExecutionContext): Fox[OpenGraphTags] = uriPath match { case workflowRouteRegex(workflowHash: String) => for { // TODO access check workflow <- voxelyticsDAO.findWorkflowByHash(workflowHash) } yield OpenGraphTags(Some(f"${workflow.name} | WEBKNOSSOS"), Some("Voxelytics Workflow Report"), None) + case _ => Fox.failure("not a matching uri") } } From 46723f1f0d80a7f3761d1caf2597b0a38327dd57 Mon Sep 17 00:00:00 2001 From: Florian M Date: Mon, 25 Sep 2023 16:15:42 +0200 Subject: [PATCH 14/18] cleanup --- app/oxalis/opengraph/OpenGraphPageType.scala | 8 ----- app/oxalis/opengraph/OpenGraphService.scala | 36 +++++++++++--------- 2 files changed, 19 insertions(+), 25 deletions(-) delete mode 100644 app/oxalis/opengraph/OpenGraphPageType.scala diff --git a/app/oxalis/opengraph/OpenGraphPageType.scala b/app/oxalis/opengraph/OpenGraphPageType.scala deleted file mode 100644 index 16706a32c4b..00000000000 --- a/app/oxalis/opengraph/OpenGraphPageType.scala +++ /dev/null @@ -1,8 +0,0 @@ -package oxalis.opengraph - -import com.scalableminds.util.enumeration.ExtendedEnumeration - -object OpenGraphPageType extends ExtendedEnumeration { - type OpenGraphPageType = Value - val dataset, annotation, workflow, unknown = Value -} diff --git a/app/oxalis/opengraph/OpenGraphService.scala b/app/oxalis/opengraph/OpenGraphService.scala index 9caa00c76bb..365b7005d21 100644 --- a/app/oxalis/opengraph/OpenGraphService.scala +++ b/app/oxalis/opengraph/OpenGraphService.scala @@ -2,6 +2,7 @@ package oxalis.opengraph import com.google.inject.Inject import com.scalableminds.util.accesscontext.DBAccessContext +import com.scalableminds.util.enumeration.ExtendedEnumeration import com.scalableminds.util.tools.Fox import com.scalableminds.webknossos.datastore.models.datasource.{Category, DataLayerLike} import com.typesafe.scalalogging.LazyLogging @@ -10,7 +11,6 @@ import models.binary.{Dataset, DatasetDAO, DatasetLayerDAO} import models.organization.{Organization, OrganizationDAO} import models.voxelytics.VoxelyticsDAO import net.liftweb.common.Full -import oxalis.opengraph.OpenGraphPageType.OpenGraphPageType import oxalis.security.URLSharing import utils.{ObjectId, WkConf} @@ -22,20 +22,8 @@ case class OpenGraphTags( image: Option[String] ) -object OpenGraphTags { - def default(pageType: OpenGraphPageType = OpenGraphPageType.unknown): OpenGraphTags = { - val description = pageType match { - case OpenGraphPageType.dataset => Some("View this dataset in WEBKNOSSOS") - case OpenGraphPageType.annotation => Some("View this annotation in WEBKNOSSOS") - case OpenGraphPageType.workflow => Some("View this voxelytics workflow report in WEBKNOSSOS") - case _ => None // most clients will fall back to , see template - } - OpenGraphTags( - Some("WEBKNOSSOS"), - description, - None - ) - } +object OpenGraphPageType extends ExtendedEnumeration { + val dataset, annotation, workflow, unknown = Value } class OpenGraphService @Inject()(voxelyticsDAO: VoxelyticsDAO, @@ -56,7 +44,7 @@ class OpenGraphService @Inject()(voxelyticsDAO: VoxelyticsDAO, case OpenGraphPageType.dataset => datasetOpenGraphTags(uriPath, uriToken)(ec, ctxWithToken) case OpenGraphPageType.annotation => annotationOpenGraphTags(uriPath, uriToken)(ec, ctxWithToken) case OpenGraphPageType.workflow => workflowOpenGraphTags(uriPath) - case OpenGraphPageType.unknown => Fox.successful(OpenGraphTags.default()) + case OpenGraphPageType.unknown => Fox.successful(defaultTags()) } // In error case (probably no access permissions), fall back to default, so the html template does not break @@ -64,7 +52,7 @@ class OpenGraphService @Inject()(voxelyticsDAO: VoxelyticsDAO, tagsBox <- tagsFox.futureBox tags = tagsBox match { case Full(tags) => tags - case _ => OpenGraphTags.default(pageType) + case _ => defaultTags(pageType) } } yield tags } @@ -148,4 +136,18 @@ class OpenGraphService @Inject()(voxelyticsDAO: VoxelyticsDAO, } yield OpenGraphTags(Some(f"${workflow.name} | WEBKNOSSOS"), Some("Voxelytics Workflow Report"), None) case _ => Fox.failure("not a matching uri") } + + private def defaultTags(pageType: OpenGraphPageType.Value = OpenGraphPageType.unknown): OpenGraphTags = { + val description = pageType match { + case OpenGraphPageType.dataset => Some("View this dataset in WEBKNOSSOS") + case OpenGraphPageType.annotation => Some("View this annotation in WEBKNOSSOS") + case OpenGraphPageType.workflow => Some("View this voxelytics workflow report in WEBKNOSSOS") + case _ => None // most clients will fall back to , see template + } + OpenGraphTags( + Some("WEBKNOSSOS"), + description, + None + ) + } } From 562f7e6b5ee6833cd7c7f036eeb55f622a6cd6bc Mon Sep 17 00:00:00 2001 From: Florian M Date: Thu, 28 Sep 2023 12:04:00 +0200 Subject: [PATCH 15/18] add sharing token to thumbnail route --- app/controllers/DatasetController.scala | 7 +++++-- app/controllers/WkorgProxyController.scala | 4 +++- app/oxalis/opengraph/OpenGraphService.scala | 19 ++++++++----------- conf/webknossos.latest.routes | 2 +- 4 files changed, 17 insertions(+), 15 deletions(-) diff --git a/app/controllers/DatasetController.scala b/app/controllers/DatasetController.scala index 7a58a522237..f8cbc2fc74a 100755 --- a/app/controllers/DatasetController.scala +++ b/app/controllers/DatasetController.scala @@ -98,10 +98,13 @@ class DatasetController @Inject()(userService: UserService, dataLayerName: String, w: Option[Int], h: Option[Int], - mappingName: Option[String]): Action[AnyContent] = + mappingName: Option[String], + sharingToken: Option[String]): Action[AnyContent] = sil.UserAwareAction.async { implicit request => + val ctx = URLSharing.fallbackTokenAccessContext(sharingToken) for { - _ <- datasetDAO.findOneByNameAndOrganizationName(dataSetName, organizationName) ?~> notFoundMessage(dataSetName) ~> NOT_FOUND // To check Access Rights + _ <- datasetDAO.findOneByNameAndOrganizationName(dataSetName, organizationName)(ctx) ?~> notFoundMessage( + dataSetName) ~> NOT_FOUND // To check Access Rights image <- thumbnailService.getThumbnailWithCache(organizationName, dataSetName, dataLayerName, w, h, mappingName) } yield { addRemoteOriginHeaders(Ok(image)).as(jpegMimeType).withHeaders(CACHE_CONTROL -> "public, max-age=86400") diff --git a/app/controllers/WkorgProxyController.scala b/app/controllers/WkorgProxyController.scala index 14f94348c1f..c1e573794ef 100644 --- a/app/controllers/WkorgProxyController.scala +++ b/app/controllers/WkorgProxyController.scala @@ -29,7 +29,9 @@ class WkorgProxyController @Inject()(ws: WSClient, for { multiUserOpt <- Fox.runOptional(request.identity)(user => multiUserDAO.findOne(user._multiUser)(GlobalAccessContext)) - openGraphTags <- openGraphService.getOpenGraphTags(request.path, request.getQueryString("token")) + openGraphTags <- openGraphService.getOpenGraphTags( + request.path, + request.getQueryString("sharingToken").orElse(request.getQueryString("token"))) } yield Ok( views.html.main( diff --git a/app/oxalis/opengraph/OpenGraphService.scala b/app/oxalis/opengraph/OpenGraphService.scala index 365b7005d21..5d0f2684d9c 100644 --- a/app/oxalis/opengraph/OpenGraphService.scala +++ b/app/oxalis/opengraph/OpenGraphService.scala @@ -34,15 +34,15 @@ class OpenGraphService @Inject()(voxelyticsDAO: VoxelyticsDAO, conf: WkConf) extends LazyLogging { - def getOpenGraphTags(uriPath: String, uriToken: Option[String])(implicit ec: ExecutionContext, - ctx: DBAccessContext): Fox[OpenGraphTags] = { - val ctxWithToken = URLSharing.fallbackTokenAccessContext(uriToken) + def getOpenGraphTags(uriPath: String, sharingToken: Option[String])(implicit ec: ExecutionContext, + ctx: DBAccessContext): Fox[OpenGraphTags] = { + val ctxWithToken = URLSharing.fallbackTokenAccessContext(sharingToken) val pageType = detectPageType(uriPath) val tagsFox = pageType match { - case OpenGraphPageType.dataset => datasetOpenGraphTags(uriPath, uriToken)(ec, ctxWithToken) - case OpenGraphPageType.annotation => annotationOpenGraphTags(uriPath, uriToken)(ec, ctxWithToken) + case OpenGraphPageType.dataset => datasetOpenGraphTags(uriPath, sharingToken)(ec, ctxWithToken) + case OpenGraphPageType.annotation => annotationOpenGraphTags(uriPath, sharingToken)(ec, ctxWithToken) case OpenGraphPageType.workflow => workflowOpenGraphTags(uriPath) case OpenGraphPageType.unknown => Fox.successful(defaultTags()) } @@ -120,12 +120,9 @@ class OpenGraphService @Inject()(voxelyticsDAO: VoxelyticsDAO, layerOpt: Option[DataLayerLike], organization: Organization, token: Option[String]): Option[String] = - layerOpt match { - case Some(layer) if dataset.isPublic => - val tokenParam = token.map(t => s"&token=$t").getOrElse("") - Some( - s"${conf.Http.uri}/api/datasets/${organization.name}/${dataset.name}/layers/${layer.name}/thumbnail?w=1000&h=300$tokenParam") - case _ => None + layerOpt.map { layer => + val tokenParam = token.map(t => s"&sharingToken=$t").getOrElse("") + s"${conf.Http.uri}/api/datasets/${organization.name}/${dataset.name}/layers/${layer.name}/thumbnail?w=1000&h=300$tokenParam" } private def workflowOpenGraphTags(uriPath: String)(implicit ec: ExecutionContext): Fox[OpenGraphTags] = diff --git a/conf/webknossos.latest.routes b/conf/webknossos.latest.routes index 551c3692a13..0bd945b1d16 100644 --- a/conf/webknossos.latest.routes +++ b/conf/webknossos.latest.routes @@ -88,7 +88,7 @@ GET /datasets/:organizationName/:dataSetName/accessList GET /datasets/:organizationName/:dataSetName/sharingToken controllers.DatasetController.getSharingToken(organizationName: String, dataSetName: String) DELETE /datasets/:organizationName/:dataSetName/sharingToken controllers.DatasetController.deleteSharingToken(organizationName: String, dataSetName: String) PATCH /datasets/:organizationName/:dataSetName/teams controllers.DatasetController.updateTeams(organizationName: String, dataSetName: String) -GET /datasets/:organizationName/:dataSetName/layers/:layer/thumbnail controllers.DatasetController.thumbnail(organizationName: String, dataSetName: String, layer: String, w: Option[Int], h: Option[Int], mappingName: Option[String]) +GET /datasets/:organizationName/:dataSetName/layers/:layer/thumbnail controllers.DatasetController.thumbnail(organizationName: String, dataSetName: String, layer: String, w: Option[Int], h: Option[Int], mappingName: Option[String], sharingToken: Option[String]) POST /datasets/:organizationName/:dataSetName/layers/:layer/segmentAnythingEmbedding controllers.DatasetController.segmentAnythingEmbedding(organizationName: String, dataSetName: String, layer: String, intensityMin: Option[Float], intensityMax: Option[Float]) PUT /datasets/:organizationName/:dataSetName/clearThumbnailCache controllers.DatasetController.removeFromThumbnailCache(organizationName: String, dataSetName: String) GET /datasets/:organizationName/:dataSetName/isValidNewName controllers.DatasetController.isValidNewName(organizationName: String, dataSetName: String) From 6447a99f50adc580f49da22328cb641369867e1e Mon Sep 17 00:00:00 2001 From: Florian M Date: Thu, 28 Sep 2023 13:52:48 +0200 Subject: [PATCH 16/18] resolve shortlinks, remove workflow-specific tags --- app/oxalis/opengraph/OpenGraphService.scala | 53 ++++++++++++--------- 1 file changed, 30 insertions(+), 23 deletions(-) diff --git a/app/oxalis/opengraph/OpenGraphService.scala b/app/oxalis/opengraph/OpenGraphService.scala index 5d0f2684d9c..300aeb0e009 100644 --- a/app/oxalis/opengraph/OpenGraphService.scala +++ b/app/oxalis/opengraph/OpenGraphService.scala @@ -1,5 +1,6 @@ package oxalis.opengraph +import akka.http.scaladsl.model.Uri import com.google.inject.Inject import com.scalableminds.util.accesscontext.DBAccessContext import com.scalableminds.util.enumeration.ExtendedEnumeration @@ -9,9 +10,11 @@ import com.typesafe.scalalogging.LazyLogging import models.annotation.AnnotationDAO import models.binary.{Dataset, DatasetDAO, DatasetLayerDAO} import models.organization.{Organization, OrganizationDAO} +import models.shortlinks.ShortLinkDAO import models.voxelytics.VoxelyticsDAO import net.liftweb.common.Full import oxalis.security.URLSharing +import java.net.URLDecoder import utils.{ObjectId, WkConf} import scala.concurrent.ExecutionContext @@ -31,31 +34,43 @@ class OpenGraphService @Inject()(voxelyticsDAO: VoxelyticsDAO, organizationDAO: OrganizationDAO, datasetLayerDAO: DatasetLayerDAO, annotationDAO: AnnotationDAO, + shortLinkDAO: ShortLinkDAO, conf: WkConf) extends LazyLogging { def getOpenGraphTags(uriPath: String, sharingToken: Option[String])(implicit ec: ExecutionContext, - ctx: DBAccessContext): Fox[OpenGraphTags] = { - val ctxWithToken = URLSharing.fallbackTokenAccessContext(sharingToken) - - val pageType = detectPageType(uriPath) - - val tagsFox = pageType match { - case OpenGraphPageType.dataset => datasetOpenGraphTags(uriPath, sharingToken)(ec, ctxWithToken) - case OpenGraphPageType.annotation => annotationOpenGraphTags(uriPath, sharingToken)(ec, ctxWithToken) - case OpenGraphPageType.workflow => workflowOpenGraphTags(uriPath) - case OpenGraphPageType.unknown => Fox.successful(defaultTags()) - } - - // In error case (probably no access permissions), fall back to default, so the html template does not break + ctx: DBAccessContext): Fox[OpenGraphTags] = for { + (uriPathResolved, sharingTokenResolved) <- resolveShortLinkIfNeeded(uriPath, sharingToken) + ctxWithToken = URLSharing.fallbackTokenAccessContext(sharingTokenResolved) + pageType = detectPageType(uriPathResolved) + tagsFox = pageType match { + case OpenGraphPageType.dataset => datasetOpenGraphTags(uriPathResolved, sharingTokenResolved)(ec, ctxWithToken) + case OpenGraphPageType.annotation => + annotationOpenGraphTags(uriPathResolved, sharingTokenResolved)(ec, ctxWithToken) + case OpenGraphPageType.workflow => + Fox.successful(defaultTags(OpenGraphPageType.workflow)) // No sharing token mechanism for workflows yet + case OpenGraphPageType.unknown => Fox.successful(defaultTags()) + } + // In error case (probably no access permissions), fall back to default, so the html template does not break tagsBox <- tagsFox.futureBox tags = tagsBox match { case Full(tags) => tags case _ => defaultTags(pageType) } } yield tags - } + + private def resolveShortLinkIfNeeded(uriPath: String, sharingToken: Option[String])( + implicit ec: ExecutionContext): Fox[(String, Option[String])] = + uriPath match { + case shortLinkRouteRegex(key) => + for { + shortLink <- shortLinkDAO.findOneByKey(key) + _ = logger.info(shortLink.longLink) + asUri: Uri = Uri(URLDecoder.decode(shortLink.longLink, "UTF-8")) + } yield (asUri.path.toString, asUri.query().get("token").orElse(asUri.query().get("sharingToken"))) + case _ => Fox.successful(uriPath, sharingToken) + } private def detectPageType(uriPath: String) = uriPath match { @@ -65,6 +80,7 @@ class OpenGraphService @Inject()(voxelyticsDAO: VoxelyticsDAO, case _ => OpenGraphPageType.unknown } + private val shortLinkRouteRegex = "^/links/(.*)".r private val datasetRoute1Regex = "^/datasets/([^/^#]+)/([^/^#]+)/view".r private val datasetRoute2Regex = "^/datasets/([^/^#]+)/([^/^#]+)".r private val workflowRouteRegex = "^/workflows/([^/^#]+)".r @@ -125,15 +141,6 @@ class OpenGraphService @Inject()(voxelyticsDAO: VoxelyticsDAO, s"${conf.Http.uri}/api/datasets/${organization.name}/${dataset.name}/layers/${layer.name}/thumbnail?w=1000&h=300$tokenParam" } - private def workflowOpenGraphTags(uriPath: String)(implicit ec: ExecutionContext): Fox[OpenGraphTags] = - uriPath match { - case workflowRouteRegex(workflowHash: String) => - for { // TODO access check - workflow <- voxelyticsDAO.findWorkflowByHash(workflowHash) - } yield OpenGraphTags(Some(f"${workflow.name} | WEBKNOSSOS"), Some("Voxelytics Workflow Report"), None) - case _ => Fox.failure("not a matching uri") - } - private def defaultTags(pageType: OpenGraphPageType.Value = OpenGraphPageType.unknown): OpenGraphTags = { val description = pageType match { case OpenGraphPageType.dataset => Some("View this dataset in WEBKNOSSOS") From c5c9662634b6f3a5af7f43510c0388c8082b82bb Mon Sep 17 00:00:00 2001 From: Florian M Date: Thu, 28 Sep 2023 14:26:51 +0200 Subject: [PATCH 17/18] cleanup, changelog --- .circleci/not-on-master.sh | 10 +++++----- CHANGELOG.unreleased.md | 2 ++ app/oxalis/opengraph/OpenGraphService.scala | 9 ++------- conf/application.conf | 2 +- 4 files changed, 10 insertions(+), 13 deletions(-) diff --git a/.circleci/not-on-master.sh b/.circleci/not-on-master.sh index 56eeb116ea1..581393ebead 100755 --- a/.circleci/not-on-master.sh +++ b/.circleci/not-on-master.sh @@ -1,8 +1,8 @@ #!/usr/bin/env bash set -Eeuo pipefail -#if [ "${CIRCLE_BRANCH}" == "master" ]; then -echo "Skipping this step on master..." -#else -# exec "$@" -#fi +if [ "${CIRCLE_BRANCH}" == "master" ]; then + echo "Skipping this step on master..." +else + exec "$@" +fi diff --git a/CHANGELOG.unreleased.md b/CHANGELOG.unreleased.md index 11f083756ab..b543a181f77 100644 --- a/CHANGELOG.unreleased.md +++ b/CHANGELOG.unreleased.md @@ -12,6 +12,8 @@ For upgrade instructions, please check the [migration guide](MIGRATIONS.released ### Added +- Added social media link previews for links to datasets and annotations (only if they are public or if the links contain sharing tokens). [#7331](https://github.com/scalableminds/webknossos/pull/7331) + ### Changed ### Fixed diff --git a/app/oxalis/opengraph/OpenGraphService.scala b/app/oxalis/opengraph/OpenGraphService.scala index 300aeb0e009..7479636d4e6 100644 --- a/app/oxalis/opengraph/OpenGraphService.scala +++ b/app/oxalis/opengraph/OpenGraphService.scala @@ -6,12 +6,10 @@ import com.scalableminds.util.accesscontext.DBAccessContext import com.scalableminds.util.enumeration.ExtendedEnumeration import com.scalableminds.util.tools.Fox import com.scalableminds.webknossos.datastore.models.datasource.{Category, DataLayerLike} -import com.typesafe.scalalogging.LazyLogging import models.annotation.AnnotationDAO import models.binary.{Dataset, DatasetDAO, DatasetLayerDAO} import models.organization.{Organization, OrganizationDAO} import models.shortlinks.ShortLinkDAO -import models.voxelytics.VoxelyticsDAO import net.liftweb.common.Full import oxalis.security.URLSharing import java.net.URLDecoder @@ -29,14 +27,12 @@ object OpenGraphPageType extends ExtendedEnumeration { val dataset, annotation, workflow, unknown = Value } -class OpenGraphService @Inject()(voxelyticsDAO: VoxelyticsDAO, - datasetDAO: DatasetDAO, +class OpenGraphService @Inject()(datasetDAO: DatasetDAO, organizationDAO: OrganizationDAO, datasetLayerDAO: DatasetLayerDAO, annotationDAO: AnnotationDAO, shortLinkDAO: ShortLinkDAO, - conf: WkConf) - extends LazyLogging { + conf: WkConf) { def getOpenGraphTags(uriPath: String, sharingToken: Option[String])(implicit ec: ExecutionContext, ctx: DBAccessContext): Fox[OpenGraphTags] = @@ -66,7 +62,6 @@ class OpenGraphService @Inject()(voxelyticsDAO: VoxelyticsDAO, case shortLinkRouteRegex(key) => for { shortLink <- shortLinkDAO.findOneByKey(key) - _ = logger.info(shortLink.longLink) asUri: Uri = Uri(URLDecoder.decode(shortLink.longLink, "UTF-8")) } yield (asUri.path.toString, asUri.query().get("token").orElse(asUri.query().get("sharingToken"))) case _ => Fox.successful(uriPath, sharingToken) diff --git a/conf/application.conf b/conf/application.conf index 23b6d426851..79c7960e11a 100644 --- a/conf/application.conf +++ b/conf/application.conf @@ -135,7 +135,7 @@ features { allowDeleteDatasets = true # to enable jobs for local development, use "yarn enable-jobs" to also activate it in the database jobsEnabled = false - voxelyticsEnabled = true + voxelyticsEnabled = false # For new users, the dashboard will show a banner which encourages the user to check out the following dataset. # If isWkorgInstance == true, `/createExplorative/hybrid/true` is appended to the URL so that a new tracing is opened. # If isWkorgInstance == false, `/view` is appended to the URL so that it's opened in view mode (since the user might not From b67cc0eb6fcd07cc9297abf2384031942938e8c2 Mon Sep 17 00:00:00 2001 From: Florian M Date: Mon, 2 Oct 2023 11:06:06 +0200 Subject: [PATCH 18/18] implement feedback --- app/oxalis/opengraph/OpenGraphService.scala | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/app/oxalis/opengraph/OpenGraphService.scala b/app/oxalis/opengraph/OpenGraphService.scala index 7479636d4e6..1f8767fb57c 100644 --- a/app/oxalis/opengraph/OpenGraphService.scala +++ b/app/oxalis/opengraph/OpenGraphService.scala @@ -34,6 +34,16 @@ class OpenGraphService @Inject()(datasetDAO: DatasetDAO, shortLinkDAO: ShortLinkDAO, conf: WkConf) { + private val thumbnailWidth = 1000 + private val thumbnailHeight = 300 + + // This should match the frontend-side routes, not api routes, since those are the links people send around + private val shortLinkRouteRegex = "^/links/(.*)".r + private val datasetRoute1Regex = "^/datasets/([^/^#]+)/([^/^#]+)/view".r + private val datasetRoute2Regex = "^/datasets/([^/^#]+)/([^/^#]+)".r + private val workflowRouteRegex = "^/workflows/([^/^#]+)".r + private val annotationRouteRegex = "^/annotations/([^/^#]+)".r + def getOpenGraphTags(uriPath: String, sharingToken: Option[String])(implicit ec: ExecutionContext, ctx: DBAccessContext): Fox[OpenGraphTags] = for { @@ -75,12 +85,6 @@ class OpenGraphService @Inject()(datasetDAO: DatasetDAO, case _ => OpenGraphPageType.unknown } - private val shortLinkRouteRegex = "^/links/(.*)".r - private val datasetRoute1Regex = "^/datasets/([^/^#]+)/([^/^#]+)/view".r - private val datasetRoute2Regex = "^/datasets/([^/^#]+)/([^/^#]+)".r - private val workflowRouteRegex = "^/workflows/([^/^#]+)".r - private val annotationRouteRegex = "^/annotations/([^/^#]+)".r - private def datasetOpenGraphTags(uriPath: String, token: Option[String])(implicit ec: ExecutionContext, ctx: DBAccessContext): Fox[OpenGraphTags] = uriPath match { @@ -102,7 +106,7 @@ class OpenGraphService @Inject()(datasetDAO: DatasetDAO, } yield OpenGraphTags( Some(s"${dataset.displayName.getOrElse(datasetName)} | WEBKNOSSOS"), - Some(s"View this dataset in WEBKNOSSOS"), + Some("View this dataset in WEBKNOSSOS"), thumbnailUri(dataset, layerOpt, organization, token) ) @@ -133,7 +137,7 @@ class OpenGraphService @Inject()(datasetDAO: DatasetDAO, token: Option[String]): Option[String] = layerOpt.map { layer => val tokenParam = token.map(t => s"&sharingToken=$t").getOrElse("") - s"${conf.Http.uri}/api/datasets/${organization.name}/${dataset.name}/layers/${layer.name}/thumbnail?w=1000&h=300$tokenParam" + s"${conf.Http.uri}/api/datasets/${organization.name}/${dataset.name}/layers/${layer.name}/thumbnail?w=$thumbnailWidth&h=$thumbnailHeight$tokenParam" } private def defaultTags(pageType: OpenGraphPageType.Value = OpenGraphPageType.unknown): OpenGraphTags = {