diff --git a/api/src/org/labkey/api/audit/query/DefaultAuditTypeTable.java b/api/src/org/labkey/api/audit/query/DefaultAuditTypeTable.java index 8391050a671..fcecf0e304c 100644 --- a/api/src/org/labkey/api/audit/query/DefaultAuditTypeTable.java +++ b/api/src/org/labkey/api/audit/query/DefaultAuditTypeTable.java @@ -62,7 +62,7 @@ public class DefaultAuditTypeTable extends FilteredTable @Override protected ContainerFilter getDefaultContainerFilter() { - return ContainerFilter.Type.CurrentWithUser.create(_userSchema); + return ContainerFilter.Type.Current.create(_userSchema); } public DefaultAuditTypeTable(AuditTypeProvider provider, TableInfo storage, UserSchema schema, ContainerFilter cf, List defaultVisibleColumns) diff --git a/api/src/org/labkey/api/data/ContainerFilter.java b/api/src/org/labkey/api/data/ContainerFilter.java index 342c698e99e..d186bc9b5f2 100644 --- a/api/src/org/labkey/api/data/ContainerFilter.java +++ b/api/src/org/labkey/api/data/ContainerFilter.java @@ -256,7 +256,7 @@ protected SQLFragment _getSQLFragment(DbSchema schema, Container container, SQLF List containers = ids.stream() .map(ContainerManager::getForId) .filter(Objects::nonNull) - .collect(Collectors.toUnmodifiableList()); + .toList(); boolean hasNoSpecialChildren = includedChildTypes.isEmpty() || containers.stream().noneMatch(c -> c.hasChildrenOfAnyType(finalIncludedChildTypes)); @@ -355,14 +355,6 @@ public interface Factory public enum Type implements Factory { Current("Current folder") - { - @Override - public ContainerFilter create(Container c, User user) - { - return new CurrentContainerFilter(c); - } - }, - CurrentWithUser("Current folder with permissions applied to user") { @Override public ContainerFilter create(Container c, User user) @@ -494,7 +486,7 @@ public ContainerFilter create(Container container, User user) private final String _description; - private Type(String description) + Type(String description) { _description = description; } @@ -514,17 +506,16 @@ public ContainerFilter create(ContainerUser cu) } } - // short for ContainerFilter.Type.Current.create(container, null) + // Does not validate permissions! public static ContainerFilter current(Container c) { return new CurrentContainerFilter(c); } - public static class CurrentContainerFilter extends ContainerFilter + private static class CurrentContainerFilter extends ContainerFilter { CurrentContainerFilter(Container c) { - // CurrentContainerFilter does not validate permission super(c,null); Objects.requireNonNull(c); } @@ -563,6 +554,10 @@ public static class ContainerFilterWithPermission extends ContainerFilter public ContainerFilterWithPermission(Container c, User user) { super(c, user); + // TODO: InternalNoContainerFilter should extend ContainerFilter instead of ContainerFilterWithPermission, + // which would allow a more strict check below (c != null && user != null). Also, once verified on + // TeamCity, throw an exception here instead of asserting. + assert c == null || user != null : "User is required for permissions check if container is provided!"; } @Override @@ -588,7 +583,7 @@ public SQLFragment getSQLFragment(DbSchema schema, SQLFragment containerColumnSQ return getSQLFragment(schema, _container, containerColumnSQL, ids, allowNulls, getIncludedChildTypes()); } - /** return null means return all rows (1=1), empty collection means return no rows (1=0) */ + /** return null means return all rows (1=1), empty collection means return no rows (1=0) */ @Nullable public Collection generateIds(Container currentContainer, Class permission, Set roles) { @@ -619,7 +614,7 @@ public final Collection getIds() @Override public Type getType() { - return Type.CurrentWithUser; + return Type.Current; } } @@ -1029,7 +1024,7 @@ public Type getType() public static class StudyAndSourceStudy extends ContainerFilterWithPermission { - private boolean _skipPermissionChecks; + private final boolean _skipPermissionChecks; public StudyAndSourceStudy(Container c, User user, boolean skipPermissionChecks) { diff --git a/api/src/org/labkey/api/data/DataRegion.java b/api/src/org/labkey/api/data/DataRegion.java index 0928165bdea..b656b62c251 100644 --- a/api/src/org/labkey/api/data/DataRegion.java +++ b/api/src/org/labkey/api/data/DataRegion.java @@ -2341,9 +2341,9 @@ private void renderForm(RenderContext ctx, Writer out) throws IOException { out.write(""); diff --git a/api/src/org/labkey/api/search/SearchService.java b/api/src/org/labkey/api/search/SearchService.java index 1fb2ed32c43..f897b8b5ae1 100644 --- a/api/src/org/labkey/api/search/SearchService.java +++ b/api/src/org/labkey/api/search/SearchService.java @@ -66,6 +66,7 @@ import java.util.Objects; import java.util.Set; import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; import java.util.function.Function; public interface SearchService extends SearchMXBean @@ -113,6 +114,12 @@ static void setInstance(SearchService impl) */ void reindexContainerFiles(Container c); + /** + * Puts work in the indexer queue at the specified priority and waits up to the timeout for it to complete + * @return true if the task in the queue completed before the timeout + */ + boolean drainQueue(PRIORITY priority, long timeout, TimeUnit unit) throws InterruptedException; + enum PRIORITY { commit, diff --git a/api/webapp/extPatches/ext3-patches.js b/api/webapp/extPatches/ext3-patches.js index 6691bd08d73..25702ae63fa 100644 --- a/api/webapp/extPatches/ext3-patches.js +++ b/api/webapp/extPatches/ext3-patches.js @@ -377,4 +377,6 @@ Ext.override(Ext.tree.TreeNodeUI, { this.anchor = cs[index]; this.textNode = cs[index].firstChild; } -}); \ No newline at end of file +}); + +Ext.labkeyPatches = true; // Allow short-circuiting in 'LABKEY.requiresExt3' diff --git a/api/webapp/extPatches/ext4-patches.js b/api/webapp/extPatches/ext4-patches.js index 12fe6fb21f5..34091fa0620 100644 --- a/api/webapp/extPatches/ext4-patches.js +++ b/api/webapp/extPatches/ext4-patches.js @@ -1149,3 +1149,4 @@ Ext4.override(Ext4.form.FieldSet, { } }); +Ext4.labkeyPatches = true; // Allow short-circuiting in 'LABKEY.requiresExt4Sandbox' diff --git a/core/src/org/labkey/core/admin/createFolder.jsp b/core/src/org/labkey/core/admin/createFolder.jsp index c4f47c0729d..1bcfb4480c2 100644 --- a/core/src/org/labkey/core/admin/createFolder.jsp +++ b/core/src/org/labkey/core/admin/createFolder.jsp @@ -622,7 +622,7 @@ const getTemplateFolders = function(data) { // add the container itself to the templateFolder object if it is not the root and the user has admin perm to it // and if it is not a workbook or container tab folder - if (data.path !== "/" && LABKEY.Security.hasEffectivePermission(data.effectivePermissions, LABKEY.Security.effectivePermissions.admin) + if (data.path !== "/" && data.effectivePermissions && LABKEY.Security.hasEffectivePermission(data.effectivePermissions, LABKEY.Security.effectivePermissions.admin) && !data.isWorkbook && !data.isContainerTab) { templateFolders.push([data.id, data.path]); diff --git a/core/src/org/labkey/core/search/NoopSearchService.java b/core/src/org/labkey/core/search/NoopSearchService.java index 0ac6ca42e4d..52b095c92f5 100644 --- a/core/src/org/labkey/core/search/NoopSearchService.java +++ b/core/src/org/labkey/core/search/NoopSearchService.java @@ -197,6 +197,12 @@ public void reindexContainerFiles(Container c) { } + @Override + public boolean drainQueue(PRIORITY priority, long timeout, TimeUnit unit) + { + return true; + } + @Override public void addPathToCrawl(Path path, Date d) { diff --git a/core/webapp/labkey.js b/core/webapp/labkey.js index 7095f9b2465..add12874a88 100644 --- a/core/webapp/labkey.js +++ b/core/webapp/labkey.js @@ -474,7 +474,14 @@ if (typeof LABKEY == "undefined") checkCallback('requiresExt3', callback); - requiresLib('Ext3', callback, scope); + if (window.Ext && window.Ext.labkeyPatches) + { + handle(callback, scope); + } + else + { + requiresLib('Ext3', callback, scope); + } }; var requiresExt3ClientAPI = function(callback, scope) @@ -522,7 +529,14 @@ if (typeof LABKEY == "undefined") checkCallback('requiresExt4Sandbox', callback); - requiresLib('Ext4', callback, scope); + if (window.Ext4 && window.Ext4.labkeyPatches) + { + handle(callback, scope); + } + else + { + requiresLib('Ext4', callback, scope); + } }; var requiresLib = function(lib, callback, scope) diff --git a/core/webapp/vis/src/plot.js b/core/webapp/vis/src/plot.js index 4ecc8f80afe..1a67e46ffcf 100644 --- a/core/webapp/vis/src/plot.js +++ b/core/webapp/vis/src/plot.js @@ -2142,7 +2142,7 @@ boxPlot.render(); // during the log conversion at getLogScale L810, the below code ensures that the minimum scale of the y-axis // is not less than 0 due to calculations in convertYAxisDomain if (config.properties.yAxisScale === 'log') { - if (config?.properties?.yAxisDomain?.length >= 0 && config.properties.yAxisDomain[0] <= 0) { + if (config?.properties?.yAxisDomain?.length >= 0 && config.properties.yAxisDomain[0] < 0) { config.properties.yAxisDomain[0] = minimumValue - cushion; } } diff --git a/experiment/src/org/labkey/experiment/api/BaseFieldNamesTable.java b/experiment/src/org/labkey/experiment/api/BaseFieldNamesTable.java index 5ad9af23d80..17c9c5997bb 100644 --- a/experiment/src/org/labkey/experiment/api/BaseFieldNamesTable.java +++ b/experiment/src/org/labkey/experiment/api/BaseFieldNamesTable.java @@ -12,9 +12,7 @@ import org.labkey.api.exp.query.ExpSchema; import org.labkey.api.query.FieldKey; import org.labkey.api.query.FilteredTable; -import org.labkey.api.security.UserPrincipal; import org.labkey.api.security.permissions.AdminPermission; -import org.labkey.api.security.permissions.Permission; public abstract class BaseFieldNamesTable extends FilteredTable { @@ -40,11 +38,10 @@ protected MutableColumnInfo addColumn(String name, JdbcType type) return addColumn(new BaseColumnInfo(FieldKey.fromParts(name), this, type)); } - // This is a hack, required because ContainerFilter.Type.Current doesn't fill in the user @Override - public boolean hasPermission(@NotNull UserPrincipal user, @NotNull Class perm) + protected ContainerFilter getDefaultContainerFilter() { - return getContainer().hasPermission(user, AdminPermission.class); + return ContainerFilter.Type.Current.create(getUserSchema()); } @Override diff --git a/experiment/src/org/labkey/experiment/api/ExpDataClassDataTestCase.jsp b/experiment/src/org/labkey/experiment/api/ExpDataClassDataTestCase.jsp index a0f761ee6c6..a9884ad8473 100644 --- a/experiment/src/org/labkey/experiment/api/ExpDataClassDataTestCase.jsp +++ b/experiment/src/org/labkey/experiment/api/ExpDataClassDataTestCase.jsp @@ -100,6 +100,8 @@ <%@ page import="org.labkey.api.data.Sort" %> <%@ page import="org.labkey.api.exp.api.DataClassDomainKindProperties" %> <%@ page import="org.labkey.api.action.ApiUsageException" %> +<%@ page import="org.labkey.api.search.SearchService" %> +<%@ page import="java.util.concurrent.TimeUnit" %> <%@ page extends="org.labkey.api.jsp.JspTest.BVT" %> @@ -118,8 +120,10 @@ public void setUp() } @After -public void tearDown() +public void tearDown() throws InterruptedException { + // Wait for the indexer to finish working on the data we just added to help avoid deadlocks + SearchService.get().drainQueue(SearchService.PRIORITY.crawl, 15, TimeUnit.SECONDS); ContainerManager.deleteAll(c, TestContext.get().getUser()); } diff --git a/experiment/src/org/labkey/experiment/api/ExpSampleTypeTestCase.jsp b/experiment/src/org/labkey/experiment/api/ExpSampleTypeTestCase.jsp index 0047affbe97..1bd7702a047 100644 --- a/experiment/src/org/labkey/experiment/api/ExpSampleTypeTestCase.jsp +++ b/experiment/src/org/labkey/experiment/api/ExpSampleTypeTestCase.jsp @@ -90,6 +90,8 @@ <%@ page import="org.labkey.api.reader.MapLoader" %> <%@ page import="org.labkey.api.action.ApiUsageException" %> <%@ page import="org.labkey.api.exp.api.ExpLineageService" %> +<%@ page import="org.labkey.api.search.SearchService" %> +<%@ page import="java.util.concurrent.TimeUnit" %> <%@ page extends="org.labkey.api.jsp.JspTest.BVT" %> @@ -114,8 +116,10 @@ public void setUp() } @After -public void tearDown() +public void tearDown() throws InterruptedException { + // Wait for the indexer to finish working on the data we just added to help avoid deadlocks + SearchService.get().drainQueue(SearchService.PRIORITY.crawl, 15, TimeUnit.SECONDS); ContainerManager.deleteAll(c, TestContext.get().getUser()); } diff --git a/experiment/src/org/labkey/experiment/api/ExperimentServiceImpl.java b/experiment/src/org/labkey/experiment/api/ExperimentServiceImpl.java index 3234799f728..1381d590ff2 100644 --- a/experiment/src/org/labkey/experiment/api/ExperimentServiceImpl.java +++ b/experiment/src/org/labkey/experiment/api/ExperimentServiceImpl.java @@ -2233,7 +2233,7 @@ public Set getDataInputRoles(Container container, ContainerFilter filter @Override public Set getMaterialInputRoles(Container container, ExpProtocol.ApplicationType... types) { - return getInputRoles(container, ContainerFilter.Type.Current.create(container, null), getTinfoMaterialInput(), types); + return getInputRoles(container, ContainerFilter.current(container), getTinfoMaterialInput(), types); } private Set getInputRoles(Container container, ContainerFilter filter, TableInfo table, ExpProtocol.ApplicationType... types) diff --git a/issues/src/org/labkey/issue/model/IssueManager.java b/issues/src/org/labkey/issue/model/IssueManager.java index 97ea50e81f0..e706f20491c 100644 --- a/issues/src/org/labkey/issue/model/IssueManager.java +++ b/issues/src/org/labkey/issue/model/IssueManager.java @@ -186,6 +186,18 @@ private static IssueObject _getRawIssue(@Nullable Container c, int issueId) @Nullable public static IssueObject getIssue(@Nullable Container c, User user, int issueId) + { + return getIssue(c, user, issueId, true); + } + + @Nullable + public static IssueObject getIssue( + @Nullable Container c, + User user, + int issueId, + boolean throwOnRestrictedFailure // controls whether we throw on a RestrictedIssueProvider failure + // or just return null + ) { IssueObject issue = _getIssue(c, user, issueId); @@ -206,12 +218,17 @@ public static IssueObject getIssue(@Nullable Container c, User user, int issueId if (!provider.hasPermission(user, issue, relatedIssues, errors)) { - StringBuilder msg = new StringBuilder(errors.isEmpty() ? "Access denied" : ""); - for (ValidationError ve : errors) + if (throwOnRestrictedFailure) { - msg.append(ve.getMessage()).append("\n"); + StringBuilder msg = new StringBuilder(errors.isEmpty() ? "Access denied" : ""); + for (ValidationError ve : errors) + { + msg.append(ve.getMessage()).append("\n"); + } + throw new UnauthorizedException(msg.toString()); } - throw new UnauthorizedException(msg.toString()); + else + return null; } } return issue; @@ -280,7 +297,7 @@ public static List getCommentsForRelatedIssues(IssueO for (Integer relatedIssueInt : relatedIssues) { // only add related issues that the user has permission to see - IssueObject relatedIssue = IssueManager.getIssue(null, user, relatedIssueInt); + IssueObject relatedIssue = IssueManager.getIssue(null, user, relatedIssueInt, false); if (relatedIssue != null) { boolean hasReadPermission = ContainerManager.getForId(relatedIssue.getContainerId()).hasPermission(user, ReadPermission.class); @@ -315,7 +332,7 @@ public static boolean hasRelatedIssues(IssueObject issue, User user) { for (Integer relatedIssueInt : issue.getRelatedIssues()) { - IssueObject relatedIssue = IssueManager.getIssue(null, user, relatedIssueInt); + IssueObject relatedIssue = IssueManager.getIssue(null, user, relatedIssueInt, false); if (relatedIssue != null && relatedIssue.getCommentObjects().size() > 0) { boolean hasReadPermission = ContainerManager.getForId(relatedIssue.getContainerId()).hasPermission(user, ReadPermission.class); diff --git a/issues/src/org/labkey/issue/model/IssuePage.java b/issues/src/org/labkey/issue/model/IssuePage.java index 5cefdfc599d..a6cfa5a0a61 100644 --- a/issues/src/org/labkey/issue/model/IssuePage.java +++ b/issues/src/org/labkey/issue/model/IssuePage.java @@ -674,7 +674,7 @@ public HtmlString renderAttachments(ViewContext context, CommentObject comment) public String renderIssueIdLink(Integer id) { - IssueObject issue = IssueManager.getIssue(null, _user, id); + IssueObject issue = IssueManager.getIssue(null, _user, id, false); Container c = issue != null ? issue.lookupContainer() : null; if (c != null && c.hasPermission(_user, ReadPermission.class)) { diff --git a/issues/src/org/labkey/issue/view/RelatedIssuesView.java b/issues/src/org/labkey/issue/view/RelatedIssuesView.java index 9127325e95b..9e25271af52 100644 --- a/issues/src/org/labkey/issue/view/RelatedIssuesView.java +++ b/issues/src/org/labkey/issue/view/RelatedIssuesView.java @@ -37,6 +37,7 @@ import org.labkey.api.view.ViewContext; import org.labkey.issue.model.IssueListDef; import org.labkey.issue.model.IssueManager; +import org.labkey.issue.model.IssueObject; import org.labkey.issue.query.IssuesQuerySchema; import org.springframework.validation.BindException; @@ -67,6 +68,7 @@ public RelatedIssuesView(@NotNull ViewContext context, @NotNull Set rel Integer issueId = (Integer)m.get("issueId"); String containerId = (String)m.get("container"); Container c = ContainerManager.getForId(containerId); + if (c == null || !c.hasPermission(getViewContext().getUser(), ReadPermission.class)) return; @@ -74,6 +76,10 @@ public RelatedIssuesView(@NotNull ViewContext context, @NotNull Set rel if (d == null) return; + IssueObject issue = IssueManager.getIssue(null, context.getUser(), issueId, false); + if (issue == null) + return; + // If the user doesn't have ReadPermission to the domain container, we won't be able to create a query // table in that container. In this case, just use the issue's container. As a consequence, any other // from the same domain definition issueListDef that live in different containers will appear in separate grids. diff --git a/search/src/org/labkey/search/SearchController.java b/search/src/org/labkey/search/SearchController.java index cd6cdf13b76..f7ce30e173e 100644 --- a/search/src/org/labkey/search/SearchController.java +++ b/search/src/org/labkey/search/SearchController.java @@ -861,22 +861,7 @@ public class WaitForIndexerAction extends ExportAction public void export(PriorityForm form, HttpServletResponse response, BindException errors) throws Exception { SearchService ss = SearchService.get(); - final CountDownLatch latch = new CountDownLatch(1); - - SearchService.IndexTask task = ss.createTask("WaitForIndexer", new SearchService.TaskListener() - { - @Override public void success() - { - latch.countDown(); - } - @Override public void indexError(Resource r, Throwable t) { } - }); - task.addNoop(form.getPriority()); - task.setReady(); - - boolean success = latch.await(5, TimeUnit.MINUTES); - if (ss instanceof LuceneSearchServiceImpl) - ((LuceneSearchServiceImpl)ss).refreshNow(); + boolean success = ss.drainQueue(form.getPriority(), 5, TimeUnit.MINUTES); // Return an error if we time out if (!success) diff --git a/search/src/org/labkey/search/model/AbstractSearchService.java b/search/src/org/labkey/search/model/AbstractSearchService.java index 415d4c9d82c..99c999537c8 100644 --- a/search/src/org/labkey/search/model/AbstractSearchService.java +++ b/search/src/org/labkey/search/model/AbstractSearchService.java @@ -34,6 +34,7 @@ import org.labkey.api.data.SQLFragment; import org.labkey.api.data.SqlExecutor; import org.labkey.api.data.dialect.SqlDialect; +import org.labkey.api.resource.Resource; import org.labkey.api.search.SearchResultTemplate; import org.labkey.api.search.SearchService; import org.labkey.api.security.User; @@ -74,6 +75,7 @@ import java.util.Map; import java.util.Set; import java.util.concurrent.CopyOnWriteArrayList; +import java.util.concurrent.CountDownLatch; import java.util.concurrent.PriorityBlockingQueue; import java.util.concurrent.TimeUnit; @@ -599,6 +601,26 @@ public boolean _eq(URLHelper a, URLHelper b) return URLHelper.queryEqual(a,b); } + @Override + public boolean drainQueue(PRIORITY priority, long timeout, TimeUnit unit) throws InterruptedException + { + final CountDownLatch latch = new CountDownLatch(1); + SearchService.IndexTask task = createTask("WaitForIndexer", new SearchService.TaskListener() + { + @Override public void success() + { + latch.countDown(); + } + @Override public void indexError(Resource r, Throwable t) { } + }); + task.addNoop(priority); + task.setReady(); + + boolean success = latch.await(timeout, unit); + refreshNow(); + return success; + } + @Override public void notFound(URLHelper in) { @@ -725,7 +747,7 @@ public Map> getCustomSearchJsonMap(User user, @NotNu { Pair resourceResolverKeyIdentifier = getResourceResolverKeyIdentifier(resourceIdentifier); if (resourceResolverKeyIdentifier == null) - continue;; + continue; resolverIdentifiers .computeIfAbsent(resourceResolverKeyIdentifier.first, (k) -> new HashMap<>()) .put(resourceResolverKeyIdentifier.second, resourceIdentifier); diff --git a/search/src/org/labkey/search/model/LuceneSearchServiceImpl.java b/search/src/org/labkey/search/model/LuceneSearchServiceImpl.java index 2dfa6c399d7..6ccbdbe58be 100644 --- a/search/src/org/labkey/search/model/LuceneSearchServiceImpl.java +++ b/search/src/org/labkey/search/model/LuceneSearchServiceImpl.java @@ -143,6 +143,7 @@ import java.util.Objects; import java.util.Set; import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicLong; import java.util.function.Predicate; import java.util.regex.Matcher; @@ -1104,7 +1105,6 @@ private void parse(WebdavResource r, FileStream fs, InputStream is, ContentHandl } } - /** * This method is used to indicate to the crawler (or any external process) which files * this indexer will not index. diff --git a/study/src/org/labkey/study/query/DatasetColumnsTable.java b/study/src/org/labkey/study/query/DatasetColumnsTable.java index 42cd9ce7eee..037148bc08c 100644 --- a/study/src/org/labkey/study/query/DatasetColumnsTable.java +++ b/study/src/org/labkey/study/query/DatasetColumnsTable.java @@ -99,7 +99,7 @@ public boolean supportsContainerFilter() @Override protected void applyContainerFilter(ContainerFilter filter) { - assert null == filter || filter.getType() == ContainerFilter.Type.Current || filter.getType() == ContainerFilter.Type.CurrentWithUser; + assert null == filter || filter.getType() == ContainerFilter.Type.Current; } @Override diff --git a/study/src/org/labkey/study/query/StudySnapshotTable.java b/study/src/org/labkey/study/query/StudySnapshotTable.java index f46c33c5cea..43d537986e8 100644 --- a/study/src/org/labkey/study/query/StudySnapshotTable.java +++ b/study/src/org/labkey/study/query/StudySnapshotTable.java @@ -15,7 +15,6 @@ */ package org.labkey.study.query; -import com.fasterxml.jackson.databind.ObjectMapper; import org.jetbrains.annotations.NotNull; import org.labkey.api.compliance.ComplianceService; import org.labkey.api.data.Container; @@ -50,7 +49,7 @@ public class StudySnapshotTable extends FilteredTable @Override protected ContainerFilter getDefaultContainerFilter() { - return ContainerFilter.Type.CurrentWithUser.create(getUserSchema()); + return ContainerFilter.Type.Current.create(getUserSchema()); } public StudySnapshotTable(StudyQuerySchema schema, ContainerFilter cf) @@ -109,7 +108,7 @@ public void renderGridCellContents(RenderContext ctx, Writer out) throws IOExcep Object jsonValue = JsonUtil.DEFAULT_MAPPER.readValue(String.valueOf(value), Object.class); out.write(PageFlowUtil.filter(JsonUtil.DEFAULT_MAPPER.writerWithDefaultPrettyPrinter().writeValueAsString(jsonValue), true, true)); - } + } }); ComplianceService complianceService = ComplianceService.get();