Skip to content

Commit 46dc56d

Browse files
Merge 26.7 to develop
2 parents ec34e9f + 52cde52 commit 46dc56d

3 files changed

Lines changed: 117 additions & 2 deletions

File tree

‎panoramapublic/src/org/labkey/panoramapublic/PanoramaPublicSchema.java‎

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,11 @@ public class PanoramaPublicSchema extends UserSchema
9292
public static final String TABLE_EXPT_ISOTOPE_MOD_INFO = "ExperimentIsotopeModInfo";
9393
public static final String TABLE_ISOTOPE_UNIMOD_INFO = "IsotopeUnimodInfo";
9494

95+
// Queries behind the Structural and Isotope modification web parts. Both aggregate over
96+
// container-scoped targetedms tables, so they must stay scoped to the current folder tree.
97+
public static final String QUERY_STRUCTURAL_MODIFICATIONS = "StructuralModifications";
98+
public static final String QUERY_ISOTOPE_MODIFICATIONS = "IsotopeModifications";
99+
95100
public static final String TABLE_LIB_DEPENDENCY_TYPE = "SpecLibDependencyType";
96101
public static final String TABLE_LIB_SOURCE_TYPE = "SpecLibSourceType";
97102

@@ -430,9 +435,39 @@ protected void populateButtonBar(DataView view, ButtonBar bar)
430435
};
431436
}
432437

438+
else if (QUERY_ISOTOPE_MODIFICATIONS.equalsIgnoreCase(settings.getQueryName())
439+
|| QUERY_STRUCTURAL_MODIFICATIONS.equalsIgnoreCase(settings.getQueryName()))
440+
{
441+
// The modification web part title links to this query grid. Restrict it to the current folder tree.
442+
// The standalone web part grid is restricted the same way in ModificationsView.
443+
QueryView view = new QueryView(this, settings, errors)
444+
{
445+
@Override
446+
protected ContainerFilter getContainerFilter()
447+
{
448+
return limitContainerScope(super.getContainerFilter(), getContainer(), getUser());
449+
}
450+
};
451+
view.setAllowableContainerFilterTypes(ContainerFilter.Type.Current, ContainerFilter.Type.CurrentAndSubfolders);
452+
return view;
453+
}
454+
433455
return super.createView(context, settings, errors);
434456
}
435457

458+
// Limit the modification queries to the current folder tree. setAllowableContainerFilterTypes only applies
459+
// to the options in the Folder Filter menu. query.containerFilterName=AllFolders in the URL still gets applied.
460+
public static ContainerFilter limitContainerScope(ContainerFilter cf, Container container, User user)
461+
{
462+
if (cf != null
463+
&& cf.getType() != ContainerFilter.Type.Current
464+
&& cf.getType() != ContainerFilter.Type.CurrentAndSubfolders)
465+
{
466+
return ContainerFilter.Type.CurrentAndSubfolders.create(container, user);
467+
}
468+
return cf;
469+
}
470+
436471
@Override
437472
public Set<String> getTableNames()
438473
{

‎panoramapublic/src/org/labkey/panoramapublic/query/modification/ModificationsView.java‎

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,14 @@ protected QuerySettings createQuerySettings(ViewContext portalCtx)
7070
return settings;
7171
}
7272

73+
@Override
74+
protected ContainerFilter getContainerFilter()
75+
{
76+
// The standalone web part grid reads the container scope from the URL, which could be set to "AllFolders".
77+
// Restrict it the same way createView does.
78+
return PanoramaPublicSchema.limitContainerScope(super.getContainerFilter(), getContainer(), getUser());
79+
}
80+
7381
public static class StructuralModsView extends ModificationsView
7482
{
7583
public StructuralModsView(ViewContext portalCtx)
@@ -79,7 +87,7 @@ public StructuralModsView(ViewContext portalCtx)
7987

8088
public StructuralModsView(ViewContext portalCtx, @Nullable ExperimentAnnotations exptAnnotations)
8189
{
82-
super(portalCtx, "StructuralModifications", "Structural Modifications", "ExperimentStructuralModInfo", exptAnnotations);
90+
super(portalCtx, PanoramaPublicSchema.QUERY_STRUCTURAL_MODIFICATIONS, "Structural Modifications", "ExperimentStructuralModInfo", exptAnnotations);
8391
}
8492
}
8593

@@ -92,7 +100,7 @@ public IsotopeModsView(ViewContext portalCtx)
92100

93101
public IsotopeModsView(ViewContext portalCtx, @Nullable ExperimentAnnotations exptAnnotations)
94102
{
95-
super(portalCtx, "IsotopeModifications", "Isotope Modifications", "ExperimentIsotopeModInfo", exptAnnotations);
103+
super(portalCtx, PanoramaPublicSchema.QUERY_ISOTOPE_MODIFICATIONS, "Isotope Modifications", "ExperimentIsotopeModInfo", exptAnnotations);
96104
}
97105
}
98106
}

‎panoramapublic/test/src/org/labkey/test/tests/panoramapublic/PanoramaPublicModificationsTest.java‎

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,18 +20,25 @@
2020
import org.labkey.api.util.Pair;
2121
import org.labkey.test.BaseWebDriverTest;
2222
import org.labkey.test.Locator;
23+
import org.labkey.test.WebTestHelper;
2324
import org.labkey.test.categories.External;
2425
import org.labkey.test.categories.MacCossLabModules;
2526
import org.labkey.test.components.WebPart;
27+
import org.labkey.test.components.html.BootstrapMenu;
2628
import org.labkey.test.components.panoramapublic.TargetedMsExperimentWebPart;
2729
import org.labkey.test.pages.panoramapublic.DataValidationPage;
2830
import org.labkey.test.util.DataRegionTable;
31+
import org.labkey.test.util.EscapeUtil;
2932
import org.labkey.test.util.Ext4Helper;
3033
import org.labkey.test.util.TextSearcher;
34+
import org.openqa.selenium.WebElement;
3135

