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
4 changes: 3 additions & 1 deletion api/src/org/labkey/api/security/GroupManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,9 @@ private static void appendDotAttribute(StringBuilder sb, boolean prependComma, S
if (prependComma)
sb.append(", ");

sb.append(name).append("=\"").append(value).append("\"");
// Escape backslashes first, then quotes, to produce a valid DOT quoted string
String escaped = value.replace("\\", "\\\\").replace("\"", "\\\"");
sb.append(name).append("=\"").append(escaped).append("\"");
}

public static void exportGroupMembers(Group group, List<Group> memberGroups, List<User> memberUsers, GroupType xmlGroupType)
Expand Down
2 changes: 1 addition & 1 deletion api/src/org/labkey/api/util/MemTrackerListener.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ public interface MemTrackerListener
{
/**
* Called before GC and tallying of held objects. Implementors should purge held objects and (optionally) add
* objects to the passed in set that should be ignored .
* objects to the passed in set that should be ignored.
*/
void beforeReport(Set<Object> set);
}
14 changes: 12 additions & 2 deletions core/src/org/labkey/core/CoreController.java
Original file line number Diff line number Diff line change
Expand Up @@ -487,7 +487,7 @@ else if (form.getSchemaName() != null && form.getQueryName() != null && form.get
{
throw new NotFoundException("The file '" + file.getName() + "' attached to the object '" + identifiable.getName() + "' cannot be found. It may have been deleted.");
}
throw new NotFoundException("File " + file.getPath() + " does not exist on the server file system. It may have been deleted.");
throw new NotFoundException("File " + file.getName() + " does not exist on the server file system. It may have been deleted.");
}

if (file.isDirectory())
Expand Down Expand Up @@ -654,6 +654,7 @@ private static byte[] compressCSS(String s)
catch (StackOverflowError e)
{
// replaceAll() can blow up
_log.error("StackOverflowError compressing CSS");
}
return Compress.compressGzip(c.trim());
}
Expand Down Expand Up @@ -935,6 +936,11 @@ public void validateForm(SimpleApiJsonForm form, Errors errors)
errors.reject(ERROR_MSG, "The container '" + parentIdentifier + "' is not a valid parent folder.");
return;
}

if (!target.hasPermission(getUser(), AdminPermission.class))
{
throw new UnauthorizedException("You must be an administrator for the target container");
}
}

@Override
Expand Down Expand Up @@ -2390,6 +2396,10 @@ public Object execute(Object o, BindException errors)
}
}

/**
* This action doesn't require any permissions, as the call to WarningService.getWarnings()
* only returns warnings appropriate for the user/guest
*/
@RequiresNoPermission
@AllowedDuringUpgrade
public static class DisplayWarningsAction extends MutatingApiAction<Object>
Expand Down Expand Up @@ -2721,7 +2731,7 @@ public void setToFormat(String toFormat)
}

