Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions api/src/org/labkey/api/exp/OntologyManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -1332,6 +1332,7 @@ public static void deleteAllObjects(Container c, User user) throws ValidationExc
String deleteObjPropSql = "DELETE FROM " + getTinfoObjectProperty() + " WHERE ObjectId IN (SELECT ObjectId FROM " + getTinfoObject() + " WHERE Container = ?)";
executor.execute(deleteObjPropSql, c);
String deleteObjSql = "DELETE FROM " + getTinfoObject() + " WHERE Container = ?";
_log.info("Deleting from exp.object in container {}", c);
executor.execute(deleteObjSql, c);

// delete property validator references on property descriptors
Expand Down
4 changes: 3 additions & 1 deletion api/src/org/labkey/api/exp/api/ExperimentService.java
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@
import org.labkey.api.exp.query.ExpRunTable;
import org.labkey.api.exp.query.ExpSampleTypeTable;
import org.labkey.api.exp.query.ExpSchema;
import org.labkey.api.exp.query.ExpUnreferencedSampleFilesTable;
import org.labkey.api.exp.query.SampleStatusTable;
import org.labkey.api.gwt.client.AuditBehaviorType;
import org.labkey.api.gwt.client.model.GWTDomain;
Expand All @@ -81,7 +82,6 @@
import org.labkey.api.util.Pair;
import org.labkey.api.util.StringUtilsLabKey;
import org.labkey.api.view.HttpView;
import org.labkey.api.view.NotFoundException;
import org.labkey.api.view.ViewBackgroundInfo;
import org.labkey.api.view.ViewContext;
import org.labkey.vfs.FileLike;
Expand Down Expand Up @@ -670,6 +670,8 @@ static void validateParentAlias(Map<String, String> aliasMap, Set<String> reserv

SampleStatusTable createSampleStatusTable(ExpSchema expSchema, ContainerFilter cf);

ExpUnreferencedSampleFilesTable createUnreferencedSampleFilesTable(ExpSchema expSchema, ContainerFilter cf);

FilteredTable<ExpSchema> createFieldsTable(ExpSchema expSchema, ContainerFilter cf);

FilteredTable<ExpSchema> createPhiFieldsTable(ExpSchema expSchema, ContainerFilter cf);
Expand Down
1 change: 1 addition & 0 deletions api/src/org/labkey/api/exp/query/ExpDataTable.java
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ enum Column
SourceProtocolApplication,
SourceApplicationInput,
DataFileUrl,
ReferenceCount,
Run,
RunApplication,
RunApplicationOutput,
Expand Down
1 change: 1 addition & 0 deletions api/src/org/labkey/api/exp/query/ExpMaterialTable.java
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ enum Column
Inputs,
IsAliquot,
IsPlated,
LastIndexed,
LSID,
MaterialExpDate,
MaterialSourceId,
Expand Down
16 changes: 16 additions & 0 deletions api/src/org/labkey/api/exp/query/ExpSchema.java
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
import org.labkey.api.security.User;
import org.labkey.api.security.permissions.InsertPermission;
import org.labkey.api.security.permissions.ReadPermission;
import org.labkey.api.settings.AppProps;
import org.labkey.api.util.StringExpression;
import org.labkey.api.view.ActionURL;
import org.labkey.api.view.ViewContext;
Expand All @@ -70,6 +71,7 @@ public class ExpSchema extends AbstractExpSchema
public static final String SAMPLE_STATE_TYPE_TABLE = "SampleStateType";
public static final String SAMPLE_TYPE_CATEGORY_TABLE = "SampleTypeCategoryType";
public static final String MEASUREMENT_UNITS_TABLE = "MeasurementUnits";
public static final String SAMPLE_FILES_TABLE = "UnreferencedSampleFiles";

public static final SchemaKey SCHEMA_EXP = SchemaKey.fromParts(ExpSchema.SCHEMA_NAME);
public static final SchemaKey SCHEMA_EXP_DATA = SchemaKey.fromString(SCHEMA_EXP, ExpSchema.NestedSchemas.data.name());
Expand Down Expand Up @@ -220,6 +222,20 @@ public TableInfo createTable(ExpSchema expSchema, String queryName, ContainerFil
return expSchema.setupTable(result);
}
},
UnreferencedSampleFiles
{
@Override
public TableInfo createTable(ExpSchema expSchema, String queryName, ContainerFilter cf)
{
return ExperimentService.get().createUnreferencedSampleFilesTable(expSchema, cf);
}

@Override
public boolean includeTable()
{
return AppProps.getInstance().isOptionalFeatureEnabled(SAMPLE_FILES_TABLE);
}
},
SampleStatus
{
@Override
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package org.labkey.api.exp.query;

import org.labkey.api.data.ContainerFilterable;
import org.labkey.api.data.TableInfo;

public interface ExpUnreferencedSampleFilesTable extends ContainerFilterable, TableInfo
{
}
10 changes: 10 additions & 0 deletions api/src/org/labkey/api/files/FileContentService.java
Original file line number Diff line number Diff line change
Expand Up @@ -325,6 +325,8 @@ default void fireFileDeletedEvent(@NotNull Path deleted, @Nullable User user, @N
*/
SQLFragment listFilesQuery(@NotNull User currentUser);

SQLFragment listSampleFilesQuery(@NotNull User currentUser);

void setWebfilesEnabled(boolean enabled, User user);

/**
Expand All @@ -345,6 +347,14 @@ enum PathType { full, serverRelative, folderRelative }
*/
void ensureFileData(@NotNull ExpDataTable table);

/**
* Fix the container column in the exp.data table for files that were moved as part of a sample move operation
* but did not have their containers updated
* @param admin The user doing the repair
* @return Number of duplicate rows removed from exp.data table
*/
int fixContainerForExpDataFiles(User admin);

/**
* Allows a module to register a directory pattern to be checked in the files webpart in order to zip the matching directory before uploading.
* @param directoryPattern DirectoryPattern
Expand Down
5 changes: 5 additions & 0 deletions api/src/org/labkey/api/files/FileListener.java
Original file line number Diff line number Diff line change
Expand Up @@ -94,4 +94,9 @@ default void fileDeleted(@NotNull Path deleted, @Nullable User user, @Nullable C
* </ul>
*/
SQLFragment listFilesQuery();

@Nullable default SQLFragment listSampleFilesQuery()
{
return null;
}
}
26 changes: 19 additions & 7 deletions api/src/org/labkey/api/files/TableUpdaterFileListener.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@
package org.labkey.api.files;

import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
Expand All @@ -34,6 +33,7 @@
import org.labkey.api.data.dialect.SqlDialect;
import org.labkey.api.security.User;
import org.labkey.api.util.FileUtil;
import org.labkey.api.util.logging.LogHelper;

import java.io.File;
import java.nio.file.Files;
Expand All @@ -45,13 +45,13 @@

/**
* FileListener implementation that can update tables that store file paths in various flavors (URI, standard OS
* paths, etc).
* paths, etc.).
* User: jeckels
* Date: 11/7/12
*/
public class TableUpdaterFileListener implements FileListener
{
private static final Logger LOG = LogManager.getLogger(TableUpdaterFileListener.class);
protected static final Logger LOG = LogHelper.getLogger(TableUpdaterFileListener.class, "File listener activity");

public static final String TABLE_ALIAS = "x";

Expand Down Expand Up @@ -273,7 +273,7 @@ public int fileMoved(@NotNull Path src, @NotNull Path dest, @Nullable User user,
singleEntrySQL.append(")");

int rows = schema.getScope().executeWithRetry(tx -> new SqlExecutor(schema).execute(singleEntrySQL));
LOG.info("Updated " + rows + " row in " + _table + " for move from " + src + " to " + dest);
LOG.info("Updated {} row in {} for move from {} to {}", rows, _table, src, dest);

// Handle updating child paths, unless we know that the entry is a file. If it's not (either it's a
// directory or it doesn't exist), then try to fix up child records
Expand Down Expand Up @@ -305,7 +305,7 @@ public int fileMoved(@NotNull Path src, @NotNull Path dest, @Nullable User user,
childPathsSQL.append(whereClause);
childRowsUpdated += new SqlExecutor(schema).execute(childPathsSQL);

LOG.info("Updated " + childRowsUpdated + " child paths in " + _table + " rows for move from " + src + " to " + dest);
LOG.info("Updated {} child paths in {} rows for move from {} to {}", childRowsUpdated, _table, src, dest);
return childRowsUpdated;
}
return 0;
Expand Down Expand Up @@ -353,10 +353,10 @@ public Collection<File> listFiles(@Nullable Container container)
@Override
public SQLFragment listFilesQuery()
{
return listFilesQuery(false, null);
return listFilesQuery(false, null, false);
}

public SQLFragment listFilesQuery(boolean skipCreatedModified, String filePath)
public SQLFragment listFilesQuery(boolean skipCreatedModified, CharSequence filePath, boolean extractName)
{
SQLFragment selectFrag = new SQLFragment();
selectFrag.append("SELECT\n");
Expand Down Expand Up @@ -395,6 +395,16 @@ else if (_table.getColumn("Folder") != null)

selectFrag.append(" ").appendIdentifier(_pathColumn.getSelectIdentifier()).append(" AS FilePath,\n");

if (extractName)
{
SqlDialect dialect = _table.getSchema().getSqlDialect();
SQLFragment fileNameFrag = new SQLFragment();
fileNameFrag.append("regexp_replace(").appendIdentifier(_pathColumn.getSelectIdentifier()).append(", ");
fileNameFrag.append(dialect.getStringHandler().quoteStringLiteral(".*/")).append(", ");
fileNameFrag.append(dialect.getStringHandler().quoteStringLiteral("")).append(")");
selectFrag.append(" ").append(fileNameFrag).append(" AS FilePathShort,\n");
}

if (_keyColumn != null)
selectFrag.append(" ").appendIdentifier(_keyColumn.getSelectIdentifier()).append(" AS SourceKey,\n");
else
Expand All @@ -408,6 +418,8 @@ else if (_table.getColumn("Folder") != null)

if (StringUtils.isEmpty(filePath))
selectFrag.append(" IS NOT NULL\n");
else if (filePath instanceof SQLFragment)
selectFrag.append(" = ").append(filePath).append("\n");
else
selectFrag.append(" = ").appendStringLiteral(filePath, _table.getSchema().getSqlDialect()).append("\n");

Expand Down
97 changes: 68 additions & 29 deletions api/webapp/clientapi/dom/DataRegion.js
Original file line number Diff line number Diff line change
Expand Up @@ -493,6 +493,7 @@ if (!LABKEY.DataRegions) {
* Non-configurable Options
*/
this.selectionModified = false;
this.selectionLoading = false;

if (this.panelConfigurations === undefined) {
this.panelConfigurations = {};
Expand Down Expand Up @@ -1171,19 +1172,11 @@ if (!LABKEY.DataRegions) {
config.selectionKey = this.selectionKey;
config.scope = config.scope || me;

config = _chainSelectionCountCallback(this, config);
// set loading flag for DataRegion, will be cleared in callback
this.selectionLoading = true;
_updateSelectedCountMessage(this);

var failure = LABKEY.Utils.getOnFailure(config);
if ($.isFunction(failure)) {
config.failure = failure;
}
else {
config.failure = function(error) {
let msg = 'Error setting selection';
if (error && error.exception) msg += ': ' + error.exception;
me.addMessage(msg, 'selection');
};
}
config = _chainSelectionCountCallback(this, config);

if (config.selectionKey) {
LABKEY.DataRegion.setSelected(config);
Expand Down Expand Up @@ -3058,12 +3051,39 @@ if (!LABKEY.DataRegions) {

var _chainSelectionCountCallback = function(region, config) {

var success = LABKEY.Utils.getOnSuccess(config);
const failure = LABKEY.Utils.getOnFailure(config);
config.failure = function(error) {
region.selectionLoading = false;

let msg = 'Error setting selection';
if (error && error.exception) msg += ': ' + error.exception;
config.scope.addMessage(msg, 'selection');

if ($.isFunction(failure)) {
failure.call(config.scope, error);
}
}

// On success, update the current selectedCount on this DataRegion and fire the 'selectchange' event
config.success = function(data) {
const success = LABKEY.Utils.getOnSuccess(config);
config.success = function(data, response) {

// Workaround for GitHub Issue 778 where the response payload is JSON but the response has been
// configured by the server as non-JSON.
if (!data && response?.responseText) {
try {
data = JSON.parse(response.responseText);
} catch (e) {
const msg = 'failed to parse response';
console.error(msg, e, response);
config.failure.call(config.scope, { exception: msg });
return;
}
}

region.removeMessage('selection');
region.selectionModified = true;
region.selectionLoading = false;
region.selectedCount = data.count;
_onSelectionChange(region);

Expand Down Expand Up @@ -3363,7 +3383,10 @@ if (!LABKEY.DataRegions) {
var _buttonSelectionBind = function(region, cls, fn) {
var partEl = region.msgbox.getParent().find('div[data-msgpart="selection"]');
partEl.find('.labkey-button' + cls).off('click').on('click', $.proxy(function() {
fn.call(this);
// if one of the buttons is clicked while another action is loading selections, skip the click (the button should also be disabled)
if (!region.selectionLoading) {
fn.call(this);
}
}, region));
};

Expand Down Expand Up @@ -3592,18 +3615,20 @@ if (!LABKEY.DataRegions) {

var _showSelectMessage = function(region, msg) {
if (region.showRecordSelectors) {
const cls = 'labkey-button ' + (region.selectionLoading ? 'disabled ' : '');

if (_isShowSelectAll(region)) {
msg += "&nbsp;<span class='labkey-button select-all'>" + _getSelectAllText(region) + "</span>";
msg += "&nbsp;<span class='" + cls + " select-all'>" + _getSelectAllText(region) + "</span>";
}

msg += "&nbsp;" + "<span class='labkey-button select-none'>Select None</span>";
msg += "&nbsp;" + "<span class='" + cls + " select-none'>Select None</span>";
var showOpts = [];
if (region.showRows !== 'all' && !_isMaxRowsAllRows(region))
showOpts.push("<span class='labkey-button show-all'>Show All</span>");
showOpts.push("<span class='" + cls + " show-all'>Show All</span>");
if (region.showRows !== 'selected')
showOpts.push("<span class='labkey-button show-selected'>Show Selected</span>");
showOpts.push("<span class='" + cls + " show-selected'>Show Selected</span>");
if (region.showRows !== 'unselected')
showOpts.push("<span class='labkey-button show-unselected'>Show Unselected</span>");
showOpts.push("<span class='" + cls + " show-unselected'>Show Unselected</span>");
msg += "&nbsp;&nbsp;" + showOpts.join(" ");
}

Expand Down Expand Up @@ -4081,6 +4106,21 @@ if (!LABKEY.DataRegions) {
_setParameters(region, params, [OFFSET_PREFIX].concat(skipPrefixes));
};

var _updateSelectedCountMessage = function(region) {
// If not all rows are visible and some rows are selected, show selection message
if (region.totalRows && 0 !== region.selectedCount && !region.complete) {
var msg;
if (region.selectedCount === region.totalRows) {
msg = 'All <span class="labkey-strong">' + region.totalRows.toLocaleString() + '</span> rows selected.';
} else if (region.selectionLoading) {
msg = 'Selected <i class="fa fa-spinner fa-pulse" ></i> of ' + region.totalRows.toLocaleString() + ' rows.';
} else {
msg = 'Selected <span class="labkey-strong">' + region.selectedCount.toLocaleString() + '</span> of ' + region.totalRows.toLocaleString() + ' rows.';
}
_showSelectMessage(region, msg);
}
}

var _updateRequiresSelectionButtons = function(region, selectedCount) {

// update the 'select all on page' checkbox state
Expand All @@ -4100,13 +4140,7 @@ if (!LABKEY.DataRegions) {
}
});

// If not all rows are visible and some rows are selected, show selection message
if (region.totalRows && 0 !== region.selectedCount && !region.complete) {
var msg = (region.selectedCount === region.totalRows) ?
'All <span class="labkey-strong">' + region.totalRows.toLocaleString() + '</span> rows selected.' :
'Selected <span class="labkey-strong">' + region.selectedCount.toLocaleString() + '</span> of ' + region.totalRows.toLocaleString() + ' rows.';
_showSelectMessage(region, msg);
}
_updateSelectedCountMessage(region);

// Issue 10566: for javascript perf on IE stash the requires selection buttons
if (!region._requiresSelectionButtons) {
Expand Down Expand Up @@ -4446,6 +4480,11 @@ if (!LABKEY.DataRegions) {
};

LABKEY.DataRegion.selectAll = function(config) {
// GitHub Issue 778: Track selection loading state on DataRegion so we can disable buttons and prevent another click
var region = config.scope;
region.selectionLoading = true;
_updateSelectedCountMessage(region);

var params = {};
if (!config.url) {
// DataRegion doesn't have selectAllURL so generate url and query parameters manually
Expand Down Expand Up @@ -4485,8 +4524,8 @@ if (!LABKEY.DataRegions) {
url: config.url,
method: 'POST',
params: params,
success: LABKEY.Utils.getCallbackWrapper(LABKEY.Utils.getOnSuccess(config), config.scope),
failure: LABKEY.Utils.getCallbackWrapper(LABKEY.Utils.getOnFailure(config), config.scope, true)
success: LABKEY.Utils.getCallbackWrapper(LABKEY.Utils.getOnSuccess(config), region),
failure: LABKEY.Utils.getCallbackWrapper(LABKEY.Utils.getOnFailure(config), region, true)
});
};

Expand Down
Loading