3236
import java.util.ArrayList;
3337
import java.util.List;
38+
import java.util.Map;
3439

40+
import static org.junit.Assert.assertEquals;
41+
import static org.junit.Assert.assertFalse;
3542
import static org.junit.Assert.assertNotEquals;
3643
import static org.junit.Assert.assertNotNull;
3744
import static org.junit.Assert.assertTrue;
@@ -107,6 +114,12 @@ public void testAddModInfo()
107114
testCustomizeGrid(STRUCTURAL_MOD, true);
108115
testCustomizeGrid(ISOTOPE_MOD, true);
109116

117+
// The web part title links to the full query grid. It must not let a caller widen the
118+
// scope to all folders.
119+
verifyModificationQueryScopeIsRestricted();
120+
// The standalone web part grid on this page must not be widened by a URL either.
121+
verifyModificationWebPartScopeIsRestricted();
122+
110123
// Match structural modification
111124
goToExperimentDetailsPage();
112125
testSaveMatchForStructuralMod(propionylation, List.of(propionyl, new Unimod(206, "Delta:H(4)C(3)O(1)", propionyl.getFormula())),
@@ -128,6 +141,65 @@ public void testAddModInfo()
128141
testCopy(projectName, folderName, experimentTitle, folderName + " Copy");
129142
}
130143

144+
// CreateView on these queries restricts the grids to the current folder or the current folder and subfolders.
145+
// Check that a wider scope asked for in the URL is restricted, and that an allowed scope still works.
146+
private void verifyModificationQueryScopeIsRestricted()
147+
{
148+
for (String queryName : List.of("IsotopeModifications", "StructuralModifications"))
149+
{
150+
// The Folder Filter dropdown offers the narrow scopes but not All Folders.
151+
assertFolderFilterMenuHidesAllFolders(queryName);
152+
// A URL asking for All Folders is restricted to the current and subfolders.
153+
assertContainerFilterScope(queryName, "AllFolders", DataRegionTable.ContainerFilterType.CURRENT_AND_SUBFOLDERS);
154+
// "Current and Subfolders" is allowed.
155+
assertContainerFilterScope(queryName, "CurrentAndSubfolders", DataRegionTable.ContainerFilterType.CURRENT_AND_SUBFOLDERS);
156+
}
157+
}
158+
159+
// The standalone modification web parts (added to a folder page, not the experiment details page)
160+
// read the scope straight from the URL. Check that a wider folder scope in the URL is restricted there too.
161+
private void verifyModificationWebPartScopeIsRestricted()
162+
{
163+
for (String webPartName : List.of(STRUCTURAL_MOD, ISOTOPE_MOD))
164+
{
165+
// Go to the dashboard tab that holds the web parts, then re-request the same page with a
166+
// "AllFolders" filter in the URL. The web part's data region is named after its title.
167+
goToDashboard();
168+
String url = getCurrentRelativeURL();
169+
url += (url.contains("?") ? "&" : "?") + EscapeUtil.encode(webPartName) + ".containerFilterName=AllFolders";
170+
beginAt(url);
171+
var grid = new DataRegionTable(webPartName, this);
172+
assertEquals("Standalone '" + webPartName + "' web part should restrict an All Folders URL to the current and subfolders",
173+
DataRegionTable.ContainerFilterType.CURRENT_AND_SUBFOLDERS, grid.getContainerFilter());
174+
}
175+
}
176+
177+
private void assertFolderFilterMenuHidesAllFolders(String queryName)
178+
{
179+
beginAt(WebTestHelper.buildURL("query", getCurrentContainerPath(), "executeQuery",
180+
Map.of("schemaName", "panoramapublic", "query.queryName", queryName)));
181+
BootstrapMenu viewsMenu = new DataRegionTable("query", this).getViewsMenu();
182+
viewsMenu.openMenuTo("Folder Filter", "Folder Filter");
183+
List<String> options = viewsMenu.findVisibleMenuItems().stream().map(WebElement::getText).map(String::trim).toList();
184+
// The allowed scope is present. This also guards against an empty menu making the check below pass for free.
185+
assertTrue("Folder Filter for query '" + queryName + "' should offer 'Current folder and subfolders'. Found: " + options,
186+
options.contains(DataRegionTable.ContainerFilterType.CURRENT_AND_SUBFOLDERS.getLabel()));
187+
// All Folders is not offered.
188+
assertFalse("Folder Filter for query '" + queryName + "' must not offer 'All Folders'. Found: " + options,
189+
options.contains(DataRegionTable.ContainerFilterType.ALL_FOLDERS.getLabel()));
190+
}
191+
192+
private void assertContainerFilterScope(String queryName, String requestedFilterName, DataRegionTable.ContainerFilterType expected)
193+
{
194+
beginAt(WebTestHelper.buildURL("query", getCurrentContainerPath(), "executeQuery",
195+
Map.of("schemaName", "panoramapublic",
196+
"query.queryName", queryName,
197+
"query.containerFilterName", requestedFilterName)));
198+
var grid = new DataRegionTable("query", this);
199+
assertEquals("Folder Filter for query '" + queryName + "' requested as '" + requestedFilterName + "' was not the expected scope",
200+
expected, grid.getContainerFilter());
201+
}
202+
131203
private void testNoMatchForStructuralMod(String modificationName)
132204
{
133205
var modsTable = new DataRegionTable(STRUCTURAL_MOD, this);

0 commit comments

Comments
 (0)