@SuppressWarnings("unused") // Called from JavaScript: discuss.js, wikiEdit.js
@RequiresNoPermission
@RequiresPermission(ReadPermission.class)
public static class TransformWikiAction extends MutatingApiAction<TransformWikiForm>
{
@Override
Expand Down
408 changes: 180 additions & 228 deletions core/src/org/labkey/core/admin/AdminController.java

Large diffs are not rendered by default.

19 changes: 16 additions & 3 deletions core/src/org/labkey/core/admin/memTracker.jsp
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,16 @@
<%@ page import="org.labkey.api.view.JspView" %>
<%@ page import="org.labkey.core.admin.AdminController" %>
<%@ page import="org.labkey.core.admin.AdminController.MemBean" %>
<%@ page import="org.labkey.api.view.template.ClientDependencies" %>
<%@ page import="java.text.DecimalFormat" %>
<%@ page extends="org.labkey.api.jsp.JspBase" %>
<%!
@Override
public void addClientDependencies(ClientDependencies dependencies)
{
dependencies.add("admin/caches.js");
}
%>
<%
JspView<MemBean> me = HttpView.currentView();
MemBean bean = me.getModelBean();
Expand All @@ -45,11 +53,16 @@
%>
<% if (hasAdminPerm) { %>
<p>
<%=link("Clear Caches, GC and Refresh", AdminController.getMemTrackerURL(true, true))%>
<%=link("GC and Refresh", AdminController.getMemTrackerURL(false, true))%>
<%=link("Refresh", AdminController.getMemTrackerURL(false, false))%>
<a href="#" id="clearCachesGc" class="labkey-text-link">Clear Caches, GC and Refresh</a>
<a href="#" id="gcOnly" class="labkey-text-link">GC and Refresh</a>
<a href="#" id="refreshPage" class="labkey-text-link">Refresh</a>
<% if (getUser().hasSiteAdminPermission()) { %> <%=link("Memory Stress Test", new ActionURL(AdminController.MemoryStressTestAction.class, ContainerManager.getRoot()))%> <% } %>
<span id="cacheSpinner" style="display:none; margin-left:8px;">
<span style="display:inline-block; width:14px; height:14px; border:2px solid #ccc; border-top-color:#333; border-radius:50%; animation:lk-spin 0.7s linear infinite; vertical-align:middle;"></span>
</span>
<style>@keyframes lk-spin { to { transform: rotate(360deg); } }</style>
</p>
<div id="cacheError" class="labkey-error" style="display:none;"></div>
<% } %>
<table class="labkey-wp">
<tr class="labkey-wp-header">
Expand Down
81 changes: 81 additions & 0 deletions core/webapp/admin/caches.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
LABKEY.Admin = LABKEY.Admin || {};

LABKEY.Admin.Caches = new function() {
var API_URL = LABKEY.ActionURL.buildURL('admin', 'clearCaches');

function showSpinner() {
var el = document.getElementById('cacheSpinner');
if (el) el.style.display = 'inline';
}

function hideSpinner() {
var el = document.getElementById('cacheSpinner');
if (el) el.style.display = 'none';
}

function showError(msg) {
hideSpinner();
var el = document.getElementById('cacheError');
if (el) {
el.textContent = msg || 'An error occurred. Please try again.';
el.style.display = 'block';
}
}

function hideError() {
var el = document.getElementById('cacheError');
if (el) el.style.display = 'none';
}

function doPost(params) {
hideError();
showSpinner();
LABKEY.Ajax.request({
url: API_URL,
method: 'POST',
params: params,
success: reloadPage,
failure: function(response) {
var msg = 'Request failed.';
try {
var json = JSON.parse(response.responseText);
if (json && json.exception) msg = json.exception;
} catch (e) { /* ignore */ }
showError(msg);
}
});
}

function reloadPage() { window.location.reload(); }

// clearSingle is called from inline onclick handlers (addHandler), so must be defined immediately.
this.clearSingle = function(debugName) {
doPost({ debugName: debugName });
};

// Bind button handlers once the DOM is ready — elements don't exist yet when this script runs.
window.addEventListener('DOMContentLoaded', function() {
function bindIfPresent(id, params) {
var el = document.getElementById(id);
if (el) {
el.addEventListener('click', function(e) {
e.preventDefault();
el.disabled = true;
doPost(params);
});
}
}

bindIfPresent('clearAllCaches', { clearCaches: true });
bindIfPresent('clearCachesGc', { clearCaches: true, gc: 1 });
bindIfPresent('gcOnly', { gc: true });

const refreshEl = document.getElementById('refreshPage');
if (refreshEl) {
refreshEl.addEventListener('click', function(e) {
e.preventDefault();
reloadPage();
});
}
});
};
50 changes: 28 additions & 22 deletions core/webapp/vis/src/plot.js
Original file line number Diff line number Diff line change
Expand Up @@ -2272,31 +2272,37 @@ boxPlot.render();
};
}

// Issue 23626: map line/point color based on legend data
if (config.legendData && config.properties.color && !config.properties.colorRange) {
var legendColorMap = {};
for (var i = 0; i < config.legendData.length; i++) {
if (config.legendData[i].name) {
legendColorMap[config.legendData[i].name] = config.legendData[i].color;
// Issue 23626: map line/point color based on explicit colorMap property or legendData name entries
if (config.properties.color && !config.properties.colorRange) {
var legendColorMap;
if (config.properties.colorMap) {
legendColorMap = config.properties.colorMap;
} else if (config.legendData) {
legendColorMap = {};
for (var i = 0; i < config.legendData.length; i++) {
if (config.legendData[i].name) {
legendColorMap[config.legendData[i].name] = config.legendData[i].color;
}
}
}

if (config.qcPlotType === LABKEY.vis.TrendingLinePlotType.CUSUM) {
config.scales.color = {
scale: function(group) {
var normalizedGroup = group.replace('CUSUMmN', 'CUSUMm').replace('CUSUMmP', 'CUSUMm');
normalizedGroup = normalizedGroup.replace('CUSUMvN', 'CUSUMv').replace('CUSUMvP', 'CUSUMv');
return legendColorMap[normalizedGroup];
}
};
}
else
{
config.scales.color = {
scale: function(group) {
return legendColorMap[group];
}
};
if (legendColorMap) {
if (config.qcPlotType === LABKEY.vis.TrendingLinePlotType.CUSUM) {
config.scales.color = {
scale: function(group) {
var normalizedGroup = group.replace('CUSUMmN', 'CUSUMm').replace('CUSUMmP', 'CUSUMm');
normalizedGroup = normalizedGroup.replace('CUSUMvN', 'CUSUMv').replace('CUSUMvP', 'CUSUMv');
return legendColorMap[normalizedGroup];
}
};
}
else {
config.scales.color = {
scale: function(group) {
return legendColorMap[group];
}
};
}
}
}

Expand Down
Loading
Loading