diff --git a/authentication/source/java/ch/systemsx/cisd/authentication/DefaultSessionManager.java b/authentication/source/java/ch/systemsx/cisd/authentication/DefaultSessionManager.java index 7879d1b8c7e498b91e203ddc0090166935741086..41f9aeaaea898d05cce102206d3a6a461d1a526b 100644 --- a/authentication/source/java/ch/systemsx/cisd/authentication/DefaultSessionManager.java +++ b/authentication/source/java/ch/systemsx/cisd/authentication/DefaultSessionManager.java @@ -19,6 +19,7 @@ package ch.systemsx.cisd.authentication; import java.io.File; import java.text.DateFormat; import java.text.SimpleDateFormat; +import java.util.Collection; import java.util.Date; import java.util.LinkedHashMap; import java.util.Map; @@ -116,8 +117,7 @@ public class DefaultSessionManager<T extends BasicSession> implements ISessionMa */ boolean hasExpired(Long sessionExpirationTimeOrNull) { - long sessionExpirationTime = sessionExpirationTimeOrNull == null ? - session.getSessionExpirationTime() : sessionExpirationTimeOrNull; + long sessionExpirationTime = sessionExpirationTimeOrNull == null ? session.getSessionExpirationTime() : sessionExpirationTimeOrNull; return System.currentTimeMillis() - lastActiveTime > sessionExpirationTime; } } @@ -131,7 +131,7 @@ public class DefaultSessionManager<T extends BasicSession> implements ISessionMa */ protected final Map<String, FullSession<T>> sessions = new LinkedHashMap<String, FullSession<T>>(); - + private final IAuthenticationService authenticationService; private final IRemoteHostProvider remoteHostProvider; @@ -209,6 +209,17 @@ public class DefaultSessionManager<T extends BasicSession> implements ISessionMa + tokenGenerator.getNewToken(now, TIMESTAMP_TOKEN_SEPARATOR); synchronized (sessions) { + int maxNumberOfSessions = getMaxNumberOfSessionsFor(user); + if (maxNumberOfSessions > 0) + { + int numberOfOpenSessions = getNumberOfOpenSessionsFor(user); + if (numberOfOpenSessions >= maxNumberOfSessions) + { + throw new UserFailureException(numberOfOpenSessions + " open session(s) but only " + + maxNumberOfSessions + " session(s) are allowed for user " + user + "."); + } + } + final T session = sessionFactory.create(sessionToken, user, principal, getRemoteHost(), now, sessionExpirationPeriodMillis); @@ -221,6 +232,25 @@ public class DefaultSessionManager<T extends BasicSession> implements ISessionMa } } + private int getNumberOfOpenSessionsFor(String user) + { + int count = 0; + Collection<FullSession<T>> values = sessions.values(); + for (FullSession<T> session : values) + { + if (session.getSession().getUserName().equals(user)) + { + count++; + } + } + return count; + } + + protected int getMaxNumberOfSessionsFor(String user) + { + return 0; + } + private ISessionMonitor getSessionMonitor() { if (sessionMonitor == null) diff --git a/authentication/sourceTest/java/ch/systemsx/cisd/authentication/DefaultSessionManagerTest.java b/authentication/sourceTest/java/ch/systemsx/cisd/authentication/DefaultSessionManagerTest.java index a51ea5a87fe5ca5d39479a9997a9ffc190df18f0..78ed2c035dfb0d7a263d661c437f97504bc80f4a 100644 --- a/authentication/sourceTest/java/ch/systemsx/cisd/authentication/DefaultSessionManagerTest.java +++ b/authentication/sourceTest/java/ch/systemsx/cisd/authentication/DefaultSessionManagerTest.java @@ -20,12 +20,16 @@ import static org.testng.AssertJUnit.assertEquals; import static org.testng.AssertJUnit.assertTrue; import static org.testng.AssertJUnit.fail; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; import java.util.regex.Pattern; import org.apache.commons.lang3.StringUtils; import org.apache.log4j.Level; import org.jmock.Expectations; import org.jmock.Mockery; +import org.jmock.Sequence; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Listeners; @@ -72,6 +76,8 @@ public class DefaultSessionManagerTest private IPrincipalProvider principalProvider; + private Sequence sequence; + private void assertExceptionMessageForInvalidSessionToken(final UserFailureException ex) { final String message = ex.getMessage(); @@ -88,6 +94,7 @@ public class DefaultSessionManagerTest authenticationService = context.mock(IAuthenticationService.class); remoteHostProvider = context.mock(IRemoteHostProvider.class); principalProvider = context.mock(IPrincipalProvider.class); + sequence = context.sequence("sequence"); context.checking(new Expectations() { { @@ -98,12 +105,27 @@ public class DefaultSessionManagerTest logRecorder = new BufferedAppender("%-5p %c - %m%n", Level.DEBUG); } - @SuppressWarnings( - { "unchecked", "rawtypes" }) private ISessionManager<BasicSession> createSessionManager(int sessionExpiration) + { + return createSessionManager(sessionExpiration, new HashMap<>()); + } + + @SuppressWarnings({ "unchecked", "rawtypes" }) + private ISessionManager<BasicSession> createSessionManager(int sessionExpiration, + Map<String, Integer> maxNumberOfSessionByUser) { return new DefaultSessionManager(sessionFactory, prefixGenerator, authenticationService, - remoteHostProvider, sessionExpiration, 0, true); + remoteHostProvider, sessionExpiration, 0, true) + { + + @Override + protected int getMaxNumberOfSessionsFor(String user) + { + Integer maxNumber = maxNumberOfSessionByUser.get(user); + return maxNumber == null ? 0 : maxNumber; + } + + }; } @AfterMethod @@ -169,6 +191,92 @@ public class DefaultSessionManagerTest context.assertIsSatisfied(); } + @Test + public void testUnlimitedNumberOfSessions() + { + final String user = "bla"; + context.checking(new Expectations() + { + { + allowing(remoteHostProvider).getRemoteHost(); + will(returnValue(REMOTE_HOST)); + + for (int i = 0; i < 3; i++) + { + one(authenticationService).tryGetAndAuthenticateUser(user, "blub"); + will(returnValue(principal)); + + one(sessionFactory).create(with(any(String.class)), with(equal(user)), + with(equal(principal)), with(equal(REMOTE_HOST)), + with(any(Long.class)), with(any(Integer.class))); + BasicSession session = + new BasicSession(user + "-" + (i + 1), user, principal, REMOTE_HOST, 42L, 0); + will(returnValue(session)); + inSequence(sequence); + + one(prefixGenerator).createPrefix(session); + will(returnValue("[USER:'" + user + "', HOST:'remote-host']")); + } + + } + }); + + assertEquals("bla-1", sessionManager.tryToOpenSession("bla", "blub")); + assertEquals("bla-2", sessionManager.tryToOpenSession("bla", "blub")); + assertEquals("bla-3", sessionManager.tryToOpenSession("bla", "blub")); + + context.assertIsSatisfied(); + } + + @Test + public void testLimitedNumberOfSession() + { + final String user = "bla"; + context.checking(new Expectations() + { + { + one(authenticationService).check(); + + allowing(remoteHostProvider).getRemoteHost(); + will(returnValue(REMOTE_HOST)); + + exactly(3).of(authenticationService).tryGetAndAuthenticateUser(user, "blub"); + will(returnValue(principal)); + + for (int i = 0; i < 2; i++) + { + one(sessionFactory).create(with(any(String.class)), with(equal(user)), + with(equal(principal)), with(equal(REMOTE_HOST)), + with(any(Long.class)), with(any(Integer.class))); + BasicSession session = + new BasicSession(user + "-" + (i + 1), user, principal, REMOTE_HOST, 42L, 0); + will(returnValue(session)); + inSequence(sequence); + + one(prefixGenerator).createPrefix(session); + will(returnValue("[USER:'" + user + "', HOST:'remote-host']")); + } + + one(prefixGenerator).createPrefix("bla", REMOTE_HOST); + will(returnValue("[USER:'bla', HOST:'remote-host']")); + } + }); + sessionManager = createSessionManager(SESSION_EXPIRATION_PERIOD_MINUTES, Collections.singletonMap("bla", 2)); + + assertEquals("bla-1", sessionManager.tryToOpenSession("bla", "blub")); + assertEquals("bla-2", sessionManager.tryToOpenSession("bla", "blub")); + try + { + assertEquals("bla-3", sessionManager.tryToOpenSession("bla", "blub")); + fail("UserFailureException expected"); + } catch (UserFailureException e) + { + assertEquals("2 open session(s) but only 2 session(s) are allowed for user bla.", e.getMessage()); + } + + context.assertIsSatisfied(); + } + @Test public void testSuccessfulAuthenticationWithEmailAddress() { diff --git a/datastore_server/build.gradle b/datastore_server/build.gradle index 8bacacef8fd90f58b54e7a9b7fa1687ae45de76c..8c4931600ae7b8dc8f02c7d3cdcf2761888eec40 100644 --- a/datastore_server/build.gradle +++ b/datastore_server/build.gradle @@ -10,6 +10,7 @@ apply from: '../gradle/javaproject.gradle' configurations.create('testRuntimeFirst') configurations.create('testRuntimeSecond') +configurations.create('datastoreExecRuntime') dependencies { @@ -35,6 +36,19 @@ dependencies { runtime 'bioformats:bioformats:5.9.2' + // eln-lims Jython APIs dependencies + datastoreExecRuntime files("../openbis_standard_technologies/dist/core-plugins/eln-lims/1/dss/reporting-plugins/eln-lims-api/lib/zip4j_1.3.2.jar"), + files("../openbis_standard_technologies/dist/core-plugins/eln-lims/1/dss/reporting-plugins/eln-lims-api/lib/plasmapper.jar"), + files("../openbis_standard_technologies/dist/core-plugins/eln-lims/1/dss/reporting-plugins/eln-lims-api/lib/htmlcleaner-2.16.jar"), + files("../openbis_standard_technologies/dist/core-plugins/eln-lims/1/dss/reporting-plugins/exports-api/lib/docx4j-3.3.3.jar"), + files("../openbis_standard_technologies/dist/core-plugins/eln-lims/1/dss/reporting-plugins/exports-api/lib/docxbuilder.jar"), + files("../openbis_standard_technologies/dist/core-plugins/eln-lims/1/dss/reporting-plugins/exports-api/lib/jsoup-1.11.3.jar"), + files("../openbis_standard_technologies/dist/core-plugins/eln-lims/1/dss/reporting-plugins/exports-api/lib/serializer-2.7.2.jar"), + files("../openbis_standard_technologies/dist/core-plugins/eln-lims/1/dss/reporting-plugins/exports-api/lib/slf4j-api-1.7.21.jar"), + files("../openbis_standard_technologies/dist/core-plugins/eln-lims/1/dss/reporting-plugins/exports-api/lib/xalan-2.7.2.jar"), + files("../openbis_standard_technologies/dist/core-plugins/eln-lims/1/dss/reporting-plugins/zenodo-exports-api/lib/job-scheduler.jar"), + files("../openbis_standard_technologies/dist/core-plugins/eln-lims/1/dss/reporting-plugins/password-reset-api/lib/persistentkeyvaluestore.jar") + testCompile (project(path: ':openbis', configuration: 'tests')) { exclude group: 'google', module: 'gwt-user' } @@ -73,7 +87,7 @@ sourceSets { } compileClasspath += sourceSets.main.compileClasspath - runtimeClasspath += sourceSets.main.runtimeClasspath + runtimeClasspath += sourceSets.main.runtimeClasspath + configurations.datastoreExecRuntime } } diff --git a/integration-tests/test_archiving.py b/integration-tests/test_archiving.py index a039e94ad930b38965878b5d1d1ae72c98db2c62..346dedbbdd53691c25899fcc81b909423e27d0f0 100644 --- a/integration-tests/test_archiving.py +++ b/integration-tests/test_archiving.py @@ -27,6 +27,7 @@ class TestCase(systemtest.testcase.TestCase): openbisController = self.createOpenbisController() openbisController.createTestDatabase("openbis") + openbisController.asProperties['max-number-of-sessions-per-user'] = '0' openbisController.allUp() diff --git a/integration-tests/test_obis.py b/integration-tests/test_obis.py index 03a14dfef81894f32f28004bc6575946ea4e2808..ddf3dc7ce8d5470a8ec0b07aeb4a881e80a16224 100755 --- a/integration-tests/test_obis.py +++ b/integration-tests/test_obis.py @@ -45,6 +45,8 @@ class TestCase(systemtest.testcase.TestCase): self.installObis() self.openbisController = self.createOpenbisController() self.openbisController.createTestDatabase("openbis") + self.openbisController.asProperties['max-number-of-sessions-per-user'] = '0' + self.openbisController.allUp() tmpdir = os.path.abspath('obis_data_' + str(randrange(100000))) diff --git a/integration-tests/test_openbis_sync_big.py b/integration-tests/test_openbis_sync_big.py index 8b47544bcb3d8936d92dde565d7bebc0b17d5cf7..2ca1e90159d474a2989b70b9d2503741a7824db6 100755 --- a/integration-tests/test_openbis_sync_big.py +++ b/integration-tests/test_openbis_sync_big.py @@ -410,6 +410,7 @@ class TestCase(systemtest.testcase.TestCase): openbis_data_source.enableProjectSamples() openbis_data_source.setDummyAuthentication() openbis_data_source.setDataStoreServerProperty("host-address", "https://localhost") + openbis_data_source.asProperties['max-number-of-sessions-per-user'] = '0' openbis_data_source.dssProperties['database.kind'] = openbis_data_source.databaseKind openbis_data_source.createTestDatabase('openbis') openbis_data_source.createTestDatabase('pathinfo') @@ -426,6 +427,7 @@ class TestCase(systemtest.testcase.TestCase): openbis_harvester.enableProjectSamples() openbis_harvester.setDummyAuthentication() openbis_harvester.setDataStoreServerProperty("host-address", "https://localhost") + openbis_harvester.asProperties['max-number-of-sessions-per-user'] = '0' openbis_harvester.dssProperties['database.kind'] = openbis_harvester.databaseKind openbis_harvester.enableCorePlugin("openbis-sync") util.copyFromTo(self.getTemplatesFolder(), openbis_harvester.installPath, "harvester-config.txt") diff --git a/js-test/servers/common/openBIS-server/etc/service.properties b/js-test/servers/common/openBIS-server/etc/service.properties index 288dbe25af96844bc75fedd71f67aec354afe95c..e750d54ee21c229eb671293f286e32e326a26be4 100644 --- a/js-test/servers/common/openBIS-server/etc/service.properties +++ b/js-test/servers/common/openBIS-server/etc/service.properties @@ -130,6 +130,7 @@ trusted-cross-origin-domains=* # --------------------------------------------------------------------------- # The time after which an inactive session is expired by the service (in minutes). session-timeout = 720 +max-number-of-sessions-per-user = 0 # --------------------------------------------------------------------------- # Business rules configuration diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/SingleSignOnServlet.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/SingleSignOnServlet.java index dd45e86850433efcc9ddaddbbc062192444d829e..a2ec8176f5403ac11ff0f02a23af4222be325b56 100644 --- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/SingleSignOnServlet.java +++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/SingleSignOnServlet.java @@ -17,10 +17,10 @@ package ch.ethz.sis.openbis.generic.server; import java.io.IOException; -import java.util.Arrays; -import java.util.Enumeration; +import java.util.ArrayList; import java.util.HashMap; import java.util.Map; +import java.util.Map.Entry; import javax.annotation.Resource; import javax.servlet.ServletContext; @@ -51,6 +51,8 @@ import ch.systemsx.cisd.openbis.generic.shared.dto.Session; @Controller public class SingleSignOnServlet extends AbstractServlet { + private static final String OPENBIS_COOKIE = "openbis"; + public static final String SERVLET_NAME = "ssos"; public static final String SESSION_ID_KEY = "session-id-key"; @@ -108,15 +110,32 @@ public class SingleSignOnServlet extends AbstractServlet @RequestMapping({ SERVLET_NAME }) protected void respondToRequest(HttpServletRequest request, HttpServletResponse response) throws Exception, IOException { + operationLog.info("handle sso event"); + removeStaleSessions(); String sessionId = getHeader(request, SESSION_ID_KEY, DEFAULT_SESSION_ID_KEY); - String sessionToken = sessionTokenBySessionId.get(sessionId); - String returnURL = request.getParameter("return"); - if (returnURL != null) + synchronized (this) { - handleLogOut(request, response, sessionId, sessionToken, returnURL); - } else + String sessionToken = sessionTokenBySessionId.get(sessionId); + String returnURL = request.getParameter("return"); + if (returnURL != null) + { + handleLogOut(request, response, sessionId, sessionToken, returnURL); + } else + { + handleLogIn(request, response, sessionId, sessionToken); + } + } + } + + private void removeStaleSessions() + { + for (Entry<String, String> entry : new ArrayList<>(sessionTokenBySessionId.entrySet())) { - handleLogIn(request, response, sessionId, sessionToken); + String sessionToken = entry.getValue(); + if (sessionManager.tryGetSession(sessionToken) == null) + { + sessionTokenBySessionId.remove(entry.getKey()); + } } } @@ -148,7 +167,7 @@ public class SingleSignOnServlet extends AbstractServlet applicationServerApi.registerUser(sessionToken); sessionTokenBySessionId.put(sessionId, sessionToken); - operationLog.info("Session token " + sessionToken + " created for SSO session id " + sessionId); + operationLog.info("Session token " + sessionToken + " created for SSO session id " + sessionId + " (" + sessionTokenBySessionId.size() + ")"); redirectToApp(request, response, sessionToken); } @@ -156,6 +175,7 @@ public class SingleSignOnServlet extends AbstractServlet throws IOException { operationLog.info("log out session id: " + sessionId); + sessionTokenBySessionId.remove(sessionId); if (sessionToken != null) { Session session = sessionManager.tryGetSession(sessionToken); @@ -178,7 +198,7 @@ public class SingleSignOnServlet extends AbstractServlet String redirectUrl = configurer.getResolvedProps().getProperty(REDIRECT_URL_KEY, template.createText()); operationLog.info("redirect to " + redirectUrl); removeOpenbisCookies(request, response); - Cookie cookie = new Cookie("openbis", sessionToken); + Cookie cookie = new Cookie(OPENBIS_COOKIE, sessionToken); cookie.setPath("/"); response.addCookie(cookie); response.sendRedirect(redirectUrl); @@ -189,7 +209,7 @@ public class SingleSignOnServlet extends AbstractServlet Cookie[] cookies = request.getCookies(); for (Cookie cookie : cookies) { - if (cookie.getName().equals("openbis")) + if (cookie.getName().equals(OPENBIS_COOKIE)) { cookie.setValue(""); cookie.setPath("/"); diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/OpenBisSessionManager.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/OpenBisSessionManager.java index 4b91a77070c1684feb0f74f85605540ca55b45eb..5934df568036cf9b0dd5edb32385a2bf2bfe26f3 100644 --- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/OpenBisSessionManager.java +++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/OpenBisSessionManager.java @@ -16,6 +16,9 @@ package ch.systemsx.cisd.openbis.generic.server; +import java.util.HashSet; +import java.util.Set; + import ch.systemsx.cisd.authentication.DefaultSessionManager; import ch.systemsx.cisd.authentication.IAuthenticationService; import ch.systemsx.cisd.authentication.ILogMessagePrefixGenerator; @@ -36,14 +39,14 @@ public class OpenBisSessionManager extends DefaultSessionManager<Session> implem { private static final int DEFAULT_SESSION_EXPIRATION_PERIOD_FOR_NO_LOGIN = 10; - private static final int getSessionExpirationPeriodMinutesForNoLogin(String property) + private static final int parseAsIntOrReturnDefaultValue(String string, int defaultValue) { try { - return Integer.parseInt(property); + return Integer.parseInt(string); } catch (NumberFormatException ex) { - return DEFAULT_SESSION_EXPIRATION_PERIOD_FOR_NO_LOGIN; + return defaultValue; } } @@ -51,12 +54,17 @@ public class OpenBisSessionManager extends DefaultSessionManager<Session> implem private String userForAnonymousLogin; + private int maxNumberOfSessionsPerUser; + + private Set<String> usersWithUnrestrictedNumberOfSessions = new HashSet<>(); + public OpenBisSessionManager(ISessionFactory<Session> sessionFactory, ILogMessagePrefixGenerator<Session> prefixGenerator, IAuthenticationService authenticationService, IRemoteHostProvider remoteHostProvider, int sessionExpirationPeriodMinutes, String sessionExpirationPeriodMinutesForNoLogin, boolean tryEmailAsUserName, IDAOFactory daoFactory) { super(sessionFactory, prefixGenerator, authenticationService, remoteHostProvider, sessionExpirationPeriodMinutes, - getSessionExpirationPeriodMinutesForNoLogin(sessionExpirationPeriodMinutesForNoLogin), tryEmailAsUserName); + parseAsIntOrReturnDefaultValue(sessionExpirationPeriodMinutesForNoLogin, DEFAULT_SESSION_EXPIRATION_PERIOD_FOR_NO_LOGIN), + tryEmailAsUserName); this.daoFactory = daoFactory; } @@ -68,6 +76,37 @@ public class OpenBisSessionManager extends DefaultSessionManager<Session> implem sessionExpirationPeriodMinutesForNoLogin, false, daoFactory); } + @Override + protected int getMaxNumberOfSessionsFor(String user) + { + if (usersWithUnrestrictedNumberOfSessions.contains(user)) + { + return 0; + } + PersonPE person = daoFactory.getPersonDAO().tryFindPersonByUserId(user); + if (person != null && person.isSystemUser()) + { + return 0; + } + return maxNumberOfSessionsPerUser; + } + + public void setMaxNumberOfSessionsPerUser(String number) + { + maxNumberOfSessionsPerUser = parseAsIntOrReturnDefaultValue(number, 1); + } + + public void setUsersWithUnrestrictedNumberOfSessions(String users) + { + if (users.startsWith("${") == false) + { + for (String user : users.split(",")) + { + usersWithUnrestrictedNumberOfSessions.add(user.trim()); + } + } + } + @Override public void updateAllSessions() { diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/public/resources/js/openbis.js b/openbis/source/java/ch/systemsx/cisd/openbis/public/resources/js/openbis.js index 07704d7a495f5313e5ae0f287f922367dfe78f2c..4d485fede1580cbe0f689a84fe0ffd8e725ae80a 100644 --- a/openbis/source/java/ch/systemsx/cisd/openbis/public/resources/js/openbis.js +++ b/openbis/source/java/ch/systemsx/cisd/openbis/public/resources/js/openbis.js @@ -312,7 +312,7 @@ openbis.prototype.login = function(userId, userPassword, action) { success: function(loginResponse) { if(loginResponse.error){ - alert("Login failed"); + alert("Login failed: " + loginResponse.error.message); }else{ openbisObj._internal.sessionToken = loginResponse.result; openbisObj.rememberSession(); diff --git a/openbis/source/java/genericApplicationContext.xml b/openbis/source/java/genericApplicationContext.xml index face9a10576a97b196023a4823b4613205d50c94..745156dc98404b2588278e8488df3c4015d956a5 100644 --- a/openbis/source/java/genericApplicationContext.xml +++ b/openbis/source/java/genericApplicationContext.xml @@ -151,6 +151,8 @@ <constructor-arg value="true" /> <constructor-arg ref="dao-factory" /> <property name="userForAnonymousLogin" value="${user-for-anonymous-login}" /> + <property name="maxNumberOfSessionsPerUser" value="${max-number-of-sessions-per-user}" /> + <property name="usersWithUnrestrictedNumberOfSessions" value="${users-with-unrestricted-number-of-sessions}" /> </bean> <bean id="display-settings-provider" diff --git a/openbis/source/java/service.properties b/openbis/source/java/service.properties index 9094f3ce5928771f42d13cba4611a766ef4c289d..aba585ceb49aed9663e64929fdd1f2d26f29fea1 100644 --- a/openbis/source/java/service.properties +++ b/openbis/source/java/service.properties @@ -2,6 +2,7 @@ authentication-service = dummy-authentication-service # The time after which an inactive session is expired by the service (in minutes). session-timeout = 720 +max-number-of-sessions-per-user = 0 session-workspace-root-dir = targets/session-workspace diff --git a/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/shared/util/SimplePropertyValidatorTest.java b/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/shared/util/SimplePropertyValidatorTest.java index 599b46bc2ae77f9706510f8e10557a81837b0876..19b825d9464490448c8b522e805ded4b401c6dd1 100644 --- a/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/shared/util/SimplePropertyValidatorTest.java +++ b/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/shared/util/SimplePropertyValidatorTest.java @@ -101,7 +101,7 @@ public class SimplePropertyValidatorTest "M/d/yy HH:mm\n" + "yyyy-MM-dd HH:mm:ss Z\n" + "yyyy-MM-dd'T'HH:mm:ssX\n" + - "yyyy-MM-dd HH:mm:ss ZZZ\n" + + "yyyy-MM-dd HH:mm:ss Z\n" + "yyyy-MM-dd'T'HH:mm:ssXXX]'."); } } diff --git a/openbis_standard_technologies/dist/core-plugins/eln-lims-life-sciences/1/as/initialize-master-data.py b/openbis_standard_technologies/dist/core-plugins/eln-lims-life-sciences/1/as/initialize-master-data.py index d76ed8acd11035a28bd2b1b8c72d737f68e93147..be96493467fe5f9cb9b1448029cf3e59c0af9876 100644 --- a/openbis_standard_technologies/dist/core-plugins/eln-lims-life-sciences/1/as/initialize-master-data.py +++ b/openbis_standard_technologies/dist/core-plugins/eln-lims-life-sciences/1/as/initialize-master-data.py @@ -25,9 +25,9 @@ helper = MasterDataRegistrationHelper(sys.path) api = CommonServiceProvider.getApplicationContext().getBean(ApplicationServerApi.INTERNAL_SERVICE_NAME) sessionToken = api.loginAsSystem() props = CustomASServiceExecutionOptions().withParameter('xls', helper.listXlsByteArrays()) \ - .withParameter('xls_name', 'ELN-LIMS-LIFE-SCIENCES').withParameter('update_mode', 'IGNORE_EXISTING') \ + .withParameter('xls_name', 'ELN-LIMS-LIFE-SCIENCES').withParameter('update_mode', 'UPDATE_IF_EXISTS') \ .withParameter('scripts', helper.getAllScripts()) -result = api.executeCustomASService(sessionToken, CustomASServiceCode("xls-import-api"), props) +result = api.executeCustomASService(sessionToken, CustomASServiceCode("xls-import-api"), props); print("======================== master-data xls ingestion result ========================") print(result) print("======================== master-data xls ingestion result ========================") diff --git a/openbis_standard_technologies/dist/core-plugins/eln-lims-life-sciences/1/as/master-data/data-model.xls b/openbis_standard_technologies/dist/core-plugins/eln-lims-life-sciences/1/as/master-data/data-model.xls index e2755043112873eaa41408f7a01ab4e152444d93..8b219a0b5747b886494c221c82bd3f3f1f470262 100644 Binary files a/openbis_standard_technologies/dist/core-plugins/eln-lims-life-sciences/1/as/master-data/data-model.xls and b/openbis_standard_technologies/dist/core-plugins/eln-lims-life-sciences/1/as/master-data/data-model.xls differ diff --git a/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/initialize-master-data.py b/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/initialize-master-data.py index 078f2ae70bab91de09d7b2d35c21281433dc731d..e3eaee0601ed6c89d3e53f6372cf7ed6e572ce6a 100644 --- a/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/initialize-master-data.py +++ b/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/initialize-master-data.py @@ -16,7 +16,6 @@ # MasterDataRegistrationTransaction Class from ch.ethz.sis.openbis.generic.server.asapi.v3 import ApplicationServerApi from ch.systemsx.cisd.openbis.generic.server import CommonServiceProvider -from ch.systemsx.cisd.openbis.generic.server import ComponentNames from ch.ethz.sis.openbis.generic.asapi.v3.dto.service.id import CustomASServiceCode from ch.ethz.sis.openbis.generic.asapi.v3.dto.service import CustomASServiceExecutionOptions from ch.systemsx.cisd.openbis.generic.server.jython.api.v1.impl import MasterDataRegistrationHelper @@ -26,18 +25,9 @@ helper = MasterDataRegistrationHelper(sys.path) api = CommonServiceProvider.getApplicationContext().getBean(ApplicationServerApi.INTERNAL_SERVICE_NAME) sessionToken = api.loginAsSystem() props = CustomASServiceExecutionOptions().withParameter('xls', helper.listXlsByteArrays())\ - .withParameter('xls_name', 'ELN-LIMS').withParameter('update_mode', 'IGNORE_EXISTING')\ + .withParameter('xls_name', 'ELN-LIMS').withParameter('update_mode', 'UPDATE_IF_EXISTS')\ .withParameter('scripts', helper.getAllScripts()) result = api.executeCustomASService(sessionToken, CustomASServiceCode("xls-import-api"), props); -# Updating MULTILINE_VARCHAR to use "Word Processor" for all Properties -daoFactory = CommonServiceProvider.getApplicationContext().getBean(ComponentNames.DAO_FACTORY); -currentSession = daoFactory.getSessionFactory().getCurrentSession(); -SQL = "UPDATE property_types SET meta_data = cast(:meta_data AS jsonb) WHERE id IN (SELECT id FROM property_types WHERE daty_id = (SELECT id FROM data_types WHERE code = 'MULTILINE_VARCHAR'))"; -meta_data = "{ \"custom_widget\" : \"Word Processor\" }"; -sqlQuery = currentSession.createSQLQuery(SQL); -sqlQuery.setParameter("meta_data", meta_data); -sqlQuery.executeUpdate(); -# print("======================== master-data xls ingestion result ========================") print(result) print("======================== master-data xls ingestion result ========================") diff --git a/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/master-data/data-model.xls b/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/master-data/data-model.xls index c061f53d70d87bbbb9cf15446655855d2d1bf0ce..16332eb71f3a9ddbf34d818a04319e5fb81200c1 100644 Binary files a/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/master-data/data-model.xls and b/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/master-data/data-model.xls differ diff --git a/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/css/style.css b/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/css/style.css index aab016d67217035d3eccdf3be19dd960a8d226f4..febb7e52dbfb12562b6e7af2bf6f2ebd9bfdfd63 100644 --- a/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/css/style.css +++ b/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/css/style.css @@ -778,11 +778,17 @@ span.fancytree-custom-icon { box-sizing: initial !important; } +.jexcel_toolbar { + position: sticky; + top: 0px; + z-index : 800; +} + .jcolor-content > table > tr > td { padding : 7px !important; } + .jexcel > thead > tr > td { - position : relative !important; z-index : auto !important; } \ No newline at end of file diff --git a/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/config/Profile.js b/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/config/Profile.js index 8b1a0d7ebb7f7f357ea1016d155394606e58f0fd..b713b303923d04fa508d679c6db1851f4d2ce138 100644 --- a/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/config/Profile.js +++ b/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/config/Profile.js @@ -135,7 +135,6 @@ $.extend(DefaultProfile.prototype, { // "ADMIN-BS-MBPR28.D.ETHZ.CH-E96954A7" : "http://localhost:8080/download" } this.singleSignOnUrlTemplate = null; - this.singleSignOnUrlLogoutTemplate = null; this.singleSignOnLinkLabel = 'Single Sign On Login'; this.customWidgetSettings = {}; diff --git a/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/controllers/MainController.js b/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/controllers/MainController.js index 1294aec2ba78fe8eec0fbf4992a436edea3db223..a4c8633e158bed6a2a2c053e454cc75e19aeff45 100644 --- a/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/controllers/MainController.js +++ b/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/controllers/MainController.js @@ -96,9 +96,13 @@ function MainController(profile) { if(data.result == null){ $("#username").focus(); var callback = function() {Util.unblockUI();}; - Util.showUserError('The given username or password is not correct.', callback); + var msg = 'The given username or password is not correct.'; + if (data.error && data.error.message) { + msg = data.error.message; + } + Util.showUserError(msg, callback); this.serverFacade.doIfFileAuthenticationService((function() { - this._enablePasswordResetLink(); + this._enablePasswordResetLink(); }).bind(this)); return; } diff --git a/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/util/FormUtil.js b/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/util/FormUtil.js index 19a34eb8422cac3228815ef88103b4e019eafa83..7ed58572f255491f4f021d23a13582c4512fab74 100644 --- a/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/util/FormUtil.js +++ b/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/util/FormUtil.js @@ -274,15 +274,20 @@ var FormUtil = new function() { return $component; } - this.getExperimentTypeDropdown = function(id, isRequired) { + this.getExperimentTypeDropdown = function(id, isRequired, defaultValue) { var experimentTypes = this.profile.allExperimentTypes; - var $component = $("<select>", {"id" : id, class : 'form-control'}); + var $component = $("<select>", {"id" : id, class : "form-control"}); if (isRequired) { - $component.attr('required', ''); + $component.attr("required", ""); } - - $component.append($("<option>").attr('value', '').attr('selected', '').text("Select an " + ELNDictionary.getExperimentDualName() + " type")); + + var $emptyOption = $("<option>").attr("value", "").attr('disabled', '').text("Select an " + ELNDictionary.getExperimentDualName() + " type"); + if (!defaultValue || defaultValue === "") { + $emptyOption.attr("selected", ""); + } + + $component.append($emptyOption); for(var i = 0; i < experimentTypes.length; i++) { var experimentType = experimentTypes[i]; if(profile.isExperimentTypeHidden(experimentType.code)) { @@ -294,12 +299,22 @@ var FormUtil = new function() { if(description !== "") { label += " (" + description + ")"; } - - $component.append($("<option>").attr('value',experimentType.code).text(label)); + + var $option = $("<option>").attr("value", experimentType.code).text(label); + if (experimentType.code === defaultValue) { + $option.attr("selected", ""); + } + $component.append($option); } Select2Manager.add($component); return $component; - } + }; + + this.getInlineExperimentTypeDropdown = function(id, isRequired, defaultValue) { + var $wrapper = $("<span>", { class : "dropdown" }); + $wrapper.append(this.getExperimentTypeDropdown(id, isRequired, defaultValue)); + return $wrapper; + }; this.getSpaceDropdown = function(id, isRequired) { var spaces = this.profile.allSpaces; @@ -351,7 +366,7 @@ var FormUtil = new function() { $component.attr('required', ''); - $component.append($("<option>").attr('value', '').attr('selected', '').attr('disabled', '').text('Select a dataset type')); + $component.append($("<option>").attr('value', '').attr('selected', '').text('Select a dataset type')); for (var i = 0; i < dataSetTypes.length; i++) { var datasetType = dataSetTypes[i]; @@ -779,6 +794,8 @@ var FormUtil = new function() { } $component.append($("<option>").attr('value', '').attr('selected', '').attr('disabled', '').text(alt)); + $component.append($("<option>").attr('value', '').text('(empty)')); + for(var i = 0; i < terms.length; i++) { $component.append($("<option>").attr('value',terms[i].code).text(terms[i].label)); } diff --git a/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/util/JExcelEditorManager.js b/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/util/JExcelEditorManager.js index 2ccaf756822bc54ac1990f91246f2f71b7f048be..4c1ab4c4bf814f16365bc0b97ff969d0d85277be 100644 --- a/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/util/JExcelEditorManager.js +++ b/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/util/JExcelEditorManager.js @@ -4,16 +4,28 @@ var JExcelEditorManager = new function() { this.getOnChange = function(guid, propertyCode, entity) { var _this = this; - return function() { + return function(el, record, x, y, value) { var jExcelEditor = _this.jExcelEditors[guid]; if(jExcelEditor) { + // Change column width + var columnWidth = parseInt(jExcelEditor.getWidth(x)); + var td = el.children[1].children[0].children[2].children[y].children[parseInt(x)+1]; + var columnScrollWidth = td.scrollWidth; + + if(columnScrollWidth > columnWidth) { + jExcelEditor.setWidth(x, columnScrollWidth + 10); + } + + // Save Editor var data = jExcelEditor.getData(); var style = jExcelEditor.getStyle(); var meta = jExcelEditor.getMeta(); + var width = jExcelEditor.getWidth(); var jExcelEditorValue = { data : data, style : style, - meta : meta + meta : meta, + width : width } entity.properties[propertyCode] = "<DATA>" + window.btoa(JSON.stringify(jExcelEditorValue)) + "</DATA>"; } @@ -108,7 +120,7 @@ var JExcelEditorManager = new function() { var data = []; var style = null; var meta = null; - + var width = null; if(entity.properties && entity.properties[propertyCode]) { var jExcelEditorValueAsStringWithTags = entity.properties[propertyCode]; var jExcelEditorValue = null; @@ -120,6 +132,7 @@ var JExcelEditorManager = new function() { data = jExcelEditorValue.data; style = jExcelEditorValue.style; meta = jExcelEditorValue.meta; + width = jExcelEditorValue.width; } } @@ -137,6 +150,10 @@ var JExcelEditorManager = new function() { onchangemeta: null }; + if(width) { + options.colWidths = width; + } + if(mode === FormMode.VIEW) { options.allowInsertRow = false; options.allowManualInsertRow = false; diff --git a/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/views/ProjectForm/ProjectFormController.js b/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/views/ProjectForm/ProjectFormController.js index 96b88be43f7e8d24f7e437d969416bd90e98363e..e41f3e82aaab0443bcaf0bf87af6d5833fd43962 100644 --- a/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/views/ProjectForm/ProjectFormController.js +++ b/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/views/ProjectForm/ProjectFormController.js @@ -125,4 +125,12 @@ function ProjectFormController(mainController, mode, project) { Util.showError("No DSS available.", function() {Util.unblockUI();}); } } + + this.getDefaultSpaceValue = function (key, callback) { + this._mainController.serverFacade.getSetting(key, callback); + }; + + this.setDefaultSpaceValue = function (key, value) { + this._mainController.serverFacade.setSetting(key, value); + }; } diff --git a/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/views/ProjectForm/ProjectFormView.js b/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/views/ProjectForm/ProjectFormView.js index 00426c6e9b8b7aca4cbfbc3fd293cdacd28ba00a..67397978ff96378c0be5e27ba058d66d09281d25 100644 --- a/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/views/ProjectForm/ProjectFormView.js +++ b/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/views/ProjectForm/ProjectFormView.js @@ -61,36 +61,47 @@ function ProjectFormView(projectFormController, projectFormModel) { // var toolbarModel = []; if(this._projectFormModel.mode === FormMode.VIEW) { - var showSelectExperimentType = function() { - var $dropdown = FormUtil.getExperimentTypeDropdown("experimentTypeDropdown", true); - Util.showDropdownAndBlockUI("experimentTypeDropdown", $dropdown); - - $("#experimentTypeDropdown").on("change", function(event) { - var experimentTypeCode = $("#experimentTypeDropdown")[0].value; - _this._projectFormController.createNewExperiment(experimentTypeCode); - }); - - $("#experimentTypeDropdownCancel").on("click", function(event) { - Util.unblockUI(); - }); - } + var experimentKindName = ELNDictionary.getExperimentKindName(projectIdentifier); if (_this._allowedToCreateExperiments()) { //Create Experiment - var isDefaultExperimentPressent = mainController.profile.getExperimentTypeForExperimentTypeCode("DEFAULT_EXPERIMENT") != null; - if(isDefaultExperimentPressent) { - var $createExpBtn = FormUtil.getButtonWithIcon("glyphicon-plus", function() { - if(profile.isInventorySpace(_this._projectFormModel.project.spaceCode)) { + var isDefaultExperimentPresent = mainController.profile.getExperimentTypeForExperimentTypeCode("DEFAULT_EXPERIMENT") != null; + if(isDefaultExperimentPresent) { + var newExperimentTypeDropdownId = "new-experiment-type-dropdown"; + var defaultValueKey = entityPath.text() + "-FORM-" + newExperimentTypeDropdownId; + + this._projectFormController.getDefaultSpaceValue(defaultValueKey, function (settingsValue) { + var defaultValue; + if (settingsValue) { + defaultValue = settingsValue; + } else if (profile.isInventorySpace(_this._projectFormModel.project.spaceCode)) { var experimentType = profile.getExperimentTypeForExperimentTypeCode(_this._projectFormModel.project.spaceCode); - if(experimentType) { - _this._projectFormController.createNewExperiment(_this._projectFormModel.project.spaceCode); + if (experimentType) { + defaultValue = _this._projectFormModel.project.spaceCode; } else { - showSelectExperimentType(); + defaultValue = ""; } } else { - _this._projectFormController.createNewExperiment("DEFAULT_EXPERIMENT"); + defaultValue = "DEFAULT_EXPERIMENT"; + } + + $("option[value=" + defaultValue + "]").prop("selected", true); + }); + + var $experimentTypeDropdown = FormUtil.getInlineExperimentTypeDropdown(newExperimentTypeDropdownId, true); + var $createExpBtn = FormUtil.getButtonWithIcon("glyphicon-plus", function() { + var experimentTypeCode = $("#" + newExperimentTypeDropdownId)[0].value; + + if (experimentTypeCode && experimentTypeCode !== "") { + _this._projectFormController.createNewExperiment(experimentTypeCode); } }); - toolbarModel.push({ component : $createExpBtn, tooltip: "Create " + ELNDictionary.getExperimentKindName(projectIdentifier) }); + + toolbarModel.push({ component: $createExpBtn, tooltip: "Create " + experimentKindName }); + toolbarModel.push({ component: $experimentTypeDropdown, tooltip: "Type of the " + experimentKindName + " to create" }); + + $experimentTypeDropdown.change(function(event) { + _this._projectFormController.setDefaultSpaceValue(defaultValueKey, $(event.target).val()); + }); } } if(_this._allowedToEdit()) { @@ -142,8 +153,22 @@ function ProjectFormView(projectFormController, projectFormModel) { toolbarModel.push({ component : $freezeButton, tooltip: isEntityFrozenTooltip }); } + var showSelectExperimentType = function() { + var $dropdown = FormUtil.getExperimentTypeDropdown("experimentTypeDropdown", true); + Util.showDropdownAndBlockUI("experimentTypeDropdown", $dropdown); + + $("#experimentTypeDropdown").on("change", function(event) { + var experimentTypeCode = $("#experimentTypeDropdown")[0].value; + _this._projectFormController.createNewExperiment(experimentTypeCode); + }); + + $("#experimentTypeDropdownCancel").on("click", function(event) { + Util.unblockUI(); + }); + }; + //Operations - var $operationsMenu = FormUtil.getOperationsMenu([{ label: "Create " + ELNDictionary.getExperimentKindName(projectIdentifier), event: function() { + var $operationsMenu = FormUtil.getOperationsMenu([{ label: "Create " + experimentKindName, event: function() { showSelectExperimentType(); }}]); toolbarModel.push({ component : $operationsMenu, tooltip: "Extra operations" }); diff --git a/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/views/SampleForm/SampleFormView.js b/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/views/SampleForm/SampleFormView.js index a4b303cd98b51654a4aa5817221b0ef10342324e..f6b3bcf2857e4c4d2b8e96ffa4a9b9143964d8da 100644 --- a/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/views/SampleForm/SampleFormView.js +++ b/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/views/SampleForm/SampleFormView.js @@ -361,6 +361,7 @@ function SampleFormView(sampleFormController, sampleFormModel) { sample = results.objects[rIdx]; } } + _this._sampleFormModel.sample.properties = sample.properties; if(_this._sampleFormModel.views.header) { _this._sampleFormModel.views.header.empty(); diff --git a/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/views/SideMenu/SideMenuWidgetView.js b/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/views/SideMenu/SideMenuWidgetView.js index c7f77e5c4cd57151ba75599ade67fbfcd3b60f52..ecd57517a2c01f717c852ea89b205c396594b486 100644 --- a/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/views/SideMenu/SideMenuWidgetView.js +++ b/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/views/SideMenu/SideMenuWidgetView.js @@ -107,12 +107,7 @@ function SideMenuWidgetView(sideMenuWidgetController, sideMenuWidgetModel) { var logoutButton = FormUtil.getButtonWithIcon("glyphicon-off", function() { $('body').addClass('bodyLogin'); - var logoutTemplate = mainController.profile.singleSignOnUrlLogoutTemplate; - if (logoutTemplate) { - window.location = logoutTemplate.replace("${host}", window.location.hostname); - } else { - mainController.serverFacade.logout(); - } + mainController.serverFacade.logout(); }); var $searchForm = $("<form>", { "onsubmit": "return false;" }) diff --git a/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/lib/jexcel/VERSION.txt b/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/lib/jexcel/VERSION.txt index 4e48a183b1e04b13e790490acc20f37e0b25afd3..ba5b1b9b6e69d49a787cdf726c7f9f53f98e6279 100644 --- a/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/lib/jexcel/VERSION.txt +++ b/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/lib/jexcel/VERSION.txt @@ -1 +1 @@ -3.4.1 from Master 2019.08.08 \ No newline at end of file +3.4.4 from Master 2019.08.27 \ No newline at end of file diff --git a/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/lib/jexcel/jexcel.css b/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/lib/jexcel/jexcel.css index 55d0153866db4889f77742f1f0984696bbf9eb29..8ee0a9794bca5a00ed1026ceb7c6dc11a8564b8d 100644 --- a/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/lib/jexcel/jexcel.css +++ b/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/lib/jexcel/jexcel.css @@ -1,5 +1,5 @@ /** - * (c) jExcel v3.4.1 + * (c) jExcel v3.4.4 * * Author: Paul Hodel <paul.hodel@gmail.com> * Website: https://bossanova.uk/jexcel/ @@ -76,6 +76,11 @@ z-index:2000; } +.with-toolbar .jexcel > thead > tr > td +{ + top:42px; +} + .jexcel > thead.draggable > tr > td::before { content:'\00a0'; @@ -635,44 +640,21 @@ background-color:#ddd; } -/*.copying -{ - border-top:1px solid #ccc !important; - border-left:1px solid #ccc !important; - border-right:1px solid #fff !important; - border-bottom:1px solid #fff !important; -} - -.copying-top -{ - border-top:1px solid transparent !important; -} - -.copying-bottom +.jexcel_hidden_index tr > td:first-child, .jexcel_hidden_index colgroup > col:first-child { - border-bottom:1px solid transparent !important; -} - -.copying-left -{ - border-left:1px solid transparent !important; -} - -.copying-right -{ - border-right:1px solid transparent !important; + display:none; } -.copying +.jexcel_border { - background: linear-gradient(white, white) padding-box, repeating-linear-gradient(-45deg, black 0, black 25%, transparent 0, transparent 50%) 0 / .6em .6em !important; - animation: ants 12s linear infinite; + position:absolute; + border:1px solid transparent; + pointer-events: none; } -@keyframes ants +.jexcel_copying { - to - { - background-position: 50% 50% - } -}*/ + border:1px solid transparent; + border-image: url('data:image/gif;base64,R0lGODlhCgAKAJECAAAAAP///////wAAACH/C05FVFNDQVBFMi4wAwEAAAAh/wtYTVAgRGF0YVhNUDw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6OEI5RDc5MTFDNkE2MTFFM0JCMDZEODI2QTI4MzJBOTIiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6OEI5RDc5MTBDNkE2MTFFM0JCMDZEODI2QTI4MzJBOTIiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNiAoTWFjaW50b3NoKSI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuZGlkOjAyODAxMTc0MDcyMDY4MTE4MDgzQzNDMjA5MzREQ0ZDIiBzdFJlZjpkb2N1bWVudElEPSJ4bXAuZGlkOjAyODAxMTc0MDcyMDY4MTE4MDgzQzNDMjA5MzREQ0ZDIi8+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+Af/+/fz7+vn49/b19PPy8fDv7u3s6+rp6Ofm5eTj4uHg397d3Nva2djX1tXU09LR0M/OzczLysnIx8bFxMPCwcC/vr28u7q5uLe2tbSzsrGwr66trKuqqainpqWko6KhoJ+enZybmpmYl5aVlJOSkZCPjo2Mi4qJiIeGhYSDgoGAf359fHt6eXh3dnV0c3JxcG9ubWxramloZ2ZlZGNiYWBfXl1cW1pZWFdWVVRTUlFQT05NTEtKSUhHRkVEQ0JBQD8+PTw7Ojk4NzY1NDMyMTAvLi0sKyopKCcmJSQjIiEgHx4dHBsaGRgXFhUUExIREA8ODQwLCgkIBwYFBAMCAQAAIfkEBQoAAgAsAAAAAAoACgAAAhWEERkn7W3ei7KlagMWF/dKgYeyGAUAIfkEBQoAAgAsAAAAAAoACgAAAg+UYwLJ7RnQm7QmsCyVKhUAIfkEBQoAAgAsAAAAAAoACgAAAhCUYgLJHdiinNSAVfOEKoUCACH5BAUKAAIALAAAAAAKAAoAAAIRVISAdusPo3RAzYtjaMIaUQAAIfkEBQoAAgAsAAAAAAoACgAAAg+MDiem7Q8bSLFaG5il6xQAIfkEBQoAAgAsAAAAAAoACgAAAg+UYRLJ7QnQm7SmsCyVKhUAIfkEBQoAAgAsAAAAAAoACgAAAhCUYBLJDdiinNSEVfOEKoECACH5BAUKAAIALAAAAAAKAAoAAAIRFISBdusPo3RBzYsjaMIaUQAAOw==') 8% repeat; + pointer-events: none; +} \ No newline at end of file diff --git a/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/lib/jexcel/jexcel.js b/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/lib/jexcel/jexcel.js index eb9fd03d45440fc121bae03209cdf633dc6a8265..db91aa915d0188ac02cd4b84a1371e6981d07f93 100644 --- a/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/lib/jexcel/jexcel.js +++ b/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/lib/jexcel/jexcel.js @@ -1,5 +1,5 @@ /** - * (c) jExcel v3.4.1 + * (c) jExcel v3.4.4 * * Author: Paul Hodel <paul.hodel@gmail.com> * Website: https://bossanova.uk/jexcel/ @@ -111,10 +111,14 @@ var jexcel = (function(el, options) { meta: null, // Style style:null, + // Execute formulas + parseFormulas:true, + autoIncrement:true, // Event handles onload:null, onchange:null, onbeforechange:null, + onafterchanges:null, onbeforeinsertrow: null, oninsertrow:null, onbeforeinsertcolumn: null, @@ -175,7 +179,7 @@ var jexcel = (function(el, options) { noCellsSelected: 'No cells selected', }, // About message - about:"jExcel CE Spreadsheet\nVersion 3.4.1\nAuthor: Paul Hodel <paul.hodel@gmail.com>\nWebsite: https://jexcel.net/v3", + about:"jExcel CE Spreadsheet\nVersion 3.4.4\nAuthor: Paul Hodel <paul.hodel@gmail.com>\nWebsite: https://jexcel.net/v3", }; // Loading initial configuration from user @@ -220,6 +224,7 @@ var jexcel = (function(el, options) { obj.style = []; obj.meta = []; obj.data = null; + obj.border = {}; // Internal controllers obj.cursor = null; @@ -516,9 +521,6 @@ var jexcel = (function(el, options) { // Fullscreen if (obj.options.fullscreen == true) { el.classList.add('fullscreen'); - if (obj.options.toolbar) { - el.classList.add('with-toolbar'); - } } else { // Overflow if (obj.options.tableOverflow == true) { @@ -533,6 +535,11 @@ var jexcel = (function(el, options) { } } + // With toolbars + if (obj.options.toolbar) { + el.classList.add('with-toolbar'); + } + // Actions if (obj.options.columnDrag == true) { obj.thead.classList.add('draggable'); @@ -830,7 +837,7 @@ var jexcel = (function(el, options) { td.appendChild(img); } } else { - if ((''+value).substr(0,1) == '=') { + if ((''+value).substr(0,1) == '=' && obj.options.parseFormulas == true) { value = obj.executeFormula(value, i, j) } if (obj.options.columns[i].mask) { @@ -1469,6 +1476,13 @@ var jexcel = (function(el, options) { } } + // On edition end + if (! obj.ignoreEvents) { + if (typeof(obj.options.oneditionend) == 'function') { + obj.options.oneditionend(el, cell, x, y, value, save); + } + } + // Update values var ignoreEvents = obj.ignoreEvents ? true : false; var ignoreHistory = obj.ignoreHistory ? true : false; @@ -1508,6 +1522,13 @@ var jexcel = (function(el, options) { // Restore value cell.innerHTML = obj.edition[1]; + + // On edition end + if (! obj.ignoreEvents) { + if (typeof(obj.options.oneditionend) == 'function') { + obj.options.oneditionend(el, cell, x, y, value, save); + } + } } // Remove editor class @@ -1515,14 +1536,37 @@ var jexcel = (function(el, options) { // Finish edition obj.edition = null; + } - // On edition end - if (! obj.ignoreEvents) { - if (typeof(obj.options.oneditionend) == 'function') { - obj.options.oneditionend(el, cell, x, y, value); - } - } - }, + /** + * Get the cell object + * + * @param object cell + * @return string value + */ + obj.getCell = function(cell) { + // Convert in case name is excel liked ex. A10, BB92 + cell = jexcel.getIdFromColumnName(cell, true); + var x = cell[0]; + var y = cell[1]; + + return obj.records[y][x]; + } + + /** + * Get label + * + * @param object cell + * @return string value + */ + obj.getLabel = function(cell) { + // Convert in case name is excel liked ex. A10, BB92 + cell = jexcel.getIdFromColumnName(cell, true); + var x = cell[0]; + var y = cell[1]; + + return obj.records[y][x].innerHTML; + } /** * Get the value from a cell @@ -1638,6 +1682,13 @@ var jexcel = (function(el, options) { // Update table with custom configurations if applicable obj.updateTable(); + + // On after change + if (! obj.ignoreEvents) { + if (typeof(obj.options.onafterchanges) == 'function') { + obj.options.onafterchanges(el, records, value); + } + } } /** @@ -1664,6 +1715,13 @@ var jexcel = (function(el, options) { // Update table with custom configurations if applicable obj.updateTable(); + + // On after change + if (! obj.ignoreEvents) { + if (typeof(obj.options.onafterchanges) == 'function') { + obj.options.onafterchanges(el, records, value); + } + } } /** @@ -1689,6 +1747,13 @@ var jexcel = (function(el, options) { records:records, selection:obj.selectedCell, }); + + // On after change + if (! obj.ignoreEvents) { + if (typeof(obj.options.onafterchanges) == 'function') { + obj.options.onafterchanges(el, records); + } + } } } @@ -1710,7 +1775,7 @@ var jexcel = (function(el, options) { var val = obj.options.onbeforechange(el, obj.records[y][x], x, y, value); // If you return something this will overwrite the value - if (val != 'undefined') { + if (val != undefined) { value = val; } } @@ -1776,13 +1841,9 @@ var jexcel = (function(el, options) { } } else { // Update data and cell - if (obj.options.columns[x].autoCasting != false) { - obj.options.data[y][x] = (value && Number(value) == value) ? Number(value) : value; - } else { - obj.options.data[y][x] = value; - } + obj.options.data[y][x] = value; // Label - if (('' + value).substr(0,1) == '=') { + if (('' + value).substr(0,1) == '=' && obj.options.parseFormulas == true) { value = obj.executeFormula(value, x, y); } if (obj.options.columns[x].mask) { @@ -1881,7 +1942,7 @@ var jexcel = (function(el, options) { var value = data[posy][posx]; } - if (value && t0 == t1) { + if (value && t0 == t1 && obj.options.autoIncrement == true) { if (obj.options.columns[i].type == 'text' || obj.options.columns[i].type == 'number') { if ((''+value).substr(0,1) == '=') { var tokens = value.match(/([A-Z]+[0-9]+)/g); @@ -1943,6 +2004,42 @@ var jexcel = (function(el, options) { // Update table with custom configuration if applicable obj.updateTable(); + + // On after change + if (! obj.ignoreEvents) { + if (typeof(obj.options.onafterchanges) == 'function') { + obj.options.onafterchanges(el, records); + } + } + } + + obj.setBorder = function(x1, y1, x2, y2, border) { + if (! obj.border[border]) { + obj.border[border] = document.createElement('div'); + obj.border[border].classList.add('jexcel_border'); + if (border == 'copying') { + obj.border[border].classList.add('jexcel_copying'); + } else { + obj.border[border].style.borderColor = border; + //obj.border[border].style.backgroundColor = 'rgba(0,0,0,0.1)'; + } + el.appendChild(obj.border[border]); + } + + // Get last cell + var first = obj.records[x1][y1].getBoundingClientRect(); + var x1 = first.left; + var y1 = first.top; + + var last = obj.records[x2][y2].getBoundingClientRect(); + var w1 = (last.left + last.width) - first.left - 2; + var h1 = (last.top + last.height) - first.top - 2; + + // Place the corner in the correct place + obj.border[border].style.top = y1 + 'px'; + obj.border[border].style.left = x1 + 'px'; + obj.border[border].style.width = w1 + 'px'; + obj.border[border].style.height = h1 + 'px'; } /** @@ -2510,18 +2607,26 @@ var jexcel = (function(el, options) { /** * Get the row height * - * @param row - row number (first column is: 0) + * @param row - row number (first row is: 0) * @return height - current row height */ obj.getHeight = function(row) { if (! row) { + // Get height of all rows + var data = []; + for (var j = 0; j < obj.rows.length; j++) { + var h = obj.rows[j].style.height; + if (h) { + data[j] = h; + } + } } else { - // In case the column is an object + // In case the row is an object if (typeof(row) == 'object') { row = $(row).getAttribute('data-y'); } - data = obj.rows[row].getAttribute('height') + var data = obj.rows[row].style.height; } return data; @@ -3941,14 +4046,14 @@ var jexcel = (function(el, options) { * Show index column */ obj.showIndex = function() { - obj.colgroupContainer.children[0].width = 40; + obj.table.classList.remove('jexcel_hidden_index'); } /** * Hide index column */ obj.hideIndex = function() { - obj.colgroupContainer.children[0].width = 0; + obj.table.classList.add('jexcel_hidden_index'); } /** @@ -4108,11 +4213,13 @@ var jexcel = (function(el, options) { tokensUpdate(tokens); } + // String + var evalstring = ''; + // Get tokens var tokens = expression.match(/([A-Z]+[0-9]+)/g); if (tokens) { - var evalstring = ""; for (var i = 0; i < tokens.length; i++) { // Keep chain if (! obj.formula[tokens[i]]) { @@ -4125,8 +4232,6 @@ var jexcel = (function(el, options) { // Do not calculate again if (eval('typeof(' + tokens[i] + ') == "undefined"')) { - // Declaretion - evalstring += "var " + tokens[i] + " = null;"; // Coords var position = jexcel.getIdFromColumnName(tokens[i], 1); // Get value @@ -4142,32 +4247,24 @@ var jexcel = (function(el, options) { value = obj.executeFormula(value, position[0], position[1]); } // Type! - if ((''+value).trim() == '' || value != Number(value)) { - // Trying any formatted number - var number = ('' + value); - var decimal = obj.options.columns[position[0]].decimal || '.'; - number = number.split(decimal); - number[0] = number[0].match(/[+-]?[0-9]/g); - if (number[0]) { - number[0] = number[0].join(''); - } - if (number[1]) { - number[1] = number[1].match(/[0-9]*/g).join(''); - } - // Got a valid number - if (number[0] && Number(number[0]) >= 0) { - if (! number[1]) { - evalstring += "var " + tokens[i] + " = " + number[0] + ".00;"; + if ((''+value).trim() == '') { + // Null + evalstring += "var " + tokens[i] + " = null;"; + } else { + if (value == Number(value)) { + // Number + evalstring += "var " + tokens[i] + " = " + value + ";"; + } else { + // Trying any formatted number + var number = null; + if (number = obj.parseNumber(value, position[0])) { + // Render as number + evalstring += "var " + tokens[i] + " = " + number + ";"; } else { - evalstring += "var " + tokens[i] + " = " + number[0] + '.' + number[1] + ";"; + // Render as string + evalstring += "var " + tokens[i] + " = '" + value + "';"; } - } else { - // Render as string - evalstring += "var " + tokens[i] + " = '" + value + "';"; } - } else { - // Number - evalstring += "var " + tokens[i] + " = " + value + ";"; } } } @@ -4185,6 +4282,38 @@ var jexcel = (function(el, options) { return res; } + /** + * Trying to extract a number from a string + **/ + obj.parseNumber = function(value, columnNumber) { + // Decimal point + var decimal = columnNumber && obj.options.columns[columnNumber].decimal ? obj.options.columns[columnNumber].decimal : '.'; + + // Parse both parts of the number + var number = ('' + value); + number = number.split(decimal); + number[0] = number[0].match(/[+-]?[0-9]/g); + if (number[0]) { + number[0] = number[0].join(''); + } + if (number[1]) { + number[1] = number[1].match(/[0-9]*/g).join(''); + } + + // Is a valid number + if (number[0] && Number(number[0]) >= 0) { + if (! number[1]) { + var value = Number(number[0] + '.00'); + } else { + var value = Number(number[0] + '.' + number[1]); + } + } else { + var value = null; + } + + return value; + } + /** * Get row number */ @@ -4908,6 +5037,9 @@ var jexcel = (function(el, options) { } else if (quantyOfPages - obj.pageNumber < 5) { var startNumber = quantyOfPages - 9; var finalNumber = quantyOfPages; + if (startNumber < 1) { + startNumber = 1; + } } else { var startNumber = obj.pageNumber - 4; var finalNumber = obj.pageNumber + 5; @@ -5081,9 +5213,7 @@ var jexcel = (function(el, options) { obj.textarea.value = str; } obj.textarea.select(); - jexcel.copyControls.enabled = false; document.execCommand("copy"); - jexcel.copyControls.enabled = true; } // Keep data @@ -5091,24 +5221,6 @@ var jexcel = (function(el, options) { // Keep non visible information obj.hashString = obj.hash(obj.textarea.value); - // Highlight - /*for (var i = 0; i < obj.highlighted.length; i++) { - obj.highlighted[i].classList.add('copying'); - - if (obj.highlighted[i].classList.contains('highlight-top')) { - obj.highlighted[i].classList.add('copying-top'); - } - if (obj.highlighted[i].classList.contains('highlight-right')) { - obj.highlighted[i].classList.add('copying-right'); - } - if (obj.highlighted[i].classList.contains('highlight-bottom')) { - obj.highlighted[i].classList.add('copying-bottom'); - } - if (obj.highlighted[i].classList.contains('highlight-left')) { - obj.highlighted[i].classList.add('copying-left'); - } - }*/ - return str; } @@ -5165,7 +5277,7 @@ var jexcel = (function(el, options) { var columnName = jexcel.getColumnNameFromId([colIndex, rowIndex]); newStyle[columnName] = style[styleIndex]; oldStyle[columnName] = obj.getStyle(columnName); - obj.records[rowIndex][colIndex].style = style[styleIndex]; + obj.records[rowIndex][colIndex].setAttribute('style', style[styleIndex]); styleIndex++ } i++; @@ -5198,13 +5310,13 @@ var jexcel = (function(el, options) { oldStyle:oldStyle, }); + // Update table + obj.updateTable(); + // Paste event if (typeof(obj.options.onpaste) == 'function') { obj.options.onpaste(el, records); } - - // Update table - obj.updateTable(); } } @@ -5872,8 +5984,6 @@ jexcel.destroy = function(element, destroyEventHandlers) { document.removeEventListener("mousemove", jexcel.mouseMoveControls); document.removeEventListener("mouseover", jexcel.mouseOverControls); document.removeEventListener("dblclick", jexcel.doubleClickControls); - document.removeEventListener("copy", jexcel.copyControls); - document.removeEventListener("cut", jexcel.cutControls); document.removeEventListener("paste", jexcel.pasteControls); document.removeEventListener("contextmenu", jexcel.contextMenuControls); document.removeEventListener("touchstart", jexcel.touchStartControls); @@ -5891,8 +6001,6 @@ jexcel.build = function() { document.addEventListener("mousemove", jexcel.mouseMoveControls); document.addEventListener("mouseover", jexcel.mouseOverControls); document.addEventListener("dblclick", jexcel.doubleClickControls); - document.addEventListener("copy", jexcel.copyControls); - document.addEventListener("cut", jexcel.cutControls); document.addEventListener("paste", jexcel.pasteControls); document.addEventListener("contextmenu", jexcel.contextMenuControls); document.addEventListener("touchstart", jexcel.touchStartControls); @@ -6158,6 +6266,25 @@ jexcel.keyDownControls = function(e) { // Ctrl + Z jexcel.current.undo(); e.preventDefault(); + } else if (e.which == 67) { + // Ctrl + C + jexcel.current.copy(true); + e.preventDefault(); + } else if (e.which == 67) { + // Ctrl + C + jexcel.current.copy(true); + e.preventDefault(); + } else if (e.which == 88) { + // Ctrl + X + if (jexcel.current.options.editable == true) { + jexcel.cutControls(); + } else { + jexcel.copyControls(); + } + e.preventDefault(); + } else if (e.which == 86) { + // Ctrl + V + jexcel.pasteControls(); } } else { if (jexcel.current.selectedCell) { @@ -6177,6 +6304,9 @@ jexcel.keyDownControls = function(e) { // Start edition jexcel.current.openEditor(jexcel.current.records[rowId][columnId], true); } + } else if (e.keyCode == 113) { + // Start edition with current content F2 + jexcel.current.openEditor(jexcel.current.records[rowId][columnId], false); } else if ((e.keyCode == 8) || (e.keyCode >= 48 && e.keyCode <= 57) || (e.keyCode >= 65 && e.keyCode <= 90) || @@ -6188,9 +6318,6 @@ jexcel.keyDownControls = function(e) { if (jexcel.current.options.columns[columnId].type == 'calendar') { e.preventDefault(); } - } else if (e.keyCode == 113) { - // Start edition with current content F2 - jexcel.current.openEditor(jexcel.current.records[rowId][columnId], false); } } } @@ -6789,11 +6916,13 @@ jexcel.cutControls = function(e) { jexcel.pasteControls = function(e) { if (jexcel.current && jexcel.current.selectedCell) { if (! jexcel.current.edition) { - if (e.clipboardData) { - if (jexcel.current.options.editable == true) { + if (jexcel.current.options.editable == true) { + if (e && e.clipboardData) { jexcel.current.paste(jexcel.current.selectedCell[0], jexcel.current.selectedCell[1], e.clipboardData.getData('text')); + e.preventDefault(); + } else if (window.clipboardData) { + jexcel.current.paste(jexcel.current.selectedCell[0], jexcel.current.selectedCell[1], window.clipboardData.getData('text')); } - e.preventDefault(); } } } @@ -6871,8 +7000,6 @@ jexcel.touchEndControls = function(e) { } } -jexcel.copyControls.enabled = true; - /** * Jquery Support */ diff --git a/openbis_standard_technologies/dist/core-plugins/eln-lims/1/dss/reporting-plugins/rc-exports-api/plugin.properties b/openbis_standard_technologies/dist/core-plugins/eln-lims/1/dss/reporting-plugins/rc-exports-api/plugin.properties index 3b491463420d4b44d08eefafbf5d543a7b2ef812..682300955e379a9321d06d8acf2b2a456c0399c6 100644 --- a/openbis_standard_technologies/dist/core-plugins/eln-lims/1/dss/reporting-plugins/rc-exports-api/plugin.properties +++ b/openbis_standard_technologies/dist/core-plugins/eln-lims/1/dss/reporting-plugins/rc-exports-api/plugin.properties @@ -1,8 +1,8 @@ label = Research Collection Exports API class = ch.systemsx.cisd.openbis.dss.generic.server.plugins.jython.JythonIngestionService script-path = rcExports.py -limit-data-size-megabytes = 4000 -service-document-url=https://test.research-collection.ethz.ch/swordv2/servicedocument realm=SWORD2 -user=youruser -password=yourpassword \ No newline at end of file +limit-data-size-megabytes=${rc-exports-api-limit-data-size-megabytes:4000} +service-document-url=${rc-exports-api-service-document-url:https://test.research-collection.ethz.ch/swordv2/servicedocument} +user=${rc-exports-api-user} +password=${rc-exports-api-password} \ No newline at end of file diff --git a/openbis_standard_technologies/dist/core-plugins/eln-lims/1/dss/reporting-plugins/zenodo-exports-api/plugin.properties b/openbis_standard_technologies/dist/core-plugins/eln-lims/1/dss/reporting-plugins/zenodo-exports-api/plugin.properties index 46637e2ba22178234ec544460bef0591959e8a9a..cc77ea6f8cfc5d2c91b4292696d89990276b2b7b 100644 --- a/openbis_standard_technologies/dist/core-plugins/eln-lims/1/dss/reporting-plugins/zenodo-exports-api/plugin.properties +++ b/openbis_standard_technologies/dist/core-plugins/eln-lims/1/dss/reporting-plugins/zenodo-exports-api/plugin.properties @@ -1,7 +1,6 @@ label = Zenodo Exports API class = ch.systemsx.cisd.openbis.dss.generic.server.plugins.jython.JythonIngestionService script-path = zenodoExports.py -service-document-url=https://test.research-collection.ethz.ch/swordv2/servicedocument -limit-data-size-megabytes = 50000 -zenodoUrl=https://localhost -accessToken= \ No newline at end of file +limit-data-size-megabytes=${zenodo-exports-api-limit-data-size-megabytes:4000} +zenodoUrl=${zenodo-exports-api-zenodoUrl:https://localhost} +accessToken=${zenodo-exports-api-accessToken} \ No newline at end of file diff --git a/openbis_standard_technologies/dist/server/service.properties b/openbis_standard_technologies/dist/server/service.properties index 0e38ec4f10eea30ad869f6b8a5e1f88fb63a0d93..bcbe9b8b1c22a17b32e8edca36d1c8986073f95d 100644 --- a/openbis_standard_technologies/dist/server/service.properties +++ b/openbis_standard_technologies/dist/server/service.properties @@ -173,6 +173,14 @@ session-timeout = 720 # Session time (in minutes) in case of presents of file etc/nologin.html. Should be < 30. #session-timeout-no-login = 10 +# Maximum number of sessions allowed per user. Zero means unlimited number of sessions. Default value is 1. +# max-number-of-sessions-per-user = 1 + +# Comma separated list of users allowed to have unlimited number of sessions. Default: Empty list. +# Note: The DSS (user 'etlserver' by default, see property 'username' of DSS service.properties) +# should be added to this list. +# users-with-unrestricted-number-of-sessions = + # --------------------------------------------------------------------------- # Business rules configuration # --------------------------------------------------------------------------- diff --git a/pybis/src/python/pybis/__init__.py b/pybis/src/python/pybis/__init__.py index 4aa2b995675dc04f0874952732c359ada99bdd0f..b2c5fe70dd5b62fe2b565da8abc8041646f66371 100644 --- a/pybis/src/python/pybis/__init__.py +++ b/pybis/src/python/pybis/__init__.py @@ -1,7 +1,7 @@ name = 'pybis' __author__ = 'Swen Vermeul' __email__ = 'swen@ethz.ch' -__version__ = '1.9.1' +__version__ = '1.9.2' from . import pybis from .pybis import Openbis diff --git a/pybis/src/python/setup.py b/pybis/src/python/setup.py index 723fcda825c708e6a385739579d327aa8d44c80e..b7f8b9853da7d66554103c7c93286cd344a3eb3d 100644 --- a/pybis/src/python/setup.py +++ b/pybis/src/python/setup.py @@ -11,7 +11,7 @@ with open("README.md", "r", encoding="utf-8") as fh: setup( name='PyBIS', - version= '1.9.1', + version= '1.9.2', author='Swen Vermeul • ID SIS • ETH Zürich', author_email='swen@ethz.ch', description='openBIS connection and interaction, optimized for using with Jupyter', diff --git a/screening/source/java/service.properties b/screening/source/java/service.properties index 54ea1996dee1c979ffd2cda4c6cc582932af8687..c8f492aa0e35bdad5ae098dfcecb4d523f470f48 100644 --- a/screening/source/java/service.properties +++ b/screening/source/java/service.properties @@ -2,6 +2,7 @@ authentication-service = dummy-authentication-service # The time after which an inactive session is expired by the service (in minutes). session-timeout = 720 +max-number-of-sessions-per-user = 0 session-workspace-root-dir = targets/session-workspace diff --git a/ui-test/resource/service.properties b/ui-test/resource/service.properties index ca31420f298374fc16fb6baa90a4338d775ee5d3..c9a308a53d4b229aea6107a7e2f6d041a451ed3e 100644 --- a/ui-test/resource/service.properties +++ b/ui-test/resource/service.properties @@ -2,6 +2,7 @@ authentication-service = dummy-authentication-service # The time after which an inactive session is expired by the service (in minutes). session-timeout = 720 +max-number-of-sessions-per-user = 0 # Authorization # Supported: 'no-authorization' and 'active-authorization'