diff --git a/api/src/org/labkey/api/exp/query/ExpMaterialTable.java b/api/src/org/labkey/api/exp/query/ExpMaterialTable.java index 7c2f9fba195..679bee2909f 100644 --- a/api/src/org/labkey/api/exp/query/ExpMaterialTable.java +++ b/api/src/org/labkey/api/exp/query/ExpMaterialTable.java @@ -60,6 +60,7 @@ enum Column SourceProtocolLSID, StoredAmount, Units, + IsPlated, } default void setSupportTableRules(boolean supportTableRules) diff --git a/api/src/org/labkey/api/reports/report/r/view/DownloadOutputView.java b/api/src/org/labkey/api/reports/report/r/view/DownloadOutputView.java index dcad587b5d3..b3c81545643 100644 --- a/api/src/org/labkey/api/reports/report/r/view/DownloadOutputView.java +++ b/api/src/org/labkey/api/reports/report/r/view/DownloadOutputView.java @@ -20,7 +20,7 @@ import org.labkey.api.attachments.AttachmentParent; import org.labkey.api.reports.report.ReportUrls; import org.labkey.api.reports.report.r.ParamReplacement; -import org.labkey.api.util.GUID; +import org.labkey.api.util.ImageUtil; import org.labkey.api.util.PageFlowUtil; import java.io.File; @@ -64,10 +64,12 @@ protected String renderInternalAsString(File file) { File newFile = moveToTemp(file, "RReportPdf"); // file hasn't been saved yet - String key = "temp:" + GUID.makeGUID(); - getViewContext().getRequest().getSession(true).setAttribute(key, newFile); + String key = ImageUtil.setFileInSession(getViewContext().getRequest(), newFile); downloadUrl = PageFlowUtil.urlProvider(ReportUrls.class).urlStreamFile(getViewContext().getContainer()). - addParameters(PageFlowUtil.map("sessionKey", key, "deleteFile", "false", "attachment", "true")).getLocalURIString(); + addParameters(PageFlowUtil.map( + ImageUtil.FILE_SESSION_PARAM, key, + ImageUtil.DELETE_FILE_PARAM, "false", + ImageUtil.ATTACHMENT_PARAM, "true")).getLocalURIString(); } return downloadUrl; } diff --git a/api/src/org/labkey/api/reports/report/r/view/ImageOutput.java b/api/src/org/labkey/api/reports/report/r/view/ImageOutput.java index cdeb9adbf8b..6f5b7c631b0 100644 --- a/api/src/org/labkey/api/reports/report/r/view/ImageOutput.java +++ b/api/src/org/labkey/api/reports/report/r/view/ImageOutput.java @@ -18,16 +18,15 @@ import org.apache.commons.lang3.BooleanUtils; import org.labkey.api.reports.Report; -import org.labkey.api.reports.report.r.RReport; import org.labkey.api.reports.report.ReportDescriptor; import org.labkey.api.reports.report.ReportUrls; import org.labkey.api.reports.report.ScriptOutput; import org.labkey.api.reports.report.ScriptReportDescriptor; import org.labkey.api.reports.report.r.AbstractParamReplacement; import org.labkey.api.reports.report.r.ParamReplacement; +import org.labkey.api.reports.report.r.RReport; import org.labkey.api.thumbnail.Thumbnail; import org.labkey.api.util.FileUtil; -import org.labkey.api.util.GUID; import org.labkey.api.util.ImageUtil; import org.labkey.api.util.PageFlowUtil; import org.labkey.api.view.ActionURL; @@ -133,10 +132,9 @@ protected String renderInternalAsString(File file) if (imgFile != null) { - String key = "temp:" + GUID.makeGUID(); - getViewContext().getRequest().getSession(true).setAttribute(key, imgFile); + String key = ImageUtil.setFileInSession(getViewContext().getRequest(), imgFile); ActionURL url = PageFlowUtil.urlProvider(ReportUrls.class).urlStreamFile(getViewContext().getContainer()); - url.addParameters(PageFlowUtil.map("sessionKey", key, "deleteFile", Boolean.toString(_deleteFile), "cacheFile", "true")); + url.addParameters(PageFlowUtil.map(ImageUtil.FILE_SESSION_PARAM, key, ImageUtil.DELETE_FILE_PARAM, Boolean.toString(_deleteFile), ImageUtil.CACHE_FILE_PARAM, "true")); imgUrl = url.getLocalURIString(); } } diff --git a/api/src/org/labkey/api/security/AuthFilter.java b/api/src/org/labkey/api/security/AuthFilter.java index 093a481522e..d1e23cbc97f 100644 --- a/api/src/org/labkey/api/security/AuthFilter.java +++ b/api/src/org/labkey/api/security/AuthFilter.java @@ -125,43 +125,52 @@ public void doFilter(ServletRequest request, ServletResponse response, FilterCha } } - // No startup failure, so check for SSL redirection - if (!req.getScheme().equalsIgnoreCase("https") && AppProps.getInstance().isSSLRequired()) + if (AppProps.getInstance().isSSLRequired()) { - // We can't redirect posts (we'll lose the post body), so return an error code - if ("post".equalsIgnoreCase(req.getMethod())) + // No startup failure, so check for SSL redirection + if (!req.getScheme().equalsIgnoreCase("https")) { - resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED, "Can't POST to an http URL; POSTs to this server require https"); - return; - } + // We can't redirect posts (we'll lose the post body), so return an error code + if ("post".equalsIgnoreCase(req.getMethod())) + { + resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED, "Can't POST to an http URL; POSTs to this server require https"); + return; + } - StringBuffer originalURL = req.getRequestURL(); - if (!StringUtils.isBlank(req.getQueryString())) - { - originalURL.append("?"); - originalURL.append(req.getQueryString()); - } - URL url = new URL(originalURL.toString()); - int port = AppProps.getInstance().getSSLPort(); + StringBuffer originalURL = req.getRequestURL(); + if (!StringUtils.isBlank(req.getQueryString())) + { + originalURL.append("?"); + originalURL.append(req.getQueryString()); + } + URL url = new URL(originalURL.toString()); + int port = AppProps.getInstance().getSSLPort(); - // Check the SSL configuration if this is the first time doing an SSL redirect. Note: The redirect and check must - // happen before ensureFirstRequestHandled() so AppProps gets initialized with the SSL scheme & port. That means - // this check can't be handled in a FirstRequestListener. - if (!_sslChecked) - { - HttpsUtil.checkSslRedirectConfiguration(req, port); - _sslChecked = true; - } + // Check the SSL configuration if this is the first time doing an SSL redirect. Note: The redirect and check must + // happen before ensureFirstRequestHandled() so AppProps gets initialized with the SSL scheme & port. That means + // this check can't be handled in a FirstRequestListener. + if (!_sslChecked) + { + HttpsUtil.checkSslRedirectConfiguration(req, port); + _sslChecked = true; + } - if (port == 443) + if (port == 443) + { + port = -1; + } + url = new URL("https", url.getHost(), port, url.getFile()); + // Use 301 redirect instead of a 302 to indicate it's a permanent move + resp.setStatus(HttpServletResponse.SC_MOVED_PERMANENTLY); + resp.setHeader("Location", resp.encodeRedirectURL(url.toString())); + return; + } + else if (!AppProps.getInstance().isDevMode()) { - port = -1; + // Issue 51904: Strict-Transport-Security header when HTTPS is required + // Avoid setting when in dev mode to make it easier to toggle HTTPS on and off again for local deployments + resp.setHeader("Strict-Transport-Security", "max-age=31536000"); } - url = new URL("https", url.getHost(), port, url.getFile()); - // Use 301 redirect instead of a 302 to indicate it's a permanent move - resp.setStatus(HttpServletResponse.SC_MOVED_PERMANENTLY); - resp.setHeader("Location", resp.encodeRedirectURL(url.toString())); - return; } // allow CSRFUtil early access to req/resp if it wants to write cookies diff --git a/api/src/org/labkey/api/security/UserManager.java b/api/src/org/labkey/api/security/UserManager.java index e9be0dd6f1e..fd712817e7e 100644 --- a/api/src/org/labkey/api/security/UserManager.java +++ b/api/src/org/labkey/api/security/UserManager.java @@ -1181,10 +1181,16 @@ public static List parseUserListInput(Set theList) { if (null == (name = StringUtils.trimToNull(name))) continue; - User u = null; - try { u = getUser(new ValidEmail(name)); } catch (ValidEmail.InvalidEmailException ignored) {} - if (null == u) - u = getUserByDisplayName(name); + // First try by display name. See issue 40987 + User u = getUserByDisplayName(name); + if (u == null) + { + try + { + u = getUser(new ValidEmail(name)); + } + catch (ValidEmail.InvalidEmailException ignored) {} + } parsed.add(null == u ? name : String.valueOf(u.getUserId())); } return parsed; diff --git a/api/src/org/labkey/api/util/ImageUtil.java b/api/src/org/labkey/api/util/ImageUtil.java index 6f2509a2a96..9adcb186414 100644 --- a/api/src/org/labkey/api/util/ImageUtil.java +++ b/api/src/org/labkey/api/util/ImageUtil.java @@ -15,6 +15,7 @@ */ package org.labkey.api.util; +import jakarta.servlet.http.HttpServletRequest; import net.coobird.thumbnailator.Thumbnails; import org.apache.commons.lang3.BooleanUtils; import org.apache.hc.core5.http.ParseException; @@ -53,6 +54,10 @@ public class ImageUtil { private static Logger LOG = LogManager.getLogger(ImageUtil.class); + public static final String FILE_SESSION_PARAM = "fileCacheKey"; + public static final String DELETE_FILE_PARAM = "deleteFile"; + public static final String CACHE_FILE_PARAM = "cacheFile"; + public static final String ATTACHMENT_PARAM = "attachment"; static { @@ -248,6 +253,35 @@ public void setBaseURL(String url) } } + /** + * Retrieves a file cached in the session + */ + @Nullable + public static File getFileFromSession(HttpServletRequest request, String key) + { + Object o = request.getSession().getAttribute(key); + if (o instanceof File file && file.exists()) + return file; + + return null; + } + + /** + * Adds a file to the request session and returns the generated session attribute key. + */ + @Nullable + public static String setFileInSession(HttpServletRequest request, File file) + { + if (file != null && file.exists()) + { + String key = "temp:" + GUID.makeGUID(); + request.getSession(true).setAttribute(key, file); + + return key; + } + return null; + } + // LabKey user agent is used to resolve image resources (or others if required in the future) using the // same session as the incoming request. Right now this occurs when we are generating a thumbnail for a Knitr // R report @@ -262,7 +296,7 @@ private LabKeyUserAgent(ViewContext context, String baseURL) } @Override - public org.xhtmlrenderer.resource.ImageResource getImageResource(java.lang.String uri) + public ImageResource getImageResource(String uri) { ImageResource ir; String uriResolved = resolveURI(uri); @@ -276,10 +310,11 @@ public org.xhtmlrenderer.resource.ImageResource getImageResource(java.lang.Strin try { URLHelper helper = new URLHelper(uriResolved); - String sessionKey = helper.getParameter("sessionKey"); - String deleteFile = helper.getParameter("deleteFile"); - File file = (File) _context.getRequest().getSession().getAttribute(sessionKey); - if (file != null && file.exists()) + String sessionKey = helper.getParameter(FILE_SESSION_PARAM); + String deleteFile = helper.getParameter(DELETE_FILE_PARAM); + + File file = getFileFromSession(_context.getRequest(), sessionKey); + if (file != null) { try { diff --git a/experiment/src/org/labkey/experiment/api/ExpMaterialTableImpl.java b/experiment/src/org/labkey/experiment/api/ExpMaterialTableImpl.java index b68df37d6c8..2fab97bfcc2 100644 --- a/experiment/src/org/labkey/experiment/api/ExpMaterialTableImpl.java +++ b/experiment/src/org/labkey/experiment/api/ExpMaterialTableImpl.java @@ -770,6 +770,37 @@ public void addQueryFieldKeys(Set keys) if (InventoryService.get() != null && (st == null || !st.isMedia())) defaultCols.addAll(InventoryService.get().addInventoryStatusColumns(st == null ? null : st.getMetricUnit(), this, getContainer(), _userSchema.getUser())); + UserSchema plateUserSchema = QueryService.get().getUserSchema(_userSchema.getUser(), getContainer(), "plate"); + SQLFragment sql; + if (plateUserSchema != null) + { + String rowIdField = ExprColumn.STR_TABLE_ALIAS + "." + Column.RowId.name(); + SQLFragment existsSubquery = new SQLFragment() + .append("SELECT 1 FROM ") + .append(plateUserSchema.getTable("Well"), "well") + .append(" WHERE well.sampleid = ").append(rowIdField); + + sql = new SQLFragment() + .append("CASE WHEN EXISTS (") + .append(existsSubquery) + .append(") THEN 'Plated' ELSE 'Not Plated'") + .append(" END"); + } + else + { + sql = new SQLFragment("SELECT NULL"); + } + var col = new ExprColumn(this, Column.IsPlated.name(), sql, JdbcType.VARCHAR); + col.setDescription("Whether the sample that has been plated, if plating is supported."); + col.setUserEditable(false); + col.setReadOnly(true); + col.setShownInDetailsView(false); + col.setShownInInsertView(false); + col.setShownInUpdateView(false); + if (plateUserSchema != null) + col.setURL(DetailsURL.fromString("plate-isPlated.api?sampleId=${" + Column.RowId.name() + "}")); + addColumn(col); + addVocabularyDomains(); addColumn(Column.Properties); diff --git a/experiment/src/org/labkey/experiment/pipeline/SampleReloadTask.java b/experiment/src/org/labkey/experiment/pipeline/SampleReloadTask.java index 0a9791fe763..6a19f3e780b 100644 --- a/experiment/src/org/labkey/experiment/pipeline/SampleReloadTask.java +++ b/experiment/src/org/labkey/experiment/pipeline/SampleReloadTask.java @@ -118,7 +118,10 @@ else if ("summary".equalsIgnoreCase(auditLevel)) if (sampleType != null) log.info("Sample Type matching the 'name' capture group was resolved : " + sampleName); else - log.info("Sample Type matching the 'name' capture group was not resolved : " + sampleName); + { + log.error("Sample Type matching the 'name' capture group was not resolved : " + sampleName); + return new RecordedActionSet(); + } } else if (params.containsKey(SAMPLE_ID_KEY)) { @@ -127,7 +130,10 @@ else if (params.containsKey(SAMPLE_ID_KEY)) if (sampleType != null) log.info("Sample Type matching the 'id' capture group was resolved : " + sampleType.getName()); else - log.info("Sample Type matching the 'id' capture group was not resolved : " + params.get(SAMPLE_ID_KEY)); + { + log.error("Sample Type matching the 'id' capture group was not resolved : " + params.get(SAMPLE_ID_KEY)); + return new RecordedActionSet(); + } } if (sampleType == null) diff --git a/query/src/org/labkey/query/reports/ReportsController.java b/query/src/org/labkey/query/reports/ReportsController.java index 5d2be4d7bd4..f20aec23e44 100644 --- a/query/src/org/labkey/query/reports/ReportsController.java +++ b/query/src/org/labkey/query/reports/ReportsController.java @@ -132,6 +132,7 @@ import org.labkey.api.util.ExceptionUtil; import org.labkey.api.util.HtmlString; import org.labkey.api.util.HtmlStringBuilder; +import org.labkey.api.util.ImageUtil; import org.labkey.api.util.JsonUtil; import org.labkey.api.util.MimeMap; import org.labkey.api.util.PageFlowUtil; @@ -1523,14 +1524,14 @@ public static class StreamFileAction extends SimpleViewAction @Override public ModelAndView getView(Object o, BindException errors) throws Exception { - String sessionKey = (String) getViewContext().get("sessionKey"); - String deleteFile = (String) getViewContext().get("deleteFile"); - String attachment = (String) getViewContext().get("attachment"); - String cacheFile = (String) getViewContext().get("cacheFile"); + String sessionKey = (String) getViewContext().get(ImageUtil.FILE_SESSION_PARAM); + String deleteFile = (String) getViewContext().get(ImageUtil.DELETE_FILE_PARAM); + String attachment = (String) getViewContext().get(ImageUtil.ATTACHMENT_PARAM); + String cacheFile = (String) getViewContext().get(ImageUtil.CACHE_FILE_PARAM); if (sessionKey != null) { - File file = (File) getViewContext().getRequest().getSession().getAttribute(sessionKey); - if (file != null && file.isFile()) + File file = ImageUtil.getFileFromSession(getViewContext().getRequest(), sessionKey); + if (file != null) { Map responseHeaders = Collections.emptyMap(); if (BooleanUtils.toBoolean(cacheFile)) diff --git a/study/src/org/labkey/study/assay/StudyPublishManager.java b/study/src/org/labkey/study/assay/StudyPublishManager.java index 07fc3c9c017..b35400a5389 100644 --- a/study/src/org/labkey/study/assay/StudyPublishManager.java +++ b/study/src/org/labkey/study/assay/StudyPublishManager.java @@ -136,6 +136,7 @@ import org.labkey.study.query.StudyQuerySchema; import org.springframework.beans.MutablePropertyValues; +import java.io.Closeable; import java.io.IOException; import java.io.OutputStream; import java.math.BigDecimal; @@ -1173,48 +1174,52 @@ public void autoLinkDerivedSamples(ExpSampleType sampleType, List keys, { if (sampleType != null && sampleType.getAutoLinkTargetContainer() != null) { - // attempt to auto link the results - QuerySettings qs = new QuerySettings(new MutablePropertyValues(), QueryView.DATAREGIONNAME_DEFAULT); - qs.setSchemaName(SamplesSchema.SCHEMA_NAME); - qs.setQueryName(sampleType.getName()); - qs.setBaseFilter(new SimpleFilter().addInClause(FieldKey.fromParts("RowId"), keys)); - - Map fieldKeyMap = StudyPublishService.get().getSamplePublishFieldKeys(user, container, sampleType, qs); - UserSchema userSchema = QueryService.get().getUserSchema(user, container, SamplesSchema.SCHEMA_NAME); - QueryView view = new QueryView(userSchema, qs, null); - // Issue 45238 - configure as API style invocation to skip setting up buttons and other items that - // rely on being invoked inside an HTTP request/ViewContext - view.setApiResponseView(true); - DataView dataView = view.createDataView(); - RenderContext ctx = dataView.getRenderContext(); - Map selectColumns = dataView.getDataRegion().getSelectColumns(); - List> rows = new ArrayList<>(); - - try (Results rs = view.getResults()) + // Issue 51454 : QueryView needs a view context to initialize properly. Ensure a mock view context when running in the background + try (EnsureViewContext ignore = new EnsureViewContext(container, user)) { - ctx.setResults(rs); - ResultSetRowMapFactory factory = ResultSetRowMapFactory.create(rs); - while (rs.next()) + // attempt to auto link the results + QuerySettings qs = new QuerySettings(new MutablePropertyValues(), QueryView.DATAREGIONNAME_DEFAULT); + qs.setSchemaName(SamplesSchema.SCHEMA_NAME); + qs.setQueryName(sampleType.getName()); + qs.setBaseFilter(new SimpleFilter().addInClause(FieldKey.fromParts("RowId"), keys)); + + Map fieldKeyMap = StudyPublishService.get().getSamplePublishFieldKeys(user, container, sampleType, qs); + UserSchema userSchema = QueryService.get().getUserSchema(user, container, SamplesSchema.SCHEMA_NAME); + QueryView view = new QueryView(userSchema, qs, null); + // Issue 45238 - configure as API style invocation to skip setting up buttons and other items that + // rely on being invoked inside an HTTP request/ViewContext + view.setApiResponseView(true); + DataView dataView = view.createDataView(); + RenderContext ctx = dataView.getRenderContext(); + Map selectColumns = dataView.getDataRegion().getSelectColumns(); + List> rows = new ArrayList<>(); + + try (Results rs = view.getResults()) { - Map row = new HashMap<>(); - ctx.setRow(factory.getRowMap(rs)); + ctx.setResults(rs); + ResultSetRowMapFactory factory = ResultSetRowMapFactory.create(rs); + while (rs.next()) + { + Map row = new HashMap<>(); + ctx.setRow(factory.getRowMap(rs)); - getColumnValue(fieldKeyMap.get(LinkToStudyKeys.ParticipantId), ctx, selectColumns, row); - getColumnValue(fieldKeyMap.get(LinkToStudyKeys.VisitId), ctx, selectColumns, row); - getColumnValue(fieldKeyMap.get(LinkToStudyKeys.VisitLabel), ctx, selectColumns, row); - getColumnValue(fieldKeyMap.get(LinkToStudyKeys.Date), ctx, selectColumns, row); - getColumnValue(FieldKey.fromParts(StudyPublishService.ROWID_PROPERTY_NAME), ctx, selectColumns, row); + getColumnValue(fieldKeyMap.get(LinkToStudyKeys.ParticipantId), ctx, selectColumns, row); + getColumnValue(fieldKeyMap.get(LinkToStudyKeys.VisitId), ctx, selectColumns, row); + getColumnValue(fieldKeyMap.get(LinkToStudyKeys.VisitLabel), ctx, selectColumns, row); + getColumnValue(fieldKeyMap.get(LinkToStudyKeys.Date), ctx, selectColumns, row); + getColumnValue(FieldKey.fromParts(StudyPublishService.ROWID_PROPERTY_NAME), ctx, selectColumns, row); - rows.add(row); + rows.add(row); + } + } + catch (Exception e) + { + throw new ExperimentException(e); } - } - catch (Exception e) - { - throw new ExperimentException(e); - } - if (!rows.isEmpty()) - autoLinkSamples(sampleType, rows, container, user); + if (!rows.isEmpty()) + autoLinkSamples(sampleType, rows, container, user); + } } } @@ -1254,58 +1259,62 @@ public void autoLinkSamples(ExpSampleType sampleType, List Set validStudies = StudyPublishService.get().getValidPublishTargets(user, InsertPermission.class); if (validStudies.contains(study)) { - LOG.debug(String.format("Resolved target study in container %s for auto-linking with %s from container %s", targetContainerPath, sampleTypeName, containerPath)); - List> dataMaps = new ArrayList<>(); + // Issue 49253 : QueryView needs a view context to initialize properly. Ensure a mock view context when running in the background + try (EnsureViewContext ignore = new EnsureViewContext(container, user)) + { + LOG.debug(String.format("Resolved target study in container %s for auto-linking with %s from container %s", targetContainerPath, sampleTypeName, containerPath)); + List> dataMaps = new ArrayList<>(); - // attempt to match up the subject/timepoint information even if the sample has not been published to - // a study yet, this includes traversing any parent lineage samples for corresponding information - QuerySettings qs = new QuerySettings(new MutablePropertyValues(), QueryView.DATAREGIONNAME_DEFAULT); - qs.setSchemaName(SamplesSchema.SCHEMA_NAME); - qs.setQueryName(sampleType.getName()); + // attempt to match up the subject/timepoint information even if the sample has not been published to + // a study yet, this includes traversing any parent lineage samples for corresponding information + QuerySettings qs = new QuerySettings(new MutablePropertyValues(), QueryView.DATAREGIONNAME_DEFAULT); + qs.setSchemaName(SamplesSchema.SCHEMA_NAME); + qs.setQueryName(sampleType.getName()); - Map publishKeys = StudyPublishService.get().getSamplePublishFieldKeys(user, container, sampleType, qs); - final boolean visitBased = study.getTimepointType().isVisitBased(); - Map translateMap = Collections.emptyMap(); + Map publishKeys = StudyPublishService.get().getSamplePublishFieldKeys(user, container, sampleType, qs); + final boolean visitBased = study.getTimepointType().isVisitBased(); + Map translateMap = Collections.emptyMap(); - if (visitBased && publishKeys.containsKey(LinkToStudyKeys.VisitLabel)) - { - // try visit label if we don't have visit ID - translateMap = StudyService.get().getVisitImportMap(study, true); - } + if (visitBased && publishKeys.containsKey(LinkToStudyKeys.VisitLabel)) + { + // try visit label if we don't have visit ID + translateMap = StudyService.get().getVisitImportMap(study, true); + } - // the schema supports the subject/timepoint fields - if (publishKeys.containsKey(LinkToStudyKeys.ParticipantId)) - { - String timePointPropName = visitBased ? StudyPublishService.SEQUENCENUM_PROPERTY_NAME : StudyPublishService.DATE_PROPERTY_NAME; - for (Map row : results) + // the schema supports the subject/timepoint fields + if (publishKeys.containsKey(LinkToStudyKeys.ParticipantId)) { - Object timePointValue = getTimepointValue(row, publishKeys, visitBased, translateMap); - if (row.containsKey(publishKeys.get(LinkToStudyKeys.ParticipantId)) && timePointValue != null) + String timePointPropName = visitBased ? StudyPublishService.SEQUENCENUM_PROPERTY_NAME : StudyPublishService.DATE_PROPERTY_NAME; + for (Map row : results) { - dataMaps.add(Map.of( - LinkToStudyKeys.ParticipantId.name(), row.get(publishKeys.get(LinkToStudyKeys.ParticipantId)), - timePointPropName, timePointValue, - StudyPublishService.ROWID_PROPERTY_NAME, row.get(FieldKey.fromParts(StudyPublishService.ROWID_PROPERTY_NAME)), - StudyPublishService.SOURCE_LSID_PROPERTY_NAME, sampleType.getLSID() - )); + Object timePointValue = getTimepointValue(row, publishKeys, visitBased, translateMap); + if (row.containsKey(publishKeys.get(LinkToStudyKeys.ParticipantId)) && timePointValue != null) + { + dataMaps.add(Map.of( + LinkToStudyKeys.ParticipantId.name(), row.get(publishKeys.get(LinkToStudyKeys.ParticipantId)), + timePointPropName, timePointValue, + StudyPublishService.ROWID_PROPERTY_NAME, row.get(FieldKey.fromParts(StudyPublishService.ROWID_PROPERTY_NAME)), + StudyPublishService.SOURCE_LSID_PROPERTY_NAME, sampleType.getLSID() + )); + } } } - } - StudyPublishService.get().publishData( - user, - container, - targetContainer, - sampleType.getAutoLinkCategory(), - sampleTypeName, - Pair.of(Dataset.PublishSource.SampleType, sampleType.getRowId()), - dataMaps, - ExpMaterialTable.Column.RowId.toString(), - publishErrors - ); - - if (!publishErrors.isEmpty()) - publishErrors.forEach(LOG::error); + StudyPublishService.get().publishData( + user, + container, + targetContainer, + sampleType.getAutoLinkCategory(), + sampleTypeName, + Pair.of(Dataset.PublishSource.SampleType, sampleType.getRowId()), + dataMaps, + ExpMaterialTable.Column.RowId.toString(), + publishErrors + ); + + if (!publishErrors.isEmpty()) + publishErrors.forEach(LOG::error); + } } else { @@ -1745,19 +1754,7 @@ public Map getSamplePublishFieldKeys(User user, Conta // optionally add columns added through a view, useful for picking up any lineage fields if (qs != null) - { - if (HttpView.hasCurrentView()) - getViewColumns(userSchema, qs, columns); - else - { - // Issue 49253 : the QueryView needs a view context to initialize properly. If we are running in a background job - // push a fake view context onto the stack and remove it when we are done - try (ViewContext.StackResetter ignored = ViewContext.pushMockViewContext(user, container, new ActionURL())) - { - getViewColumns(userSchema, qs, columns); - } - } - } + getViewColumns(userSchema, qs, columns); for (ColumnInfo ci : columns.values()) { @@ -1806,4 +1803,26 @@ private void getViewColumns(UserSchema userSchema, QuerySettings qs, Map @Override public ModelAndView getView(Object o, BindException errors) throws Exception { - String sessionKey = (String) getViewContext().get("sessionKey"); + String sessionKey = (String) getViewContext().get(ImageUtil.FILE_SESSION_PARAM); if (null == sessionKey) { //TODO: Return a GIF that says not found?? return null; } - File file = (File) getViewContext().getRequest().getSession().getAttribute(sessionKey); - if (file.exists()) + File file = ImageUtil.getFileFromSession(getViewContext().getRequest(), sessionKey); + if (file != null) { PageFlowUtil.streamFile(getViewContext().getResponse(), file.toPath(), false); file.delete();