From 1473d359df55348605488ef6544cd90914a3b106 Mon Sep 17 00:00:00 2001 From: Lum Date: Thu, 11 Dec 2025 12:06:46 -0800 Subject: [PATCH 1/5] checkpoint --- .../dbscripts/postgresql/core-create.sql | 24 ++++++++++++++++++ .../dbscripts/sqlserver/core-create.sql | 25 +++++++++++++++++++ .../labkey/core/query/ModulesTableInfo.java | 23 +++++++++++++++-- 3 files changed, 70 insertions(+), 2 deletions(-) diff --git a/core/resources/schemas/dbscripts/postgresql/core-create.sql b/core/resources/schemas/dbscripts/postgresql/core-create.sql index 0acfc95dad0..01f2e43229f 100644 --- a/core/resources/schemas/dbscripts/postgresql/core-create.sql +++ b/core/resources/schemas/dbscripts/postgresql/core-create.sql @@ -44,4 +44,28 @@ CREATE VIEW core.ActiveUsers AS FROM core.Users WHERE Active=true; +CREATE VIEW core.ContainerPath AS + WITH RECURSIVE container_hierarchy AS ( + SELECT + EntityId, + Name, + Parent, + Type, + 1 as Level, + CAST('' AS VARCHAR(4000)) AS Path + FROM core.Containers + WHERE Parent IS NULL AND Type = 'normal' + + UNION ALL + + SELECT + c.EntityId, + c.Name, + c.Parent, + c.Type, + ch.Level + 1, + CAST(ch.Path || '/' || c.Name AS VARCHAR(4000)) AS Path + FROM core.Containers c JOIN container_hierarchy ch ON c.Parent = ch.EntityId + ) SELECT * from container_hierarchy + diff --git a/core/resources/schemas/dbscripts/sqlserver/core-create.sql b/core/resources/schemas/dbscripts/sqlserver/core-create.sql index b97d76ddd10..57245c20970 100644 --- a/core/resources/schemas/dbscripts/sqlserver/core-create.sql +++ b/core/resources/schemas/dbscripts/sqlserver/core-create.sql @@ -29,3 +29,28 @@ CREATE VIEW core.ActiveUsers AS GO +CREATE VIEW core.ContainerPath AS + WITH container_hierarchy AS ( + SELECT + EntityId, + Name, + Parent, + Type, + 1 as Level, + CAST('' AS NVARCHAR(MAX)) AS Path + FROM core.Containers + WHERE Parent IS NULL AND Type = 'normal' + + UNION ALL + + SELECT + c.EntityId, + c.Name, + c.Parent, + c.Type, + ch.Level + 1, + CAST(ch.Path + '/' + c.Name AS NVARCHAR(MAX)) AS Path + FROM core.Containers c JOIN container_hierarchy ch ON c.Parent = ch.EntityId + ) SELECT * from container_hierarchy + +GO \ No newline at end of file diff --git a/core/src/org/labkey/core/query/ModulesTableInfo.java b/core/src/org/labkey/core/query/ModulesTableInfo.java index c5ca00905b8..cb74a0481d7 100644 --- a/core/src/org/labkey/core/query/ModulesTableInfo.java +++ b/core/src/org/labkey/core/query/ModulesTableInfo.java @@ -23,7 +23,9 @@ import org.labkey.api.data.DisplayColumn; import org.labkey.api.data.DisplayColumnDecorator; import org.labkey.api.data.DisplayColumnFactory; +import org.labkey.api.data.ExpandableTextDisplayColumnFactory; import org.labkey.api.data.JdbcType; +import org.labkey.api.data.PropertySchema; import org.labkey.api.data.RenderContext; import org.labkey.api.data.SQLFragment; import org.labkey.api.data.dialect.DialectStringHandler; @@ -94,6 +96,7 @@ public void addColumns() var licenseCol = addTextColumn("License"); licenseCol.setURL(StringExpressionFactory.createURL("${LicenseURL}")); licenseCol.setURLTargetWindow("_blank"); + addTextColumn("Folder").setDisplayColumnFactory(new ExpandableTextDisplayColumnFactory()); addTextColumn("LicenseURL").setHidden(true); addTextColumn("VcsRevision"); addTextColumn("VcsURL"); @@ -109,7 +112,8 @@ public void addColumns() FieldKey.fromParts("SchemaVersion"), FieldKey.fromParts("Label"), FieldKey.fromParts("Organization"), - FieldKey.fromParts("License") + FieldKey.fromParts("License"), + FieldKey.fromParts("Folder") )); } @@ -225,13 +229,28 @@ public SQLFragment getFromSQL(String alias) String token = ret.addCommonTableExpression(getSqlDialect(), "modulestableconstants", tableName, cte); // join with core.modules - ret.append("(SELECT m.name, m.schemaversion, m.classname, m.schemas"); + ret.append("(SELECT m.name, m.schemaversion, m.classname, m.schemas, AF.folder"); ret.append(",").append(tableName).append(".*"); ret.append("\n"); ret.append("FROM ").append(getFromTable().getFromSQL("m")).append("\n"); ret.append("INNER JOIN ").append(token).append(" ").append(tableName).append(" ON m.name = ").append(tableName).append(".ModuleName\n"); // CONSIDER: LEFT OUTER JOIN to include rows from core.modules for modules not currently installed + PropertySchema.getInstance().getTableInfoProperties(); + // find the folders that the module is active in, I don't think this is entirely accurate. + SQLFragment moduleFolders = new SQLFragment("(SELECT PROP.name AS module, ") + .append(getSqlDialect().getGroupConcat(new SQLFragment("C.path"), true, true, "\n")).append(" AS folder FROM ") + .append(PropertySchema.getInstance().getTableInfoPropertySets(), "PS") + .append(" JOIN ").append(PropertySchema.getInstance().getTableInfoProperties(), "PROP") + .append(" ON PS.set = PROP.set") + .append(" JOIN ").append(CoreSchema.getInstance().getTableInfoContainerPath(), "C") + .append(" ON PS.objectId = C.entityid") + .append(" WHERE PS.category = 'activeModules'") + .append(" GROUP BY PROP.name") + .append(") AF"); + + ret.append("JOIN ").append(moduleFolders).append(" ON AF.module = m.name "); + // WHERE SQLFragment filterFrag = getFilter().getSQLFragment(getFromTable(), null); ret.append("\n").append(filterFrag).append(") ").appendIdentifier(alias); From fa9d7cc81003633f356496adfe5d5626f0b8e726 Mon Sep 17 00:00:00 2001 From: Lum Date: Thu, 11 Dec 2025 12:12:21 -0800 Subject: [PATCH 2/5] expose view --- api/src/org/labkey/api/data/CoreSchema.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/api/src/org/labkey/api/data/CoreSchema.java b/api/src/org/labkey/api/data/CoreSchema.java index 44cfdd2430e..8d581224a0b 100644 --- a/api/src/org/labkey/api/data/CoreSchema.java +++ b/api/src/org/labkey/api/data/CoreSchema.java @@ -191,4 +191,9 @@ public TableInfo getTableInfoEmailOptions() { return getSchema().getTable("EmailOptions"); } + + public TableInfo getTableInfoContainerPath() + { + return getSchema().getTable("ContainerPath"); + } } From 510ad96a77850ed310e966c6523b170f5204369d Mon Sep 17 00:00:00 2001 From: lum Date: Fri, 12 Dec 2025 16:09:23 -0800 Subject: [PATCH 3/5] in memory module list --- .../labkey/core/query/ModulesTableInfo.java | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/core/src/org/labkey/core/query/ModulesTableInfo.java b/core/src/org/labkey/core/query/ModulesTableInfo.java index cb74a0481d7..a4175d5d0f7 100644 --- a/core/src/org/labkey/core/query/ModulesTableInfo.java +++ b/core/src/org/labkey/core/query/ModulesTableInfo.java @@ -19,6 +19,8 @@ import org.jetbrains.annotations.NotNull; import org.labkey.api.data.BaseColumnInfo; import org.labkey.api.data.ColumnInfo; +import org.labkey.api.data.Container; +import org.labkey.api.data.ContainerManager; import org.labkey.api.data.CoreSchema; import org.labkey.api.data.DisplayColumn; import org.labkey.api.data.DisplayColumnDecorator; @@ -38,7 +40,12 @@ import org.labkey.api.util.StringExpressionFactory; import org.labkey.api.writer.HtmlWriter; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; import java.util.List; +import java.util.Map; /** * User: kevink @@ -97,6 +104,7 @@ public void addColumns() licenseCol.setURL(StringExpressionFactory.createURL("${LicenseURL}")); licenseCol.setURLTargetWindow("_blank"); addTextColumn("Folder").setDisplayColumnFactory(new ExpandableTextDisplayColumnFactory()); + addTextColumn("ActiveContainers").setDisplayColumnFactory(new ExpandableTextDisplayColumnFactory()); addTextColumn("LicenseURL").setHidden(true); addTextColumn("VcsRevision"); addTextColumn("VcsURL"); @@ -186,6 +194,8 @@ public SQLFragment getFromSQL(String alias) cte.append("SELECT * FROM (\n"); cte.append("VALUES "); String sep = ""; + + Map> moduleContainers = getContainersForModule(); for (Module module : ModuleLoader.getInstance().getModules()) { cte.append(sep); @@ -207,6 +217,7 @@ public SQLFragment getFromSQL(String alias) appendStringLiteral(h, cte,",",module.getSourcePath()); appendStringLiteral(h, cte,",",StringUtils.join(module.getModuleDependenciesAsSet(), ", ")); appendStringLiteral(h, cte,",",module.getSupportedDatabasesSet().toString()); + appendStringLiteral(h, cte,",", StringUtils.join(moduleContainers.getOrDefault(module, Collections.emptyList()), "\n")); cte.append(")"); } cte.append(") AS T ("); @@ -223,6 +234,7 @@ public SQLFragment getFromSQL(String alias) cte.append(",VcsRevision, VcsURL"); cte.append(",SourcePath"); cte.append(",Dependencies, SupportedDatabases"); + cte.append(",ActiveContainers"); cte.append(")\n"); String tableName = getSqlDialect().truncate(alias + "$m", 0); @@ -258,6 +270,22 @@ public SQLFragment getFromSQL(String alias) return ret; } + private Map> getContainersForModule() + { + Map> moduleFolders = new HashMap<>(); + + for (Container container : ContainerManager.getAllChildren(ContainerManager.getRoot())) + { + if (container != null) + container.getActiveModules().forEach(m -> moduleFolders.computeIfAbsent(m, k -> new ArrayList<>()).add(container.getPath())); + } + + for (List folders : moduleFolders.values()) + Collections.sort(folders); + + return moduleFolders; + } + // Format SchemaVersion column using the standard ModuleContext formatting rules: force three-decimal places for >= 20.000, // otherwise suppress trailing zeroes. Also, right align the values in the grid. private static class SchemaVersionDisplayColumnFactory implements DisplayColumnFactory From 077120ef2034b47646bb240f3d185fb6f34bf871 Mon Sep 17 00:00:00 2001 From: lum Date: Fri, 19 Dec 2025 16:13:35 -0800 Subject: [PATCH 4/5] Use java solution --- api/src/org/labkey/api/data/CoreSchema.java | 5 ---- .../dbscripts/postgresql/core-create.sql | 25 ------------------ .../dbscripts/sqlserver/core-create.sql | 26 ------------------- .../labkey/core/query/ModulesTableInfo.java | 26 +++---------------- 4 files changed, 4 insertions(+), 78 deletions(-) diff --git a/api/src/org/labkey/api/data/CoreSchema.java b/api/src/org/labkey/api/data/CoreSchema.java index 8d581224a0b..44cfdd2430e 100644 --- a/api/src/org/labkey/api/data/CoreSchema.java +++ b/api/src/org/labkey/api/data/CoreSchema.java @@ -191,9 +191,4 @@ public TableInfo getTableInfoEmailOptions() { return getSchema().getTable("EmailOptions"); } - - public TableInfo getTableInfoContainerPath() - { - return getSchema().getTable("ContainerPath"); - } } diff --git a/core/resources/schemas/dbscripts/postgresql/core-create.sql b/core/resources/schemas/dbscripts/postgresql/core-create.sql index 01f2e43229f..26ac4927dba 100644 --- a/core/resources/schemas/dbscripts/postgresql/core-create.sql +++ b/core/resources/schemas/dbscripts/postgresql/core-create.sql @@ -44,28 +44,3 @@ CREATE VIEW core.ActiveUsers AS FROM core.Users WHERE Active=true; -CREATE VIEW core.ContainerPath AS - WITH RECURSIVE container_hierarchy AS ( - SELECT - EntityId, - Name, - Parent, - Type, - 1 as Level, - CAST('' AS VARCHAR(4000)) AS Path - FROM core.Containers - WHERE Parent IS NULL AND Type = 'normal' - - UNION ALL - - SELECT - c.EntityId, - c.Name, - c.Parent, - c.Type, - ch.Level + 1, - CAST(ch.Path || '/' || c.Name AS VARCHAR(4000)) AS Path - FROM core.Containers c JOIN container_hierarchy ch ON c.Parent = ch.EntityId - ) SELECT * from container_hierarchy - - diff --git a/core/resources/schemas/dbscripts/sqlserver/core-create.sql b/core/resources/schemas/dbscripts/sqlserver/core-create.sql index 57245c20970..b745ea73d6a 100644 --- a/core/resources/schemas/dbscripts/sqlserver/core-create.sql +++ b/core/resources/schemas/dbscripts/sqlserver/core-create.sql @@ -28,29 +28,3 @@ CREATE VIEW core.ActiveUsers AS WHERE Active=1; GO - -CREATE VIEW core.ContainerPath AS - WITH container_hierarchy AS ( - SELECT - EntityId, - Name, - Parent, - Type, - 1 as Level, - CAST('' AS NVARCHAR(MAX)) AS Path - FROM core.Containers - WHERE Parent IS NULL AND Type = 'normal' - - UNION ALL - - SELECT - c.EntityId, - c.Name, - c.Parent, - c.Type, - ch.Level + 1, - CAST(ch.Path + '/' + c.Name AS NVARCHAR(MAX)) AS Path - FROM core.Containers c JOIN container_hierarchy ch ON c.Parent = ch.EntityId - ) SELECT * from container_hierarchy - -GO \ No newline at end of file diff --git a/core/src/org/labkey/core/query/ModulesTableInfo.java b/core/src/org/labkey/core/query/ModulesTableInfo.java index a4175d5d0f7..135c53a060a 100644 --- a/core/src/org/labkey/core/query/ModulesTableInfo.java +++ b/core/src/org/labkey/core/query/ModulesTableInfo.java @@ -27,7 +27,6 @@ import org.labkey.api.data.DisplayColumnFactory; import org.labkey.api.data.ExpandableTextDisplayColumnFactory; import org.labkey.api.data.JdbcType; -import org.labkey.api.data.PropertySchema; import org.labkey.api.data.RenderContext; import org.labkey.api.data.SQLFragment; import org.labkey.api.data.dialect.DialectStringHandler; @@ -42,7 +41,6 @@ import java.util.ArrayList; import java.util.Collections; -import java.util.Comparator; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -103,8 +101,7 @@ public void addColumns() var licenseCol = addTextColumn("License"); licenseCol.setURL(StringExpressionFactory.createURL("${LicenseURL}")); licenseCol.setURLTargetWindow("_blank"); - addTextColumn("Folder").setDisplayColumnFactory(new ExpandableTextDisplayColumnFactory()); - addTextColumn("ActiveContainers").setDisplayColumnFactory(new ExpandableTextDisplayColumnFactory()); + addTextColumn("ActiveFolders").setDisplayColumnFactory(new ExpandableTextDisplayColumnFactory()); addTextColumn("LicenseURL").setHidden(true); addTextColumn("VcsRevision"); addTextColumn("VcsURL"); @@ -121,7 +118,7 @@ public void addColumns() FieldKey.fromParts("Label"), FieldKey.fromParts("Organization"), FieldKey.fromParts("License"), - FieldKey.fromParts("Folder") + FieldKey.fromParts("ActiveFolders") )); } @@ -234,35 +231,20 @@ public SQLFragment getFromSQL(String alias) cte.append(",VcsRevision, VcsURL"); cte.append(",SourcePath"); cte.append(",Dependencies, SupportedDatabases"); - cte.append(",ActiveContainers"); + cte.append(",ActiveFolders"); cte.append(")\n"); String tableName = getSqlDialect().truncate(alias + "$m", 0); String token = ret.addCommonTableExpression(getSqlDialect(), "modulestableconstants", tableName, cte); // join with core.modules - ret.append("(SELECT m.name, m.schemaversion, m.classname, m.schemas, AF.folder"); + ret.append("(SELECT m.name, m.schemaversion, m.classname, m.schemas"); ret.append(",").append(tableName).append(".*"); ret.append("\n"); ret.append("FROM ").append(getFromTable().getFromSQL("m")).append("\n"); ret.append("INNER JOIN ").append(token).append(" ").append(tableName).append(" ON m.name = ").append(tableName).append(".ModuleName\n"); // CONSIDER: LEFT OUTER JOIN to include rows from core.modules for modules not currently installed - PropertySchema.getInstance().getTableInfoProperties(); - // find the folders that the module is active in, I don't think this is entirely accurate. - SQLFragment moduleFolders = new SQLFragment("(SELECT PROP.name AS module, ") - .append(getSqlDialect().getGroupConcat(new SQLFragment("C.path"), true, true, "\n")).append(" AS folder FROM ") - .append(PropertySchema.getInstance().getTableInfoPropertySets(), "PS") - .append(" JOIN ").append(PropertySchema.getInstance().getTableInfoProperties(), "PROP") - .append(" ON PS.set = PROP.set") - .append(" JOIN ").append(CoreSchema.getInstance().getTableInfoContainerPath(), "C") - .append(" ON PS.objectId = C.entityid") - .append(" WHERE PS.category = 'activeModules'") - .append(" GROUP BY PROP.name") - .append(") AF"); - - ret.append("JOIN ").append(moduleFolders).append(" ON AF.module = m.name "); - // WHERE SQLFragment filterFrag = getFilter().getSQLFragment(getFromTable(), null); ret.append("\n").append(filterFrag).append(") ").appendIdentifier(alias); From 15603d7150e79bb30fcf4c0cb805e99655c86029 Mon Sep 17 00:00:00 2001 From: Lum Date: Tue, 23 Dec 2025 14:45:39 -0800 Subject: [PATCH 5/5] Revert whitespace changes. --- core/resources/schemas/dbscripts/postgresql/core-create.sql | 1 + core/resources/schemas/dbscripts/sqlserver/core-create.sql | 1 + 2 files changed, 2 insertions(+) diff --git a/core/resources/schemas/dbscripts/postgresql/core-create.sql b/core/resources/schemas/dbscripts/postgresql/core-create.sql index 26ac4927dba..0acfc95dad0 100644 --- a/core/resources/schemas/dbscripts/postgresql/core-create.sql +++ b/core/resources/schemas/dbscripts/postgresql/core-create.sql @@ -44,3 +44,4 @@ CREATE VIEW core.ActiveUsers AS FROM core.Users WHERE Active=true; + diff --git a/core/resources/schemas/dbscripts/sqlserver/core-create.sql b/core/resources/schemas/dbscripts/sqlserver/core-create.sql index b745ea73d6a..b97d76ddd10 100644 --- a/core/resources/schemas/dbscripts/sqlserver/core-create.sql +++ b/core/resources/schemas/dbscripts/sqlserver/core-create.sql @@ -28,3 +28,4 @@ CREATE VIEW core.ActiveUsers AS WHERE Active=1; GO +