From d648bcb1677475c445ca2f9faa9ae74e05e98297 Mon Sep 17 00:00:00 2001 From: anhefti Date: Tue, 30 Aug 2022 11:10:45 +0200 Subject: [PATCH] SEBSERV-324 finished maintenance --- .../seb/sebserver/gbl/api/POSTMapper.java | 1 - .../sebserver/gbl/model/exam/ClientGroup.java | 64 +++-- .../gbl/model/exam/ClientGroupData.java | 28 +- .../gbl/model/exam/ClientGroupTemplate.java | 34 ++- .../ClientGroupConnectionMatcher.java | 2 +- .../monitoring/ClientGroupMatcherService.java | 2 +- .../IPv4RangeClientGroupMatcher.java | 2 +- .../ch/ethz/seb/sebserver/gbl/util/Utils.java | 12 + .../gui/content/action/ActionCategory.java | 1 + .../gui/content/action/ActionDefinition.java | 29 ++- .../gui/content/action/ActionPane.java | 4 +- .../activity/PageStateDefinitionImpl.java | 2 + .../gui/content/exam/ClientGroupForm.java | 241 ++++++++++++++++++ .../content/exam/ClientGroupTemplateForm.java | 40 ++- .../gui/content/exam/ExamClientGroupList.java | 172 +++++++++++++ .../sebserver/gui/content/exam/ExamForm.java | 19 +- ...ndicators.java => ExamIndicatorsList.java} | 42 +-- .../gui/content/exam/ExamTemplateForm.java | 29 +-- .../ch/ethz/seb/sebserver/gui/form/Form.java | 14 + .../gui/service/ResourceService.java | 27 +- .../sebserver/gui/widget/ThresholdList.java | 32 +++ .../sebserver/gui/widget/WidgetFactory.java | 47 +++- .../dao/impl/ClientGroupDAOImpl.java | 2 +- .../dao/impl/ExamTemplateDAOImpl.java | 6 +- .../servicelayer/exam/ExamAdminService.java | 24 +- .../weblayer/api/ClientGroupController.java | 10 +- .../api/ExamAdministrationController.java | 51 ---- .../weblayer/api/ExamTemplateController.java | 9 +- src/main/resources/messages.properties | 33 ++- .../static/images/add_clientgroup.png | Bin 0 -> 232 bytes .../resources/static/images/clientgroup.png | Bin 0 -> 224 bytes .../integration/UseCasesIntegrationTest.java | 2 +- 32 files changed, 781 insertions(+), 200 deletions(-) create mode 100644 src/main/java/ch/ethz/seb/sebserver/gui/content/exam/ClientGroupForm.java create mode 100644 src/main/java/ch/ethz/seb/sebserver/gui/content/exam/ExamClientGroupList.java rename src/main/java/ch/ethz/seb/sebserver/gui/content/exam/{ExamFormIndicators.java => ExamIndicatorsList.java} (79%) create mode 100644 src/main/resources/static/images/add_clientgroup.png create mode 100644 src/main/resources/static/images/clientgroup.png diff --git a/src/main/java/ch/ethz/seb/sebserver/gbl/api/POSTMapper.java b/src/main/java/ch/ethz/seb/sebserver/gbl/api/POSTMapper.java index 857f19e9..aabeeb7b 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gbl/api/POSTMapper.java +++ b/src/main/java/ch/ethz/seb/sebserver/gbl/api/POSTMapper.java @@ -270,5 +270,4 @@ public class POSTMapper { this.params.putIfAbsent(name, Arrays.asList(value)); return (T) this; } - } diff --git a/src/main/java/ch/ethz/seb/sebserver/gbl/model/exam/ClientGroup.java b/src/main/java/ch/ethz/seb/sebserver/gbl/model/exam/ClientGroup.java index f1b720e4..7731e0f5 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gbl/model/exam/ClientGroup.java +++ b/src/main/java/ch/ethz/seb/sebserver/gbl/model/exam/ClientGroup.java @@ -8,6 +8,9 @@ package ch.ethz.seb.sebserver.gbl.model.exam; +import java.net.InetAddress; +import java.net.UnknownHostException; + import javax.validation.constraints.NotNull; import javax.validation.constraints.Size; @@ -22,20 +25,12 @@ import ch.ethz.seb.sebserver.gbl.Constants; import ch.ethz.seb.sebserver.gbl.api.EntityType; import ch.ethz.seb.sebserver.gbl.api.POSTMapper; import ch.ethz.seb.sebserver.gbl.model.Domain.CLIENT_GROUP; +import ch.ethz.seb.sebserver.gbl.util.Utils; @JsonIgnoreProperties(ignoreUnknown = true) public class ClientGroup implements ClientGroupData { public static final String FILTER_ATTR_EXAM_ID = "examId"; - public static final String ATTR_IP_RANGE_START = "ipRangeStart"; - public static final String ATTR_IP_RANGE_END = "ipRangeEnd"; - public static final String ATTR_CLIENT_OS = "clientOS"; - - public enum ClientGroupType { - NONE, - IP_V4_RANGE, - CLIENT_OS - } @JsonProperty(CLIENT_GROUP.ATTR_ID) public final Long id; @@ -66,7 +61,7 @@ public class ClientGroup implements ClientGroupData { public final String ipRangeEnd; @JsonProperty(ATTR_CLIENT_OS) - public final String clientOS; + public final ClientOS clientOS; @JsonCreator public ClientGroup( @@ -78,18 +73,18 @@ public class ClientGroup implements ClientGroupData { @JsonProperty(CLIENT_GROUP.ATTR_ICON) final String icon, @JsonProperty(ATTR_IP_RANGE_START) final String ipRangeStart, @JsonProperty(ATTR_IP_RANGE_END) final String ipRangeEnd, - @JsonProperty(ATTR_CLIENT_OS) final String clientOS) { + @JsonProperty(ATTR_CLIENT_OS) final ClientOS clientOS) { super(); this.id = id; this.examId = examId; this.name = name; - this.type = type; + this.type = type == null ? ClientGroupType.NONE : type; this.color = color; this.icon = icon; this.ipRangeStart = ipRangeStart; this.ipRangeEnd = ipRangeEnd; - this.clientOS = clientOS; + this.clientOS = clientOS == null ? ClientOS.NONE : clientOS; } public ClientGroup( @@ -105,7 +100,8 @@ public class ClientGroup implements ClientGroupData { this.id = id; this.examId = examId; this.name = name; - this.type = type; + this.type = type == null ? ClientGroupType.NONE : type; + ; this.color = color; this.icon = icon; @@ -114,19 +110,19 @@ public class ClientGroup implements ClientGroupData { final String[] split = StringUtils.split(data, Constants.EMBEDDED_LIST_SEPARATOR); this.ipRangeStart = split[0]; this.ipRangeEnd = split[1]; - this.clientOS = null; + this.clientOS = ClientOS.NONE; break; } case CLIENT_OS: { this.ipRangeStart = null; this.ipRangeEnd = null; - this.clientOS = data; + this.clientOS = Utils.enumFromString(data, ClientOS.class, ClientOS.NONE); break; } default: { this.ipRangeStart = null; this.ipRangeEnd = null; - this.clientOS = null; + this.clientOS = ClientOS.NONE; } } } @@ -140,7 +136,15 @@ public class ClientGroup implements ClientGroupData { this.icon = postParams.getString(CLIENT_GROUP.ATTR_ICON); this.ipRangeStart = postParams.getString(ATTR_IP_RANGE_START); this.ipRangeEnd = postParams.getString(ATTR_IP_RANGE_END); - this.clientOS = postParams.getString(ATTR_CLIENT_OS); + this.clientOS = postParams.getEnum(ATTR_CLIENT_OS, ClientOS.class); + } + + public static ClientGroup createNew(final String examId) { + try { + return new ClientGroup(null, Long.parseLong(examId), null, null, null, null, null, null, null); + } catch (final Exception e) { + return new ClientGroup(null, null, null, null, null, null, null, null, null); + } } @Override @@ -184,16 +188,32 @@ public class ClientGroup implements ClientGroupData { @Override public String getIpRangeStart() { - return this.ipRangeStart; + if (StringUtils.isBlank(this.ipRangeStart)) { + return null; + } + + try { + return InetAddress.getByName(this.ipRangeStart).getHostAddress(); + } catch (final UnknownHostException e) { + return null; + } } @Override public String getIpRangeEnd() { - return this.ipRangeEnd; + if (StringUtils.isBlank(this.ipRangeEnd)) { + return null; + } + + try { + return InetAddress.getByName(this.ipRangeEnd).getHostAddress(); + } catch (final UnknownHostException e) { + return null; + } } @Override - public String getClientOS() { + public ClientOS getClientOS() { return this.clientOS; } @@ -204,7 +224,7 @@ public class ClientGroup implements ClientGroupData { return this.ipRangeStart + Constants.EMBEDDED_LIST_SEPARATOR + this.ipRangeEnd; } case CLIENT_OS: { - return this.clientOS; + return this.clientOS.name(); } default: { return StringUtils.EMPTY; diff --git a/src/main/java/ch/ethz/seb/sebserver/gbl/model/exam/ClientGroupData.java b/src/main/java/ch/ethz/seb/sebserver/gbl/model/exam/ClientGroupData.java index d1a939ec..6988010f 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gbl/model/exam/ClientGroupData.java +++ b/src/main/java/ch/ethz/seb/sebserver/gbl/model/exam/ClientGroupData.java @@ -9,10 +9,33 @@ package ch.ethz.seb.sebserver.gbl.model.exam; import ch.ethz.seb.sebserver.gbl.model.Entity; -import ch.ethz.seb.sebserver.gbl.model.exam.ClientGroup.ClientGroupType; public interface ClientGroupData extends Entity { + public static final String ATTR_IP_RANGE_START = "ipRangeStart"; + public static final String ATTR_IP_RANGE_END = "ipRangeEnd"; + public static final String ATTR_CLIENT_OS = "clientOS"; + + public enum ClientGroupType { + NONE, + IP_V4_RANGE, + CLIENT_OS + } + + public enum ClientOS { + NONE(null), + WINDOWS("Windows"), + MAC_OS("TODO"), + I_OS("TODO"); + + final String queryString; + + private ClientOS(final String queryString) { + this.queryString = queryString; + } + + } + Long getId(); ClientGroupType getType(); @@ -25,5 +48,6 @@ public interface ClientGroupData extends Entity { String getIpRangeEnd(); - String getClientOS(); + ClientOS getClientOS(); + } diff --git a/src/main/java/ch/ethz/seb/sebserver/gbl/model/exam/ClientGroupTemplate.java b/src/main/java/ch/ethz/seb/sebserver/gbl/model/exam/ClientGroupTemplate.java index 28596402..989ced89 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gbl/model/exam/ClientGroupTemplate.java +++ b/src/main/java/ch/ethz/seb/sebserver/gbl/model/exam/ClientGroupTemplate.java @@ -8,6 +8,9 @@ package ch.ethz.seb.sebserver.gbl.model.exam; +import java.net.InetAddress; +import java.net.UnknownHostException; + import javax.validation.constraints.NotNull; import javax.validation.constraints.Size; @@ -22,7 +25,6 @@ import ch.ethz.seb.sebserver.gbl.Constants; import ch.ethz.seb.sebserver.gbl.api.EntityType; import ch.ethz.seb.sebserver.gbl.api.POSTMapper; import ch.ethz.seb.sebserver.gbl.model.Domain.CLIENT_GROUP; -import ch.ethz.seb.sebserver.gbl.model.exam.ClientGroup.ClientGroupType; @JsonIgnoreProperties(ignoreUnknown = true) public class ClientGroupTemplate implements ClientGroupData { @@ -57,7 +59,7 @@ public class ClientGroupTemplate implements ClientGroupData { public final String ipRangeEnd; @JsonProperty(ClientGroup.ATTR_CLIENT_OS) - public final String clientOS; + public final ClientOS clientOS; @JsonCreator public ClientGroupTemplate( @@ -69,7 +71,7 @@ public class ClientGroupTemplate implements ClientGroupData { @JsonProperty(CLIENT_GROUP.ATTR_ICON) final String icon, @JsonProperty(ClientGroup.ATTR_IP_RANGE_START) final String ipRangeStart, @JsonProperty(ClientGroup.ATTR_IP_RANGE_END) final String ipRangeEnd, - @JsonProperty(ClientGroup.ATTR_CLIENT_OS) final String clientOS) { + @JsonProperty(ClientGroup.ATTR_CLIENT_OS) final ClientOS clientOS) { super(); this.id = id; @@ -93,7 +95,7 @@ public class ClientGroupTemplate implements ClientGroupData { this.icon = postParams.getString(CLIENT_GROUP.ATTR_ICON); this.ipRangeStart = postParams.getString(ClientGroup.ATTR_IP_RANGE_START); this.ipRangeEnd = postParams.getString(ClientGroup.ATTR_IP_RANGE_END); - this.clientOS = postParams.getString(ClientGroup.ATTR_CLIENT_OS); + this.clientOS = postParams.getEnum(ClientGroup.ATTR_CLIENT_OS, ClientOS.class); } public ClientGroupTemplate(final Long id, final ClientGroupTemplate other) { @@ -150,16 +152,32 @@ public class ClientGroupTemplate implements ClientGroupData { @Override public String getIpRangeStart() { - return this.ipRangeStart; + if (StringUtils.isBlank(this.ipRangeStart)) { + return null; + } + + try { + return InetAddress.getByName(this.ipRangeStart).getHostAddress(); + } catch (final UnknownHostException e) { + return null; + } } @Override public String getIpRangeEnd() { - return this.ipRangeEnd; + if (StringUtils.isBlank(this.ipRangeEnd)) { + return null; + } + + try { + return InetAddress.getByName(this.ipRangeEnd).getHostAddress(); + } catch (final UnknownHostException e) { + return null; + } } @Override - public String getClientOS() { + public ClientOS getClientOS() { return this.clientOS; } @@ -170,7 +188,7 @@ public class ClientGroupTemplate implements ClientGroupData { return this.ipRangeStart + Constants.EMBEDDED_LIST_SEPARATOR + this.ipRangeEnd; } case CLIENT_OS: { - return this.clientOS; + return this.clientOS.name(); } default: { return StringUtils.EMPTY; diff --git a/src/main/java/ch/ethz/seb/sebserver/gbl/monitoring/ClientGroupConnectionMatcher.java b/src/main/java/ch/ethz/seb/sebserver/gbl/monitoring/ClientGroupConnectionMatcher.java index ea322603..42a11d61 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gbl/monitoring/ClientGroupConnectionMatcher.java +++ b/src/main/java/ch/ethz/seb/sebserver/gbl/monitoring/ClientGroupConnectionMatcher.java @@ -9,7 +9,7 @@ package ch.ethz.seb.sebserver.gbl.monitoring; import ch.ethz.seb.sebserver.gbl.model.exam.ClientGroup; -import ch.ethz.seb.sebserver.gbl.model.exam.ClientGroup.ClientGroupType; +import ch.ethz.seb.sebserver.gbl.model.exam.ClientGroupData.ClientGroupType; import ch.ethz.seb.sebserver.gbl.model.session.ClientConnection; /** Defines a client connection to client group matcher for a specific client group type. diff --git a/src/main/java/ch/ethz/seb/sebserver/gbl/monitoring/ClientGroupMatcherService.java b/src/main/java/ch/ethz/seb/sebserver/gbl/monitoring/ClientGroupMatcherService.java index 550e919e..11d84198 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gbl/monitoring/ClientGroupMatcherService.java +++ b/src/main/java/ch/ethz/seb/sebserver/gbl/monitoring/ClientGroupMatcherService.java @@ -17,7 +17,7 @@ import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Service; import ch.ethz.seb.sebserver.gbl.model.exam.ClientGroup; -import ch.ethz.seb.sebserver.gbl.model.exam.ClientGroup.ClientGroupType; +import ch.ethz.seb.sebserver.gbl.model.exam.ClientGroupData.ClientGroupType; import ch.ethz.seb.sebserver.gbl.model.session.ClientConnection; @Lazy diff --git a/src/main/java/ch/ethz/seb/sebserver/gbl/monitoring/IPv4RangeClientGroupMatcher.java b/src/main/java/ch/ethz/seb/sebserver/gbl/monitoring/IPv4RangeClientGroupMatcher.java index d36dfdb4..03dcc456 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gbl/monitoring/IPv4RangeClientGroupMatcher.java +++ b/src/main/java/ch/ethz/seb/sebserver/gbl/monitoring/IPv4RangeClientGroupMatcher.java @@ -14,7 +14,7 @@ import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Component; import ch.ethz.seb.sebserver.gbl.model.exam.ClientGroup; -import ch.ethz.seb.sebserver.gbl.model.exam.ClientGroup.ClientGroupType; +import ch.ethz.seb.sebserver.gbl.model.exam.ClientGroupData.ClientGroupType; import ch.ethz.seb.sebserver.gbl.model.session.ClientConnection; import ch.ethz.seb.sebserver.gbl.util.Utils; diff --git a/src/main/java/ch/ethz/seb/sebserver/gbl/util/Utils.java b/src/main/java/ch/ethz/seb/sebserver/gbl/util/Utils.java index 78511844..1fe189d1 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gbl/util/Utils.java +++ b/src/main/java/ch/ethz/seb/sebserver/gbl/util/Utils.java @@ -193,6 +193,18 @@ public final class Utils { .collect(Collectors.toList())); } + public static > T enumFromString( + final String string, + final Class enumClass, + final T defaultValue) { + + try { + return Enum.valueOf(enumClass, string); + } catch (final Exception e) { + return defaultValue; + } + } + public static Collection getListOfLines(final String list) { if (list == null) { return Collections.emptyList(); diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/content/action/ActionCategory.java b/src/main/java/ch/ethz/seb/sebserver/gui/content/action/ActionCategory.java index ada235f3..7a029614 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/content/action/ActionCategory.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/content/action/ActionCategory.java @@ -23,6 +23,7 @@ public enum ActionCategory { CLIENT_GROUP_TEMPLATE_LIST(new LocTextKey("sebserver.examtemplate.clientgroup.list.actions"), 2), EXAM_CONFIG_MAPPING_LIST(new LocTextKey("sebserver.exam.configuration.list.actions"), 1), INDICATOR_LIST(new LocTextKey("sebserver.exam.indicator.list.actions"), 2), + CLIENT_GROUP_LIST(new LocTextKey("sebserver.exam.clientgroup.list.actions"), 3), SEB_CLIENT_CONFIG_LIST(new LocTextKey("sebserver.clientconfig.list.actions"), 1), SEB_EXAM_CONFIG_LIST(new LocTextKey("sebserver.examconfig.list.actions"), 1), SEB_CONFIG_TEMPLATE_LIST(new LocTextKey("sebserver.configtemplate.list.actions"), 1), diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/content/action/ActionDefinition.java b/src/main/java/ch/ethz/seb/sebserver/gui/content/action/ActionDefinition.java index 7f60bd0a..5485fc37 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/content/action/ActionDefinition.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/content/action/ActionDefinition.java @@ -388,6 +388,33 @@ public enum ActionDefinition { ImageIcon.CANCEL, PageStateDefinitionImpl.EXAM_VIEW, ActionCategory.FORM), + + EXAM_CLIENT_GROUP_NEW( + new LocTextKey("sebserver.exam.clientgroup.action.list.new"), + ImageIcon.ADD_CLIENT_GROUP, + PageStateDefinitionImpl.CLIENT_GROUP_EDIT, + ActionCategory.CLIENT_GROUP_LIST), + EXAM_CLIENT_GROUP_MODIFY_FROM_LIST( + new LocTextKey("sebserver.exam.clientgroup.action.list.modify"), + ImageIcon.EDIT, + PageStateDefinitionImpl.CLIENT_GROUP_EDIT, + ActionCategory.CLIENT_GROUP_LIST), + EXAM_CLIENT_GROUP_DELETE_FROM_LIST( + new LocTextKey("sebserver.exam.clientgroup.action.list.delete"), + ImageIcon.DELETE, + PageStateDefinitionImpl.EXAM_VIEW, + ActionCategory.CLIENT_GROUP_LIST), + EXAM_CLIENT_GROUP_SAVE( + new LocTextKey("sebserver.exam.clientgroup.action.save"), + ImageIcon.SAVE, + PageStateDefinitionImpl.EXAM_VIEW, + ActionCategory.FORM), + EXAM_CLIENT_GROUP_CANCEL_MODIFY( + new LocTextKey("sebserver.overall.action.modify.cancel"), + ImageIcon.CANCEL, + PageStateDefinitionImpl.EXAM_VIEW, + ActionCategory.FORM), + EXAM_SEB_CLIENT_CONFIG_EXPORT( new LocTextKey("sebserver.exam.action.createClientToStartExam"), ImageIcon.EXPORT, @@ -471,7 +498,7 @@ public enum ActionDefinition { CLIENT_GROUP_TEMPLATE_NEW( new LocTextKey("sebserver.examtemplate.clientgroup.action.list.new"), - ImageIcon.CLIENT_GROUP, + ImageIcon.ADD_CLIENT_GROUP, PageStateDefinitionImpl.CLIENT_GROUP_TEMPLATE_EDIT, ActionCategory.CLIENT_GROUP_TEMPLATE_LIST), CLIENT_GROUP_TEMPLATE_MODIFY_FROM_LIST( diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/content/action/ActionPane.java b/src/main/java/ch/ethz/seb/sebserver/gui/content/action/ActionPane.java index 9f791680..27a19321 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/content/action/ActionPane.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/content/action/ActionPane.java @@ -254,14 +254,14 @@ public class ActionPane implements TemplateComposer { final Template template = new Template(); final ImageCell imageCell = new ImageCell(template); imageCell.setLeft(0, -8) - .setWidth(20) + .setWidth(24) .setTop(0) .setBottom(0, 0) .setHorizontalAlignment(SWT.LEFT) .setBackground(null); imageCell.setBindingIndex(0); final TextCell textCell = new TextCell(template); - textCell.setLeft(0, 20) + textCell.setLeft(0, 24) .setWidth(SWT.DEFAULT) .setTop(7) .setBottom(0, 0) diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/content/activity/PageStateDefinitionImpl.java b/src/main/java/ch/ethz/seb/sebserver/gui/content/activity/PageStateDefinitionImpl.java index 225ad150..40261eb9 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/content/activity/PageStateDefinitionImpl.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/content/activity/PageStateDefinitionImpl.java @@ -24,6 +24,7 @@ import ch.ethz.seb.sebserver.gui.content.configs.SEBClientConfigList; import ch.ethz.seb.sebserver.gui.content.configs.SEBExamConfigForm; import ch.ethz.seb.sebserver.gui.content.configs.SEBExamConfigList; import ch.ethz.seb.sebserver.gui.content.configs.SEBSettingsForm; +import ch.ethz.seb.sebserver.gui.content.exam.ClientGroupForm; import ch.ethz.seb.sebserver.gui.content.exam.ClientGroupTemplateForm; import ch.ethz.seb.sebserver.gui.content.exam.ExamForm; import ch.ethz.seb.sebserver.gui.content.exam.ExamList; @@ -66,6 +67,7 @@ public enum PageStateDefinitionImpl implements PageStateDefinition { EXAM_VIEW(Type.FORM_VIEW, ExamForm.class, ActivityDefinition.EXAM), EXAM_EDIT(Type.FORM_EDIT, ExamForm.class, ActivityDefinition.EXAM), INDICATOR_EDIT(Type.FORM_EDIT, IndicatorForm.class, ActivityDefinition.EXAM), + CLIENT_GROUP_EDIT(Type.FORM_EDIT, ClientGroupForm.class, ActivityDefinition.EXAM), EXAM_TEMPLATE_LIST(Type.LIST_VIEW, ExamTemplateList.class, ActivityDefinition.EXAM_TEMPLATE), EXAM_TEMPLATE_VIEW(Type.LIST_VIEW, ExamTemplateForm.class, ActivityDefinition.EXAM_TEMPLATE), diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/content/exam/ClientGroupForm.java b/src/main/java/ch/ethz/seb/sebserver/gui/content/exam/ClientGroupForm.java new file mode 100644 index 00000000..885c40ff --- /dev/null +++ b/src/main/java/ch/ethz/seb/sebserver/gui/content/exam/ClientGroupForm.java @@ -0,0 +1,241 @@ +/* + * Copyright (c) 2022 ETH Zürich, Educational Development and Technology (LET) + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +package ch.ethz.seb.sebserver.gui.content.exam; + +import org.apache.commons.lang3.StringUtils; +import org.eclipse.swt.widgets.Composite; +import org.springframework.context.annotation.Lazy; +import org.springframework.stereotype.Component; + +import ch.ethz.seb.sebserver.gbl.Constants; +import ch.ethz.seb.sebserver.gbl.api.API; +import ch.ethz.seb.sebserver.gbl.api.EntityType; +import ch.ethz.seb.sebserver.gbl.model.Domain; +import ch.ethz.seb.sebserver.gbl.model.EntityKey; +import ch.ethz.seb.sebserver.gbl.model.exam.ClientGroup; +import ch.ethz.seb.sebserver.gbl.model.exam.ClientGroupData.ClientGroupType; +import ch.ethz.seb.sebserver.gbl.model.exam.ClientGroupTemplate; +import ch.ethz.seb.sebserver.gbl.model.exam.Exam; +import ch.ethz.seb.sebserver.gbl.model.exam.QuizData; +import ch.ethz.seb.sebserver.gbl.profile.GuiProfile; +import ch.ethz.seb.sebserver.gbl.util.Utils; +import ch.ethz.seb.sebserver.gui.content.action.ActionDefinition; +import ch.ethz.seb.sebserver.gui.form.Form; +import ch.ethz.seb.sebserver.gui.form.FormBuilder; +import ch.ethz.seb.sebserver.gui.form.FormHandle; +import ch.ethz.seb.sebserver.gui.service.ResourceService; +import ch.ethz.seb.sebserver.gui.service.i18n.I18nSupport; +import ch.ethz.seb.sebserver.gui.service.i18n.LocTextKey; +import ch.ethz.seb.sebserver.gui.service.page.PageContext; +import ch.ethz.seb.sebserver.gui.service.page.PageService; +import ch.ethz.seb.sebserver.gui.service.page.TemplateComposer; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestService; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.GetExam; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.clientgroup.GetClientGroup; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.clientgroup.NewClientGroup; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.clientgroup.SaveClientGroup; +import ch.ethz.seb.sebserver.gui.widget.WidgetFactory; + +@Lazy +@Component +@GuiProfile +public class ClientGroupForm implements TemplateComposer { + + private static final LocTextKey NEW_CLIENT_GROUP_TILE_TEXT_KEY = + new LocTextKey("sebserver.exam.clientgroup.form.title.new"); + private static final LocTextKey CLIENT_GROUP_TILE_TEXT_KEY = + new LocTextKey("sebserver.exam.clientgroup.form.title"); + private static final LocTextKey FORM_COLOR_TEXT_KEY = + new LocTextKey("sebserver.exam.clientgroup.form.color"); + private static final LocTextKey FORM_TYPE_TEXT_KEY = + new LocTextKey("sebserver.exam.clientgroup.form.type"); + private static final LocTextKey FORM_NAME_TEXT_KEY = + new LocTextKey("sebserver.exam.clientgroup.form.name"); + private static final LocTextKey FORM_EXAM_TEXT_KEY = + new LocTextKey("sebserver.exam.clientgroup.form.exam"); + private static final LocTextKey FORM_DESC_TEXT_KEY = + new LocTextKey("sebserver.exam.clientgroup.form.description"); + private static final LocTextKey FORM_IP_START_KEY = + new LocTextKey("sebserver.exam.clientgroup.form.ipstart"); + private static final LocTextKey FORM_IP_END_KEY = + new LocTextKey("sebserver.exam.clientgroup.form.ipend"); + private static final LocTextKey FORM_OS_TYPE_KEY = + new LocTextKey("sebserver.exam.clientgroup.form.ostype"); + + private static final String CLIENT_GROUP_TYPE_DESC_PREFIX = + "sebserver.exam.clientgroup.type.description."; + private static final String TYPE_DESCRIPTION_FIELD_NAME = + "typeDescription"; + + private final PageService pageService; + private final ResourceService resourceService; + private final I18nSupport i18nSupport; + + protected ClientGroupForm(final PageService pageService) { + + this.pageService = pageService; + this.resourceService = pageService.getResourceService(); + this.i18nSupport = pageService.getI18nSupport(); + } + + @Override + public void compose(final PageContext pageContext) { + final RestService restService = this.resourceService.getRestService(); + final WidgetFactory widgetFactory = this.pageService.getWidgetFactory(); + final EntityKey entityKey = pageContext.getEntityKey(); + final EntityKey parentEntityKey = pageContext.getParentEntityKey(); + final boolean isNew = entityKey == null; + final boolean isReadonly = pageContext.isReadonly(); + + final Exam exam = restService + .getBuilder(GetExam.class) + .withURIVariable(API.PARAM_MODEL_ID, parentEntityKey.modelId) + .call() + .onError(error -> pageContext.notifyLoadError(EntityType.EXAM, error)) + .getOrThrow(); + + // get data or create new. Handle error if happen + final ClientGroup clientGroup = (isNew) + ? ClientGroup.createNew(exam.getModelId()) + : restService + .getBuilder(GetClientGroup.class) + .withURIVariable(API.PARAM_MODEL_ID, entityKey.modelId) + .call() + .onError(error -> pageContext.notifyLoadError(EntityType.CLIENT_GROUP, error)) + .getOrThrow(); + + final boolean typeSet = clientGroup.type != null; + final String typeDescription = (typeSet) + ? Utils.formatLineBreaks( + this.i18nSupport.getText(CLIENT_GROUP_TYPE_DESC_PREFIX + clientGroup.type.name())) + : Constants.EMPTY_NOTE; + + // new PageContext with actual EntityKey + final PageContext formContext = pageContext.withEntityKey(clientGroup.getEntityKey()); + + // the default page layout + final LocTextKey titleKey = (isNew) + ? NEW_CLIENT_GROUP_TILE_TEXT_KEY + : CLIENT_GROUP_TILE_TEXT_KEY; + final Composite content = widgetFactory.defaultPageLayout( + formContext.getParent(), + titleKey); + + final FormHandle formHandle = this.pageService.formBuilder( + formContext.copyOf(content)) + .readonly(isReadonly) + .putStaticValueIf(() -> !isNew, + Domain.CLIENT_GROUP.ATTR_ID, + clientGroup.getModelId()) + .putStaticValue( + Domain.EXAM.ATTR_INSTITUTION_ID, + String.valueOf(exam.getInstitutionId())) + .putStaticValue( + Domain.CLIENT_GROUP.ATTR_EXAM_ID, + parentEntityKey.getModelId()) + + .addField(FormBuilder.text( + QuizData.QUIZ_ATTR_NAME, + FORM_EXAM_TEXT_KEY, + exam.name) + .readonly(true)) + + .addField(FormBuilder.text( + Domain.CLIENT_GROUP.ATTR_NAME, + FORM_NAME_TEXT_KEY, + clientGroup.name) + .mandatory(!isReadonly)) + + .addField(FormBuilder.colorSelection( + Domain.CLIENT_GROUP.ATTR_COLOR, + FORM_COLOR_TEXT_KEY, + clientGroup.color) + .withEmptyCellSeparation(false)) + + .addField(FormBuilder.singleSelection( + Domain.CLIENT_GROUP.ATTR_TYPE, + FORM_TYPE_TEXT_KEY, + clientGroup.type.name(), + this.resourceService::clientGroupTypeResources) + .withSelectionListener(form -> updateForm(form, this.i18nSupport)) + .mandatory(!isReadonly)) + + .addField(FormBuilder.text( + TYPE_DESCRIPTION_FIELD_NAME, + FORM_DESC_TEXT_KEY, + typeDescription) + .asArea() + .readonly(true)) + + .addField(FormBuilder.text( + ClientGroup.ATTR_IP_RANGE_START, + FORM_IP_START_KEY, + clientGroup::getIpRangeStart) + .mandatory(!isReadonly) + .visibleIf(clientGroup.type == ClientGroupType.IP_V4_RANGE)) + + .addField(FormBuilder.text( + ClientGroup.ATTR_IP_RANGE_END, + FORM_IP_END_KEY, + clientGroup::getIpRangeEnd) + .mandatory(!isReadonly) + .visibleIf(clientGroup.type == ClientGroupType.IP_V4_RANGE)) + + .addField(FormBuilder.singleSelection( + ClientGroupTemplate.ATTR_CLIENT_OS, + FORM_OS_TYPE_KEY, + clientGroup.clientOS.name(), + this.resourceService::clientClientOSResources) + .visibleIf(clientGroup.type == ClientGroupType.CLIENT_OS) + .mandatory(!isReadonly)) + + .buildFor((isNew) + ? restService.getRestCall(NewClientGroup.class) + : restService.getRestCall(SaveClientGroup.class)); + + // propagate content actions to action-pane + this.pageService.pageActionBuilder(formContext.clearEntityKeys()) + + .newAction(ActionDefinition.EXAM_CLIENT_GROUP_SAVE) + .withEntityKey(parentEntityKey) + .withExec(formHandle::processFormSave) + .ignoreMoveAwayFromEdit() + .publishIf(() -> !isReadonly) + + .newAction(ActionDefinition.EXAM_CLIENT_GROUP_CANCEL_MODIFY) + .withEntityKey(parentEntityKey) + .withExec(this.pageService.backToCurrentFunction()) + .publishIf(() -> !isReadonly); + } + + public static void updateForm(final Form form, final I18nSupport i18nSupport) { + final String typeValue = form.getFieldValue(Domain.CLIENT_GROUP.ATTR_TYPE); + if (StringUtils.isNotBlank(typeValue)) { + final String text = i18nSupport.getText(CLIENT_GROUP_TYPE_DESC_PREFIX + typeValue); + form.setFieldValue( + TYPE_DESCRIPTION_FIELD_NAME, + Utils.formatLineBreaks(text)); + final ClientGroupType type = ClientGroupType.valueOf(typeValue); + form.setFieldVisible(false, ClientGroup.ATTR_IP_RANGE_START); + form.setFieldVisible(false, ClientGroup.ATTR_IP_RANGE_END); + form.setFieldVisible(false, ClientGroupTemplate.ATTR_CLIENT_OS); + if (type == ClientGroupType.IP_V4_RANGE) { + form.setFieldVisible(true, ClientGroup.ATTR_IP_RANGE_START); + form.setFieldVisible(true, ClientGroup.ATTR_IP_RANGE_END); + } + if (type == ClientGroupType.CLIENT_OS) { + form.setFieldVisible(true, ClientGroupTemplate.ATTR_CLIENT_OS); + } + + } else { + form.setFieldValue(TYPE_DESCRIPTION_FIELD_NAME, Constants.EMPTY_NOTE); + } + } + +} diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/content/exam/ClientGroupTemplateForm.java b/src/main/java/ch/ethz/seb/sebserver/gui/content/exam/ClientGroupTemplateForm.java index dd52c1e2..33ab86db 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/content/exam/ClientGroupTemplateForm.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/content/exam/ClientGroupTemplateForm.java @@ -8,7 +8,6 @@ package ch.ethz.seb.sebserver.gui.content.exam; -import org.apache.commons.lang3.StringUtils; import org.eclipse.swt.widgets.Composite; import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Component; @@ -19,14 +18,13 @@ import ch.ethz.seb.sebserver.gbl.api.EntityType; import ch.ethz.seb.sebserver.gbl.model.Domain; import ch.ethz.seb.sebserver.gbl.model.EntityKey; import ch.ethz.seb.sebserver.gbl.model.exam.ClientGroup; -import ch.ethz.seb.sebserver.gbl.model.exam.ClientGroup.ClientGroupType; +import ch.ethz.seb.sebserver.gbl.model.exam.ClientGroupData.ClientGroupType; import ch.ethz.seb.sebserver.gbl.model.exam.ClientGroupTemplate; import ch.ethz.seb.sebserver.gbl.model.exam.ExamTemplate; import ch.ethz.seb.sebserver.gbl.model.exam.IndicatorTemplate; import ch.ethz.seb.sebserver.gbl.profile.GuiProfile; import ch.ethz.seb.sebserver.gbl.util.Utils; import ch.ethz.seb.sebserver.gui.content.action.ActionDefinition; -import ch.ethz.seb.sebserver.gui.form.Form; import ch.ethz.seb.sebserver.gui.form.FormBuilder; import ch.ethz.seb.sebserver.gui.form.FormHandle; import ch.ethz.seb.sebserver.gui.service.ResourceService; @@ -65,6 +63,8 @@ public class ClientGroupTemplateForm implements TemplateComposer { new LocTextKey("sebserver.exam.clientgroup.form.ipstart"); private static final LocTextKey FORM_IP_END_KEY = new LocTextKey("sebserver.exam.clientgroup.form.ipend"); + private static final LocTextKey FORM_OS_TYPE_KEY = + new LocTextKey("sebserver.exam.clientgroup.form.ostype"); private static final String CLIENT_GROUP_TYPE_DESC_PREFIX = "sebserver.exam.clientgroup.type.description."; @@ -166,7 +166,7 @@ public class ClientGroupTemplateForm implements TemplateComposer { FORM_TYPE_TEXT_KEY, (clientGroupTemplate.type != null) ? clientGroupTemplate.type.name() : null, this.resourceService::clientGroupTypeResources) - .withSelectionListener(this::updateForm) + .withSelectionListener(form -> ClientGroupForm.updateForm(form, this.i18nSupport)) .mandatory(!isReadonly)) .addField(FormBuilder.text( @@ -174,13 +174,13 @@ public class ClientGroupTemplateForm implements TemplateComposer { FORM_DESC_TEXT_KEY, typeDescription) .asArea() - //.asHTML(true) .readonly(true)) .addField(FormBuilder.text( ClientGroup.ATTR_IP_RANGE_START, FORM_IP_START_KEY, clientGroupTemplate::getIpRangeStart) + .mandatory(!isReadonly) .visibleIf(clientGroupTemplate.type != null && clientGroupTemplate.type == ClientGroupType.IP_V4_RANGE)) @@ -188,9 +188,19 @@ public class ClientGroupTemplateForm implements TemplateComposer { ClientGroup.ATTR_IP_RANGE_END, FORM_IP_END_KEY, clientGroupTemplate::getIpRangeEnd) + .mandatory(!isReadonly) .visibleIf(clientGroupTemplate.type != null && clientGroupTemplate.type == ClientGroupType.IP_V4_RANGE)) + .addField(FormBuilder.singleSelection( + ClientGroupTemplate.ATTR_CLIENT_OS, + FORM_OS_TYPE_KEY, + (clientGroupTemplate.clientOS != null) ? clientGroupTemplate.clientOS.name() : null, + this.resourceService::clientClientOSResources) + .visibleIf(clientGroupTemplate.type != null + && clientGroupTemplate.type == ClientGroupType.CLIENT_OS) + .mandatory(!isReadonly)) + .buildFor((isNew) ? restService.getRestCall(NewClientGroupTemplate.class) : restService.getRestCall(SaveClientGroupTemplate.class)); @@ -211,24 +221,4 @@ public class ClientGroupTemplateForm implements TemplateComposer { } - private void updateForm(final Form form) { - final String typeValue = form.getFieldValue(Domain.CLIENT_GROUP.ATTR_TYPE); - if (StringUtils.isNotBlank(typeValue)) { - final String text = this.i18nSupport.getText(CLIENT_GROUP_TYPE_DESC_PREFIX + typeValue); - form.setFieldValue( - TYPE_DESCRIPTION_FIELD_NAME, - Utils.formatLineBreaks(text)); - final ClientGroupType type = ClientGroupType.valueOf(typeValue); - form.setFieldVisible(false, ClientGroup.ATTR_IP_RANGE_START); - form.setFieldVisible(false, ClientGroup.ATTR_IP_RANGE_END); - if (type == ClientGroupType.IP_V4_RANGE) { - form.setFieldVisible(true, ClientGroup.ATTR_IP_RANGE_START); - form.setFieldVisible(true, ClientGroup.ATTR_IP_RANGE_END); - } - - } else { - form.setFieldValue(TYPE_DESCRIPTION_FIELD_NAME, Constants.EMPTY_NOTE); - } - } - } diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/content/exam/ExamClientGroupList.java b/src/main/java/ch/ethz/seb/sebserver/gui/content/exam/ExamClientGroupList.java new file mode 100644 index 00000000..7cb6ed25 --- /dev/null +++ b/src/main/java/ch/ethz/seb/sebserver/gui/content/exam/ExamClientGroupList.java @@ -0,0 +1,172 @@ +/* + * Copyright (c) 2022 ETH Zürich, Educational Development and Technology (LET) + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +package ch.ethz.seb.sebserver.gui.content.exam; + +import org.apache.commons.lang3.BooleanUtils; +import org.eclipse.swt.widgets.Composite; +import org.springframework.context.annotation.Lazy; +import org.springframework.stereotype.Component; + +import ch.ethz.seb.sebserver.gbl.Constants; +import ch.ethz.seb.sebserver.gbl.api.API; +import ch.ethz.seb.sebserver.gbl.model.Domain; +import ch.ethz.seb.sebserver.gbl.model.EntityKey; +import ch.ethz.seb.sebserver.gbl.model.exam.ClientGroup; +import ch.ethz.seb.sebserver.gbl.profile.GuiProfile; +import ch.ethz.seb.sebserver.gui.content.action.ActionDefinition; +import ch.ethz.seb.sebserver.gui.service.ResourceService; +import ch.ethz.seb.sebserver.gui.service.i18n.LocTextKey; +import ch.ethz.seb.sebserver.gui.service.page.PageContext; +import ch.ethz.seb.sebserver.gui.service.page.PageContext.AttributeKeys; +import ch.ethz.seb.sebserver.gui.service.page.PageService; +import ch.ethz.seb.sebserver.gui.service.page.PageService.PageActionBuilder; +import ch.ethz.seb.sebserver.gui.service.page.TemplateComposer; +import ch.ethz.seb.sebserver.gui.service.page.impl.PageAction; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestService; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.clientgroup.DeleteClientGroup; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.clientgroup.GetClientGroupPage; +import ch.ethz.seb.sebserver.gui.table.ColumnDefinition; +import ch.ethz.seb.sebserver.gui.table.EntityTable; +import ch.ethz.seb.sebserver.gui.widget.WidgetFactory; + +@Lazy +@Component +@GuiProfile +public class ExamClientGroupList implements TemplateComposer { + + private final static LocTextKey CLIENT_GROUP_LIST_TITLE_KEY = + new LocTextKey("sebserver.exam.clientgroup.list.title"); + private final static LocTextKey CLIENT_GROUP_LIST_TITLE_TOOLTIP_KEY = + new LocTextKey("sebserver.exam.clientgroup.list.title" + Constants.TOOLTIP_TEXT_KEY_SUFFIX); + private final static LocTextKey CLIENT_GROUP_TYPE_COLUMN_KEY = + new LocTextKey("sebserver.exam.clientgroup.list.column.type"); + private final static LocTextKey CLIENT_GROUP_NAME_COLUMN_KEY = + new LocTextKey("sebserver.exam.clientgroup.list.column.name"); + private final static LocTextKey CLIENT_GROUP_COLOR_COLUMN_KEY = + new LocTextKey("sebserver.exam.clientgroup.list.column.color"); + private final static LocTextKey CLIENT_GROUP_DATA_COLUMN_KEY = + new LocTextKey("sebserver.exam.clientgroup.list.column.data"); + private final static LocTextKey CLIENT_GROUP_EMPTY_SELECTION_TEXT_KEY = + new LocTextKey("sebserver.exam.clientgroup.list.pleaseSelect"); + private static final LocTextKey CLIENT_GROUP_EMPTY_LIST_MESSAGE = + new LocTextKey("sebserver.exam.clientgroup.list.empty"); + + private final PageService pageService; + private final ResourceService resourceService; + private final WidgetFactory widgetFactory; + private final RestService restService; + + public ExamClientGroupList(final PageService pageService) { + this.pageService = pageService; + this.resourceService = pageService.getResourceService(); + this.widgetFactory = pageService.getWidgetFactory(); + this.restService = pageService.getRestService(); + } + + @Override + public void compose(final PageContext pageContext) { + final Composite content = pageContext.getParent(); + final EntityKey entityKey = pageContext.getEntityKey(); + final boolean editable = BooleanUtils.toBoolean( + pageContext.getAttribute(ExamForm.ATTR_EDITABLE)); + + // List of ClientGroups + this.widgetFactory.addFormSubContextHeader( + content, + CLIENT_GROUP_LIST_TITLE_KEY, + CLIENT_GROUP_LIST_TITLE_TOOLTIP_KEY); + + final PageActionBuilder actionBuilder = this.pageService.pageActionBuilder(pageContext + .clearEntityKeys() + .removeAttribute(AttributeKeys.IMPORT_FROM_QUIZ_DATA)); + + final EntityTable clientGroupTable = + this.pageService + .entityTableBuilder(this.restService.getRestCall(GetClientGroupPage.class)) + .withRestCallAdapter(builder -> builder.withURIVariable( + API.PARAM_PARENT_MODEL_ID, + entityKey.modelId)) + .withEmptyMessage(CLIENT_GROUP_EMPTY_LIST_MESSAGE) + .withMarkup() + .withPaging(100) + .hideNavigation() + + .withColumn(new ColumnDefinition<>( + Domain.CLIENT_GROUP.ATTR_NAME, + CLIENT_GROUP_NAME_COLUMN_KEY, + ClientGroup::getName) + .widthProportion(2)) + + .withColumn(new ColumnDefinition( + Domain.CLIENT_GROUP.ATTR_TYPE, + CLIENT_GROUP_TYPE_COLUMN_KEY, + cgt -> this.resourceService.clientGroupTypeName(cgt)) + .widthProportion(1)) + + .withColumn(new ColumnDefinition( + Domain.CLIENT_GROUP.ATTR_COLOR, + CLIENT_GROUP_COLOR_COLUMN_KEY, + cgt -> this.widgetFactory.getColorValueHTML(cgt)) + .asMarkup() + .widthProportion(1)) + + .withColumn(new ColumnDefinition( + Domain.CLIENT_GROUP.ATTR_DATA, + CLIENT_GROUP_DATA_COLUMN_KEY, + cgt -> this.widgetFactory.clientGroupDataToHTML(cgt)) + .asMarkup() + .widthProportion(3)) + + .withDefaultActionIf( + () -> editable, + () -> actionBuilder + .newAction(ActionDefinition.EXAM_CLIENT_GROUP_MODIFY_FROM_LIST) + .withParentEntityKey(entityKey) + .create()) + + .withSelectionListener(this.pageService.getSelectionPublisher( + pageContext, + ActionDefinition.EXAM_CLIENT_GROUP_MODIFY_FROM_LIST, + ActionDefinition.EXAM_CLIENT_GROUP_DELETE_FROM_LIST)) + + .compose(pageContext.copyOf(content)); + + actionBuilder + + .newAction(ActionDefinition.EXAM_CLIENT_GROUP_MODIFY_FROM_LIST) + .withParentEntityKey(entityKey) + .withSelect( + clientGroupTable::getMultiSelection, + PageAction::applySingleSelectionAsEntityKey, + CLIENT_GROUP_EMPTY_SELECTION_TEXT_KEY) + .publishIf(() -> editable && clientGroupTable.hasAnyContent(), false) + + .newAction(ActionDefinition.EXAM_CLIENT_GROUP_DELETE_FROM_LIST) + .withEntityKey(entityKey) + .withSelect( + clientGroupTable::getMultiSelection, + this::deleteSelectedClientGroup, + CLIENT_GROUP_EMPTY_SELECTION_TEXT_KEY) + .publishIf(() -> editable && clientGroupTable.hasAnyContent(), false) + + .newAction(ActionDefinition.EXAM_CLIENT_GROUP_NEW) + .withParentEntityKey(entityKey) + .publishIf(() -> editable); + } + + private PageAction deleteSelectedClientGroup(final PageAction action) { + final EntityKey clientGroupKey = action.getSingleSelection(); + this.resourceService.getRestService() + .getBuilder(DeleteClientGroup.class) + .withURIVariable(API.PARAM_MODEL_ID, clientGroupKey.modelId) + .call(); + return action; + } + +} diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/content/exam/ExamForm.java b/src/main/java/ch/ethz/seb/sebserver/gui/content/exam/ExamForm.java index 39a39ff6..7503a416 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/content/exam/ExamForm.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/content/exam/ExamForm.java @@ -151,7 +151,8 @@ public class ExamForm implements TemplateComposer { private final RestService restService; private final ExamDeletePopup examDeletePopup; private final ExamFormConfigs examFormConfigs; - private final ExamFormIndicators examFormIndicators; + private final ExamIndicatorsList examIndicatorsList; + private final ExamClientGroupList examClientGroupList; private final ExamCreateClientConfigPopup examCreateClientConfigPopup; protected ExamForm( @@ -162,7 +163,8 @@ public class ExamForm implements TemplateComposer { final DownloadService downloadService, final ExamDeletePopup examDeletePopup, final ExamFormConfigs examFormConfigs, - final ExamFormIndicators examFormIndicators, + final ExamIndicatorsList examIndicatorsList, + final ExamClientGroupList examClientGroupList, final ExamCreateClientConfigPopup examCreateClientConfigPopup) { this.pageService = pageService; @@ -173,7 +175,8 @@ public class ExamForm implements TemplateComposer { this.restService = this.resourceService.getRestService(); this.examDeletePopup = examDeletePopup; this.examFormConfigs = examFormConfigs; - this.examFormIndicators = examFormIndicators; + this.examIndicatorsList = examIndicatorsList; + this.examClientGroupList = examClientGroupList; this.examCreateClientConfigPopup = examCreateClientConfigPopup; this.consistencyMessageMapping = new HashMap<>(); @@ -485,7 +488,15 @@ public class ExamForm implements TemplateComposer { .withAttribute(ATTR_EXAM_STATUS, examStatus.name())); // Indicators - this.examFormIndicators.compose( + this.examIndicatorsList.compose( + formContext + .copyOf(content) + .withAttribute(ATTR_READ_GRANT, String.valueOf(entityGrantCheck.r())) + .withAttribute(ATTR_EDITABLE, String.valueOf(editable)) + .withAttribute(ATTR_EXAM_STATUS, examStatus.name())); + + // Client Groups + this.examClientGroupList.compose( formContext .copyOf(content) .withAttribute(ATTR_READ_GRANT, String.valueOf(entityGrantCheck.r())) diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/content/exam/ExamFormIndicators.java b/src/main/java/ch/ethz/seb/sebserver/gui/content/exam/ExamIndicatorsList.java similarity index 79% rename from src/main/java/ch/ethz/seb/sebserver/gui/content/exam/ExamFormIndicators.java rename to src/main/java/ch/ethz/seb/sebserver/gui/content/exam/ExamIndicatorsList.java index 82c23e39..369e3ba6 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/content/exam/ExamFormIndicators.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/content/exam/ExamIndicatorsList.java @@ -8,8 +8,6 @@ package ch.ethz.seb.sebserver.gui.content.exam; -import java.util.List; - import org.apache.commons.lang3.BooleanUtils; import org.eclipse.swt.widgets.Composite; import org.springframework.context.annotation.Lazy; @@ -20,10 +18,7 @@ import ch.ethz.seb.sebserver.gbl.api.API; import ch.ethz.seb.sebserver.gbl.model.Domain; import ch.ethz.seb.sebserver.gbl.model.EntityKey; import ch.ethz.seb.sebserver.gbl.model.exam.Indicator; -import ch.ethz.seb.sebserver.gbl.model.exam.Indicator.IndicatorType; -import ch.ethz.seb.sebserver.gbl.model.exam.Indicator.Threshold; import ch.ethz.seb.sebserver.gbl.profile.GuiProfile; -import ch.ethz.seb.sebserver.gbl.util.Utils; import ch.ethz.seb.sebserver.gui.content.action.ActionDefinition; import ch.ethz.seb.sebserver.gui.service.ResourceService; import ch.ethz.seb.sebserver.gui.service.i18n.LocTextKey; @@ -38,12 +33,13 @@ import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.indicator.De import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.indicator.GetIndicatorPage; import ch.ethz.seb.sebserver.gui.table.ColumnDefinition; import ch.ethz.seb.sebserver.gui.table.EntityTable; +import ch.ethz.seb.sebserver.gui.widget.ThresholdList; import ch.ethz.seb.sebserver.gui.widget.WidgetFactory; @Lazy @Component @GuiProfile -public class ExamFormIndicators implements TemplateComposer { +public class ExamIndicatorsList implements TemplateComposer { private final static LocTextKey INDICATOR_LIST_TITLE_KEY = new LocTextKey("sebserver.exam.indicator.list.title"); @@ -65,7 +61,7 @@ public class ExamFormIndicators implements TemplateComposer { private final WidgetFactory widgetFactory; private final RestService restService; - public ExamFormIndicators(final PageService pageService) { + public ExamIndicatorsList(final PageService pageService) { this.pageService = pageService; this.resourceService = pageService.getResourceService(); this.widgetFactory = pageService.getWidgetFactory(); @@ -111,7 +107,7 @@ public class ExamFormIndicators implements TemplateComposer { .withColumn(new ColumnDefinition( Domain.THRESHOLD.REFERENCE_NAME, INDICATOR_THRESHOLD_COLUMN_KEY, - i -> thresholdsValue(i.thresholds, i.type)) + i -> ThresholdList.thresholdsToHTML(i.thresholds, i.type)) .asMarkup() .widthProportion(4)) .withDefaultActionIf( @@ -170,34 +166,4 @@ public class ExamFormIndicators implements TemplateComposer { .getText(ResourceService.EXAM_INDICATOR_TYPE_PREFIX + indicator.type.name()); } - static String thresholdsValue( - final List thresholds, - final IndicatorType indicatorType) { - - if (thresholds.isEmpty()) { - return Constants.EMPTY_NOTE; - } - - final StringBuilder builder = thresholds - .stream() - .reduce( - new StringBuilder(), - (sb, threshold) -> sb - .append("") - .append(Indicator.getDisplayValue(indicatorType, threshold.value)) - .append(" (") - .append(threshold.color) - .append(")") - .append("") - .append(" | "), - StringBuilder::append); - builder.delete(builder.length() - 3, builder.length() - 1); - return builder.toString(); - } } diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/content/exam/ExamTemplateForm.java b/src/main/java/ch/ethz/seb/sebserver/gui/content/exam/ExamTemplateForm.java index 3c041a8c..37ad1143 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/content/exam/ExamTemplateForm.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/content/exam/ExamTemplateForm.java @@ -51,6 +51,7 @@ import ch.ethz.seb.sebserver.gui.service.remote.webservice.auth.CurrentUser; import ch.ethz.seb.sebserver.gui.service.remote.webservice.auth.CurrentUser.EntityGrantCheck; import ch.ethz.seb.sebserver.gui.table.ColumnDefinition; import ch.ethz.seb.sebserver.gui.table.EntityTable; +import ch.ethz.seb.sebserver.gui.widget.ThresholdList; import ch.ethz.seb.sebserver.gui.widget.WidgetFactory; @Lazy @@ -101,6 +102,8 @@ public class ExamTemplateForm implements TemplateComposer { new LocTextKey("sebserver.examtemplate.clientgroup.list.column.name"); private final static LocTextKey CLIENT_GROUP_COLOR_COLUMN_KEY = new LocTextKey("sebserver.examtemplate.clientgroup.list.column.color"); + private final static LocTextKey CLIENT_GROUP_DATA_COLUMN_KEY = + new LocTextKey("sebserver.examtemplate.clientgroup.list.column.data"); private final static LocTextKey CLIENT_GROUP_EMPTY_SELECTION_TEXT_KEY = new LocTextKey("sebserver.examtemplate.clientgroup.list.pleaseSelect"); private static final LocTextKey CLIENT_GROUP_EMPTY_LIST_MESSAGE = @@ -287,7 +290,7 @@ public class ExamTemplateForm implements TemplateComposer { .withColumn(new ColumnDefinition( Domain.THRESHOLD.REFERENCE_NAME, INDICATOR_THRESHOLD_COLUMN_KEY, - it -> ExamFormIndicators.thresholdsValue(it.thresholds, it.type)) + it -> ThresholdList.thresholdsToHTML(it.thresholds, it.type)) .asMarkup() .widthProportion(4)) .withDefaultActionIf( @@ -347,15 +350,21 @@ public class ExamTemplateForm implements TemplateComposer { CLIENT_GROUP_NAME_COLUMN_KEY, ClientGroupTemplate::getName) .widthProportion(2)) - .withColumn(new ColumnDefinition<>( + .withColumn(new ColumnDefinition( Domain.CLIENT_GROUP.ATTR_TYPE, CLIENT_GROUP_TYPE_COLUMN_KEY, - this::clientGroupTypeName) + cgt -> this.resourceService.clientGroupTypeName(cgt)) .widthProportion(1)) - .withColumn(new ColumnDefinition<>( + .withColumn(new ColumnDefinition( Domain.CLIENT_GROUP.ATTR_COLOR, CLIENT_GROUP_COLOR_COLUMN_KEY, - ClientGroupTemplate::getColor) + cgt -> this.widgetFactory.getColorValueHTML(cgt)) + .asMarkup() + .widthProportion(1)) + .withColumn(new ColumnDefinition( + Domain.CLIENT_GROUP.ATTR_DATA, + CLIENT_GROUP_DATA_COLUMN_KEY, + cgt -> this.widgetFactory.clientGroupDataToHTML(cgt)) .asMarkup() .widthProportion(4)) .withDefaultActionIf( @@ -430,18 +439,8 @@ public class ExamTemplateForm implements TemplateComposer { if (indicator.type == null) { return Constants.EMPTY_NOTE; } - return this.resourceService.getI18nSupport() .getText(ResourceService.EXAM_INDICATOR_TYPE_PREFIX + indicator.type.name()); } - private String clientGroupTypeName(final ClientGroupTemplate clientGroup) { - if (clientGroup.type == null) { - return Constants.EMPTY_NOTE; - } - - return this.resourceService.getI18nSupport() - .getText(ResourceService.EXAM_CLIENT_GROUP_TYPE_PREFIX + clientGroup.type.name()); - } - } diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/form/Form.java b/src/main/java/ch/ethz/seb/sebserver/gui/form/Form.java index 02a90892..a6c6a4fd 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/form/Form.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/form/Form.java @@ -174,6 +174,15 @@ public final class Form implements FormBinding { return this; } + Form removeField(final String name) { + if (this.formFields.containsKey(name)) { + final List list = this.formFields.remove(name); + list.forEach(ffa -> ffa.dispose()); + } + + return this; + } + public String getFieldValue(final String attributeName) { final FormFieldAccessor fieldAccessor = this.formFields.getFirst(attributeName); if (fieldAccessor == null) { @@ -464,6 +473,11 @@ public final class Form implements FormBinding { this(label, control, null, false, errorLabel); } + public void dispose() { + this.label.dispose(); + this.input.dispose(); + } + FormFieldAccessor( final Control label, final Control input, diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/service/ResourceService.java b/src/main/java/ch/ethz/seb/sebserver/gui/service/ResourceService.java index 93793cef..4a0ee97d 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/service/ResourceService.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/service/ResourceService.java @@ -36,7 +36,9 @@ import ch.ethz.seb.sebserver.gbl.api.EntityType; import ch.ethz.seb.sebserver.gbl.model.Activatable; import ch.ethz.seb.sebserver.gbl.model.Entity; import ch.ethz.seb.sebserver.gbl.model.EntityName; -import ch.ethz.seb.sebserver.gbl.model.exam.ClientGroup.ClientGroupType; +import ch.ethz.seb.sebserver.gbl.model.exam.ClientGroupData; +import ch.ethz.seb.sebserver.gbl.model.exam.ClientGroupData.ClientGroupType; +import ch.ethz.seb.sebserver.gbl.model.exam.ClientGroupData.ClientOS; import ch.ethz.seb.sebserver.gbl.model.exam.Exam; import ch.ethz.seb.sebserver.gbl.model.exam.Exam.ExamStatus; import ch.ethz.seb.sebserver.gbl.model.exam.Exam.ExamType; @@ -125,6 +127,7 @@ public class ResourceService { public static final String USERACCOUNT_ROLE_PREFIX = "sebserver.useraccount.role."; public static final String EXAM_INDICATOR_TYPE_PREFIX = "sebserver.exam.indicator.type."; public static final String EXAM_CLIENT_GROUP_TYPE_PREFIX = "sebserver.exam.clientgroup.type."; + public static final String CLIENT_OS_TYPE_PREFIX = "sebserver.overall.seb.os.type."; public static final String LMSSETUP_TYPE_PREFIX = "sebserver.lmssetup.type."; public static final String CONFIG_ATTRIBUTE_TYPE_PREFIX = "sebserver.configtemplate.attr.type."; public static final String SEB_RESTRICTION_WHITE_LIST_PREFIX = "sebserver.exam.form.sebrestriction.whiteListPaths."; @@ -275,6 +278,19 @@ public class ResourceService { .collect(Collectors.toList()); } + public List> clientClientOSResources() { + return Arrays.stream(ClientOS.values()) + .filter(type -> type != ClientOS.NONE) + .map(type -> new Tuple3<>( + type.name(), + this.i18nSupport.getText(CLIENT_OS_TYPE_PREFIX + type.name(), type.name()), + Utils.formatLineBreaks(this.i18nSupport.getText( + CLIENT_OS_TYPE_PREFIX + type.name() + Constants.TOOLTIP_TEXT_KEY_SUFFIX, + StringUtils.EMPTY)))) + .sorted(RESOURCE_COMPARATOR_TUPLE_3) + .collect(Collectors.toList()); + } + public List> examConfigurationSelectionResources() { return getExamConfigurationSelection() .getOr(Collections.emptyList()) @@ -905,4 +921,13 @@ public class ResourceService { .collect(Collectors.toList()); } + public String clientGroupTypeName(final ClientGroupData clientGroup) { + if (clientGroup.getType() == null) { + return Constants.EMPTY_NOTE; + } + + return this.getI18nSupport() + .getText(ResourceService.EXAM_CLIENT_GROUP_TYPE_PREFIX + clientGroup.getType().name()); + } + } diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/widget/ThresholdList.java b/src/main/java/ch/ethz/seb/sebserver/gui/widget/ThresholdList.java index 14bf03b5..77f45cdb 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/widget/ThresholdList.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/widget/ThresholdList.java @@ -30,6 +30,7 @@ import ch.ethz.seb.sebserver.gbl.Constants; import ch.ethz.seb.sebserver.gbl.model.exam.Indicator; import ch.ethz.seb.sebserver.gbl.model.exam.Indicator.IndicatorType; import ch.ethz.seb.sebserver.gbl.model.exam.Indicator.Threshold; +import ch.ethz.seb.sebserver.gbl.util.Utils; import ch.ethz.seb.sebserver.gui.service.i18n.LocTextKey; import ch.ethz.seb.sebserver.gui.service.page.PageService; import ch.ethz.seb.sebserver.gui.widget.Selection.Type; @@ -129,6 +130,37 @@ public final class ThresholdList extends Composite { return collect; } + public static String thresholdsToHTML( + final List thresholds, + final IndicatorType indicatorType) { + + if (thresholds.isEmpty()) { + return Constants.EMPTY_NOTE; + } + + final StringBuilder builder = thresholds + .stream() + .reduce( + new StringBuilder(), + (sb, threshold) -> sb + .append("   ") + .append(Indicator.getDisplayValue(indicatorType, threshold.value)) + .append(" (#") + .append(threshold.color) + .append(")   ") + .append("") + .append(" | "), + StringBuilder::append); + builder.delete(builder.length() - 3, builder.length() - 1); + return builder.toString(); + } + private void removeInvalidListEntries() { this.thresholds .stream() diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/widget/WidgetFactory.java b/src/main/java/ch/ethz/seb/sebserver/gui/widget/WidgetFactory.java index a9f15f78..df443bbc 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/widget/WidgetFactory.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/widget/WidgetFactory.java @@ -49,11 +49,14 @@ import org.slf4j.LoggerFactory; import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Service; +import ch.ethz.seb.sebserver.gbl.Constants; +import ch.ethz.seb.sebserver.gbl.model.exam.ClientGroupData; import ch.ethz.seb.sebserver.gbl.model.exam.Indicator.IndicatorType; import ch.ethz.seb.sebserver.gbl.model.exam.Indicator.Threshold; import ch.ethz.seb.sebserver.gbl.profile.GuiProfile; import ch.ethz.seb.sebserver.gbl.util.Tuple; import ch.ethz.seb.sebserver.gbl.util.Utils; +import ch.ethz.seb.sebserver.gui.service.ResourceService; import ch.ethz.seb.sebserver.gui.service.i18n.I18nSupport; import ch.ethz.seb.sebserver.gui.service.i18n.LocTextKey; import ch.ethz.seb.sebserver.gui.service.i18n.PolyglotPageService; @@ -130,7 +133,8 @@ public class WidgetFactory { INSTITUTION("institution.png"), LMS_SETUP("lmssetup.png"), INDICATOR("indicator.png"), - CLIENT_GROUP("indicator.png"), + CLIENT_GROUP("clientgroup.png"), + ADD_CLIENT_GROUP("add_clientgroup.png"), TEMPLATE("template.png"), DISABLE("disable.png"), SEND_QUIT("send-quit.png"), @@ -1029,4 +1033,45 @@ public class WidgetFactory { executor.execute(builder.toString()); } + public String clientGroupDataToHTML(final ClientGroupData data) { + switch (data.getType()) { + case IP_V4_RANGE: { + final String ipRangeStart = StringUtils.isBlank(data.getIpRangeStart()) + ? Constants.EMPTY_NOTE + : data.getIpRangeStart(); + final String ipRangeEnd = StringUtils.isBlank(data.getIpRangeEnd()) + ? Constants.EMPTY_NOTE + : data.getIpRangeEnd(); + return ipRangeStart + " - " + ipRangeEnd; + } + case CLIENT_OS: { + return this.i18nSupport.getText(ResourceService.CLIENT_OS_TYPE_PREFIX + data.getClientOS().name()); + } + default: { + return Constants.EMPTY_NOTE; + } + } + } + + public String getColorValueHTML(final ClientGroupData data) { + final String color = data.getColor(); + if (StringUtils.isBlank(color)) { + return Constants.EMPTY_NOTE; + } + + return new StringBuilder().append("   ") + .append(" (#") + .append(color) + .append(")   ") + .append("") + .toString(); + + } + } diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/ClientGroupDAOImpl.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/ClientGroupDAOImpl.java index 0927fad9..c7d7d6cf 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/ClientGroupDAOImpl.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/ClientGroupDAOImpl.java @@ -29,7 +29,7 @@ import ch.ethz.seb.sebserver.gbl.api.EntityType; import ch.ethz.seb.sebserver.gbl.model.EntityDependency; import ch.ethz.seb.sebserver.gbl.model.EntityKey; import ch.ethz.seb.sebserver.gbl.model.exam.ClientGroup; -import ch.ethz.seb.sebserver.gbl.model.exam.ClientGroup.ClientGroupType; +import ch.ethz.seb.sebserver.gbl.model.exam.ClientGroupData.ClientGroupType; import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile; import ch.ethz.seb.sebserver.gbl.util.Result; import ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.ClientGroupRecordDynamicSqlSupport; diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/ExamTemplateDAOImpl.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/ExamTemplateDAOImpl.java index 82d7aa12..17b310ae 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/ExamTemplateDAOImpl.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/ExamTemplateDAOImpl.java @@ -601,11 +601,11 @@ public class ExamTemplateDAOImpl implements ExamTemplateDAO { private void checkUniqueClientGroupName( final ClientGroupTemplate clientGroupTemplate, - final Collection clinetGroups) { + final Collection clietnGroups) { // check unique name - clinetGroups.stream() - .filter(it -> !Objects.equals(it, clientGroupTemplate) + clietnGroups.stream() + .filter(it -> !Objects.equals(it.id, clientGroupTemplate.id) && Objects.equals(it.name, clientGroupTemplate.name)) .findAny() .ifPresent(it -> { diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/exam/ExamAdminService.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/exam/ExamAdminService.java index e9864917..d88459cd 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/exam/ExamAdminService.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/exam/ExamAdminService.java @@ -13,14 +13,16 @@ import java.util.Set; import java.util.stream.Collectors; import org.apache.commons.lang3.BooleanUtils; +import org.apache.commons.lang3.StringUtils; import org.springframework.validation.FieldError; import ch.ethz.seb.sebserver.gbl.api.APIMessage; import ch.ethz.seb.sebserver.gbl.api.APIMessage.APIMessageException; import ch.ethz.seb.sebserver.gbl.model.Domain; import ch.ethz.seb.sebserver.gbl.model.exam.ClientGroup; -import ch.ethz.seb.sebserver.gbl.model.exam.ClientGroup.ClientGroupType; import ch.ethz.seb.sebserver.gbl.model.exam.ClientGroupData; +import ch.ethz.seb.sebserver.gbl.model.exam.ClientGroupData.ClientGroupType; +import ch.ethz.seb.sebserver.gbl.model.exam.ClientGroupData.ClientOS; import ch.ethz.seb.sebserver.gbl.model.exam.Exam; import ch.ethz.seb.sebserver.gbl.model.exam.Indicator.Threshold; import ch.ethz.seb.sebserver.gbl.model.exam.ProctoringServiceSettings; @@ -149,14 +151,14 @@ public interface ExamAdminService { * If a check fails, the methods throws a APIMessageException with a FieldError to notify the caller * * @param clientGroup ClientGroup instance to check */ - public static void checkClientGroupConsistency(final ClientGroupData clientGroup) { + public static T checkClientGroupConsistency(final T clientGroup) { final ClientGroupType type = clientGroup.getType(); if (type == null || type == ClientGroupType.NONE) { throw new APIMessageException(APIMessage.fieldValidationError( new FieldError( Domain.CLIENT_GROUP.TYPE_NAME, Domain.CLIENT_GROUP.ATTR_TYPE, - "clientGroup:type:mandatory"))); + "clientGroup:type:notNull"))); } switch (type) { @@ -176,11 +178,13 @@ public interface ExamAdminService { "clientGroup:type:typeInvalid"))); } } + + return clientGroup; } static void checkIPRange(final String ipRangeStart, final String ipRangeEnd) { final long startIP = Utils.ipToLong(ipRangeStart); - if (startIP < 0) { + if (StringUtils.isBlank(ipRangeStart) || startIP < 0) { throw new APIMessageException(APIMessage.fieldValidationError( new FieldError( Domain.CLIENT_GROUP.TYPE_NAME, @@ -188,7 +192,7 @@ public interface ExamAdminService { "clientGroup:ipRangeStart:invalidIP"))); } final long endIP = Utils.ipToLong(ipRangeEnd); - if (endIP < 0) { + if (StringUtils.isBlank(ipRangeEnd) || endIP < 0) { throw new APIMessageException(APIMessage.fieldValidationError( new FieldError( Domain.CLIENT_GROUP.TYPE_NAME, @@ -211,8 +215,14 @@ public interface ExamAdminService { } - static void checkClientOS(final String clientOS) { - // TODO + static void checkClientOS(final ClientOS clientOS) { + if (clientOS == null) { + throw new APIMessageException(APIMessage.fieldValidationError( + new FieldError( + Domain.CLIENT_GROUP.TYPE_NAME, + ClientGroupData.ATTR_CLIENT_OS, + "clientGroup:clientOS:notNull"))); + } } } diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/ClientGroupController.java b/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/ClientGroupController.java index 4c2636f3..d77e2765 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/ClientGroupController.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/ClientGroupController.java @@ -29,6 +29,7 @@ import ch.ethz.seb.sebserver.webservice.servicelayer.bulkaction.BulkActionServic import ch.ethz.seb.sebserver.webservice.servicelayer.dao.ClientGroupDAO; import ch.ethz.seb.sebserver.webservice.servicelayer.dao.ExamDAO; import ch.ethz.seb.sebserver.webservice.servicelayer.dao.UserActivityLogDAO; +import ch.ethz.seb.sebserver.webservice.servicelayer.exam.ExamAdminService; import ch.ethz.seb.sebserver.webservice.servicelayer.session.ExamSessionService; import ch.ethz.seb.sebserver.webservice.servicelayer.validation.BeanValidationService; @@ -88,15 +89,14 @@ public class ClientGroupController extends EntityController validForCreate(final ClientGroup entity) { - // TODO check consistency!? - return super.validForCreate(entity); - + return super.validForCreate(entity) + .map(ExamAdminService::checkClientGroupConsistency); } @Override protected Result validForSave(final ClientGroup entity) { - // TODO check consistency!? - return super.validForSave(entity); + return super.validForSave(entity) + .map(ExamAdminService::checkClientGroupConsistency); } @Override diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/ExamAdministrationController.java b/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/ExamAdministrationController.java index fb292d8e..a9adc311 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/ExamAdministrationController.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/ExamAdministrationController.java @@ -118,57 +118,6 @@ public class ExamAdministrationController extends EntityController { return ExamRecordDynamicSqlSupport.examRecord; } -// @RequestMapping( -// method = RequestMethod.GET, -// consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE, -// produces = MediaType.APPLICATION_JSON_VALUE) -// @Override -// public Page getPage( -// @RequestParam( -// name = API.PARAM_INSTITUTION_ID, -// required = true, -// defaultValue = UserService.USERS_INSTITUTION_AS_DEFAULT) final Long institutionId, -// @RequestParam(name = Page.ATTR_PAGE_NUMBER, required = false) final Integer pageNumber, -// @RequestParam(name = Page.ATTR_PAGE_SIZE, required = false) final Integer pageSize, -// @RequestParam(name = Page.ATTR_SORT, required = false) final String sort, -// @RequestParam final MultiValueMap allRequestParams, -// final HttpServletRequest request) { -// -// checkReadPrivilege(institutionId); -// this.authorization.check( -// PrivilegeType.READ, -// EntityType.EXAM, -// institutionId); -// -// if (StringUtils.isBlank(sort) || -// (this.paginationService.isNativeSortingSupported(ExamRecordDynamicSqlSupport.examRecord, sort))) { -// -// System.out.println("*********************** sort, filter on DB"); -// -// return super.getPage(institutionId, pageNumber, pageSize, sort, allRequestParams, request); -// -// } else { -// -// System.out.println("*********************** sort, filter on List"); -// -// return super.getPage(institutionId, pageNumber, pageSize, sort, allRequestParams, request); -// -//// final Collection exams = this.examDAO -//// .allMatching(new FilterMap( -//// allRequestParams, -//// request.getQueryString()), -//// this::hasReadAccess) -//// .getOrThrow(); -//// -//// return this.paginationService.buildPageFromList( -//// pageNumber, -//// pageSize, -//// sort, -//// exams, -//// pageSort(sort)); -// } -// } - @RequestMapping( path = API.MODEL_ID_VAR_PATH_SEGMENT + API.EXAM_ADMINISTRATION_CHECK_IMPORTED_PATH_SEGMENT, diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/ExamTemplateController.java b/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/ExamTemplateController.java index d9cdd72d..52f8320f 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/ExamTemplateController.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/ExamTemplateController.java @@ -337,7 +337,7 @@ public class ExamTemplateController extends EntityControllerPlease select one. sebserver.exam.clientgroup.type.description.IP_V4_RANGE=IP v4 Range TODO sebserver.exam.clientgroup.type.description.CLIENT_OS=SEB Client OS TODO @@ -665,6 +685,11 @@ sebserver.exam.indicator.action.list.modify=Edit Selected Indicator sebserver.exam.indicator.action.list.delete=Delete Selected Indicator sebserver.exam.indicator.action.save=Save +sebserver.exam.clientgroup.action.list.new=Add Client Group +sebserver.exam.clientgroup.action.list.modify=Edit Selected Client Group +sebserver.exam.clientgroup.action.list.delete=Delete Selected Client Group +sebserver.exam.clientgroup.action.save=Save + sebserver.exam.indicator.form.title=Indicator sebserver.exam.indicator.form.title.subtitle= sebserver.exam.indicator.form.title.new=Add Indicator @@ -703,6 +728,8 @@ sebserver.exam.clientgroup.form.ipstart=Start of IP range sebserver.exam.clientgroup.form.ipstart.tooltip=An IP v4 formatted PI address like 111.111.111.111 that defines the start of the range sebserver.exam.clientgroup.form.ipend=End of IP range sebserver.exam.clientgroup.form.ipend.tooltip=An IP v4 formatted PI address like 111.111.111.111 that defines the end of the range +sebserver.exam.clientgroup.form.ostype=Client OS Type +sebserver.exam.clientgroup.form.ostype.tooltip=The operating system type of the machine where SEB is running on sebserver.exam.indicator.thresholds.list.title=Thresholds @@ -1797,6 +1824,8 @@ sebserver.examtemplate.clientgroup.list.column.name=Name sebserver.examtemplate.clientgroup.list.column.name.tooltip=The name of the client group sebserver.examtemplate.clientgroup.list.column.color=Color sebserver.examtemplate.clientgroup.list.column.color.tooltip=The color of the client group +sebserver.examtemplate.clientgroup.list.column.data=Data +sebserver.examtemplate.clientgroup.list.column.data.tooltip=The data of the client group that is specific to the type of the client group sebserver.examtemplate.clientgroup.list.empty=There is currently no client group defined for this exam template. Please create a new one sebserver.examtemplate.clientgroup.list.pleaseSelect=At first please select an client group from the list diff --git a/src/main/resources/static/images/add_clientgroup.png b/src/main/resources/static/images/add_clientgroup.png new file mode 100644 index 0000000000000000000000000000000000000000..a54f2e4cc63a6c00e87eafeaeab263228770012e GIT binary patch literal 232 zcmeAS@N?(olHy`uVBq!ia0vp^5+KaM0wlfaz7_+i^`0({Ar_~T6C~ms+7cxX8!N=H zr5GA$7@T3cTrp*T>JR?b{DWTV#(PpG9ABF#8Dq(z$J{l;IaB6=W>?#C2f;jrzcPCK zLQ8#^?bH=d3p~wSBgMh0-8Pwn(S-LDk0sA+i9CZECN6uHO+^h#0tdQQUhjFR(50RD zK_Q^6v9UkVs@ZVUCx^E9h^fsF^cUzbJ2M>4XRB{%`)vUfsNEp@U-I38DmM8e9}M1_ d^3*agytUoEcISq_|A9_t@O1TaS?83{1OUToQcC~; literal 0 HcmV?d00001 diff --git a/src/main/resources/static/images/clientgroup.png b/src/main/resources/static/images/clientgroup.png new file mode 100644 index 0000000000000000000000000000000000000000..10dee828f8acea56acfbbc8e35a987522634a7d7 GIT binary patch literal 224 zcmeAS@N?(olHy`uVBq!ia0vp^5+KaM0wlfaz7_+i<(@8%Ar`0aUNGci3>0a7xL)+4 zN6F=lZJoIZ(pA4VbVN49B?>PRZt+_zb2K!;XmZa96{UxBp6UOayyy3i?J7p<>N8nv z+neHFqzopr0PZ7QUH||9 literal 0 HcmV?d00001 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 4b9e5925..5c9d11db 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 @@ -66,7 +66,7 @@ import ch.ethz.seb.sebserver.gbl.model.EntityProcessingReport.ErrorEntry; import ch.ethz.seb.sebserver.gbl.model.Page; import ch.ethz.seb.sebserver.gbl.model.exam.Chapters; import ch.ethz.seb.sebserver.gbl.model.exam.ClientGroup; -import ch.ethz.seb.sebserver.gbl.model.exam.ClientGroup.ClientGroupType; +import ch.ethz.seb.sebserver.gbl.model.exam.ClientGroupData.ClientGroupType; import ch.ethz.seb.sebserver.gbl.model.exam.Exam; import ch.ethz.seb.sebserver.gbl.model.exam.Exam.ExamStatus; import ch.ethz.seb.sebserver.gbl.model.exam.Exam.ExamType;