diff --git a/api/src/org/labkey/api/attachments/SvgSource.java b/api/src/org/labkey/api/attachments/SvgSource.java index 0e5e542aebe..bf7c068a45d 100644 --- a/api/src/org/labkey/api/attachments/SvgSource.java +++ b/api/src/org/labkey/api/attachments/SvgSource.java @@ -3,6 +3,7 @@ import org.apache.batik.anim.dom.SVGDOMImplementation; import org.apache.batik.transcoder.TranscoderException; import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.Strings; import org.labkey.api.util.PageFlowUtil; import org.labkey.api.view.NotFoundException; @@ -19,6 +20,8 @@ public class SvgSource { private final String _filteredSvg; + private Float _height = null; + public SvgSource(String svg) { if (StringUtils.isBlank(svg)) @@ -29,11 +32,19 @@ public SvgSource(String svg) if (!svg.contains("xmlns=\"" + SVGDOMImplementation.SVG_NAMESPACE_URI + "\"") && !svg.contains("xmlns='" + SVGDOMImplementation.SVG_NAMESPACE_URI + "'")) svg = svg.replace(" findAndCheckContainersMatchingTabs(Container c, Fo return containersMatchingTabs; } - private static final Set containersWithBadFolderTypes = new ConcurrentHashSet<>(); + private static final Set containersWithBadFolderTypes = new ConcurrentHashSet<>(); + private static final Set badFolderTypes = new ConcurrentHashSet<>(); @NotNull public static FolderType getFolderType(Container c) @@ -602,10 +603,16 @@ public static FolderType getFolderType(Container c) // If we're upgrading then folder types won't be defined yet... don't warn in that case. if (!ModuleLoader.getInstance().isUpgradeInProgress() && !ModuleLoader.getInstance().isUpgradeRequired() && - !containersWithBadFolderTypes.contains(c)) + containersWithBadFolderTypes.add(c.getRowId())) { - LOG.warn("No such folder type " + name + " for folder " + c.toString()); - containersWithBadFolderTypes.add(c); + if (badFolderTypes.add(name)) + { + LOG.warn("No such folder type {} for container {}. Additional containers of this folder type will be logged at DEBUG level.", name, c.getPath()); + } + else + { + LOG.debug("No such folder type {} for container {}.", name, c.getPath()); + } } folderType = FolderType.NONE; diff --git a/api/src/org/labkey/api/migration/DefaultMigrationSchemaHandler.java b/api/src/org/labkey/api/migration/DefaultMigrationSchemaHandler.java index 019d5f6e30d..08f4fcd3c2c 100644 --- a/api/src/org/labkey/api/migration/DefaultMigrationSchemaHandler.java +++ b/api/src/org/labkey/api/migration/DefaultMigrationSchemaHandler.java @@ -290,7 +290,7 @@ public static void logUnseenAttachmentTypes() Set unseen = new HashSet<>(AttachmentService.get().getAttachmentTypes()); unseen.removeAll(SEEN); - if (SEEN.isEmpty()) + if (unseen.isEmpty()) DatabaseMigrationService.LOG.info("All AttachmentTypes have been seen"); else DatabaseMigrationService.LOG.info("These AttachmentTypes have not been seen: {}", unseen.stream().map(type -> type.getClass().getSimpleName()).collect(Collectors.joining(", "))); diff --git a/api/src/org/labkey/api/pipeline/PipeRoot.java b/api/src/org/labkey/api/pipeline/PipeRoot.java index 80138b4b122..da1d5f8efba 100644 --- a/api/src/org/labkey/api/pipeline/PipeRoot.java +++ b/api/src/org/labkey/api/pipeline/PipeRoot.java @@ -70,22 +70,22 @@ public interface PipeRoot extends SecurableResource Path resolveToNioPathFromUrl(String url); /** - * @return the file that's at the given relativePath from the pipeline root. Will be null if the relative path - * attempts to reference something that's not under the root (such as "../../etc/passwd". When the root + * @return the file that's at the given relativePath from the pipeline root. When the root * is configured with an alternative file path, we'll check to see if the file exists there. If not, we'll return * a path relative to the root's primary path. + * @throws org.labkey.api.util.FileUtil.InvalidPathReferenceException if the relative path + * attempts to reference something not under the root (such as "../../etc/passwd") */ @Deprecated // prefer resolvePathToFileLike() - @Nullable File resolvePath(String relativePath); /** - * @return the file that's at the given relativePath from the pipeline root. Will be null if the relative path - * attempts to reference something that's not under the root (such as "../../etc/passwd". When the root + * @return the file that's at the given relativePath from the pipeline root. When the root * is configured with an alternative file path, we'll check to see if the file exists there. If not, we'll return * a path relative to the root's primary path. + * @throws org.labkey.api.util.FileUtil.InvalidPathReferenceException if the relative path + * attempts to reference something not under the root (such as "../../etc/passwd") */ - @Nullable FileLike resolvePathToFileLike(String relativePath); @NotNull diff --git a/api/src/org/labkey/api/products/ProductRegistry.java b/api/src/org/labkey/api/products/ProductRegistry.java index a8743cb55c8..49599278727 100644 --- a/api/src/org/labkey/api/products/ProductRegistry.java +++ b/api/src/org/labkey/api/products/ProductRegistry.java @@ -15,6 +15,7 @@ */ package org.labkey.api.products; +import org.apache.commons.lang3.StringUtils; import org.apache.logging.log4j.Logger; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -219,7 +220,9 @@ public String getPrimaryProductIdForContainer(@NotNull Container container) public ProductMenuProvider getPrimaryProductMenuForContainer(@NotNull Container container) { List productIds = getProductIdsForContainer(container); + _logger.debug("Product Ids in container '{}' of type '{}' are {}", container.getPath(), container.getFolderType().getName(), StringUtils.join(productIds)); List providers = getRegisteredProducts().stream().filter(provider -> productIds.contains(provider.getProductId())).toList(); + _logger.debug("Menu providers: {}", providers.stream().map(ProductMenuProvider::getProductId).collect(Collectors.toList())); if (providers.isEmpty()) return null; @@ -229,16 +232,27 @@ public ProductMenuProvider getPrimaryProductMenuForContainer(@NotNull Container // see if there's a provider that matches the folder type (need to check this first so if LabKey LIMS or LKB is the configured product you can still show LKSM folders) Optional optionalProvider = providers.stream().filter(provider -> provider.getFolderTypeName() != null && provider.getFolderTypeName().equals(container.getFolderType().getName())).findFirst(); if (optionalProvider.isPresent()) + { + _logger.debug("Found product menu provider for folder type '{}'", container.getFolderType().getName()); return optionalProvider.get(); + } List orderedProducts = getProducts(true, false).stream().filter(Product::isEnabled).map(Product::getProductGroupId).toList(); - ProductMenuProvider highestProvider = providers.stream().min(Comparator.comparing(provider -> orderedProducts.indexOf(provider.getProductId()))).orElse(null); + _logger.debug("Products are {}", _products.keySet()); + _logger.debug("Ordered products are {}", orderedProducts); + ProductMenuProvider highestProvider = providers.stream() + .min(Comparator.comparing(provider -> orderedProducts.indexOf(provider.getProductId()))).orElse(null); + _logger.debug("Highest product menu provider: {}", highestProvider.getProductId()); // then see if there's a provider that matches the configured product Product product = new ProductConfiguration().getCurrentProduct(); if (product == null) + { + _logger.debug("Using highest product menu provider."); return highestProvider; + } optionalProvider = providers.stream().filter(provider -> provider.getProductId().equals(product.getProductGroupId())).findFirst(); + _logger.debug("Product from product group: {}", optionalProvider.isPresent() ? optionalProvider.get() : "null"); // if neither of those is true (when can this happen?), use the highest provider return optionalProvider.orElseGet(() -> highestProvider); } diff --git a/api/src/org/labkey/api/settings/ProductConfiguration.java b/api/src/org/labkey/api/settings/ProductConfiguration.java index 77ffbf22902..41b957a9b2b 100644 --- a/api/src/org/labkey/api/settings/ProductConfiguration.java +++ b/api/src/org/labkey/api/settings/ProductConfiguration.java @@ -1,11 +1,13 @@ package org.labkey.api.settings; +import org.apache.logging.log4j.Logger; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.labkey.api.data.ContainerManager; import org.labkey.api.module.ModuleLoader; import org.labkey.api.products.Product; import org.labkey.api.products.ProductRegistry; +import org.labkey.api.util.logging.LogHelper; import java.util.Collection; @@ -13,6 +15,7 @@ public class ProductConfiguration extends AbstractWriteableSettingsGroup impleme { public static final String SCOPE_PRODUCT_CONFIGURATION = "ProductConfiguration"; public static final String PROPERTY_NAME = "productKey"; + private static final Logger _logger = LogHelper.getLogger(ProductConfiguration.class, "Product Configuration properties"); @Override protected String getGroupName() @@ -54,6 +57,7 @@ public String getCurrentProductKey() public Product getCurrentProduct() { String productKey = getCurrentProductKey(); + _logger.debug("Current product key: {}", productKey); if (productKey == null) return null; return ProductRegistry.getProduct(productKey); diff --git a/api/src/org/labkey/api/util/ExceptionUtil.java b/api/src/org/labkey/api/util/ExceptionUtil.java index 6c623fe76c6..3649564a0ba 100644 --- a/api/src/org/labkey/api/util/ExceptionUtil.java +++ b/api/src/org/labkey/api/util/ExceptionUtil.java @@ -865,6 +865,21 @@ else if (ex instanceof BadRequestException) message = ex.getMessage(); unhandledException = null; } + else if (ex instanceof FileUtil.InvalidPathReferenceException ipre) + { + responseStatus = HttpServletResponse.SC_NOT_FOUND; + errorType = ErrorRenderer.ErrorType.notFound; + if (ex.getMessage() != null) + { + message = ex.getMessage(); + responseStatusMessage = message; + } + else + message = responseStatus + ": Page not Found"; + unhandledException = null; + log.info("InvalidPathReferenceException: {}", ipre.getInput()); + log.debug("InvalidPathReferenceException", ex); + } else if (ex instanceof NotFoundException) { responseStatus = HttpServletResponse.SC_NOT_FOUND; diff --git a/api/src/org/labkey/api/util/FileUtil.java b/api/src/org/labkey/api/util/FileUtil.java index fd358042a19..5f1b2ea7ca1 100644 --- a/api/src/org/labkey/api/util/FileUtil.java +++ b/api/src/org/labkey/api/util/FileUtil.java @@ -852,11 +852,20 @@ public static File appendPath(File dir, org.labkey.api.util.Path originalPath) { org.labkey.api.util.Path path = originalPath.normalize(); if (path == null || (!path.isEmpty() && "..".equals(path.get(0)))) - throw new InvalidPathException(originalPath.toString(), "Path to parent not allowed"); + throw new InvalidPathReferenceException(originalPath.toString(), "Path to parent not allowed"); @SuppressWarnings("SSBasedInspection") var ret = new File(dir, path.toString()); - if (!ret.toPath().normalize().startsWith(dir.toPath().normalize())) - throw new InvalidPathException(originalPath.toString(), "Path to parent not allowed"); + boolean valid; + try + { + valid = ret.toPath().normalize().startsWith(dir.toPath().normalize()); + } + catch (InvalidPathException e) + { + throw new InvalidPathReferenceException(originalPath.toString(), e.getMessage()); + } + if (!valid) + throw new InvalidPathReferenceException(originalPath.toString(), "Path to parent not allowed"); return ret; } @@ -866,7 +875,7 @@ public static FileLike appendPath(FileLike dir, org.labkey.api.util.Path path) { path = path.normalize(); if (!path.isEmpty() && "..".equals(path.get(0))) - throw new InvalidPathException(path.toString(), "Path to parent not allowed"); + throw new InvalidPathReferenceException(path.toString(), "Path to parent not allowed"); return dir.resolveFile(path); } @@ -897,7 +906,7 @@ public static File appendName(File dir, String name) var ret = new File(dir, name); if (!ret.toPath().normalize().startsWith(dir.toPath().normalize())) - throw new InvalidPathException(name, "Path to parent not allowed"); + throw new InvalidPathReferenceException(name, "Path to parent not allowed"); return ret; } @@ -908,7 +917,7 @@ public static Path appendName(Path dir, String name) var ret = dir.resolve(name); if (!ret.normalize().startsWith(dir.normalize())) - throw new InvalidPathException(name, "Path to parent not allowed"); + throw new InvalidPathReferenceException(name, "Path to parent not allowed"); return ret; } @@ -919,11 +928,24 @@ public static void legalPathPartThrow(String name) { int invalidCharacterIndex = StringUtils.indexOfAny(name, '/', File.separatorChar); if (invalidCharacterIndex >= 0) - throw new InvalidPathException(name, "Invalid file or directory name", invalidCharacterIndex); + throw new InvalidPathReferenceException(name, "Invalid file or directory name", invalidCharacterIndex); if (".".equals(name) || "..".equals(name)) - throw new InvalidPathException(name, "Invalid file or directory name"); + throw new InvalidPathReferenceException(name, "Invalid file or directory name"); } + /** Our own subclass for bogus paths that we can special-case in ExceptionUtil in terms of HTTP response codes and logging */ + public static class InvalidPathReferenceException extends InvalidPathException + { + public InvalidPathReferenceException(String path, String reason) + { + super(path, reason); + } + + public InvalidPathReferenceException(String name, String reason, int index) + { + super(name, reason, index); + } + } public static String decodeSpaces(@NotNull String str) { diff --git a/assay/package-lock.json b/assay/package-lock.json index 788d0e0e3f2..af3b2e60a60 100644 --- a/assay/package-lock.json +++ b/assay/package-lock.json @@ -8,7 +8,7 @@ "name": "assay", "version": "0.0.0", "dependencies": { - "@labkey/components": "6.70.2" + "@labkey/components": "6.70.8" }, "devDependencies": { "@labkey/build": "8.7.0", @@ -2525,9 +2525,9 @@ } }, "node_modules/@labkey/components": { - "version": "6.70.2", - "resolved": "https://labkey.jfrog.io/artifactory/api/npm/libs-client/@labkey/components/-/@labkey/components-6.70.2.tgz", - "integrity": "sha512-AlZRJ1TzO8ux2tup/LbHr/Sy6xI51PH0xJo0WK7W5AdTjj1kch+RgfLKJOo7pwfk4bKk5YxpEC4nEBgWOTc7IA==", + "version": "6.70.8", + "resolved": "https://labkey.jfrog.io/artifactory/api/npm/libs-client/@labkey/components/-/@labkey/components-6.70.8.tgz", + "integrity": "sha512-ZRsGq2VV7+KSFkuuHIw0vsdXxZX+QXJg4IwAgGyJXODM2TwJ94bWSeSCpLkwJv7BOH0qXrMxbQkc1nNo5xaL1Q==", "license": "SEE LICENSE IN LICENSE.txt", "dependencies": { "@hello-pangea/dnd": "18.0.1", diff --git a/assay/package.json b/assay/package.json index dd41b18a68a..d08c50a1895 100644 --- a/assay/package.json +++ b/assay/package.json @@ -12,7 +12,7 @@ "clean": "rimraf resources/web/assay/gen && rimraf resources/views/gen && rimraf resources/web/gen" }, "dependencies": { - "@labkey/components": "6.70.2" + "@labkey/components": "6.70.8" }, "devDependencies": { "@labkey/build": "8.7.0", diff --git a/assay/src/org/labkey/assay/AssayResultMigrationSchemaHandler.java b/assay/src/org/labkey/assay/AssayResultMigrationSchemaHandler.java index 16e0a57ad82..da9a0a794c2 100644 --- a/assay/src/org/labkey/assay/AssayResultMigrationSchemaHandler.java +++ b/assay/src/org/labkey/assay/AssayResultMigrationSchemaHandler.java @@ -12,18 +12,15 @@ import org.labkey.api.data.SimpleFilter.SQLClause; import org.labkey.api.data.SqlSelector; import org.labkey.api.data.TableInfo; -import org.labkey.api.data.TableSelector; import org.labkey.api.migration.AssaySkipContainers; import org.labkey.api.migration.DatabaseMigrationService.DataFilter; import org.labkey.api.migration.DefaultMigrationSchemaHandler; import org.labkey.api.migration.ExperimentDeleteService; import org.labkey.api.util.GUID; -import org.labkey.api.util.StringUtilsLabKey; import org.labkey.api.util.logging.LogHelper; import org.labkey.assay.plate.PlateReplicateStatsDomainKind; import java.util.Collection; -import java.util.Collections; import java.util.Set; class AssayResultMigrationSchemaHandler extends DefaultMigrationSchemaHandler @@ -38,7 +35,7 @@ public AssayResultMigrationSchemaHandler() private boolean skipTable(TableInfo sourceTable) { // For now, we're ignoring this table since it's empty in our first migration client's database - return Strings.CI.endsWith(sourceTable.getName(), PlateReplicateStatsDomainKind.ASSAY_PLATE_REPLICATE); + return Strings.CI.endsWith(sourceTable.getName(), PlateReplicateStatsDomainKind.NAME); } @Override @@ -63,8 +60,8 @@ public FilterClause getContainerClause(TableInfo sourceTable, Set containe @Override public void addDomainDataFilterClause(OrClause orClause, DataFilter filter, TableInfo sourceTable, Set selectColumnNames) { - // No filtering on assay results for now; just add the passed in containers. Note that these will be filtered - // if AssaySkipContainers is configured. + // No row-by-row filtering on assay results for now; just add the passed in containers. Note that these will be + // filtered by container if AssaySkipContainers is configured. orClause.addClause(getContainerClause(sourceTable, filter.containers())); } @@ -92,9 +89,6 @@ public void afterTable(TableInfo sourceTable, TableInfo targetTable, SimpleFilte // Delete exp.Data, exp.Object, etc. rows associated with the rows that weren't copied ExperimentDeleteService.get().deleteDataRows(notCopiedObjectIds); } - - // TODO: Temp! - LOG.info(" " + StringUtilsLabKey.pluralize(new TableSelector(sourceTable, Collections.singleton("DataId")).stream(Integer.class).distinct().count(), "distinct DataId")); } } } diff --git a/core/package-lock.json b/core/package-lock.json index 5309cea26cc..f2b1ebb5872 100644 --- a/core/package-lock.json +++ b/core/package-lock.json @@ -8,7 +8,7 @@ "name": "labkey-core", "version": "0.0.0", "dependencies": { - "@labkey/components": "6.70.2", + "@labkey/components": "6.70.8", "@labkey/themes": "1.5.0" }, "devDependencies": { @@ -3547,9 +3547,9 @@ } }, "node_modules/@labkey/components": { - "version": "6.70.2", - "resolved": "https://labkey.jfrog.io/artifactory/api/npm/libs-client/@labkey/components/-/@labkey/components-6.70.2.tgz", - "integrity": "sha512-AlZRJ1TzO8ux2tup/LbHr/Sy6xI51PH0xJo0WK7W5AdTjj1kch+RgfLKJOo7pwfk4bKk5YxpEC4nEBgWOTc7IA==", + "version": "6.70.8", + "resolved": "https://labkey.jfrog.io/artifactory/api/npm/libs-client/@labkey/components/-/@labkey/components-6.70.8.tgz", + "integrity": "sha512-ZRsGq2VV7+KSFkuuHIw0vsdXxZX+QXJg4IwAgGyJXODM2TwJ94bWSeSCpLkwJv7BOH0qXrMxbQkc1nNo5xaL1Q==", "license": "SEE LICENSE IN LICENSE.txt", "dependencies": { "@hello-pangea/dnd": "18.0.1", diff --git a/core/package.json b/core/package.json index 873a98f67ae..0138850d375 100644 --- a/core/package.json +++ b/core/package.json @@ -53,7 +53,7 @@ } }, "dependencies": { - "@labkey/components": "6.70.2", + "@labkey/components": "6.70.8", "@labkey/themes": "1.5.0" }, "devDependencies": { diff --git a/experiment/package-lock.json b/experiment/package-lock.json index cb6e9d2d7d6..33dbd4e69bf 100644 --- a/experiment/package-lock.json +++ b/experiment/package-lock.json @@ -8,7 +8,7 @@ "name": "experiment", "version": "0.0.0", "dependencies": { - "@labkey/components": "6.70.2" + "@labkey/components": "6.70.8" }, "devDependencies": { "@labkey/build": "8.7.0", @@ -3314,9 +3314,9 @@ } }, "node_modules/@labkey/components": { - "version": "6.70.2", - "resolved": "https://labkey.jfrog.io/artifactory/api/npm/libs-client/@labkey/components/-/@labkey/components-6.70.2.tgz", - "integrity": "sha512-AlZRJ1TzO8ux2tup/LbHr/Sy6xI51PH0xJo0WK7W5AdTjj1kch+RgfLKJOo7pwfk4bKk5YxpEC4nEBgWOTc7IA==", + "version": "6.70.8", + "resolved": "https://labkey.jfrog.io/artifactory/api/npm/libs-client/@labkey/components/-/@labkey/components-6.70.8.tgz", + "integrity": "sha512-ZRsGq2VV7+KSFkuuHIw0vsdXxZX+QXJg4IwAgGyJXODM2TwJ94bWSeSCpLkwJv7BOH0qXrMxbQkc1nNo5xaL1Q==", "license": "SEE LICENSE IN LICENSE.txt", "dependencies": { "@hello-pangea/dnd": "18.0.1", diff --git a/experiment/package.json b/experiment/package.json index 0b7336603ca..1e692e38a6d 100644 --- a/experiment/package.json +++ b/experiment/package.json @@ -13,7 +13,7 @@ "test-integration": "cross-env NODE_ENV=test jest --ci --runInBand -c test/js/jest.config.integration.js" }, "dependencies": { - "@labkey/components": "6.70.2" + "@labkey/components": "6.70.8" }, "devDependencies": { "@labkey/build": "8.7.0", diff --git a/experiment/src/org/labkey/experiment/DataClassMigrationSchemaHandler.java b/experiment/src/org/labkey/experiment/DataClassMigrationSchemaHandler.java index 45232046c93..2e93cba7b5d 100644 --- a/experiment/src/org/labkey/experiment/DataClassMigrationSchemaHandler.java +++ b/experiment/src/org/labkey/experiment/DataClassMigrationSchemaHandler.java @@ -90,14 +90,18 @@ public void addDomainDataFilterClause(OrClause orClause, DataFilter filter, Tabl @Override public void afterTable(TableInfo sourceTable, TableInfo targetTable, SimpleFilter notCopiedFilter) { - // Select all ObjectIds associated with the not-copied rows from the source database. Our notCopiedFilter - // works on the data class provisioned table, so we need to use a sub-select (as opposed to a join) to avoid - // ambiguous column references. - SQLFragment objectIdSql = new SQLFragment("SELECT ObjectId FROM exp.Object WHERE ObjectURI IN (SELECT LSID FROM ") - .appendIdentifier(sourceTable.getSelectName()) - .append(" ") - .append(notCopiedFilter.getSQLFragment(sourceTable.getSqlDialect())) - .append(")"); + // Select LSIDs from the just-copied rows in the target table + Collection copiedLsids = new TableSelector(targetTable, Collections.singleton("LSID")).getCollection(String.class); + + // Select all ObjectIds associated with the not-copied rows from the source database + SQLFragment objectIdSql = new SQLFragment("SELECT ObjectId FROM exp.Data d INNER JOIN ") + .appendIdentifier(sourceTable.getSelectName()) + .append(" dc ON d.LSID = dc.LSID"); + + // Don't create an empty IN clause; need to work around issue where "NOT xxx IN (NULL)" evaluates to NULL. + if (!copiedLsids.isEmpty()) + objectIdSql.append(" AND NOT dc.LSID").appendInClause(copiedLsids, sourceTable.getSqlDialect()); + Collection notCopiedObjectIds = new SqlSelector(sourceTable.getSchema(), objectIdSql).getCollection(Long.class); if (notCopiedObjectIds.isEmpty()) diff --git a/pipeline/package-lock.json b/pipeline/package-lock.json index 79f13ee3f5f..7ed4457fb6a 100644 --- a/pipeline/package-lock.json +++ b/pipeline/package-lock.json @@ -8,7 +8,7 @@ "name": "pipeline", "version": "0.0.0", "dependencies": { - "@labkey/components": "6.70.2" + "@labkey/components": "6.70.8" }, "devDependencies": { "@labkey/build": "8.7.0", @@ -2759,9 +2759,9 @@ } }, "node_modules/@labkey/components": { - "version": "6.70.2", - "resolved": "https://labkey.jfrog.io/artifactory/api/npm/libs-client/@labkey/components/-/@labkey/components-6.70.2.tgz", - "integrity": "sha512-AlZRJ1TzO8ux2tup/LbHr/Sy6xI51PH0xJo0WK7W5AdTjj1kch+RgfLKJOo7pwfk4bKk5YxpEC4nEBgWOTc7IA==", + "version": "6.70.8", + "resolved": "https://labkey.jfrog.io/artifactory/api/npm/libs-client/@labkey/components/-/@labkey/components-6.70.8.tgz", + "integrity": "sha512-ZRsGq2VV7+KSFkuuHIw0vsdXxZX+QXJg4IwAgGyJXODM2TwJ94bWSeSCpLkwJv7BOH0qXrMxbQkc1nNo5xaL1Q==", "license": "SEE LICENSE IN LICENSE.txt", "dependencies": { "@hello-pangea/dnd": "18.0.1", diff --git a/pipeline/package.json b/pipeline/package.json index 61efed4c1c6..55739afbd28 100644 --- a/pipeline/package.json +++ b/pipeline/package.json @@ -14,7 +14,7 @@ "build-prod": "npm run clean && cross-env NODE_ENV=production PROD_SOURCE_MAP=source-map webpack --config node_modules/@labkey/build/webpack/prod.config.js --color --progress --profile" }, "dependencies": { - "@labkey/components": "6.70.2" + "@labkey/components": "6.70.8" }, "devDependencies": { "@labkey/build": "8.7.0", diff --git a/pipeline/src/org/labkey/pipeline/api/PipeRootImpl.java b/pipeline/src/org/labkey/pipeline/api/PipeRootImpl.java index df8f896ca74..63817b23e27 100644 --- a/pipeline/src/org/labkey/pipeline/api/PipeRootImpl.java +++ b/pipeline/src/org/labkey/pipeline/api/PipeRootImpl.java @@ -353,7 +353,6 @@ private Path findRootPath(Path file) @Override - @Nullable public File resolvePath(String pathStr) { if (null == pathStr) @@ -364,7 +363,7 @@ public File resolvePath(String pathStr) @Nullable - public File resolvePath(org.labkey.api.util.Path path) + private File resolvePath(org.labkey.api.util.Path path) { var pair = _resolveRoot(path); if (null == pair) @@ -374,8 +373,11 @@ public File resolvePath(org.labkey.api.util.Path path) @Override - public @Nullable FileLike resolvePathToFileLike(String relativePath) + public FileLike resolvePathToFileLike(String relativePath) { + if (null == relativePath) + throw new FileUtil.InvalidPathReferenceException(null, "Must specify a file path"); + var parsedPath = org.labkey.api.util.Path.parse(relativePath); if (ROOT_BASE.cloud.equals(_defaultRoot)) diff --git a/visualization/src/org/labkey/visualization/VisualizationController.java b/visualization/src/org/labkey/visualization/VisualizationController.java index bab9adabf6f..c55ec658702 100644 --- a/visualization/src/org/labkey/visualization/VisualizationController.java +++ b/visualization/src/org/labkey/visualization/VisualizationController.java @@ -97,7 +97,6 @@ import org.labkey.api.view.JspView; import org.labkey.api.view.NavTree; import org.labkey.api.view.NotFoundException; -import org.labkey.api.view.VBox; import org.labkey.api.view.ViewContext; import org.labkey.api.view.WebPartView; import org.labkey.api.visualization.GenericChartReport; @@ -140,7 +139,7 @@ public static class VisualizationUrlsImpl implements VisualizationUrls private static final String VISUALIZATION_FILTER_URL = "filterUrl"; private static final String VISUALIZATION_EDIT_PARAM = "edit"; private static final String VISUALIZATION_ID_PARAM = "reportId"; - private static final String VISUALIZATION_RENDERTYPE_PARAM = "renderType"; + private static final String VISUALIZATION_RENDER_TYPE_PARAM = "renderType"; private void addQueryParams(ActionURL url, Container container, User user, QuerySettings settings) { @@ -184,7 +183,7 @@ public ActionURL getTimeChartDesignerURL(Container container) { ActionURL url = getBaseGenericChartURL(container, true); url.addParameter(QueryParam.schemaName, "study"); - url.addParameter(VISUALIZATION_RENDERTYPE_PARAM, "time_chart"); + url.addParameter(VISUALIZATION_RENDER_TYPE_PARAM, "time_chart"); return url; } @@ -662,7 +661,6 @@ private ApiQueryResponse getApiResponse(ViewContext context, UserSchema schema, } } - @RequiresSiteAdmin public static class cdsTestGetDataAction extends SimpleViewAction { @@ -740,8 +738,9 @@ public void validate(Object o, Errors errors) * Content-type of request must be text/xml, not any kind of multipart * Returns a PNG image. */ + @SuppressWarnings("unused") @RequiresPermission(ReadPermission.class) - public class ExportImageAction extends ExportSVGAction + public static class ExportImageAction extends ExportSVGAction { @Override public ModelAndView handleRequest() throws Exception @@ -753,7 +752,14 @@ public ModelAndView handleRequest() throws Exception DocumentConversionService svc = DocumentConversionService.get(); if (null != svc) - svc.svgToPng(getSVGSource(), response.getOutputStream()); + { + // Ensure high resolution image even if browser submits a small SVG. See Issue 53390. + SvgSource svgSource = getSVGSource(); + Float height = svgSource.getHeight(); + if (height != null) + height = Math.max(height * 2, 2000); + svc.svgToPng(svgSource, response.getOutputStream(), height); + } return null; } @@ -764,8 +770,9 @@ public ModelAndView handleRequest() throws Exception * Content-type of request must be text/xml, not any kind of multipart * Returns a PDF document containing the visualization as a scalable vector graphic */ + @SuppressWarnings("unused") @RequiresPermission(ReadPermission.class) - public class ExportPDFAction extends ExportSVGAction + public static class ExportPDFAction extends ExportSVGAction { @Override public ModelAndView handleRequest() throws Exception @@ -1133,74 +1140,62 @@ public ApiResponse execute(SaveVisualizationForm form, BindException errors) thr } } - @RequiresPermission(ReadPermission.class) - public class TimeChartWizardAction extends SimpleViewAction + private abstract class AbstractChartWizardAction extends SimpleViewAction { - String _navTitle = "Chart Wizard"; + private String _navTitle = "Chart Wizard"; @Override public ModelAndView getView(ChartWizardReportForm form, BindException errors) throws Exception { form.setAllowToggleMode(true); - form.setRenderType("time_chart"); // issue 27439: allow chart wizard report lookup by name if reportId not provided Report report = getReport(form); if (form.getReportId() == null && report != null && report.getDescriptor() != null) form.setReportId(report.getDescriptor().getReportId()); - JspView timeChartWizard = new JspView<>("/org/labkey/visualization/views/chartWizard.jsp", form); - timeChartWizard.setTitle(_navTitle); - timeChartWizard.setFrame(WebPartView.FrameType.NONE); - VBox boxView = new VBox(timeChartWizard); + JspView chartWizard = new JspView<>("/org/labkey/visualization/views/chartWizard.jsp", form); + chartWizard.setTitle(_navTitle); + chartWizard.setFrame(WebPartView.FrameType.NONE); if (report != null) - { _navTitle = report.getDescriptor().getReportName(); - } - return boxView; + return chartWizard; } @Override public void addNavTrail(NavTree root) { - setHelpTopic("timeChart"); root.addChild(_navTitle); } } @RequiresPermission(ReadPermission.class) - @Action(ActionType.SelectData.class) // TODO rename to just ChartWizardAction - public class GenericChartWizardAction extends SimpleViewAction + @Action(ActionType.SelectData.class) + public class TimeChartWizardAction extends AbstractChartWizardAction { - String _navTitle = "Chart Wizard"; - @Override public ModelAndView getView(ChartWizardReportForm form, BindException errors) throws Exception { - form.setAllowToggleMode(true); - - // issue 27439: allow chart wizard report lookup by name if reportId not provided - Report report = getReport(form); - if (form.getReportId() == null && report != null && report.getDescriptor() != null) - form.setReportId(report.getDescriptor().getReportId()); - - JspView view = new JspView<>("/org/labkey/visualization/views/chartWizard.jsp", form); - view.setTitle(_navTitle); - view.setFrame(WebPartView.FrameType.NONE); - - if (report != null) - _navTitle = report.getDescriptor().getReportName(); + form.setRenderType("time_chart"); + setHelpTopic("timeChart"); - return view; + return super.getView(form, errors); } + } + @RequiresPermission(ReadPermission.class) + @Action(ActionType.SelectData.class) + // TODO rename to just ChartWizardAction + public class GenericChartWizardAction extends AbstractChartWizardAction + { @Override - public void addNavTrail(NavTree root) + public ModelAndView getView(ChartWizardReportForm form, BindException errors) throws Exception { setHelpTopic("reportsAndViews"); - root.addChild(_navTitle); + + return super.getView(form, errors); } }