From 51daaca892f2e13bc46e80c7252b8ffa2e2ec5e8 Mon Sep 17 00:00:00 2001 From: anhefti Date: Mon, 8 May 2023 19:43:37 +0200 Subject: [PATCH 01/27] prepare for next release 1.5.1 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index a8627936..bba665d8 100644 --- a/pom.xml +++ b/pom.xml @@ -18,7 +18,7 @@ jar - 1.5.0 + 1.5.1 ${sebserver-version} ${sebserver-version} UTF-8 From 60f98a48fe65b3114f56a2e3077c90e9037afebb Mon Sep 17 00:00:00 2001 From: anhefti Date: Mon, 8 May 2023 20:11:41 +0200 Subject: [PATCH 02/27] fix docu? --- docs/requirements.txt | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/docs/requirements.txt b/docs/requirements.txt index c896d4df..e6e58856 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -1 +1,4 @@ -docutils<0.18 \ No newline at end of file +docutils<0.18 +sphinx==5.3.0 +sphinx_rtd_theme==1.1.1 +readthedocs-sphinx-search==0.1.1 \ No newline at end of file From 2c5f47c7e957c390e0a8bd095b324282467424d2 Mon Sep 17 00:00:00 2001 From: anhefti Date: Mon, 8 May 2023 20:15:45 +0200 Subject: [PATCH 03/27] fix docu? --- docs/requirements.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/requirements.txt b/docs/requirements.txt index e6e58856..eb2a5188 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -1,4 +1,5 @@ docutils<0.18 sphinx==5.3.0 sphinx_rtd_theme==1.1.1 -readthedocs-sphinx-search==0.1.1 \ No newline at end of file +readthedocs-sphinx-search==0.1.1 +urllib3==1.26.13 \ No newline at end of file From a896737f5bdc595de78c776dd30f9877e92754b9 Mon Sep 17 00:00:00 2001 From: anhefti Date: Tue, 9 May 2023 14:13:09 +0200 Subject: [PATCH 04/27] patch fixes monitoring sorting and zoom token refresh --- .../session/ClientConnectionTable.java | 35 +++++++++++------- .../gui/service/session/ColorData.java | 11 +----- .../proctoring/ZoomProctoringService.java | 37 +++++++++++++++++-- 3 files changed, 58 insertions(+), 25 deletions(-) diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/service/session/ClientConnectionTable.java b/src/main/java/ch/ethz/seb/sebserver/gui/service/session/ClientConnectionTable.java index 581da9f0..9e766723 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/service/session/ClientConnectionTable.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/service/session/ClientConnectionTable.java @@ -15,6 +15,7 @@ import java.util.Comparator; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedHashMap; +import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; @@ -69,8 +70,6 @@ public final class ClientConnectionTable implements FullPageMonitoringGUIUpdate private static final Logger log = LoggerFactory.getLogger(ClientConnectionTable.class); private static final int BOTTOM_PADDING = 20; - //private static final int[] TABLE_PROPORTIONS = new int[] { 3, 3, 2, 1 }; - //private static final int NUMBER_OF_NONE_INDICATOR_COLUMNS = 3; private static final String INDICATOR_NAME_TEXT_KEY_PREFIX = "sebserver.exam.indicator.type.description."; @@ -101,12 +100,13 @@ public final class ClientConnectionTable implements FullPageMonitoringGUIUpdate private final Map clientGroupMapping; private final Table table; private final ColorData colorData; + private final List sortList = new ArrayList<>(); private final Function localizedClientConnectionStatusNameFunction; private Consumer selectionListener; private int tableWidth; private boolean needsSort = false; - private LinkedHashMap tableMapping; + private final LinkedHashMap tableMapping; private final Set toDelete = new HashSet<>(); private final Set toUpdateStatic = new HashSet<>(); private final Set duplicates = new HashSet<>(); @@ -432,13 +432,15 @@ public final class ClientConnectionTable implements FullPageMonitoringGUIUpdate } private void sortTable() { - this.tableMapping = this.tableMapping.entrySet() - .stream() - .sorted(Entry.comparingByValue()) - .collect(Collectors.toMap( - Entry::getKey, - Entry::getValue, - (e1, e2) -> e1, LinkedHashMap::new)); + this.sortList.clear(); + this.sortList.addAll(this.tableMapping.values()); + Collections.sort(this.sortList); + this.tableMapping.clear(); + final Iterator iterator = this.sortList.iterator(); + while (iterator.hasNext()) { + final UpdatableTableItem item = iterator.next(); + this.tableMapping.put(item.connectionId, item); + } } private void notifySelectionChange() { @@ -494,7 +496,7 @@ public final class ClientConnectionTable implements FullPageMonitoringGUIUpdate @Override public boolean sebVersionDenied() { - return this.monitoringData.sebVersionDenied; + return (this.monitoringData == null) ? false : this.monitoringData.sebVersionDenied; } @Override @@ -559,7 +561,7 @@ public final class ClientConnectionTable implements FullPageMonitoringGUIUpdate } private void updateNotifications(final TableItem tableItem) { - if (BooleanUtils.isTrue(this.monitoringData.pendingNotification)) { + if (this.monitoringData != null && BooleanUtils.isTrue(this.monitoringData.pendingNotification)) { tableItem.setImage(0, WidgetFactory.ImageIcon.NOTIFICATION.getImage(ClientConnectionTable.this.table.getDisplay())); tableItem.setBackground(0, ClientConnectionTable.this.colorData.color2); @@ -595,7 +597,7 @@ public final class ClientConnectionTable implements FullPageMonitoringGUIUpdate final Long id = entry.getKey(); final String displayValue = entry.getValue(); final IndicatorData indicatorData = ClientConnectionTable.this.indicatorMapping.get(id); - if (indicatorData == null) { + if (indicatorData == null || this.monitoringData == null) { return; } @@ -654,6 +656,9 @@ public final class ClientConnectionTable implements FullPageMonitoringGUIUpdate } int notificationWeight() { + if (this.monitoringData == null) { + return 0; + } return BooleanUtils.isTrue(this.monitoringData.pendingNotification) || (this.monitoringData.status.establishedStatus && this.marked) ? -1 : 0; } @@ -663,6 +668,9 @@ public final class ClientConnectionTable implements FullPageMonitoringGUIUpdate } int thresholdsWeight() { + if (this.monitoringData != null && !this.monitoringData.status.clientActiveStatus) { + return 0; + } return -this.thresholdsWeight; } @@ -690,6 +698,7 @@ public final class ClientConnectionTable implements FullPageMonitoringGUIUpdate } String getConnectionIdentifier() { + if (this.staticData != null && this.staticData.userSessionId != null) { return this.staticData.userSessionId; } diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/service/session/ColorData.java b/src/main/java/ch/ethz/seb/sebserver/gui/service/session/ColorData.java index 52bf90b6..dd857e6f 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/service/session/ColorData.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/service/session/ColorData.java @@ -69,20 +69,13 @@ public class ColorData { switch (entry.getStatus()) { case CONNECTION_REQUESTED: case AUTHENTICATED: { - if (entry.incidentFlag() > 0) { - return -1; - } - return 1; + return 0; } case ACTIVE: { - final int incidentFlag = entry.incidentFlag(); - if (incidentFlag > 0) { - return -incidentFlag; - } return 2; } case CLOSED: - return 4; + return 3; default: return 5; } diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/proctoring/ZoomProctoringService.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/proctoring/ZoomProctoringService.java index 5206c7b0..38b3168c 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/proctoring/ZoomProctoringService.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/proctoring/ZoomProctoringService.java @@ -1016,6 +1016,37 @@ public class ZoomProctoringService implements ExamProctoringService { } } + @Override + boolean isValid(final ProctoringServiceSettings proctoringSettings) { + final boolean valid = super.isValid(proctoringSettings); + if (!valid) { + return false; + } + + try { + final OAuth2RestTemplate oAuth2RestTemplate = (OAuth2RestTemplate) super.restTemplate; + final OAuth2AccessToken accessToken = oAuth2RestTemplate.getAccessToken(); + if (accessToken == null) { + return false; + } + + final boolean expired = accessToken.isExpired(); + if (expired) { + return false; + } + + final int expiresIn = accessToken.getExpiresIn(); + if (expiresIn < 60) { + return false; + } + + return true; + } catch (final Exception e) { + log.error("Failed to verify Zoom OAuth2RestTemplate status", e); + return false; + } + } + @Override public HttpHeaders getHeaders() { final HttpHeaders httpHeaders = new HttpHeaders(); @@ -1123,14 +1154,15 @@ public class ZoomProctoringService implements ExamProctoringService { @Override public boolean supportsRefresh(final OAuth2ProtectedResourceDetails resource) { - return false; + return true; } @Override public OAuth2AccessToken refreshAccessToken(final OAuth2ProtectedResourceDetails resource, final OAuth2RefreshToken refreshToken, final AccessTokenRequest request) throws UserRedirectRequiredException { - return null; + + return this.obtainAccessToken(resource, request); } @Override @@ -1140,7 +1172,6 @@ public class ZoomProctoringService implements ExamProctoringService { final ClientCredentialsResourceDetails resource = (ClientCredentialsResourceDetails) details; return retrieveToken(request, resource, getParametersForTokenRequest(resource), new HttpHeaders()); - } private MultiValueMap getParametersForTokenRequest( From 56120168b11570fb65e1295286fd9f9b4da33824 Mon Sep 17 00:00:00 2001 From: anhefti Date: Thu, 11 May 2023 09:33:24 +0200 Subject: [PATCH 05/27] code improvements --- .../impl/proctoring/ExamProctoringRoomServiceImpl.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/proctoring/ExamProctoringRoomServiceImpl.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/proctoring/ExamProctoringRoomServiceImpl.java index 891c4a9e..e0c469a8 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/proctoring/ExamProctoringRoomServiceImpl.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/proctoring/ExamProctoringRoomServiceImpl.java @@ -326,6 +326,11 @@ public class ExamProctoringRoomServiceImpl implements ExamProctoringRoomService cc.getExamId(), cc.getConnectionToken()); + if (proctoringRoom == null) { + log.warn("Assign SEB client to proctoring room failed for: {}", cc); + return; + } + if (log.isDebugEnabled()) { log.debug("Assigning new SEB client to proctoring room: {}, connection: {}", proctoringRoom.id, From af17420be58fb233e88ecde802d36bef6bfacb5b Mon Sep 17 00:00:00 2001 From: anhefti Date: Thu, 11 May 2023 11:01:21 +0200 Subject: [PATCH 06/27] SEBSERV-444 fixed --- .../seb/sebserver/gui/content/exam/LmsSetupForm.java | 3 ++- .../gui/service/i18n/impl/PolyglotPageServiceImpl.java | 10 +++++++++- .../java/ch/ethz/seb/sebserver/gui/widget/Message.java | 4 ++-- 3 files changed, 13 insertions(+), 4 deletions(-) diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/content/exam/LmsSetupForm.java b/src/main/java/ch/ethz/seb/sebserver/gui/content/exam/LmsSetupForm.java index fcac8ab8..a980c810 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/content/exam/LmsSetupForm.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/content/exam/LmsSetupForm.java @@ -15,6 +15,7 @@ import java.util.function.BooleanSupplier; import java.util.function.Function; import org.apache.commons.lang3.StringUtils; +import org.apache.commons.text.StringEscapeUtils; import org.eclipse.swt.SWT; import org.eclipse.swt.widgets.Button; import org.eclipse.swt.widgets.Composite; @@ -481,7 +482,7 @@ public class LmsSetupForm implements TemplateComposer { case TOKEN_REQUEST: { throw new PageMessageException(new LocTextKey( "sebserver.lmssetup.action.test.tokenRequestError", - Utils.formatHTMLLinesForceEscaped(Utils.escapeHTML_XML_EcmaScript(error.message)))); + Utils.formatHTMLLinesForceEscaped(StringEscapeUtils.escapeHtml4(error.message)))); } case QUIZ_ACCESS_API_REQUEST: { if (error.message.contains("quizaccess_sebserver_get_exams")) { diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/service/i18n/impl/PolyglotPageServiceImpl.java b/src/main/java/ch/ethz/seb/sebserver/gui/service/i18n/impl/PolyglotPageServiceImpl.java index 1ddf4989..c642653e 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/service/i18n/impl/PolyglotPageServiceImpl.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/service/i18n/impl/PolyglotPageServiceImpl.java @@ -11,7 +11,9 @@ package ch.ethz.seb.sebserver.gui.service.i18n.impl; import java.util.Locale; import java.util.function.Consumer; +import org.apache.commons.lang3.BooleanUtils; import org.apache.commons.lang3.StringUtils; +import org.apache.commons.text.StringEscapeUtils; import org.eclipse.rap.rwt.RWT; import org.eclipse.swt.SWT; import org.eclipse.swt.widgets.Button; @@ -269,8 +271,14 @@ public final class PolyglotPageServiceImpl implements PolyglotPageService { final I18nSupport i18nSupport) { return label -> { + if (locTextKey != null) { - label.setText(i18nSupport.getText(locTextKey)); + final String text = i18nSupport.getText(locTextKey); + if (BooleanUtils.toBoolean((Boolean) label.getData(RWT.MARKUP_ENABLED))) { + label.setText(StringEscapeUtils.escapeHtml4(text)); + } else { + label.setText(text); + } } if (i18nSupport.hasText(locToolTipKey)) { label.setToolTipText(Utils.formatLineBreaks(i18nSupport.getText(locToolTipKey))); diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/widget/Message.java b/src/main/java/ch/ethz/seb/sebserver/gui/widget/Message.java index df09b0f0..9913deec 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/widget/Message.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/widget/Message.java @@ -10,6 +10,7 @@ package ch.ethz.seb.sebserver.gui.widget; import java.util.Locale; +import org.apache.commons.text.StringEscapeUtils; import org.eclipse.rap.rwt.RWT; import org.eclipse.rap.rwt.widgets.DialogCallback; import org.eclipse.swt.graphics.Rectangle; @@ -17,7 +18,6 @@ import org.eclipse.swt.layout.GridLayout; import org.eclipse.swt.widgets.MessageBox; import org.eclipse.swt.widgets.Shell; -import ch.ethz.seb.sebserver.gbl.util.Utils; import ch.ethz.seb.sebserver.gui.service.i18n.I18nSupport; import ch.ethz.seb.sebserver.gui.widget.WidgetFactory.CustomVariant; @@ -46,7 +46,7 @@ public final class Message extends MessageBox { super.prepareOpen(); } catch (final IllegalArgumentException e) { // fallback on markup text error - super.setMessage(Utils.escapeHTML_XML_EcmaScript(super.getMessage())); + super.setMessage(StringEscapeUtils.escapeHtml4(super.getMessage())); super.prepareOpen(); } final GridLayout layout = (GridLayout) super.shell.getLayout(); From d50e818d6e79b72492483db2bad493893cf1ac7b Mon Sep 17 00:00:00 2001 From: anhefti Date: Thu, 11 May 2023 11:05:23 +0200 Subject: [PATCH 07/27] SEBSERV-444 fixed --- .../gui/service/i18n/impl/PolyglotPageServiceImpl.java | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/service/i18n/impl/PolyglotPageServiceImpl.java b/src/main/java/ch/ethz/seb/sebserver/gui/service/i18n/impl/PolyglotPageServiceImpl.java index 1ddf4989..c642653e 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/service/i18n/impl/PolyglotPageServiceImpl.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/service/i18n/impl/PolyglotPageServiceImpl.java @@ -11,7 +11,9 @@ package ch.ethz.seb.sebserver.gui.service.i18n.impl; import java.util.Locale; import java.util.function.Consumer; +import org.apache.commons.lang3.BooleanUtils; import org.apache.commons.lang3.StringUtils; +import org.apache.commons.text.StringEscapeUtils; import org.eclipse.rap.rwt.RWT; import org.eclipse.swt.SWT; import org.eclipse.swt.widgets.Button; @@ -269,8 +271,14 @@ public final class PolyglotPageServiceImpl implements PolyglotPageService { final I18nSupport i18nSupport) { return label -> { + if (locTextKey != null) { - label.setText(i18nSupport.getText(locTextKey)); + final String text = i18nSupport.getText(locTextKey); + if (BooleanUtils.toBoolean((Boolean) label.getData(RWT.MARKUP_ENABLED))) { + label.setText(StringEscapeUtils.escapeHtml4(text)); + } else { + label.setText(text); + } } if (i18nSupport.hasText(locToolTipKey)) { label.setToolTipText(Utils.formatLineBreaks(i18nSupport.getText(locToolTipKey))); From 7a2f9222bdb216341b3101db6b6f9db4679f0eb2 Mon Sep 17 00:00:00 2001 From: anhefti Date: Thu, 11 May 2023 11:10:01 +0200 Subject: [PATCH 08/27] SEBSERV-444 make secure --- .../gui/service/i18n/impl/PolyglotPageServiceImpl.java | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/service/i18n/impl/PolyglotPageServiceImpl.java b/src/main/java/ch/ethz/seb/sebserver/gui/service/i18n/impl/PolyglotPageServiceImpl.java index c642653e..1a669a7f 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/service/i18n/impl/PolyglotPageServiceImpl.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/service/i18n/impl/PolyglotPageServiceImpl.java @@ -32,6 +32,8 @@ import org.eclipse.swt.widgets.TableItem; import org.eclipse.swt.widgets.Text; import org.eclipse.swt.widgets.Tree; import org.eclipse.swt.widgets.TreeItem; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Service; @@ -51,6 +53,8 @@ import ch.ethz.seb.sebserver.gui.widget.Selection; @GuiProfile public final class PolyglotPageServiceImpl implements PolyglotPageService { + private static final Logger log = LoggerFactory.getLogger(PolyglotPageServiceImpl.class); + private final I18nSupport i18nSupport; public PolyglotPageServiceImpl(final I18nSupport i18nSupport) { @@ -95,7 +99,11 @@ public final class PolyglotPageServiceImpl implements PolyglotPageService { @Override public void injectI18n(final Label label, final LocTextKey locTextKey) { - injectI18n(label, locTextKey, null); + try { + injectI18n(label, locTextKey, null); + } catch (final Exception e) { + log.error("Failed to injectI18n: {}", e.getMessage()); + } } @Override From 4e182c94e3e7966404e123cf87297c016c4599f1 Mon Sep 17 00:00:00 2001 From: anhefti Date: Mon, 15 May 2023 09:06:13 +0200 Subject: [PATCH 09/27] better error handling and logging --- .../gui/service/push/UpdateErrorHandler.java | 4 ++-- .../session/impl/ExamSessionServiceImpl.java | 4 +++- .../session/impl/SEBClientConnectionServiceImpl.java | 4 ++-- .../impl/SEBClientNotificationServiceImpl.java | 5 ++--- .../webservice/weblayer/api/APIExceptionHandler.java | 11 ++++++++++- .../weblayer/api/ExamMonitoringController.java | 5 ++++- .../gui/integration/UseCasesIntegrationTest.java | 4 ++-- 7 files changed, 25 insertions(+), 12 deletions(-) diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/service/push/UpdateErrorHandler.java b/src/main/java/ch/ethz/seb/sebserver/gui/service/push/UpdateErrorHandler.java index c453c9bc..35790109 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/service/push/UpdateErrorHandler.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/service/push/UpdateErrorHandler.java @@ -56,14 +56,14 @@ public final class UpdateErrorHandler implements Function { } catch (final Exception ee) { log.warn("Unable to auto-logout: ", ee.getMessage()); } - return true; + return false; } } @Override public Boolean apply(final Exception error) { this.errors++; - log.error("Failed to update server push: {}", error.getMessage(), error); + log.warn("Failed to update server push: {}", error.getMessage()); if (this.errors > 5) { checkUserSession(); } diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/ExamSessionServiceImpl.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/ExamSessionServiceImpl.java index d49e8c4d..d8d89ff9 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/ExamSessionServiceImpl.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/ExamSessionServiceImpl.java @@ -241,7 +241,9 @@ public class ExamSessionServiceImpl implements ExamSessionService { flushCache(exam); } - log.info("Exam {} is not currently running", examId); + if (log.isDebugEnabled()) { + log.info("Exam {} is not currently running", examId); + } return Result.ofError(new NoSuchElementException( "No currently running exam found for id: " + examId)); diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/SEBClientConnectionServiceImpl.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/SEBClientConnectionServiceImpl.java index ab726f6c..d5a80ccd 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/SEBClientConnectionServiceImpl.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/SEBClientConnectionServiceImpl.java @@ -329,7 +329,7 @@ public class SEBClientConnectionServiceImpl implements SEBClientConnectionServic if (StringUtils.isNoneBlank(clientAddress) && StringUtils.isNotBlank(clientConnection.clientAddress) && !clientAddress.equals(clientConnection.clientAddress)) { - log.error( + log.warn( "ClientConnection integrity violation: client address mismatch: {}, {}", clientAddress, clientConnection.clientAddress); @@ -337,7 +337,7 @@ public class SEBClientConnectionServiceImpl implements SEBClientConnectionServic "ClientConnection integrity violation: client address mismatch"); } } else if (!clientConnection.status.clientActiveStatus) { - log.error("ClientConnection integrity violation: client connection is not in expected state: {}", + log.warn("ClientConnection integrity violation: client connection is not in expected state: {}", clientConnection); throw new IllegalArgumentException( "ClientConnection integrity violation: client connection is not in expected state"); diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/SEBClientNotificationServiceImpl.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/SEBClientNotificationServiceImpl.java index 89a1c31f..6c2decb6 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/SEBClientNotificationServiceImpl.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/SEBClientNotificationServiceImpl.java @@ -93,7 +93,7 @@ public class SEBClientNotificationServiceImpl implements SEBClientNotificationSe this.clientEventDAO.getPendingNotificationByValue(clientConnection.id, notificationId) .flatMap(notification -> this.clientEventDAO.confirmPendingNotification(notification.id)) .map(this::removeFromCache) - .onError(error -> log.error("Failed to confirm pending notification: {}", event, error)); + .getOrThrow(); } catch (final Exception e) { log.error( @@ -110,8 +110,7 @@ public class SEBClientNotificationServiceImpl implements SEBClientNotificationSe return this.clientEventDAO.getPendingNotification(notificationId) .map(notification -> this.confirmClientSide(notification, examId, connectionToken)) .flatMap(notification -> this.clientEventDAO.confirmPendingNotification(notificationId)) - .map(this::removeFromCache) - .onError(error -> log.error("Failed to confirm pending notification: {}", notificationId, error)); + .map(this::removeFromCache); } @Override diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/APIExceptionHandler.java b/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/APIExceptionHandler.java index 4d0581d2..d8f02488 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/APIExceptionHandler.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/APIExceptionHandler.java @@ -149,6 +149,15 @@ public class APIExceptionHandler extends ResponseEntityExceptionHandler { HttpStatus.BAD_REQUEST); } + @ExceptionHandler(IllegalArgumentException.class) + public ResponseEntity handleIllegalArgumentException( + final IllegalArgumentException ex, + final WebRequest request) { + + log.warn("Illegal argument or state detected: {}\n send 400 Bad Request response", ex.getMessage()); + return new ResponseEntity<>(HttpStatus.BAD_REQUEST); + } + @ExceptionHandler(ResourceNotFoundException.class) public ResponseEntity handleResourceNotFoundException( final ResourceNotFoundException ex, @@ -182,7 +191,7 @@ public class APIExceptionHandler extends ResponseEntityExceptionHandler { final ExamNotRunningException ex, final WebRequest request) { - log.info("{}", ex.getMessage()); + log.debug("{}", ex.getMessage()); return APIMessage.ErrorMessage.INTEGRITY_VALIDATION .createErrorResponse(ex.getMessage()); } diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/ExamMonitoringController.java b/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/ExamMonitoringController.java index 81c790e9..36aec030 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/ExamMonitoringController.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/ExamMonitoringController.java @@ -425,7 +425,10 @@ public class ExamMonitoringController { notificationId, examId, connectionToken) - .getOrThrow(); + .onError(error -> { + log.error("Failed to confirm pending notification: {} for exam {}, cause: {}", + notificationId, examId, error.getMessage()); + }); } @RequestMapping( diff --git a/src/test/java/ch/ethz/seb/sebserver/gui/integration/UseCasesIntegrationTest.java b/src/test/java/ch/ethz/seb/sebserver/gui/integration/UseCasesIntegrationTest.java index e8d72124..6d62f73d 100644 --- a/src/test/java/ch/ethz/seb/sebserver/gui/integration/UseCasesIntegrationTest.java +++ b/src/test/java/ch/ethz/seb/sebserver/gui/integration/UseCasesIntegrationTest.java @@ -1578,7 +1578,7 @@ public class UseCasesIntegrationTest extends GuiIntegrationTest { fail("Exception expected here"); } catch (final Exception e) { - assertEquals("Unexpected error while rest call", e.getMessage()); + assertEquals("argument \"content\" is null", e.getMessage()); } // test follow-up integrity violation @@ -1596,7 +1596,7 @@ public class UseCasesIntegrationTest extends GuiIntegrationTest { fail("Exception expected here"); } catch (final Exception e) { - assertEquals("Unexpected error while rest call", e.getMessage()); + assertEquals("argument \"content\" is null", e.getMessage()); } final ConfigurationTableValues newTableValue = new ConfigurationTableValues( From 87b010a5fd976ffb157e3c6e420fa1fc913d3b03 Mon Sep 17 00:00:00 2001 From: anhefti Date: Tue, 16 May 2023 11:49:53 +0200 Subject: [PATCH 10/27] fixed XML text masking in Title --- .../gui/content/monitoring/MonitoringRunningExam.java | 5 ++++- .../gui/service/i18n/impl/PolyglotPageServiceImpl.java | 10 +--------- 2 files changed, 5 insertions(+), 10 deletions(-) diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/content/monitoring/MonitoringRunningExam.java b/src/main/java/ch/ethz/seb/sebserver/gui/content/monitoring/MonitoringRunningExam.java index 530e0630..ea3664ac 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/content/monitoring/MonitoringRunningExam.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/content/monitoring/MonitoringRunningExam.java @@ -18,6 +18,7 @@ import java.util.function.BooleanSupplier; import java.util.function.Consumer; import java.util.function.Function; +import org.apache.commons.text.StringEscapeUtils; import org.eclipse.swt.SWT; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.layout.GridLayout; @@ -151,7 +152,9 @@ public class MonitoringRunningExam implements TemplateComposer { final Composite content = this.pageService.getWidgetFactory().defaultPageLayout( pageContext.getParent(), - new LocTextKey("sebserver.monitoring.exam", exam.name)); + new LocTextKey( + "sebserver.monitoring.exam", + StringEscapeUtils.escapeHtml4(exam.name))); final Composite tablePane = new Composite(content, SWT.NONE); tablePane.setLayout(new GridLayout()); diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/service/i18n/impl/PolyglotPageServiceImpl.java b/src/main/java/ch/ethz/seb/sebserver/gui/service/i18n/impl/PolyglotPageServiceImpl.java index c642653e..1ddf4989 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/service/i18n/impl/PolyglotPageServiceImpl.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/service/i18n/impl/PolyglotPageServiceImpl.java @@ -11,9 +11,7 @@ package ch.ethz.seb.sebserver.gui.service.i18n.impl; import java.util.Locale; import java.util.function.Consumer; -import org.apache.commons.lang3.BooleanUtils; import org.apache.commons.lang3.StringUtils; -import org.apache.commons.text.StringEscapeUtils; import org.eclipse.rap.rwt.RWT; import org.eclipse.swt.SWT; import org.eclipse.swt.widgets.Button; @@ -271,14 +269,8 @@ public final class PolyglotPageServiceImpl implements PolyglotPageService { final I18nSupport i18nSupport) { return label -> { - if (locTextKey != null) { - final String text = i18nSupport.getText(locTextKey); - if (BooleanUtils.toBoolean((Boolean) label.getData(RWT.MARKUP_ENABLED))) { - label.setText(StringEscapeUtils.escapeHtml4(text)); - } else { - label.setText(text); - } + label.setText(i18nSupport.getText(locTextKey)); } if (i18nSupport.hasText(locToolTipKey)) { label.setToolTipText(Utils.formatLineBreaks(i18nSupport.getText(locToolTipKey))); From f0acb33e062f26fdd42c643c2b4b919c3cf63052 Mon Sep 17 00:00:00 2001 From: anhefti Date: Thu, 1 Jun 2023 15:54:03 +0200 Subject: [PATCH 11/27] SEBSERV-447 fixed --- .../sebserver/gui/service/session/ClientConnectionTable.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/service/session/ClientConnectionTable.java b/src/main/java/ch/ethz/seb/sebserver/gui/service/session/ClientConnectionTable.java index 9e766723..c33793d3 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/service/session/ClientConnectionTable.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/service/session/ClientConnectionTable.java @@ -371,7 +371,6 @@ public final class ClientConnectionTable implements FullPageMonitoringGUIUpdate } public void updateGUI() { - if (this.needsSort) { sortTable(); } @@ -725,10 +724,12 @@ public final class ClientConnectionTable implements FullPageMonitoringGUIUpdate this.indicatorWeights[i] = -1; } } + + this.monitoringData = monitoringData; + if (this.indicatorValueChanged) { updateIndicatorWeight(); } - this.monitoringData = monitoringData; return this.staticData == null || this.staticData == ClientStaticData.NULL_DATA From 3dec2f337284587419e04fc69421ff9f266bce7f Mon Sep 17 00:00:00 2001 From: anhefti Date: Thu, 1 Jun 2023 15:56:56 +0200 Subject: [PATCH 12/27] SEBSERV-446 fixed --- .../servicelayer/dao/impl/ClientConnectionDAOImpl.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/ClientConnectionDAOImpl.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/ClientConnectionDAOImpl.java index 27406d9c..ba0abd37 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/ClientConnectionDAOImpl.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/ClientConnectionDAOImpl.java @@ -814,10 +814,13 @@ public class ClientConnectionDAOImpl implements ClientConnectionDAO { .selectByExample() .where( ClientConnectionRecordDynamicSqlSupport.status, - SqlBuilder.isIn(ClientConnection.SECURE_CHECK_STATES)) + SqlBuilder.isEqualTo(ConnectionStatus.ACTIVE.name())) .and( ClientConnectionRecordDynamicSqlSupport.examId, SqlBuilder.isEqualTo(examId)) + .and( + ClientConnectionRecordDynamicSqlSupport.clientVersion, + SqlBuilder.isNotNull()) .and( ClientConnectionRecordDynamicSqlSupport.clientVersionGranted, SqlBuilder.isNull()) From 9d7ef0452fca37d3343526f7e8ed165c2d056551 Mon Sep 17 00:00:00 2001 From: anhefti Date: Thu, 1 Jun 2023 16:02:24 +0200 Subject: [PATCH 13/27] code cleanup --- .../gui/service/i18n/impl/PolyglotPageServiceImpl.java | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/service/i18n/impl/PolyglotPageServiceImpl.java b/src/main/java/ch/ethz/seb/sebserver/gui/service/i18n/impl/PolyglotPageServiceImpl.java index e4a2cd3d..1ddf4989 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/service/i18n/impl/PolyglotPageServiceImpl.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/service/i18n/impl/PolyglotPageServiceImpl.java @@ -30,8 +30,6 @@ import org.eclipse.swt.widgets.TableItem; import org.eclipse.swt.widgets.Text; import org.eclipse.swt.widgets.Tree; import org.eclipse.swt.widgets.TreeItem; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Service; @@ -51,8 +49,6 @@ import ch.ethz.seb.sebserver.gui.widget.Selection; @GuiProfile public final class PolyglotPageServiceImpl implements PolyglotPageService { - private static final Logger log = LoggerFactory.getLogger(PolyglotPageServiceImpl.class); - private final I18nSupport i18nSupport; public PolyglotPageServiceImpl(final I18nSupport i18nSupport) { @@ -97,11 +93,7 @@ public final class PolyglotPageServiceImpl implements PolyglotPageService { @Override public void injectI18n(final Label label, final LocTextKey locTextKey) { - try { - injectI18n(label, locTextKey, null); - } catch (final Exception e) { - log.error("Failed to injectI18n: {}", e.getMessage()); - } + injectI18n(label, locTextKey, null); } @Override From 3ce025c4b1ba9d79e0b10f9d9032c418199fa65e Mon Sep 17 00:00:00 2001 From: anhefti Date: Fri, 2 Jun 2023 10:52:52 +0200 Subject: [PATCH 14/27] SEBSERV-449 better timeouts and name search on moodle side --- .../ClientHttpRequestFactoryService.java | 2 +- .../lms/impl/QuizLookupServiceImpl.java | 28 +++++++++++++------ .../plugin/MoodlePluginCourseAccess.java | 18 +++++++++--- .../config/application-dev-ws.properties | 4 +-- .../config/application-ws.properties | 1 + 5 files changed, 38 insertions(+), 15 deletions(-) diff --git a/src/main/java/ch/ethz/seb/sebserver/ClientHttpRequestFactoryService.java b/src/main/java/ch/ethz/seb/sebserver/ClientHttpRequestFactoryService.java index 5272843e..3fd16e75 100644 --- a/src/main/java/ch/ethz/seb/sebserver/ClientHttpRequestFactoryService.java +++ b/src/main/java/ch/ethz/seb/sebserver/ClientHttpRequestFactoryService.java @@ -76,7 +76,7 @@ public class ClientHttpRequestFactoryService { final ClientCredentialService clientCredentialService, @Value("${sebserver.http.client.connect-timeout:15000}") final int connectTimeout, @Value("${sebserver.http.client.connection-request-timeout:20000}") final int connectionRequestTimeout, - @Value("${sebserver.http.client.read-timeout:20000}") final int readTimeout) { + @Value("${sebserver.http.client.read-timeout:30000}") final int readTimeout) { this.environment = environment; this.clientCredentialService = clientCredentialService; diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/QuizLookupServiceImpl.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/QuizLookupServiceImpl.java index e74deed9..cc83fe2e 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/QuizLookupServiceImpl.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/QuizLookupServiceImpl.java @@ -22,6 +22,7 @@ import java.util.stream.Collectors; import org.joda.time.DateTime; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Lazy; import org.springframework.core.env.Environment; import org.springframework.stereotype.Service; @@ -53,16 +54,19 @@ public class QuizLookupServiceImpl implements QuizLookupService { private final UserService userService; private final LmsSetupDAO lmsSetupDAO; private final AsyncRunner asyncRunner; + private final long fetchedDataValiditySeconds; public QuizLookupServiceImpl( final UserService userService, final LmsSetupDAO lmsSetupDAO, final AsyncService asyncService, - final Environment environment) { + final Environment environment, + @Value("${sebserver.webservice.lms.datafetch.validity.seconds:600}") final long fetchedDataValiditySeconds) { this.userService = userService; this.lmsSetupDAO = lmsSetupDAO; this.asyncRunner = asyncService.getAsyncRunner(); + this.fetchedDataValiditySeconds = fetchedDataValiditySeconds; } @Override @@ -158,7 +162,10 @@ public class QuizLookupServiceImpl implements QuizLookupService { } if (!asyncLookup.isValid(filterMap)) { - this.lookups.remove(userId); + final AsyncLookup removed = this.lookups.remove(userId); + if (removed != null) { + removed.cancel(); + } this.createNewAsyncLookup(userId, filterMap, lmsAPITemplateSupplier); } @@ -198,7 +205,12 @@ public class QuizLookupServiceImpl implements QuizLookupService { } final LookupFilterCriteria criteria = new LookupFilterCriteria(filterMap); - final AsyncLookup asyncLookup = new AsyncLookup(userInstitutionId, userId, criteria, buffers); + final AsyncLookup asyncLookup = new AsyncLookup( + userInstitutionId, + userId, + criteria, + buffers, + this.fetchedDataValiditySeconds); if (log.isDebugEnabled()) { log.debug("Create new AsyncLookup: user={} criteria={}", userId, criteria); @@ -278,18 +290,21 @@ public class QuizLookupServiceImpl implements QuizLookupService { final Collection asyncBuffers; final long timeCreated; long timeCompleted = Long.MAX_VALUE; + private final long fetchedDataValiditySeconds; public AsyncLookup( final long institutionId, final String userId, final LookupFilterCriteria lookupFilterCriteria, - final Collection asyncBuffers) { + final Collection asyncBuffers, + final long fetchedDataValiditySeconds) { this.institutionId = institutionId; this.userId = userId; this.lookupFilterCriteria = lookupFilterCriteria; this.asyncBuffers = asyncBuffers; this.timeCreated = Utils.getMillisecondsNow(); + this.fetchedDataValiditySeconds = fetchedDataValiditySeconds; } LookupResult getAvailable() { @@ -307,10 +322,7 @@ public class QuizLookupServiceImpl implements QuizLookupService { boolean isUpToDate() { final long now = Utils.getMillisecondsNow(); - if (now - this.timeCreated > 5 * Constants.MINUTE_IN_MILLIS) { - return false; - } - if (now - this.timeCompleted > Constants.MINUTE_IN_MILLIS) { + if (now - this.timeCreated > this.fetchedDataValiditySeconds * Constants.SECOND_IN_MILLIS) { return false; } return true; diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/moodle/plugin/MoodlePluginCourseAccess.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/moodle/plugin/MoodlePluginCourseAccess.java index 2e3a464e..9148964e 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/moodle/plugin/MoodlePluginCourseAccess.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/moodle/plugin/MoodlePluginCourseAccess.java @@ -61,6 +61,7 @@ import ch.ethz.seb.sebserver.webservice.servicelayer.lms.impl.moodle.MoodleUtils import ch.ethz.seb.sebserver.webservice.servicelayer.lms.impl.moodle.MoodleUtils.Courses; import ch.ethz.seb.sebserver.webservice.servicelayer.lms.impl.moodle.MoodleUtils.CoursesPlugin; import ch.ethz.seb.sebserver.webservice.servicelayer.lms.impl.moodle.MoodleUtils.MoodleUserDetails; +import io.micrometer.core.instrument.util.StringUtils; public class MoodlePluginCourseAccess extends AbstractCachedCourseAccess implements CourseAccessAPI { @@ -118,7 +119,7 @@ public class MoodlePluginCourseAccess extends AbstractCachedCourseAccess impleme environment.getProperty( "sebserver.webservice.circuitbreaker.moodleRestCall.blockingTime", Long.class, - Constants.SECOND_IN_MILLIS * 20), + Constants.SECOND_IN_MILLIS * 30), environment.getProperty( "sebserver.webservice.circuitbreaker.moodleRestCall.timeToRecover", Long.class, @@ -184,10 +185,11 @@ public class MoodlePluginCourseAccess extends AbstractCachedCourseAccess impleme quizFromTime = DateTime.now(DateTimeZone.UTC).minusYears(this.cutoffTimeOffset); } final Predicate quizFilter = LmsAPIService.quizFilterPredicate(filterMap); + final String quizName = filterMap.getQuizName(); while (!asyncQuizFetchBuffer.finished && !asyncQuizFetchBuffer.canceled) { try { - fetchQuizzesPage(page, quizFromTime, asyncQuizFetchBuffer, quizFilter); + fetchQuizzesPage(page, quizFromTime, quizName, asyncQuizFetchBuffer, quizFilter); page++; } catch (final Exception e) { log.error("Unexpected error while trying to fetch moodle quiz page: {}", page, e); @@ -371,6 +373,7 @@ public class MoodlePluginCourseAccess extends AbstractCachedCourseAccess impleme private void fetchQuizzesPage( final int page, final DateTime quizFromTime, + final String nameCondition, final AsyncQuizFetchBuffer asyncQuizFetchBuffer, final Predicate quizFilter) throws JsonParseException, JsonMappingException, IOException { @@ -382,7 +385,7 @@ public class MoodlePluginCourseAccess extends AbstractCachedCourseAccess impleme : lmsSetup.lmsApiUrl + Constants.URL_PATH_SEPARATOR + MOODLE_QUIZ_START_URL_PATH; final Collection fetchCoursesPage = - fetchCoursesPage(restTemplate, quizFromTime, page, this.pageSize); + fetchCoursesPage(restTemplate, quizFromTime, nameCondition, page, this.pageSize); // finish if page is empty (no courses left if (fetchCoursesPage.isEmpty()) { asyncQuizFetchBuffer.finish(); @@ -408,6 +411,7 @@ public class MoodlePluginCourseAccess extends AbstractCachedCourseAccess impleme private Collection fetchCoursesPage( final MoodleAPIRestTemplate restTemplate, final DateTime quizFromTime, + final String nameCondition, final int page, final int size) throws JsonParseException, JsonMappingException, IOException { @@ -422,13 +426,19 @@ public class MoodlePluginCourseAccess extends AbstractCachedCourseAccess impleme final long defaultCutOff = Utils.toUnixTimeInSeconds( DateTime.now(DateTimeZone.UTC).minusYears(this.cutoffTimeOffset)); final long cutoffDate = (filterDate < defaultCutOff) ? filterDate : defaultCutOff; - final String sqlCondition = String.format( + String sqlCondition = String.format( SQL_CONDITION_TEMPLATE, String.valueOf(cutoffDate), String.valueOf(filterDate)); final String fromElement = String.valueOf(page * size); final LinkedMultiValueMap attributes = new LinkedMultiValueMap<>(); + if (StringUtils.isNotBlank(nameCondition)) { + sqlCondition = sqlCondition + " AND (m.name LIKE '" + + Utils.toSQLWildcard(nameCondition) + + "')"; + } + // Note: courseid[]=0 means all courses. Moodle don't like empty parameter attributes.add(PARAM_COURSE_ID_ARRAY, "0"); attributes.add(PARAM_SQL_CONDITIONS, sqlCondition); diff --git a/src/main/resources/config/application-dev-ws.properties b/src/main/resources/config/application-dev-ws.properties index 25b871d9..b49bad3b 100644 --- a/src/main/resources/config/application-dev-ws.properties +++ b/src/main/resources/config/application-dev-ws.properties @@ -18,7 +18,7 @@ spring.datasource.hikari.leakDetectionThreshold=2000 sebserver.http.client.connect-timeout=15000 sebserver.http.client.connection-request-timeout=10000 -sebserver.http.client.read-timeout=20000 +sebserver.http.client.read-timeout=30000 sebserver.webservice.distributed.updateInterval=1000 sebserver.webservice.distributed.connectionUpdate=2000 sebserver.webservice.clean-db-on-startup=false @@ -52,7 +52,7 @@ sebserver.webservice.api.pagination.maxPageSize=500 sebserver.webservice.lms.openedx.api.token.request.paths=/oauth2/access_token sebserver.webservice.lms.moodle.api.token.request.paths= sebserver.webservice.lms.address.alias=lms.mockup.com=lms.address.alias -sebserver.webservice.cache.moodle.course.pageSize=10 +sebserver.webservice.cache.moodle.course.pageSize=250 springdoc.api-docs.enabled=true springdoc.swagger-ui.enabled=true diff --git a/src/main/resources/config/application-ws.properties b/src/main/resources/config/application-ws.properties index acafe27a..f215b762 100644 --- a/src/main/resources/config/application-ws.properties +++ b/src/main/resources/config/application-ws.properties @@ -83,6 +83,7 @@ sebserver.webservice.lms.moodle.prependShortCourseName=true sebserver.webservice.lms.moodle.fetch.cutoffdate.yearsBeforeNow=2 sebserver.webservice.lms.olat.sendAdditionalAttributesWithRestriction=false sebserver.webservice.lms.address.alias= +sebserver.webservice.lms.datafetch.validity.seconds=600 sebserver.webservice.proctoring.resetBroadcastOnLeav=true sebserver.webservice.proctoring.zoom.enableWaitingRoom=false From 0d650f9fb62de9ccb5968b255c7c5eba42f84a57 Mon Sep 17 00:00:00 2001 From: anhefti Date: Mon, 5 Jun 2023 16:15:37 +0200 Subject: [PATCH 15/27] SEBSERV-449 improved reload action --- .../gui/content/exam/QuizLookupList.java | 15 +++++++++------ src/main/resources/messages.properties | 1 + 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/content/exam/QuizLookupList.java b/src/main/java/ch/ethz/seb/sebserver/gui/content/exam/QuizLookupList.java index c95b2c99..374c6704 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/content/exam/QuizLookupList.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/content/exam/QuizLookupList.java @@ -59,6 +59,7 @@ import ch.ethz.seb.sebserver.gui.table.ColumnDefinition.TableFilterAttribute; import ch.ethz.seb.sebserver.gui.table.EntityTable; import ch.ethz.seb.sebserver.gui.table.TableFilter.CriteriaType; import ch.ethz.seb.sebserver.gui.widget.WidgetFactory; +import ch.ethz.seb.sebserver.gui.widget.WidgetFactory.ImageIcon; @Lazy @Component @@ -109,6 +110,8 @@ public class QuizLookupList implements TemplateComposer { new LocTextKey("sebserver.quizdiscovery.quiz.import.existing"); private final static LocTextKey TEXT_FETCH_NOTE = new LocTextKey("sebserver.quizdiscovery.list.fetchnote"); + private final static LocTextKey TEXT_FETCH_NOTE_TOOLTIP = + new LocTextKey("sebserver.quizdiscovery.list.fetchnote.tooltip"); private final static String TEXT_KEY_ADDITIONAL_ATTR_PREFIX = "sebserver.quizdiscovery.quiz.details.additional."; @@ -464,12 +467,12 @@ public class QuizLookupList implements TemplateComposer { gridData.heightHint = 28; gridData.widthHint = 25; gridData.verticalIndent = 5; - final Label action = new Label(warningPanel, SWT.NONE); - action.setImage(WidgetFactory.ImageIcon.SWITCH.getImage(notePanel.getDisplay())); - action.setLayoutData(gridData); - action.addListener(SWT.MouseDown, event -> { - table.applyFilter(); - }); + + this.widgetFactory.imageButton( + ImageIcon.SWITCH, + warningPanel, + TEXT_FETCH_NOTE_TOOLTIP, + event -> table.applyFilter()); final Label text = new Label(warningPanel, SWT.NONE); text.setData(RWT.MARKUP_ENABLED, Boolean.TRUE); diff --git a/src/main/resources/messages.properties b/src/main/resources/messages.properties index 9ebacb9a..2fdf79e6 100644 --- a/src/main/resources/messages.properties +++ b/src/main/resources/messages.properties @@ -448,6 +448,7 @@ sebserver.quizdiscovery.list.column.endtime.tooltip=The end time of the LMS exam sebserver.quizdiscovery.info.pleaseSelect=At first please select an LMS exam from the list sebserver.quizdiscovery.list.action.no.modify.privilege=No Access: A LMS exam from other institution cannot be imported. sebserver.quizdiscovery.list.fetchnote=Note: This list is not complete yet since the service is still fetching data from LMS.
            Use the reload button on the left or the search icon from the list for update. +sebserver.quizdiscovery.list.fetchnote.tooltip=Click to reload the list and get all currently fetched results. sebserver.quizdiscovery.action.list=LMS Exam Lookup sebserver.quizdiscovery.action.import=Import as Exam From a62f06781cd2b685b778721c1f08d73a9d8795e6 Mon Sep 17 00:00:00 2001 From: anhefti Date: Thu, 8 Jun 2023 08:47:55 +0200 Subject: [PATCH 16/27] tomcat settings --- src/main/resources/config/application.properties | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/main/resources/config/application.properties b/src/main/resources/config/application.properties index 2f6bd64a..bb56358d 100644 --- a/src/main/resources/config/application.properties +++ b/src/main/resources/config/application.properties @@ -15,6 +15,12 @@ server.servlet.context-path=/ # Tomcat server.tomcat.max-threads=2000 server.tomcat.accept-count=300 +server.tomcat.socket.soKeepAlive=true +server.tomcat.socket.performanceConnectionTime=1 +server.tomcat.socket.performanceLatency=2 +server.tomcat.socket.performanceBandwidth=0 +server.tomcat.keepAliveTimeout(3000); +server.tomcat.maxKeepAliveRequests(3000); server.tomcat.uri-encoding=UTF-8 ### encoding From b2f7337019a3555156704367fd0d115066a606e9 Mon Sep 17 00:00:00 2001 From: anhefti Date: Thu, 8 Jun 2023 15:05:41 +0200 Subject: [PATCH 17/27] SEBSERV-449 fixes --- .../gui/content/exam/QuizLookupList.java | 45 +++++++++---------- .../monitoring/MonitoringRunningExam.java | 2 +- .../i18n/impl/PolyglotPageServiceImpl.java | 11 ++++- .../lms/impl/mockup/MockCourseAccessAPI.java | 3 +- .../plugin/MoodlePluginCourseAccess.java | 9 +++- .../MooldePluginLmsAPITemplateFactory.java | 8 +++- .../config/application-ws.properties | 1 + .../plugin/MoodlePluginCourseAccessTest.java | 3 +- 8 files changed, 51 insertions(+), 31 deletions(-) diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/content/exam/QuizLookupList.java b/src/main/java/ch/ethz/seb/sebserver/gui/content/exam/QuizLookupList.java index 374c6704..4ba77093 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/content/exam/QuizLookupList.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/content/exam/QuizLookupList.java @@ -451,7 +451,7 @@ public class QuizLookupList implements TemplateComposer { } } - private boolean showingFetchNote = false; + private Composite warningPanel = null; private void handelPageReload( final Composite notePanel, @@ -459,29 +459,28 @@ public class QuizLookupList implements TemplateComposer { if (table.isComplete()) { PageService.clearComposite(notePanel); - this.showingFetchNote = false; - } else { - if (!this.showingFetchNote) { - final Composite warningPanel = this.widgetFactory.createWarningPanel(notePanel, 15, true); - GridData gridData = new GridData(SWT.CENTER, SWT.CENTER, false, true); - gridData.heightHint = 28; - gridData.widthHint = 25; - gridData.verticalIndent = 5; - - this.widgetFactory.imageButton( - ImageIcon.SWITCH, - warningPanel, - TEXT_FETCH_NOTE_TOOLTIP, - event -> table.applyFilter()); - - final Label text = new Label(warningPanel, SWT.NONE); - text.setData(RWT.MARKUP_ENABLED, Boolean.TRUE); - text.setText(this.pageService.getI18nSupport().getText(TEXT_FETCH_NOTE)); - gridData = new GridData(SWT.LEFT, SWT.FILL, true, true); - gridData.heightHint = 16; - text.setLayoutData(gridData); - this.showingFetchNote = true; + if (this.warningPanel != null) { + this.warningPanel.dispose(); } + this.warningPanel = null; + } else { + if (this.warningPanel != null && !this.warningPanel.isDisposed()) { + this.warningPanel.dispose(); + } + + this.warningPanel = this.widgetFactory.createWarningPanel(notePanel, 15, true); + this.widgetFactory.imageButton( + ImageIcon.SWITCH, + this.warningPanel, + TEXT_FETCH_NOTE_TOOLTIP, + event -> table.applyFilter()); + + final Label text = new Label(this.warningPanel, SWT.NONE); + text.setData(RWT.MARKUP_ENABLED, Boolean.TRUE); + text.setText(this.pageService.getI18nSupport().getText(TEXT_FETCH_NOTE)); + final GridData gridData = new GridData(SWT.LEFT, SWT.FILL, true, true); + gridData.heightHint = 28; + text.setLayoutData(gridData); } notePanel.getParent().layout(true, true); } diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/content/monitoring/MonitoringRunningExam.java b/src/main/java/ch/ethz/seb/sebserver/gui/content/monitoring/MonitoringRunningExam.java index ea3664ac..c33c5e50 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/content/monitoring/MonitoringRunningExam.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/content/monitoring/MonitoringRunningExam.java @@ -154,7 +154,7 @@ public class MonitoringRunningExam implements TemplateComposer { pageContext.getParent(), new LocTextKey( "sebserver.monitoring.exam", - StringEscapeUtils.escapeHtml4(exam.name))); + StringEscapeUtils.escapeXml11(exam.name))); final Composite tablePane = new Composite(content, SWT.NONE); tablePane.setLayout(new GridLayout()); diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/service/i18n/impl/PolyglotPageServiceImpl.java b/src/main/java/ch/ethz/seb/sebserver/gui/service/i18n/impl/PolyglotPageServiceImpl.java index 1ddf4989..bc1beaf2 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/service/i18n/impl/PolyglotPageServiceImpl.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/service/i18n/impl/PolyglotPageServiceImpl.java @@ -270,7 +270,16 @@ public final class PolyglotPageServiceImpl implements PolyglotPageService { return label -> { if (locTextKey != null) { - label.setText(i18nSupport.getText(locTextKey)); + try { + label.setText(i18nSupport.getText(locTextKey)); + } catch (final Exception e) { + label.setData(RWT.MARKUP_ENABLED, false); + try { + label.setText(i18nSupport.getText(locTextKey)); + } catch (final Exception ee) { + label.setText(locTextKey.name); + } + } } if (i18nSupport.hasText(locToolTipKey)) { label.setToolTipText(Utils.formatLineBreaks(i18nSupport.getText(locToolTipKey))); diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/mockup/MockCourseAccessAPI.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/mockup/MockCourseAccessAPI.java index c8bf545a..e5db1a9b 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/mockup/MockCourseAccessAPI.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/mockup/MockCourseAccessAPI.java @@ -63,7 +63,8 @@ public class MockCourseAccessAPI implements CourseAccessAPI { "quiz1", institutionId, lmsSetupId, lmsType, "Demo Quiz 1 (MOCKUP)", "

