From 030afb69d12d6d5e41b7da9019a42f540c92865a Mon Sep 17 00:00:00 2001
From: Adam Rauch
Date: Thu, 6 Aug 2026 12:14:38 -0700
Subject: [PATCH 1/6] Admin button that hides the search icon in the SDMS
header
---
.../labkey/api/search/NoopSearchService.java | 7 +++++
.../org/labkey/api/search/SearchService.java | 3 ++
.../core/view/template/bootstrap/header.jsp | 3 +-
.../org/labkey/search/SearchController.java | 31 +++++++++++++++++++
.../search/model/AbstractSearchService.java | 5 +++
.../search/model/SearchPropertyManager.java | 19 +++++++++---
.../search/model/SearchStartupProperties.java | 11 +++++++
.../org/labkey/search/view/indexerAdmin.jsp | 29 +++++++++++++++++
8 files changed, 102 insertions(+), 6 deletions(-)
diff --git a/api/src/org/labkey/api/search/NoopSearchService.java b/api/src/org/labkey/api/search/NoopSearchService.java
index 56be54eb815..006cbef00cb 100644
--- a/api/src/org/labkey/api/search/NoopSearchService.java
+++ b/api/src/org/labkey/api/search/NoopSearchService.java
@@ -365,6 +365,13 @@ public void pauseCrawler()
{
}
+ @Override
+ public boolean isSearchIconVisible()
+ {
+ // No persisted setting to consult, so don't be the reason the header hides the icon
+ return true;
+ }
+
@Override
public @Nullable Throwable getConfigurationError()
{
diff --git a/api/src/org/labkey/api/search/SearchService.java b/api/src/org/labkey/api/search/SearchService.java
index 0dd6ae0ada7..874b22cbe3f 100644
--- a/api/src/org/labkey/api/search/SearchService.java
+++ b/api/src/org/labkey/api/search/SearchService.java
@@ -442,6 +442,9 @@ public String normalizeHref(Path contextPath, Container c)
void resetIndex();
void startCrawler();
void pauseCrawler();
+
+ /** @return should the search icon be shown in the page header; controlled from the Full-Text Search admin page */
+ boolean isSearchIconVisible();
void updateIndex(String reason);
void refreshNow();
diff --git a/core/src/org/labkey/core/view/template/bootstrap/header.jsp b/core/src/org/labkey/core/view/template/bootstrap/header.jsp
index 5369a16f4cf..7ac0abc2352 100644
--- a/core/src/org/labkey/core/view/template/bootstrap/header.jsp
+++ b/core/src/org/labkey/core/view/template/bootstrap/header.jsp
@@ -19,6 +19,7 @@
<%@ page import="org.labkey.api.data.Container" %>
<%@ page import="org.labkey.api.module.ModuleLoader" %>
<%@ page import="org.labkey.api.portal.ProjectUrls" %>
+<%@ page import="org.labkey.api.search.SearchService" %>
<%@ page import="org.labkey.api.search.SearchUrls" %>
<%@ page import="org.labkey.api.search.SearchUtils" %>
<%@ page import="org.labkey.api.security.AuthenticationManager" %>
@@ -83,7 +84,7 @@
LookAndFeelProperties laf = LookAndFeelProperties.getInstance(c);
ModuleLoader moduleLoader = ModuleLoader.getInstance();
boolean isStartupComplete = moduleLoader.isStartupComplete();
- boolean showSearch = isStartupComplete && PageFlowUtil.urlProviderOptional(SearchUrls.class) != null;
+ boolean showSearch = isStartupComplete && PageFlowUtil.urlProviderOptional(SearchUrls.class) != null && SearchService.get().isSearchIconVisible();
HtmlView headerHtml = new HeaderProperties(getContainer()).getView();
String siteShortName = (laf.getShortName() != null && !laf.getShortName().isEmpty()) ? laf.getShortName() : null;
diff --git a/search/src/org/labkey/search/SearchController.java b/search/src/org/labkey/search/SearchController.java
index 0c7d0bb90c2..b27ff270645 100644
--- a/search/src/org/labkey/search/SearchController.java
+++ b/search/src/org/labkey/search/SearchController.java
@@ -77,6 +77,7 @@
import org.labkey.search.model.CrawlerRunningState;
import org.labkey.search.model.IndexInspector;
import org.labkey.search.model.LuceneDirectoryType;
+import org.labkey.search.model.SearchIconState;
import org.labkey.search.model.SearchPropertyManager;
import org.springframework.beans.MutablePropertyValues;
import org.springframework.beans.PropertyValues;
@@ -148,6 +149,8 @@ public static class AdminForm
private int msg = 0;
private boolean pause;
private boolean start;
+ private boolean hideSearchIcon;
+ private boolean showSearchIcon;
private boolean delete;
private String indexPath;
@@ -199,6 +202,26 @@ public void setPause(boolean pause)
this.pause = pause;
}
+ public boolean isHideSearchIcon()
+ {
+ return hideSearchIcon;
+ }
+
+ public void setHideSearchIcon(boolean hideSearchIcon)
+ {
+ this.hideSearchIcon = hideSearchIcon;
+ }
+
+ public boolean isShowSearchIcon()
+ {
+ return showSearchIcon;
+ }
+
+ public void setShowSearchIcon(boolean showSearchIcon)
+ {
+ this.showSearchIcon = showSearchIcon;
+ }
+
public String getIndexPath()
{
return indexPath;
@@ -335,6 +358,14 @@ else if (form.isPause())
SearchPropertyManager.setCrawlerRunningState(getUser(), CrawlerRunningState.Pause);
ss.pauseCrawler();
}
+ else if (form.isShowSearchIcon())
+ {
+ SearchPropertyManager.setSearchIconState(getUser(), SearchIconState.Show);
+ }
+ else if (form.isHideSearchIcon())
+ {
+ SearchPropertyManager.setSearchIconState(getUser(), SearchIconState.Hide);
+ }
else if (form.isDelete())
{
ss.deleteIndex("a site admin requested it");
diff --git a/search/src/org/labkey/search/model/AbstractSearchService.java b/search/src/org/labkey/search/model/AbstractSearchService.java
index 9c60beaff2a..55ba30b7354 100644
--- a/search/src/org/labkey/search/model/AbstractSearchService.java
+++ b/search/src/org/labkey/search/model/AbstractSearchService.java
@@ -963,6 +963,11 @@ public void pauseCrawler()
}
}
+ @Override
+ public boolean isSearchIconVisible()
+ {
+ return SearchPropertyManager.getSearchIconState();
+ }
@Override
public void updateIndex(String reason)
diff --git a/search/src/org/labkey/search/model/SearchPropertyManager.java b/search/src/org/labkey/search/model/SearchPropertyManager.java
index 3c88f75776f..9266ce373c6 100644
--- a/search/src/org/labkey/search/model/SearchPropertyManager.java
+++ b/search/src/org/labkey/search/model/SearchPropertyManager.java
@@ -30,11 +30,6 @@
import java.io.File;
import java.util.Map;
-/**
- * User: adam
- * Date: Apr 20, 2010
- * Time: 7:01:16 PM
- */
public class SearchPropertyManager
{
private static final String CATEGORY = SearchModule.class.getName();
@@ -42,6 +37,7 @@ public class SearchPropertyManager
private static final String INDEX_PATH = "primaryIndexPath"; // Note: don't change this legacy name
private static final String DIRECTORY_TYPE = "directoryType";
private static final String FILE_SIZE_LIMIT = "fileSizeLimitMB";
+ private static final String SEARCH_ICON_STATE = "searchIconState";
public static boolean getCrawlerRunningState()
@@ -60,6 +56,19 @@ public static void setCrawlerRunningState(User user, CrawlerRunningState state)
audit(user, state.getAuditMessage());
}
+ /** @return true unless an admin has explicitly hidden the search icon */
+ public static boolean getSearchIconState()
+ {
+ String state = getProperty(SEARCH_ICON_STATE);
+ return null == state || "true".equals(state);
+ }
+
+ public static void setSearchIconState(User user, SearchIconState state)
+ {
+ setProperty(SEARCH_ICON_STATE, String.valueOf(state.isVisible()));
+ audit(user, state.getAuditMessage());
+ }
+
public static String getUnsubstitutedIndexDirectory()
{
String path = getProperty(INDEX_PATH);
diff --git a/search/src/org/labkey/search/model/SearchStartupProperties.java b/search/src/org/labkey/search/model/SearchStartupProperties.java
index 92614f8d0d9..85d9b20e673 100644
--- a/search/src/org/labkey/search/model/SearchStartupProperties.java
+++ b/search/src/org/labkey/search/model/SearchStartupProperties.java
@@ -63,6 +63,17 @@ public void setProperty(@NotNull SearchService ss, SearchIndexStartupHandler ind
SearchPropertyManager.setCrawlerRunningState(null, state);
}
},
+ searchIconState("Hide or show the search icon in the page header. Valid values: " + Arrays.toString(SearchIconState.values())){
+ @Override
+ public void setProperty(@NotNull SearchService ss, SearchIndexStartupHandler indexStartupHandler, String value)
+ {
+ SearchIconState state = EnumUtils.getEnum(SearchIconState.class, value);
+ if (null == state)
+ LOG.error("Unrecognized value for \"searchIconState\": \"{}\"", value);
+ else
+ SearchPropertyManager.setSearchIconState(null, state);
+ }
+ },
deleteIndex("Delete index and clear last indexed, after setting other properties"){
@Override
public void setProperty(@NotNull SearchService ss, SearchIndexStartupHandler indexStartupHandler, String value)
diff --git a/search/src/org/labkey/search/view/indexerAdmin.jsp b/search/src/org/labkey/search/view/indexerAdmin.jsp
index 8be2e31b722..256e4052baa 100644
--- a/search/src/org/labkey/search/view/indexerAdmin.jsp
+++ b/search/src/org/labkey/search/view/indexerAdmin.jsp
@@ -127,6 +127,35 @@ else
| <%= button("Start Crawler").submit(true) %> |
<%
}
}
+ %>
+
+
+
+
+ <%
+
+ if (ss.isSearchIconVisible())
+ {
+ %>| The search icon is shown in the page header. |
<%
+
+ if (hasAdminOpsPerms)
+ {
+ %>
+ |
+ | <%= button("Hide Search Icon").submit(true) %> |
<%
+ }
+ }
+ else
+ {
+ %>| The search icon is hidden in the page header. |
<%
+
+ if (hasAdminOpsPerms)
+ {
+ %>
+ |
+ | <%= button("Show Search Icon").submit(true) %> |
<%
+ }
+ }
%>
<%
From ddfe58f99b06e5e25c698618cf84985f352169c8 Mon Sep 17 00:00:00 2001
From: Adam Rauch
Date: Thu, 6 Aug 2026 13:07:28 -0700
Subject: [PATCH 2/6] Remove unused methods
---
.../org/labkey/api/exp/api/ExperimentService.java | 13 -------------
1 file changed, 13 deletions(-)
diff --git a/api/src/org/labkey/api/exp/api/ExperimentService.java b/api/src/org/labkey/api/exp/api/ExperimentService.java
index fd0b9fce06f..259e36f75e6 100644
--- a/api/src/org/labkey/api/exp/api/ExperimentService.java
+++ b/api/src/org/labkey/api/exp/api/ExperimentService.java
@@ -82,7 +82,6 @@
import org.labkey.api.reader.TabLoader;
import org.labkey.api.security.User;
import org.labkey.api.services.ServiceRegistry;
-import org.labkey.api.util.IntegerUtils;
import org.labkey.api.util.Pair;
import org.labkey.api.util.StringUtilsLabKey;
import org.labkey.api.view.HttpView;
@@ -1384,16 +1383,4 @@ public XarImportOptions setStrictValidateExistingSampleType(boolean strictValida
return this;
}
}
-
- @Deprecated // Use IntegerUtils.asLong() instead
- static Long asLong(Object o)
- {
- return IntegerUtils.asLong(o);
- }
-
- @Deprecated // Use IntegerUtils.asInteger() instead
- static Integer asInteger(Object o)
- {
- return IntegerUtils.asInteger(o);
- }
}
From 7d8a75300a94c4d7976baa1529fb003072b498cf Mon Sep 17 00:00:00 2001
From: Adam Rauch
Date: Thu, 6 Aug 2026 13:08:38 -0700
Subject: [PATCH 3/6] Missed file
---
.../labkey/search/model/SearchIconState.java | 51 +++++++++++++++++++
1 file changed, 51 insertions(+)
create mode 100644 search/src/org/labkey/search/model/SearchIconState.java
diff --git a/search/src/org/labkey/search/model/SearchIconState.java b/search/src/org/labkey/search/model/SearchIconState.java
new file mode 100644
index 00000000000..60f66b9907f
--- /dev/null
+++ b/search/src/org/labkey/search/model/SearchIconState.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2026 LabKey Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.labkey.search.model;
+
+public enum SearchIconState
+{
+ Hide
+ {
+ @Override
+ String getAuditMessage()
+ {
+ return "Search Icon Hidden";
+ }
+
+ @Override
+ boolean isVisible()
+ {
+ return false;
+ }
+ },
+ Show
+ {
+ @Override
+ String getAuditMessage()
+ {
+ return "Search Icon Shown";
+ }
+
+ @Override
+ boolean isVisible()
+ {
+ return true;
+ }
+ };
+
+ abstract String getAuditMessage();
+ abstract boolean isVisible();
+}
From e1d472fa82512db3a8b9dc2f45e5665a25de879b Mon Sep 17 00:00:00 2001
From: Adam Rauch
Date: Thu, 6 Aug 2026 13:09:00 -0700
Subject: [PATCH 4/6] Remove pointless check
---
api/src/org/labkey/api/query/UserIdRenderer.java | 12 ++++++------
1 file changed, 6 insertions(+), 6 deletions(-)
diff --git a/api/src/org/labkey/api/query/UserIdRenderer.java b/api/src/org/labkey/api/query/UserIdRenderer.java
index f15cc015230..90320f759ff 100644
--- a/api/src/org/labkey/api/query/UserIdRenderer.java
+++ b/api/src/org/labkey/api/query/UserIdRenderer.java
@@ -78,13 +78,13 @@ public String renderURL(RenderContext ctx)
return null;
boolean isDeletedUser = UserManager.getUser(displayedUserId) == null;
- if (!isDeletedUser && displayedUserId != null)
+ if (!isDeletedUser)
{
- ActionURL url = UserManager.getUserDetailsURL(ctx.getContainer(), loggedInUser, displayedUserId);
- if (url != null)
- {
- return url.toString();
- }
+ ActionURL url = UserManager.getUserDetailsURL(ctx.getContainer(), loggedInUser, displayedUserId);
+ if (url != null)
+ {
+ return url.toString();
+ }
}
return null;
From 924047a7329d551aa4836c944add843c4ce38f99 Mon Sep 17 00:00:00 2001
From: Adam Rauch
Date: Thu, 6 Aug 2026 13:35:13 -0700
Subject: [PATCH 5/6] Put all the showSearch conditionals in one place
---
.../org/labkey/core/view/template/bootstrap/header.jsp | 8 ++++++--
1 file changed, 6 insertions(+), 2 deletions(-)
diff --git a/core/src/org/labkey/core/view/template/bootstrap/header.jsp b/core/src/org/labkey/core/view/template/bootstrap/header.jsp
index 7ac0abc2352..c43d52c28f5 100644
--- a/core/src/org/labkey/core/view/template/bootstrap/header.jsp
+++ b/core/src/org/labkey/core/view/template/bootstrap/header.jsp
@@ -84,7 +84,6 @@
LookAndFeelProperties laf = LookAndFeelProperties.getInstance(c);
ModuleLoader moduleLoader = ModuleLoader.getInstance();
boolean isStartupComplete = moduleLoader.isStartupComplete();
- boolean showSearch = isStartupComplete && PageFlowUtil.urlProviderOptional(SearchUrls.class) != null && SearchService.get().isSearchIconVisible();
HtmlView headerHtml = new HeaderProperties(getContainer()).getView();
String siteShortName = (laf.getShortName() != null && !laf.getShortName().isEmpty()) ? laf.getShortName() : null;
@@ -140,7 +139,12 @@
%>
<%
- if (showSearch && pageConfig.shouldIncludeSearch())
+ boolean showSearch =
+ isStartupComplete &&
+ PageFlowUtil.urlProviderOptional(SearchUrls.class) != null &&
+ SearchService.get().isSearchIconVisible() &&
+ pageConfig.shouldIncludeSearch();
+ if (showSearch)
{
%>
-
From f4dedf0c32d4168088e87d94b3c2f3af0c0e6531 Mon Sep 17 00:00:00 2001
From: Adam Rauch
Date: Thu, 6 Aug 2026 16:17:07 -0700
Subject: [PATCH 6/6] Hide/show search icon test
---
.../test/tests/search/HideSearchIconTest.java | 48 +++++++++++++++++++
1 file changed, 48 insertions(+)
create mode 100644 search/test/src/org/labkey/test/tests/search/HideSearchIconTest.java
diff --git a/search/test/src/org/labkey/test/tests/search/HideSearchIconTest.java b/search/test/src/org/labkey/test/tests/search/HideSearchIconTest.java
new file mode 100644
index 00000000000..2d3a4a6cfab
--- /dev/null
+++ b/search/test/src/org/labkey/test/tests/search/HideSearchIconTest.java
@@ -0,0 +1,48 @@
+package org.labkey.test.tests.search;
+
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.labkey.test.BaseWebDriverTest;
+import org.labkey.test.categories.Search;
+import org.labkey.test.components.html.SiteNavBar;
+import org.labkey.test.util.search.SearchAdminAPIHelper;
+
+import java.util.List;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+@Category({Search.class})
+@BaseWebDriverTest.ClassTimeout(minutes = 1)
+public class HideSearchIconTest extends BaseWebDriverTest
+{
+ @Override
+ protected String getProjectName()
+ {
+ return null;
+ }
+
+ @Override
+ public List getAssociatedModules()
+ {
+ return List.of("Search");
+ }
+
+ @Test
+ public void testHideSearchIcon()
+ {
+ goToHome();
+ assertTrue(new SiteNavBar(getDriver()).isSearchIconPresent());
+
+ // Hide icon, refresh page, and verify icon is gone
+ SearchAdminAPIHelper.hideSearchIcon(getDriver());
+ goToHome();
+ assertFalse(new SiteNavBar(getDriver()).isSearchIconPresent());
+
+ // Show icon, refresh page, and verify icon is back
+ SearchAdminAPIHelper.showSearchIcon(getDriver());
+ goToHome();
+ assertTrue( new SiteNavBar(getDriver()).isSearchIconPresent());
+ }
+}
+