Demo Quiz Mockup

", "2020-01-01T09:00:00Z", null, "http://lms.mockup.com/api/")); this.mockups.add(new QuizData( - "quiz2", institutionId, lmsSetupId, lmsType, "Demo Quiz 2 (MOCKUP)", "

Demo Quiz Mockup

", + "quiz2", institutionId, lmsSetupId, lmsType, "Demo Quiz 2 (MOCKUP) äöüèÜÄÖ ?< ", + "

Demo Quiz Mockup

", "2020-01-01T09:00:00Z", "2025-01-01T09:00:00Z", "http://lms.mockup.com/api/")); this.mockups.add(new QuizData( "quiz3", institutionId, lmsSetupId, lmsType, "Demo Quiz 3 (MOCKUP)", "

Demo Quiz Mockup

", diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/moodle/plugin/MoodlePluginCourseAccess.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/moodle/plugin/MoodlePluginCourseAccess.java index 9148964e..2321adeb 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/moodle/plugin/MoodlePluginCourseAccess.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/moodle/plugin/MoodlePluginCourseAccess.java @@ -93,6 +93,7 @@ public class MoodlePluginCourseAccess extends AbstractCachedCourseAccess impleme private final int pageSize; private final int maxSize; private final int cutoffTimeOffset; + private final boolean applyNameCriteria; private MoodleAPIRestTemplate restTemplate; @@ -101,11 +102,13 @@ public class MoodlePluginCourseAccess extends AbstractCachedCourseAccess impleme final AsyncService asyncService, final MoodleRestTemplateFactory restTemplateFactory, final CacheManager cacheManager, - final Environment environment) { + final Environment environment, + final boolean applyNameCriteria) { super(cacheManager); this.jsonMapper = jsonMapper; this.restTemplateFactory = restTemplateFactory; + this.applyNameCriteria = applyNameCriteria; this.prependShortCourseName = BooleanUtils.toBoolean(environment.getProperty( "sebserver.webservice.lms.moodle.prependShortCourseName", @@ -433,7 +436,9 @@ public class MoodlePluginCourseAccess extends AbstractCachedCourseAccess impleme final String fromElement = String.valueOf(page * size); final LinkedMultiValueMap attributes = new LinkedMultiValueMap<>(); - if (StringUtils.isNotBlank(nameCondition)) { + // TODO clarify with Amr and Luca if this is OK + // and if it is possible to apply the nameCondition also the the course name (shortname) + if (this.applyNameCriteria && StringUtils.isNotBlank(nameCondition)) { sqlCondition = sqlCondition + " AND (m.name LIKE '" + Utils.toSQLWildcard(nameCondition) + "')"; diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/moodle/plugin/MooldePluginLmsAPITemplateFactory.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/moodle/plugin/MooldePluginLmsAPITemplateFactory.java index b455e7b8..7b027a1e 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/moodle/plugin/MooldePluginLmsAPITemplateFactory.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/moodle/plugin/MooldePluginLmsAPITemplateFactory.java @@ -46,6 +46,7 @@ public class MooldePluginLmsAPITemplateFactory implements LmsAPITemplateFactory private final ExamConfigurationValueService examConfigurationValueService; private final ClientHttpRequestFactoryService clientHttpRequestFactoryService; private final String[] alternativeTokenRequestPaths; + private final boolean applyNameCriteria; protected MooldePluginLmsAPITemplateFactory( final JSONMapper jsonMapper, @@ -55,7 +56,8 @@ public class MooldePluginLmsAPITemplateFactory implements LmsAPITemplateFactory final ClientCredentialService clientCredentialService, final ExamConfigurationValueService examConfigurationValueService, final ClientHttpRequestFactoryService clientHttpRequestFactoryService, - @Value("${sebserver.webservice.lms.moodle.api.token.request.paths:}") final String alternativeTokenRequestPaths) { + @Value("${sebserver.webservice.lms.moodle.api.token.request.paths:}") final String alternativeTokenRequestPaths, + @Value("${sebserver.webservice.lms.moodle.fetch.applyNameCriteria:false}") final boolean applyNameCriteria) { this.jsonMapper = jsonMapper; this.cacheManager = cacheManager; @@ -67,6 +69,7 @@ public class MooldePluginLmsAPITemplateFactory implements LmsAPITemplateFactory this.alternativeTokenRequestPaths = (alternativeTokenRequestPaths != null) ? StringUtils.split(alternativeTokenRequestPaths, Constants.LIST_SEPARATOR) : null; + this.applyNameCriteria = applyNameCriteria; } @Override @@ -90,7 +93,8 @@ public class MooldePluginLmsAPITemplateFactory implements LmsAPITemplateFactory this.asyncService, moodleRestTemplateFactory, this.cacheManager, - this.environment); + this.environment, + this.applyNameCriteria); final MoodlePluginCourseRestriction moodlePluginCourseRestriction = new MoodlePluginCourseRestriction( this.jsonMapper, diff --git a/src/main/resources/config/application-ws.properties b/src/main/resources/config/application-ws.properties index f215b762..f3568646 100644 --- a/src/main/resources/config/application-ws.properties +++ b/src/main/resources/config/application-ws.properties @@ -81,6 +81,7 @@ sebserver.webservice.lms.openedx.api.token.request.paths=/oauth2/access_token sebserver.webservice.lms.moodle.api.token.request.paths=/login/token.php sebserver.webservice.lms.moodle.prependShortCourseName=true sebserver.webservice.lms.moodle.fetch.cutoffdate.yearsBeforeNow=2 +sebserver.webservice.lms.moodle.fetch.applyNameCriteria=false sebserver.webservice.lms.olat.sendAdditionalAttributesWithRestriction=false sebserver.webservice.lms.address.alias= sebserver.webservice.lms.datafetch.validity.seconds=600 diff --git a/src/test/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/moodle/plugin/MoodlePluginCourseAccessTest.java b/src/test/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/moodle/plugin/MoodlePluginCourseAccessTest.java index 924b8875..75a659be 100644 --- a/src/test/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/moodle/plugin/MoodlePluginCourseAccessTest.java +++ b/src/test/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/moodle/plugin/MoodlePluginCourseAccessTest.java @@ -324,7 +324,8 @@ public class MoodlePluginCourseAccessTest { asyncService, moodleMockupRestTemplateFactory, new NoOpCacheManager(), - mockEnvironment); + mockEnvironment, + false); } } From 2c7f4b8e0915399ddf3b3aca7969391cb12edb04 Mon Sep 17 00:00:00 2001 From: anhefti Date: Wed, 14 Jun 2023 15:36:43 +0200 Subject: [PATCH 18/27] SEBSERV-451 fixed --- .../session/impl/ExamSessionServiceImpl.java | 14 ++++++++------ src/main/resources/messages.properties | 6 +++--- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/ExamSessionServiceImpl.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/ExamSessionServiceImpl.java index d8d89ff9..a227cc04 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/ExamSessionServiceImpl.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/ExamSessionServiceImpl.java @@ -642,12 +642,14 @@ public class ExamSessionServiceImpl implements ExamSessionService { final ClientConnectionDataInternal cc = this.examSessionCacheService.getClientConnection(token); if (cc.clientConnection.status.duplicateCheckStatus) { - final Long id = this.duplicateCheck.put( - cc.clientConnection.userSessionId, - cc.getConnectionId()); - if (id != null) { - duplicates.add(id); - duplicates.add(cc.getConnectionId()); + if (cc.clientConnection.userSessionId != null) { + final Long id = this.duplicateCheck.put( + cc.clientConnection.userSessionId, + cc.getConnectionId()); + if (id != null) { + duplicates.add(id); + duplicates.add(cc.getConnectionId()); + } } } return cc; diff --git a/src/main/resources/messages.properties b/src/main/resources/messages.properties index 2fdf79e6..6af4ca76 100644 --- a/src/main/resources/messages.properties +++ b/src/main/resources/messages.properties @@ -675,14 +675,14 @@ sebserver.exam.configuration.form.title.new=Add exam configuration mapping sebserver.exam.configuration.form.title=Exam Configuration Mapping sebserver.exam.configuration.form.name=Exam Configuration sebserver.exam.configuration.form.name.tooltip=Please select an exam configuration to attach to the exam -sebserver.exam.configuration.form.encryptSecret=Encryption Password -sebserver.exam.configuration.form.encryptSecret.tooltip=Define an encryption password if the exam configuration should be encrypted by password +sebserver.exam.configuration.form.encryptSecret=Settings Password +sebserver.exam.configuration.form.encryptSecret.tooltip=Define an encryption password if the exam configuration settings should be encrypted by password sebserver.exam.configuration.form.description=Description sebserver.exam.configuration.form.description.tooltip=The description of the selected exam configuration sebserver.exam.configuration.form.status=Status sebserver.exam.configuration.form.status.tooltip=The current status of the selected exam configuration sebserver.exam.configuration.form.encryptSecret.confirm=Confirm Password -sebserver.exam.configuration.form.encryptSecret.confirm.tooltip=Please confirm the encryption password if there is one +sebserver.exam.configuration.form.encryptSecret.confirm.tooltip=Please confirm the settings password if there is one sebserver.exam.indicator.list.actions=  sebserver.exam.indicator.list.title=Indicators From 90d403c2b30634b3f91f6ade6595265e8942675a Mon Sep 17 00:00:00 2001 From: anhefti Date: Thu, 15 Jun 2023 10:40:50 +0200 Subject: [PATCH 19/27] SEBSERV-449 fixed name query --- .../lms/impl/moodle/plugin/MoodlePluginCourseAccess.java | 6 ++++-- .../moodle/plugin/MooldePluginLmsAPITemplateFactory.java | 2 +- src/main/resources/config/application-ws.properties | 2 +- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/moodle/plugin/MoodlePluginCourseAccess.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/moodle/plugin/MoodlePluginCourseAccess.java index 2321adeb..dd59758a 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/moodle/plugin/MoodlePluginCourseAccess.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/moodle/plugin/MoodlePluginCourseAccess.java @@ -438,8 +438,10 @@ public class MoodlePluginCourseAccess extends AbstractCachedCourseAccess impleme // TODO clarify with Amr and Luca if this is OK // and if it is possible to apply the nameCondition also the the course name (shortname) - if (this.applyNameCriteria && StringUtils.isNotBlank(nameCondition)) { - sqlCondition = sqlCondition + " AND (m.name LIKE '" + + if (StringUtils.isNotBlank(nameCondition)) { + sqlCondition = sqlCondition + " AND (name LIKE '" + + Utils.toSQLWildcard(nameCondition) + + "' OR shortname LIKE '" + Utils.toSQLWildcard(nameCondition) + "')"; } diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/moodle/plugin/MooldePluginLmsAPITemplateFactory.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/moodle/plugin/MooldePluginLmsAPITemplateFactory.java index 7b027a1e..f83dbecf 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/moodle/plugin/MooldePluginLmsAPITemplateFactory.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/moodle/plugin/MooldePluginLmsAPITemplateFactory.java @@ -57,7 +57,7 @@ public class MooldePluginLmsAPITemplateFactory implements LmsAPITemplateFactory final ExamConfigurationValueService examConfigurationValueService, final ClientHttpRequestFactoryService clientHttpRequestFactoryService, @Value("${sebserver.webservice.lms.moodle.api.token.request.paths:}") final String alternativeTokenRequestPaths, - @Value("${sebserver.webservice.lms.moodle.fetch.applyNameCriteria:false}") final boolean applyNameCriteria) { + @Value("${sebserver.webservice.lms.moodle.fetch.applyNameCriteria:true}") final boolean applyNameCriteria) { this.jsonMapper = jsonMapper; this.cacheManager = cacheManager; diff --git a/src/main/resources/config/application-ws.properties b/src/main/resources/config/application-ws.properties index f3568646..f1f2727a 100644 --- a/src/main/resources/config/application-ws.properties +++ b/src/main/resources/config/application-ws.properties @@ -81,7 +81,7 @@ sebserver.webservice.lms.openedx.api.token.request.paths=/oauth2/access_token sebserver.webservice.lms.moodle.api.token.request.paths=/login/token.php sebserver.webservice.lms.moodle.prependShortCourseName=true sebserver.webservice.lms.moodle.fetch.cutoffdate.yearsBeforeNow=2 -sebserver.webservice.lms.moodle.fetch.applyNameCriteria=false +sebserver.webservice.lms.moodle.fetch.applyNameCriteria=true sebserver.webservice.lms.olat.sendAdditionalAttributesWithRestriction=false sebserver.webservice.lms.address.alias= sebserver.webservice.lms.datafetch.validity.seconds=600 From 922149e7401556573f8208a373abb899866fe1c5 Mon Sep 17 00:00:00 2001 From: anhefti Date: Thu, 15 Jun 2023 10:41:20 +0200 Subject: [PATCH 20/27] SEBSERV-449 fixed name query --- .../lms/impl/moodle/plugin/MoodlePluginCourseAccess.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/moodle/plugin/MoodlePluginCourseAccess.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/moodle/plugin/MoodlePluginCourseAccess.java index dd59758a..d8568f3b 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/moodle/plugin/MoodlePluginCourseAccess.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/moodle/plugin/MoodlePluginCourseAccess.java @@ -436,9 +436,7 @@ public class MoodlePluginCourseAccess extends AbstractCachedCourseAccess impleme final String fromElement = String.valueOf(page * size); final LinkedMultiValueMap attributes = new LinkedMultiValueMap<>(); - // TODO clarify with Amr and Luca if this is OK - // and if it is possible to apply the nameCondition also the the course name (shortname) - if (StringUtils.isNotBlank(nameCondition)) { + if (this.applyNameCriteria && StringUtils.isNotBlank(nameCondition)) { sqlCondition = sqlCondition + " AND (name LIKE '" + Utils.toSQLWildcard(nameCondition) + "' OR shortname LIKE '" + From c99922c7ef9272b00275d6b4d9b7b20cd81f02c5 Mon Sep 17 00:00:00 2001 From: anhefti Date: Thu, 15 Jun 2023 10:42:18 +0200 Subject: [PATCH 21/27] fixed exam lms availability update --- .../servicelayer/session/impl/ExamUpdateHandler.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/ExamUpdateHandler.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/ExamUpdateHandler.java index 665e9400..033e7e5f 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/ExamUpdateHandler.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/ExamUpdateHandler.java @@ -410,11 +410,14 @@ class ExamUpdateHandler { .getLmsAPITemplate(lmsSetupId) .getOrThrow(); + final Exam exam = exams.get(quizId); if (!lmsTemplate.getType().features.contains(Features.COURSE_RECOVERY)) { + if (exam.lmsAvailable == null || exam.isLmsAvailable()) { + this.examDAO.markLMSAvailability(quizId, false, updateId); + } throw new UnsupportedOperationException("No Course Recovery"); } - final Exam exam = exams.get(quizId); final int attempts = Integer.parseInt(this.additionalAttributesDAO.getAdditionalAttribute( EntityType.EXAM, exam.id, From 4b051fbc97eefbd98a39eb2929dc08107ef0a11d Mon Sep 17 00:00:00 2001 From: anhefti Date: Thu, 15 Jun 2023 10:42:53 +0200 Subject: [PATCH 22/27] fixed also marked as lms unavailable exams shows in running exams --- .../servicelayer/session/impl/ExamSessionServiceImpl.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/ExamSessionServiceImpl.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/ExamSessionServiceImpl.java index a227cc04..b5f61c64 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/ExamSessionServiceImpl.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/ExamSessionServiceImpl.java @@ -267,8 +267,7 @@ public class ExamSessionServiceImpl implements ExamSessionService { filterMap .putIfAbsent(Exam.FILTER_ATTR_ACTIVE, Constants.TRUE_STRING) - .putIfAbsent(Exam.FILTER_ATTR_STATUS, ExamStatus.RUNNING.name()) - .putIfAbsent(Exam.FILTER_ATTR_HIDE_MISSING, Constants.TRUE_STRING); + .putIfAbsent(Exam.FILTER_ATTR_STATUS, ExamStatus.RUNNING.name()); return this.examDAO.allMatching(filterMap, predicate) .map(col -> col.stream() From 768fcea15b2b2090558e3359d1e1ca809a6a6ed9 Mon Sep 17 00:00:00 2001 From: anhefti Date: Thu, 15 Jun 2023 10:43:37 +0200 Subject: [PATCH 23/27] SEBSERV-444 fixed also in exam delete message --- .../ethz/seb/sebserver/gui/content/exam/ExamDeletePopup.java | 3 ++- src/main/java/ch/ethz/seb/sebserver/gui/widget/Message.java | 2 +- .../servicelayer/lms/impl/mockup/MockCourseAccessAPI.java | 2 +- .../webservice/integration/api/admin/ExamAPITest.java | 4 ++-- 4 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/content/exam/ExamDeletePopup.java b/src/main/java/ch/ethz/seb/sebserver/gui/content/exam/ExamDeletePopup.java index 5ec0af5d..c039a91d 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/content/exam/ExamDeletePopup.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/content/exam/ExamDeletePopup.java @@ -16,6 +16,7 @@ import java.util.function.Predicate; import java.util.function.Supplier; import java.util.stream.Collectors; +import org.apache.commons.text.StringEscapeUtils; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Label; @@ -161,7 +162,7 @@ public class ExamDeletePopup { new ActionEvent(action), action.pageContext()); - final String examName = examToDelete.toName().name; + final String examName = StringEscapeUtils.escapeXml11(examToDelete.toName().name); final List dependencies = report.results.stream() .filter(key -> !key.equals(entityKey)) .collect(Collectors.toList()); diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/widget/Message.java b/src/main/java/ch/ethz/seb/sebserver/gui/widget/Message.java index 9913deec..4ad20c14 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/widget/Message.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/widget/Message.java @@ -46,7 +46,7 @@ public final class Message extends MessageBox { super.prepareOpen(); } catch (final IllegalArgumentException e) { // fallback on markup text error - super.setMessage(StringEscapeUtils.escapeHtml4(super.getMessage())); + super.setMessage(StringEscapeUtils.escapeXml11(super.getMessage())); super.prepareOpen(); } final GridLayout layout = (GridLayout) super.shell.getLayout(); diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/mockup/MockCourseAccessAPI.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/mockup/MockCourseAccessAPI.java index e5db1a9b..b65da02a 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/mockup/MockCourseAccessAPI.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/mockup/MockCourseAccessAPI.java @@ -63,7 +63,7 @@ public class MockCourseAccessAPI implements CourseAccessAPI { "quiz1", institutionId, lmsSetupId, lmsType, "Demo Quiz 1 (MOCKUP)", "

Demo Quiz Mockup

", "2020-01-01T09:00:00Z", null, "http://lms.mockup.com/api/")); this.mockups.add(new QuizData( - "quiz2", institutionId, lmsSetupId, lmsType, "Demo Quiz 2 (MOCKUP) äöüèÜÄÖ ?< ", + "quiz2 äöüèÜÄÖ ?<", institutionId, lmsSetupId, lmsType, "Demo Quiz 2 (MOCKUP) äöüèÜÄÖ ?< ", "

Demo Quiz Mockup

", "2020-01-01T09:00:00Z", "2025-01-01T09:00:00Z", "http://lms.mockup.com/api/")); this.mockups.add(new QuizData( diff --git a/src/test/java/ch/ethz/seb/sebserver/webservice/integration/api/admin/ExamAPITest.java b/src/test/java/ch/ethz/seb/sebserver/webservice/integration/api/admin/ExamAPITest.java index 3076b492..5bd8c9b1 100644 --- a/src/test/java/ch/ethz/seb/sebserver/webservice/integration/api/admin/ExamAPITest.java +++ b/src/test/java/ch/ethz/seb/sebserver/webservice/integration/api/admin/ExamAPITest.java @@ -36,12 +36,12 @@ public class ExamAPITest extends AdministrationAPIIntegrationTester { sebAdminAccess, sebAdminAccess, "LmsSetupMock", - "quiz2", + "quiz2 äöüèÜÄÖ ?<", ExamType.MANAGED, "user5"); assertNotNull(exam); - assertEquals("quiz2", exam.getExternalId()); + assertEquals("quiz2 äöüèÜÄÖ ?<", exam.getExternalId()); // Note cannot set right collation on h2 assertEquals(ExamType.MANAGED, exam.getType()); assertFalse(exam.getSupporter().isEmpty()); From 20429ce1275f3ca94ba2956435e2d5f4f846c9c8 Mon Sep 17 00:00:00 2001 From: anhefti Date: Thu, 15 Jun 2023 11:12:36 +0200 Subject: [PATCH 24/27] SEBSERV-444 --- .../ch/ethz/seb/sebserver/gui/content/exam/LmsSetupForm.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/content/exam/LmsSetupForm.java b/src/main/java/ch/ethz/seb/sebserver/gui/content/exam/LmsSetupForm.java index a980c810..fcac8ab8 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/content/exam/LmsSetupForm.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/content/exam/LmsSetupForm.java @@ -15,7 +15,6 @@ import java.util.function.BooleanSupplier; import java.util.function.Function; import org.apache.commons.lang3.StringUtils; -import org.apache.commons.text.StringEscapeUtils; import org.eclipse.swt.SWT; import org.eclipse.swt.widgets.Button; import org.eclipse.swt.widgets.Composite; @@ -482,7 +481,7 @@ public class LmsSetupForm implements TemplateComposer { case TOKEN_REQUEST: { throw new PageMessageException(new LocTextKey( "sebserver.lmssetup.action.test.tokenRequestError", - Utils.formatHTMLLinesForceEscaped(StringEscapeUtils.escapeHtml4(error.message)))); + Utils.formatHTMLLinesForceEscaped(Utils.escapeHTML_XML_EcmaScript(error.message)))); } case QUIZ_ACCESS_API_REQUEST: { if (error.message.contains("quizaccess_sebserver_get_exams")) { From 03140052372250751e3f209a4a4c59aba8d000fc Mon Sep 17 00:00:00 2001 From: anhefti Date: Thu, 15 Jun 2023 15:09:03 +0200 Subject: [PATCH 25/27] SEBSERV-450 better error handling in case of parese error --- .../seb/sebserver/gui/service/page/impl/PageAction.java | 3 +++ .../gui/service/remote/webservice/api/RestCall.java | 6 +++++- .../sebserver/gui/integration/UseCasesIntegrationTest.java | 4 ++-- 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/service/page/impl/PageAction.java b/src/main/java/ch/ethz/seb/sebserver/gui/service/page/impl/PageAction.java index 1c78953e..03149686 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/service/page/impl/PageAction.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/service/page/impl/PageAction.java @@ -185,6 +185,9 @@ public final class PageAction { } catch (final PageMessageException pme) { PageAction.this.pageContext.publishPageMessage(pme); return; + } catch (final Exception e) { + this.pageContext.notifyUnexpectedError(e); + return; } } else { callback.accept(exec()); diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/service/remote/webservice/api/RestCall.java b/src/main/java/ch/ethz/seb/sebserver/gui/service/remote/webservice/api/RestCall.java index 8f1dd4c2..483e48cf 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/service/remote/webservice/api/RestCall.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/service/remote/webservice/api/RestCall.java @@ -180,7 +180,7 @@ public abstract class RestCall { e, "NO RESPONSE AVAILABLE", String.valueOf(builder))); - return Result.ofError(e); + return Result.ofError(restCallError); } } @@ -216,6 +216,10 @@ public abstract class RestCall { } else { restCallError.errors.add(APIMessage.ErrorMessage.GENERIC.of(responseEntity.getBody())); } + } catch (final Exception e) { + final String body = responseEntity.getBody(); + log.error("Failed to parse rest response error message: {}", body); + throw e; } log.debug( diff --git a/src/test/java/ch/ethz/seb/sebserver/gui/integration/UseCasesIntegrationTest.java b/src/test/java/ch/ethz/seb/sebserver/gui/integration/UseCasesIntegrationTest.java index 6d62f73d..e8d72124 100644 --- a/src/test/java/ch/ethz/seb/sebserver/gui/integration/UseCasesIntegrationTest.java +++ b/src/test/java/ch/ethz/seb/sebserver/gui/integration/UseCasesIntegrationTest.java @@ -1578,7 +1578,7 @@ public class UseCasesIntegrationTest extends GuiIntegrationTest { fail("Exception expected here"); } catch (final Exception e) { - assertEquals("argument \"content\" is null", e.getMessage()); + assertEquals("Unexpected error while rest call", e.getMessage()); } // test follow-up integrity violation @@ -1596,7 +1596,7 @@ public class UseCasesIntegrationTest extends GuiIntegrationTest { fail("Exception expected here"); } catch (final Exception e) { - assertEquals("argument \"content\" is null", e.getMessage()); + assertEquals("Unexpected error while rest call", e.getMessage()); } final ConfigurationTableValues newTableValue = new ConfigurationTableValues( From 06e8c9b4e8a477c059cb2c803343cef87412b851 Mon Sep 17 00:00:00 2001 From: anhefti Date: Mon, 19 Jun 2023 13:36:15 +0200 Subject: [PATCH 26/27] SEBSERV-449 fixed name search on Moodle side and fixed query SEB Server --- .../impl/moodle/plugin/MoodlePluginCourseAccess.java | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/moodle/plugin/MoodlePluginCourseAccess.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/moodle/plugin/MoodlePluginCourseAccess.java index d8568f3b..a5505674 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/moodle/plugin/MoodlePluginCourseAccess.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/moodle/plugin/MoodlePluginCourseAccess.java @@ -82,6 +82,9 @@ public class MoodlePluginCourseAccess extends AbstractCachedCourseAccess impleme public static final String PARAM_PAGE_START = "startneedle"; public static final String PARAM_PAGE_SIZE = "perpage"; + public static final String SQL_QUIZ_NAME = "m.name"; + public static final String SQL_COURSE_NAME = "shortname"; + public static final String SQL_CONDITION_TEMPLATE = //"(startdate >= %s or timecreated >=%s) and (enddate is null or enddate = 0 or enddate >= %s)"; "(startdate is null OR startdate = 0 OR startdate >= %s) AND (enddate is null or enddate = 0 OR enddate >= %s)"; @@ -437,9 +440,13 @@ public class MoodlePluginCourseAccess extends AbstractCachedCourseAccess impleme final LinkedMultiValueMap attributes = new LinkedMultiValueMap<>(); if (this.applyNameCriteria && StringUtils.isNotBlank(nameCondition)) { - sqlCondition = sqlCondition + " AND (name LIKE '" + + sqlCondition = sqlCondition + " AND (" + + SQL_QUIZ_NAME + + " LIKE '" + Utils.toSQLWildcard(nameCondition) + - "' OR shortname LIKE '" + + "' OR " + + SQL_COURSE_NAME + + " LIKE '" + Utils.toSQLWildcard(nameCondition) + "')"; } From c0e6687647999895a2514fdd3249ba4270fe3612 Mon Sep 17 00:00:00 2001 From: anhefti Date: Mon, 19 Jun 2023 13:42:39 +0200 Subject: [PATCH 27/27] preparing for patch release 1.5.1 --- README.rst | 2 -- 1 file changed, 2 deletions(-) diff --git a/README.rst b/README.rst index 9d34656a..d36ebd4f 100644 --- a/README.rst +++ b/README.rst @@ -98,8 +98,6 @@ Bugfixes: Docker-Image: -TODO - SEB - SEB Server Compatibility ------------------------------