SEBSERV-324 finished maintenance

This commit is contained in:
anhefti 2022-08-30 11:10:45 +02:00
parent 5aeb14ea8e
commit d648bcb167
32 changed files with 781 additions and 200 deletions

View file

@ -270,5 +270,4 @@ public class POSTMapper {
this.params.putIfAbsent(name, Arrays.asList(value)); this.params.putIfAbsent(name, Arrays.asList(value));
return (T) this; return (T) this;
} }
} }

View file

@ -8,6 +8,9 @@
package ch.ethz.seb.sebserver.gbl.model.exam; 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.NotNull;
import javax.validation.constraints.Size; 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.EntityType;
import ch.ethz.seb.sebserver.gbl.api.POSTMapper; 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.Domain.CLIENT_GROUP;
import ch.ethz.seb.sebserver.gbl.util.Utils;
@JsonIgnoreProperties(ignoreUnknown = true) @JsonIgnoreProperties(ignoreUnknown = true)
public class ClientGroup implements ClientGroupData { public class ClientGroup implements ClientGroupData {
public static final String FILTER_ATTR_EXAM_ID = "examId"; 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) @JsonProperty(CLIENT_GROUP.ATTR_ID)
public final Long id; public final Long id;
@ -66,7 +61,7 @@ public class ClientGroup implements ClientGroupData {
public final String ipRangeEnd; public final String ipRangeEnd;
@JsonProperty(ATTR_CLIENT_OS) @JsonProperty(ATTR_CLIENT_OS)
public final String clientOS; public final ClientOS clientOS;
@JsonCreator @JsonCreator
public ClientGroup( public ClientGroup(
@ -78,18 +73,18 @@ public class ClientGroup implements ClientGroupData {
@JsonProperty(CLIENT_GROUP.ATTR_ICON) final String icon, @JsonProperty(CLIENT_GROUP.ATTR_ICON) final String icon,
@JsonProperty(ATTR_IP_RANGE_START) final String ipRangeStart, @JsonProperty(ATTR_IP_RANGE_START) final String ipRangeStart,
@JsonProperty(ATTR_IP_RANGE_END) final String ipRangeEnd, @JsonProperty(ATTR_IP_RANGE_END) final String ipRangeEnd,
@JsonProperty(ATTR_CLIENT_OS) final String clientOS) { @JsonProperty(ATTR_CLIENT_OS) final ClientOS clientOS) {
super(); super();
this.id = id; this.id = id;
this.examId = examId; this.examId = examId;
this.name = name; this.name = name;
this.type = type; this.type = type == null ? ClientGroupType.NONE : type;
this.color = color; this.color = color;
this.icon = icon; this.icon = icon;
this.ipRangeStart = ipRangeStart; this.ipRangeStart = ipRangeStart;
this.ipRangeEnd = ipRangeEnd; this.ipRangeEnd = ipRangeEnd;
this.clientOS = clientOS; this.clientOS = clientOS == null ? ClientOS.NONE : clientOS;
} }
public ClientGroup( public ClientGroup(
@ -105,7 +100,8 @@ public class ClientGroup implements ClientGroupData {
this.id = id; this.id = id;
this.examId = examId; this.examId = examId;
this.name = name; this.name = name;
this.type = type; this.type = type == null ? ClientGroupType.NONE : type;
;
this.color = color; this.color = color;
this.icon = icon; this.icon = icon;
@ -114,19 +110,19 @@ public class ClientGroup implements ClientGroupData {
final String[] split = StringUtils.split(data, Constants.EMBEDDED_LIST_SEPARATOR); final String[] split = StringUtils.split(data, Constants.EMBEDDED_LIST_SEPARATOR);
this.ipRangeStart = split[0]; this.ipRangeStart = split[0];
this.ipRangeEnd = split[1]; this.ipRangeEnd = split[1];
this.clientOS = null; this.clientOS = ClientOS.NONE;
break; break;
} }
case CLIENT_OS: { case CLIENT_OS: {
this.ipRangeStart = null; this.ipRangeStart = null;
this.ipRangeEnd = null; this.ipRangeEnd = null;
this.clientOS = data; this.clientOS = Utils.enumFromString(data, ClientOS.class, ClientOS.NONE);
break; break;
} }
default: { default: {
this.ipRangeStart = null; this.ipRangeStart = null;
this.ipRangeEnd = 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.icon = postParams.getString(CLIENT_GROUP.ATTR_ICON);
this.ipRangeStart = postParams.getString(ATTR_IP_RANGE_START); this.ipRangeStart = postParams.getString(ATTR_IP_RANGE_START);
this.ipRangeEnd = postParams.getString(ATTR_IP_RANGE_END); 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 @Override
@ -184,16 +188,32 @@ public class ClientGroup implements ClientGroupData {
@Override @Override
public String getIpRangeStart() { 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 @Override
public String getIpRangeEnd() { 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 @Override
public String getClientOS() { public ClientOS getClientOS() {
return this.clientOS; return this.clientOS;
} }
@ -204,7 +224,7 @@ public class ClientGroup implements ClientGroupData {
return this.ipRangeStart + Constants.EMBEDDED_LIST_SEPARATOR + this.ipRangeEnd; return this.ipRangeStart + Constants.EMBEDDED_LIST_SEPARATOR + this.ipRangeEnd;
} }
case CLIENT_OS: { case CLIENT_OS: {
return this.clientOS; return this.clientOS.name();
} }
default: { default: {
return StringUtils.EMPTY; return StringUtils.EMPTY;

View file

@ -9,10 +9,33 @@
package ch.ethz.seb.sebserver.gbl.model.exam; package ch.ethz.seb.sebserver.gbl.model.exam;
import ch.ethz.seb.sebserver.gbl.model.Entity; import ch.ethz.seb.sebserver.gbl.model.Entity;
import ch.ethz.seb.sebserver.gbl.model.exam.ClientGroup.ClientGroupType;
public interface ClientGroupData extends Entity { 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(); Long getId();
ClientGroupType getType(); ClientGroupType getType();
@ -25,5 +48,6 @@ public interface ClientGroupData extends Entity {
String getIpRangeEnd(); String getIpRangeEnd();
String getClientOS(); ClientOS getClientOS();
} }

View file

@ -8,6 +8,9 @@
package ch.ethz.seb.sebserver.gbl.model.exam; 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.NotNull;
import javax.validation.constraints.Size; 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.EntityType;
import ch.ethz.seb.sebserver.gbl.api.POSTMapper; 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.Domain.CLIENT_GROUP;
import ch.ethz.seb.sebserver.gbl.model.exam.ClientGroup.ClientGroupType;
@JsonIgnoreProperties(ignoreUnknown = true) @JsonIgnoreProperties(ignoreUnknown = true)
public class ClientGroupTemplate implements ClientGroupData { public class ClientGroupTemplate implements ClientGroupData {
@ -57,7 +59,7 @@ public class ClientGroupTemplate implements ClientGroupData {
public final String ipRangeEnd; public final String ipRangeEnd;
@JsonProperty(ClientGroup.ATTR_CLIENT_OS) @JsonProperty(ClientGroup.ATTR_CLIENT_OS)
public final String clientOS; public final ClientOS clientOS;
@JsonCreator @JsonCreator
public ClientGroupTemplate( public ClientGroupTemplate(
@ -69,7 +71,7 @@ public class ClientGroupTemplate implements ClientGroupData {
@JsonProperty(CLIENT_GROUP.ATTR_ICON) final String icon, @JsonProperty(CLIENT_GROUP.ATTR_ICON) final String icon,
@JsonProperty(ClientGroup.ATTR_IP_RANGE_START) final String ipRangeStart, @JsonProperty(ClientGroup.ATTR_IP_RANGE_START) final String ipRangeStart,
@JsonProperty(ClientGroup.ATTR_IP_RANGE_END) final String ipRangeEnd, @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(); super();
this.id = id; this.id = id;
@ -93,7 +95,7 @@ public class ClientGroupTemplate implements ClientGroupData {
this.icon = postParams.getString(CLIENT_GROUP.ATTR_ICON); this.icon = postParams.getString(CLIENT_GROUP.ATTR_ICON);
this.ipRangeStart = postParams.getString(ClientGroup.ATTR_IP_RANGE_START); this.ipRangeStart = postParams.getString(ClientGroup.ATTR_IP_RANGE_START);
this.ipRangeEnd = postParams.getString(ClientGroup.ATTR_IP_RANGE_END); 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) { public ClientGroupTemplate(final Long id, final ClientGroupTemplate other) {
@ -150,16 +152,32 @@ public class ClientGroupTemplate implements ClientGroupData {
@Override @Override
public String getIpRangeStart() { 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 @Override
public String getIpRangeEnd() { 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 @Override
public String getClientOS() { public ClientOS getClientOS() {
return this.clientOS; return this.clientOS;
} }
@ -170,7 +188,7 @@ public class ClientGroupTemplate implements ClientGroupData {
return this.ipRangeStart + Constants.EMBEDDED_LIST_SEPARATOR + this.ipRangeEnd; return this.ipRangeStart + Constants.EMBEDDED_LIST_SEPARATOR + this.ipRangeEnd;
} }
case CLIENT_OS: { case CLIENT_OS: {
return this.clientOS; return this.clientOS.name();
} }
default: { default: {
return StringUtils.EMPTY; return StringUtils.EMPTY;

View file

@ -9,7 +9,7 @@
package ch.ethz.seb.sebserver.gbl.monitoring; 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;
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.model.session.ClientConnection;
/** Defines a client connection to client group matcher for a specific client group type. /** Defines a client connection to client group matcher for a specific client group type.

View file

@ -17,7 +17,7 @@ import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import ch.ethz.seb.sebserver.gbl.model.exam.ClientGroup; 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.model.session.ClientConnection;
@Lazy @Lazy

View file

@ -14,7 +14,7 @@ import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import ch.ethz.seb.sebserver.gbl.model.exam.ClientGroup; 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.model.session.ClientConnection;
import ch.ethz.seb.sebserver.gbl.util.Utils; import ch.ethz.seb.sebserver.gbl.util.Utils;

View file

@ -193,6 +193,18 @@ public final class Utils {
.collect(Collectors.toList())); .collect(Collectors.toList()));
} }
public static <T extends Enum<T>> T enumFromString(
final String string,
final Class<T> enumClass,
final T defaultValue) {
try {
return Enum.valueOf(enumClass, string);
} catch (final Exception e) {
return defaultValue;
}
}
public static Collection<String> getListOfLines(final String list) { public static Collection<String> getListOfLines(final String list) {
if (list == null) { if (list == null) {
return Collections.emptyList(); return Collections.emptyList();

View file

@ -23,6 +23,7 @@ public enum ActionCategory {
CLIENT_GROUP_TEMPLATE_LIST(new LocTextKey("sebserver.examtemplate.clientgroup.list.actions"), 2), CLIENT_GROUP_TEMPLATE_LIST(new LocTextKey("sebserver.examtemplate.clientgroup.list.actions"), 2),
EXAM_CONFIG_MAPPING_LIST(new LocTextKey("sebserver.exam.configuration.list.actions"), 1), EXAM_CONFIG_MAPPING_LIST(new LocTextKey("sebserver.exam.configuration.list.actions"), 1),
INDICATOR_LIST(new LocTextKey("sebserver.exam.indicator.list.actions"), 2), 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_CLIENT_CONFIG_LIST(new LocTextKey("sebserver.clientconfig.list.actions"), 1),
SEB_EXAM_CONFIG_LIST(new LocTextKey("sebserver.examconfig.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), SEB_CONFIG_TEMPLATE_LIST(new LocTextKey("sebserver.configtemplate.list.actions"), 1),

View file

@ -388,6 +388,33 @@ public enum ActionDefinition {
ImageIcon.CANCEL, ImageIcon.CANCEL,
PageStateDefinitionImpl.EXAM_VIEW, PageStateDefinitionImpl.EXAM_VIEW,
ActionCategory.FORM), 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( EXAM_SEB_CLIENT_CONFIG_EXPORT(
new LocTextKey("sebserver.exam.action.createClientToStartExam"), new LocTextKey("sebserver.exam.action.createClientToStartExam"),
ImageIcon.EXPORT, ImageIcon.EXPORT,
@ -471,7 +498,7 @@ public enum ActionDefinition {
CLIENT_GROUP_TEMPLATE_NEW( CLIENT_GROUP_TEMPLATE_NEW(
new LocTextKey("sebserver.examtemplate.clientgroup.action.list.new"), new LocTextKey("sebserver.examtemplate.clientgroup.action.list.new"),
ImageIcon.CLIENT_GROUP, ImageIcon.ADD_CLIENT_GROUP,
PageStateDefinitionImpl.CLIENT_GROUP_TEMPLATE_EDIT, PageStateDefinitionImpl.CLIENT_GROUP_TEMPLATE_EDIT,
ActionCategory.CLIENT_GROUP_TEMPLATE_LIST), ActionCategory.CLIENT_GROUP_TEMPLATE_LIST),
CLIENT_GROUP_TEMPLATE_MODIFY_FROM_LIST( CLIENT_GROUP_TEMPLATE_MODIFY_FROM_LIST(

View file

@ -254,14 +254,14 @@ public class ActionPane implements TemplateComposer {
final Template template = new Template(); final Template template = new Template();
final ImageCell imageCell = new ImageCell(template); final ImageCell imageCell = new ImageCell(template);
imageCell.setLeft(0, -8) imageCell.setLeft(0, -8)
.setWidth(20) .setWidth(24)
.setTop(0) .setTop(0)
.setBottom(0, 0) .setBottom(0, 0)
.setHorizontalAlignment(SWT.LEFT) .setHorizontalAlignment(SWT.LEFT)
.setBackground(null); .setBackground(null);
imageCell.setBindingIndex(0); imageCell.setBindingIndex(0);
final TextCell textCell = new TextCell(template); final TextCell textCell = new TextCell(template);
textCell.setLeft(0, 20) textCell.setLeft(0, 24)
.setWidth(SWT.DEFAULT) .setWidth(SWT.DEFAULT)
.setTop(7) .setTop(7)
.setBottom(0, 0) .setBottom(0, 0)

View file

@ -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.SEBExamConfigForm;
import ch.ethz.seb.sebserver.gui.content.configs.SEBExamConfigList; 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.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.ClientGroupTemplateForm;
import ch.ethz.seb.sebserver.gui.content.exam.ExamForm; import ch.ethz.seb.sebserver.gui.content.exam.ExamForm;
import ch.ethz.seb.sebserver.gui.content.exam.ExamList; 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_VIEW(Type.FORM_VIEW, ExamForm.class, ActivityDefinition.EXAM),
EXAM_EDIT(Type.FORM_EDIT, ExamForm.class, ActivityDefinition.EXAM), EXAM_EDIT(Type.FORM_EDIT, ExamForm.class, ActivityDefinition.EXAM),
INDICATOR_EDIT(Type.FORM_EDIT, IndicatorForm.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_LIST(Type.LIST_VIEW, ExamTemplateList.class, ActivityDefinition.EXAM_TEMPLATE),
EXAM_TEMPLATE_VIEW(Type.LIST_VIEW, ExamTemplateForm.class, ActivityDefinition.EXAM_TEMPLATE), EXAM_TEMPLATE_VIEW(Type.LIST_VIEW, ExamTemplateForm.class, ActivityDefinition.EXAM_TEMPLATE),

View file

@ -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<ClientGroup> 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);
}
}
}

View file

@ -8,7 +8,6 @@
package ch.ethz.seb.sebserver.gui.content.exam; package ch.ethz.seb.sebserver.gui.content.exam;
import org.apache.commons.lang3.StringUtils;
import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Composite;
import org.springframework.context.annotation.Lazy; import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component; 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.Domain;
import ch.ethz.seb.sebserver.gbl.model.EntityKey; 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;
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.ClientGroupTemplate;
import ch.ethz.seb.sebserver.gbl.model.exam.ExamTemplate; import ch.ethz.seb.sebserver.gbl.model.exam.ExamTemplate;
import ch.ethz.seb.sebserver.gbl.model.exam.IndicatorTemplate; import ch.ethz.seb.sebserver.gbl.model.exam.IndicatorTemplate;
import ch.ethz.seb.sebserver.gbl.profile.GuiProfile; import ch.ethz.seb.sebserver.gbl.profile.GuiProfile;
import ch.ethz.seb.sebserver.gbl.util.Utils; import ch.ethz.seb.sebserver.gbl.util.Utils;
import ch.ethz.seb.sebserver.gui.content.action.ActionDefinition; 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.FormBuilder;
import ch.ethz.seb.sebserver.gui.form.FormHandle; import ch.ethz.seb.sebserver.gui.form.FormHandle;
import ch.ethz.seb.sebserver.gui.service.ResourceService; import ch.ethz.seb.sebserver.gui.service.ResourceService;
@ -65,6 +63,8 @@ public class ClientGroupTemplateForm implements TemplateComposer {
new LocTextKey("sebserver.exam.clientgroup.form.ipstart"); new LocTextKey("sebserver.exam.clientgroup.form.ipstart");
private static final LocTextKey FORM_IP_END_KEY = private static final LocTextKey FORM_IP_END_KEY =
new LocTextKey("sebserver.exam.clientgroup.form.ipend"); 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 = private static final String CLIENT_GROUP_TYPE_DESC_PREFIX =
"sebserver.exam.clientgroup.type.description."; "sebserver.exam.clientgroup.type.description.";
@ -166,7 +166,7 @@ public class ClientGroupTemplateForm implements TemplateComposer {
FORM_TYPE_TEXT_KEY, FORM_TYPE_TEXT_KEY,
(clientGroupTemplate.type != null) ? clientGroupTemplate.type.name() : null, (clientGroupTemplate.type != null) ? clientGroupTemplate.type.name() : null,
this.resourceService::clientGroupTypeResources) this.resourceService::clientGroupTypeResources)
.withSelectionListener(this::updateForm) .withSelectionListener(form -> ClientGroupForm.updateForm(form, this.i18nSupport))
.mandatory(!isReadonly)) .mandatory(!isReadonly))
.addField(FormBuilder.text( .addField(FormBuilder.text(
@ -174,13 +174,13 @@ public class ClientGroupTemplateForm implements TemplateComposer {
FORM_DESC_TEXT_KEY, FORM_DESC_TEXT_KEY,
typeDescription) typeDescription)
.asArea() .asArea()
//.asHTML(true)
.readonly(true)) .readonly(true))
.addField(FormBuilder.text( .addField(FormBuilder.text(
ClientGroup.ATTR_IP_RANGE_START, ClientGroup.ATTR_IP_RANGE_START,
FORM_IP_START_KEY, FORM_IP_START_KEY,
clientGroupTemplate::getIpRangeStart) clientGroupTemplate::getIpRangeStart)
.mandatory(!isReadonly)
.visibleIf(clientGroupTemplate.type != null .visibleIf(clientGroupTemplate.type != null
&& clientGroupTemplate.type == ClientGroupType.IP_V4_RANGE)) && clientGroupTemplate.type == ClientGroupType.IP_V4_RANGE))
@ -188,9 +188,19 @@ public class ClientGroupTemplateForm implements TemplateComposer {
ClientGroup.ATTR_IP_RANGE_END, ClientGroup.ATTR_IP_RANGE_END,
FORM_IP_END_KEY, FORM_IP_END_KEY,
clientGroupTemplate::getIpRangeEnd) clientGroupTemplate::getIpRangeEnd)
.mandatory(!isReadonly)
.visibleIf(clientGroupTemplate.type != null .visibleIf(clientGroupTemplate.type != null
&& clientGroupTemplate.type == ClientGroupType.IP_V4_RANGE)) && 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) .buildFor((isNew)
? restService.getRestCall(NewClientGroupTemplate.class) ? restService.getRestCall(NewClientGroupTemplate.class)
: restService.getRestCall(SaveClientGroupTemplate.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);
}
}
} }

View file

@ -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<ClientGroup> 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<ClientGroup>(
Domain.CLIENT_GROUP.ATTR_TYPE,
CLIENT_GROUP_TYPE_COLUMN_KEY,
cgt -> this.resourceService.clientGroupTypeName(cgt))
.widthProportion(1))
.withColumn(new ColumnDefinition<ClientGroup>(
Domain.CLIENT_GROUP.ATTR_COLOR,
CLIENT_GROUP_COLOR_COLUMN_KEY,
cgt -> this.widgetFactory.getColorValueHTML(cgt))
.asMarkup()
.widthProportion(1))
.withColumn(new ColumnDefinition<ClientGroup>(
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;
}
}

View file

@ -151,7 +151,8 @@ public class ExamForm implements TemplateComposer {
private final RestService restService; private final RestService restService;
private final ExamDeletePopup examDeletePopup; private final ExamDeletePopup examDeletePopup;
private final ExamFormConfigs examFormConfigs; private final ExamFormConfigs examFormConfigs;
private final ExamFormIndicators examFormIndicators; private final ExamIndicatorsList examIndicatorsList;
private final ExamClientGroupList examClientGroupList;
private final ExamCreateClientConfigPopup examCreateClientConfigPopup; private final ExamCreateClientConfigPopup examCreateClientConfigPopup;
protected ExamForm( protected ExamForm(
@ -162,7 +163,8 @@ public class ExamForm implements TemplateComposer {
final DownloadService downloadService, final DownloadService downloadService,
final ExamDeletePopup examDeletePopup, final ExamDeletePopup examDeletePopup,
final ExamFormConfigs examFormConfigs, final ExamFormConfigs examFormConfigs,
final ExamFormIndicators examFormIndicators, final ExamIndicatorsList examIndicatorsList,
final ExamClientGroupList examClientGroupList,
final ExamCreateClientConfigPopup examCreateClientConfigPopup) { final ExamCreateClientConfigPopup examCreateClientConfigPopup) {
this.pageService = pageService; this.pageService = pageService;
@ -173,7 +175,8 @@ public class ExamForm implements TemplateComposer {
this.restService = this.resourceService.getRestService(); this.restService = this.resourceService.getRestService();
this.examDeletePopup = examDeletePopup; this.examDeletePopup = examDeletePopup;
this.examFormConfigs = examFormConfigs; this.examFormConfigs = examFormConfigs;
this.examFormIndicators = examFormIndicators; this.examIndicatorsList = examIndicatorsList;
this.examClientGroupList = examClientGroupList;
this.examCreateClientConfigPopup = examCreateClientConfigPopup; this.examCreateClientConfigPopup = examCreateClientConfigPopup;
this.consistencyMessageMapping = new HashMap<>(); this.consistencyMessageMapping = new HashMap<>();
@ -485,7 +488,15 @@ public class ExamForm implements TemplateComposer {
.withAttribute(ATTR_EXAM_STATUS, examStatus.name())); .withAttribute(ATTR_EXAM_STATUS, examStatus.name()));
// Indicators // 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 formContext
.copyOf(content) .copyOf(content)
.withAttribute(ATTR_READ_GRANT, String.valueOf(entityGrantCheck.r())) .withAttribute(ATTR_READ_GRANT, String.valueOf(entityGrantCheck.r()))

View file

@ -8,8 +8,6 @@
package ch.ethz.seb.sebserver.gui.content.exam; package ch.ethz.seb.sebserver.gui.content.exam;
import java.util.List;
import org.apache.commons.lang3.BooleanUtils; import org.apache.commons.lang3.BooleanUtils;
import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Composite;
import org.springframework.context.annotation.Lazy; 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.Domain;
import ch.ethz.seb.sebserver.gbl.model.EntityKey; 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;
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.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.content.action.ActionDefinition;
import ch.ethz.seb.sebserver.gui.service.ResourceService; import ch.ethz.seb.sebserver.gui.service.ResourceService;
import ch.ethz.seb.sebserver.gui.service.i18n.LocTextKey; 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.service.remote.webservice.api.exam.indicator.GetIndicatorPage;
import ch.ethz.seb.sebserver.gui.table.ColumnDefinition; import ch.ethz.seb.sebserver.gui.table.ColumnDefinition;
import ch.ethz.seb.sebserver.gui.table.EntityTable; import ch.ethz.seb.sebserver.gui.table.EntityTable;
import ch.ethz.seb.sebserver.gui.widget.ThresholdList;
import ch.ethz.seb.sebserver.gui.widget.WidgetFactory; import ch.ethz.seb.sebserver.gui.widget.WidgetFactory;
@Lazy @Lazy
@Component @Component
@GuiProfile @GuiProfile
public class ExamFormIndicators implements TemplateComposer { public class ExamIndicatorsList implements TemplateComposer {
private final static LocTextKey INDICATOR_LIST_TITLE_KEY = private final static LocTextKey INDICATOR_LIST_TITLE_KEY =
new LocTextKey("sebserver.exam.indicator.list.title"); new LocTextKey("sebserver.exam.indicator.list.title");
@ -65,7 +61,7 @@ public class ExamFormIndicators implements TemplateComposer {
private final WidgetFactory widgetFactory; private final WidgetFactory widgetFactory;
private final RestService restService; private final RestService restService;
public ExamFormIndicators(final PageService pageService) { public ExamIndicatorsList(final PageService pageService) {
this.pageService = pageService; this.pageService = pageService;
this.resourceService = pageService.getResourceService(); this.resourceService = pageService.getResourceService();
this.widgetFactory = pageService.getWidgetFactory(); this.widgetFactory = pageService.getWidgetFactory();
@ -111,7 +107,7 @@ public class ExamFormIndicators implements TemplateComposer {
.withColumn(new ColumnDefinition<Indicator>( .withColumn(new ColumnDefinition<Indicator>(
Domain.THRESHOLD.REFERENCE_NAME, Domain.THRESHOLD.REFERENCE_NAME,
INDICATOR_THRESHOLD_COLUMN_KEY, INDICATOR_THRESHOLD_COLUMN_KEY,
i -> thresholdsValue(i.thresholds, i.type)) i -> ThresholdList.thresholdsToHTML(i.thresholds, i.type))
.asMarkup() .asMarkup()
.widthProportion(4)) .widthProportion(4))
.withDefaultActionIf( .withDefaultActionIf(
@ -170,34 +166,4 @@ public class ExamFormIndicators implements TemplateComposer {
.getText(ResourceService.EXAM_INDICATOR_TYPE_PREFIX + indicator.type.name()); .getText(ResourceService.EXAM_INDICATOR_TYPE_PREFIX + indicator.type.name());
} }
static String thresholdsValue(
final List<Threshold> thresholds,
final IndicatorType indicatorType) {
if (thresholds.isEmpty()) {
return Constants.EMPTY_NOTE;
}
final StringBuilder builder = thresholds
.stream()
.reduce(
new StringBuilder(),
(sb, threshold) -> sb
.append("<span style='padding: 2px 5px 2px 5px; background-color: #")
.append(threshold.color)
.append("; ")
.append((Utils.darkColorContrast(Utils.parseRGB(threshold.color)))
? "color: #4a4a4a; "
: "color: #FFFFFF;")
.append("'>")
.append(Indicator.getDisplayValue(indicatorType, threshold.value))
.append(" (")
.append(threshold.color)
.append(")")
.append("</span>")
.append(" | "),
StringBuilder::append);
builder.delete(builder.length() - 3, builder.length() - 1);
return builder.toString();
}
} }

View file

@ -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.service.remote.webservice.auth.CurrentUser.EntityGrantCheck;
import ch.ethz.seb.sebserver.gui.table.ColumnDefinition; import ch.ethz.seb.sebserver.gui.table.ColumnDefinition;
import ch.ethz.seb.sebserver.gui.table.EntityTable; import ch.ethz.seb.sebserver.gui.table.EntityTable;
import ch.ethz.seb.sebserver.gui.widget.ThresholdList;
import ch.ethz.seb.sebserver.gui.widget.WidgetFactory; import ch.ethz.seb.sebserver.gui.widget.WidgetFactory;
@Lazy @Lazy
@ -101,6 +102,8 @@ public class ExamTemplateForm implements TemplateComposer {
new LocTextKey("sebserver.examtemplate.clientgroup.list.column.name"); new LocTextKey("sebserver.examtemplate.clientgroup.list.column.name");
private final static LocTextKey CLIENT_GROUP_COLOR_COLUMN_KEY = private final static LocTextKey CLIENT_GROUP_COLOR_COLUMN_KEY =
new LocTextKey("sebserver.examtemplate.clientgroup.list.column.color"); 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 = private final static LocTextKey CLIENT_GROUP_EMPTY_SELECTION_TEXT_KEY =
new LocTextKey("sebserver.examtemplate.clientgroup.list.pleaseSelect"); new LocTextKey("sebserver.examtemplate.clientgroup.list.pleaseSelect");
private static final LocTextKey CLIENT_GROUP_EMPTY_LIST_MESSAGE = private static final LocTextKey CLIENT_GROUP_EMPTY_LIST_MESSAGE =
@ -287,7 +290,7 @@ public class ExamTemplateForm implements TemplateComposer {
.withColumn(new ColumnDefinition<IndicatorTemplate>( .withColumn(new ColumnDefinition<IndicatorTemplate>(
Domain.THRESHOLD.REFERENCE_NAME, Domain.THRESHOLD.REFERENCE_NAME,
INDICATOR_THRESHOLD_COLUMN_KEY, INDICATOR_THRESHOLD_COLUMN_KEY,
it -> ExamFormIndicators.thresholdsValue(it.thresholds, it.type)) it -> ThresholdList.thresholdsToHTML(it.thresholds, it.type))
.asMarkup() .asMarkup()
.widthProportion(4)) .widthProportion(4))
.withDefaultActionIf( .withDefaultActionIf(
@ -347,15 +350,21 @@ public class ExamTemplateForm implements TemplateComposer {
CLIENT_GROUP_NAME_COLUMN_KEY, CLIENT_GROUP_NAME_COLUMN_KEY,
ClientGroupTemplate::getName) ClientGroupTemplate::getName)
.widthProportion(2)) .widthProportion(2))
.withColumn(new ColumnDefinition<>( .withColumn(new ColumnDefinition<ClientGroupTemplate>(
Domain.CLIENT_GROUP.ATTR_TYPE, Domain.CLIENT_GROUP.ATTR_TYPE,
CLIENT_GROUP_TYPE_COLUMN_KEY, CLIENT_GROUP_TYPE_COLUMN_KEY,
this::clientGroupTypeName) cgt -> this.resourceService.clientGroupTypeName(cgt))
.widthProportion(1)) .widthProportion(1))
.withColumn(new ColumnDefinition<>( .withColumn(new ColumnDefinition<ClientGroupTemplate>(
Domain.CLIENT_GROUP.ATTR_COLOR, Domain.CLIENT_GROUP.ATTR_COLOR,
CLIENT_GROUP_COLOR_COLUMN_KEY, CLIENT_GROUP_COLOR_COLUMN_KEY,
ClientGroupTemplate::getColor) cgt -> this.widgetFactory.getColorValueHTML(cgt))
.asMarkup()
.widthProportion(1))
.withColumn(new ColumnDefinition<ClientGroupTemplate>(
Domain.CLIENT_GROUP.ATTR_DATA,
CLIENT_GROUP_DATA_COLUMN_KEY,
cgt -> this.widgetFactory.clientGroupDataToHTML(cgt))
.asMarkup() .asMarkup()
.widthProportion(4)) .widthProportion(4))
.withDefaultActionIf( .withDefaultActionIf(
@ -430,18 +439,8 @@ public class ExamTemplateForm implements TemplateComposer {
if (indicator.type == null) { if (indicator.type == null) {
return Constants.EMPTY_NOTE; return Constants.EMPTY_NOTE;
} }
return this.resourceService.getI18nSupport() return this.resourceService.getI18nSupport()
.getText(ResourceService.EXAM_INDICATOR_TYPE_PREFIX + indicator.type.name()); .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());
}
} }

View file

@ -174,6 +174,15 @@ public final class Form implements FormBinding {
return this; return this;
} }
Form removeField(final String name) {
if (this.formFields.containsKey(name)) {
final List<FormFieldAccessor> list = this.formFields.remove(name);
list.forEach(ffa -> ffa.dispose());
}
return this;
}
public String getFieldValue(final String attributeName) { public String getFieldValue(final String attributeName) {
final FormFieldAccessor fieldAccessor = this.formFields.getFirst(attributeName); final FormFieldAccessor fieldAccessor = this.formFields.getFirst(attributeName);
if (fieldAccessor == null) { if (fieldAccessor == null) {
@ -464,6 +473,11 @@ public final class Form implements FormBinding {
this(label, control, null, false, errorLabel); this(label, control, null, false, errorLabel);
} }
public void dispose() {
this.label.dispose();
this.input.dispose();
}
FormFieldAccessor( FormFieldAccessor(
final Control label, final Control label,
final Control input, final Control input,

View file

@ -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.Activatable;
import ch.ethz.seb.sebserver.gbl.model.Entity; import ch.ethz.seb.sebserver.gbl.model.Entity;
import ch.ethz.seb.sebserver.gbl.model.EntityName; 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;
import ch.ethz.seb.sebserver.gbl.model.exam.Exam.ExamStatus; import ch.ethz.seb.sebserver.gbl.model.exam.Exam.ExamStatus;
import ch.ethz.seb.sebserver.gbl.model.exam.Exam.ExamType; 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 USERACCOUNT_ROLE_PREFIX = "sebserver.useraccount.role.";
public static final String EXAM_INDICATOR_TYPE_PREFIX = "sebserver.exam.indicator.type."; 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 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 LMSSETUP_TYPE_PREFIX = "sebserver.lmssetup.type.";
public static final String CONFIG_ATTRIBUTE_TYPE_PREFIX = "sebserver.configtemplate.attr.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."; public static final String SEB_RESTRICTION_WHITE_LIST_PREFIX = "sebserver.exam.form.sebrestriction.whiteListPaths.";
@ -275,6 +278,19 @@ public class ResourceService {
.collect(Collectors.toList()); .collect(Collectors.toList());
} }
public List<Tuple<String>> 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<Tuple<String>> examConfigurationSelectionResources() { public List<Tuple<String>> examConfigurationSelectionResources() {
return getExamConfigurationSelection() return getExamConfigurationSelection()
.getOr(Collections.emptyList()) .getOr(Collections.emptyList())
@ -905,4 +921,13 @@ public class ResourceService {
.collect(Collectors.toList()); .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());
}
} }

View file

@ -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;
import ch.ethz.seb.sebserver.gbl.model.exam.Indicator.IndicatorType; 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.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.i18n.LocTextKey;
import ch.ethz.seb.sebserver.gui.service.page.PageService; import ch.ethz.seb.sebserver.gui.service.page.PageService;
import ch.ethz.seb.sebserver.gui.widget.Selection.Type; import ch.ethz.seb.sebserver.gui.widget.Selection.Type;
@ -129,6 +130,37 @@ public final class ThresholdList extends Composite {
return collect; return collect;
} }
public static String thresholdsToHTML(
final List<Threshold> thresholds,
final IndicatorType indicatorType) {
if (thresholds.isEmpty()) {
return Constants.EMPTY_NOTE;
}
final StringBuilder builder = thresholds
.stream()
.reduce(
new StringBuilder(),
(sb, threshold) -> sb
.append("<span style='padding: 2px 5px 2px 5px; background-color: #")
.append(threshold.color)
.append("; ")
.append((Utils.darkColorContrast(Utils.parseRGB(threshold.color)))
? "color: #4a4a4a; "
: "color: #FFFFFF;")
.append("'>&nbsp;&nbsp;&nbsp;")
.append(Indicator.getDisplayValue(indicatorType, threshold.value))
.append("&nbsp;(#")
.append(threshold.color)
.append(")&nbsp;&nbsp;&nbsp;")
.append("</span>")
.append(" | "),
StringBuilder::append);
builder.delete(builder.length() - 3, builder.length() - 1);
return builder.toString();
}
private void removeInvalidListEntries() { private void removeInvalidListEntries() {
this.thresholds this.thresholds
.stream() .stream()

View file

@ -49,11 +49,14 @@ import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Lazy; import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service; 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.IndicatorType;
import ch.ethz.seb.sebserver.gbl.model.exam.Indicator.Threshold; import ch.ethz.seb.sebserver.gbl.model.exam.Indicator.Threshold;
import ch.ethz.seb.sebserver.gbl.profile.GuiProfile; import ch.ethz.seb.sebserver.gbl.profile.GuiProfile;
import ch.ethz.seb.sebserver.gbl.util.Tuple; import ch.ethz.seb.sebserver.gbl.util.Tuple;
import ch.ethz.seb.sebserver.gbl.util.Utils; 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.I18nSupport;
import ch.ethz.seb.sebserver.gui.service.i18n.LocTextKey; import ch.ethz.seb.sebserver.gui.service.i18n.LocTextKey;
import ch.ethz.seb.sebserver.gui.service.i18n.PolyglotPageService; import ch.ethz.seb.sebserver.gui.service.i18n.PolyglotPageService;
@ -130,7 +133,8 @@ public class WidgetFactory {
INSTITUTION("institution.png"), INSTITUTION("institution.png"),
LMS_SETUP("lmssetup.png"), LMS_SETUP("lmssetup.png"),
INDICATOR("indicator.png"), INDICATOR("indicator.png"),
CLIENT_GROUP("indicator.png"), CLIENT_GROUP("clientgroup.png"),
ADD_CLIENT_GROUP("add_clientgroup.png"),
TEMPLATE("template.png"), TEMPLATE("template.png"),
DISABLE("disable.png"), DISABLE("disable.png"),
SEND_QUIT("send-quit.png"), SEND_QUIT("send-quit.png"),
@ -1029,4 +1033,45 @@ public class WidgetFactory {
executor.execute(builder.toString()); 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 + "&nbsp;-&nbsp;" + 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("<span style='padding: 2px 5px 2px 5px; background-color: #")
.append(color)
.append("; ")
.append((Utils.darkColorContrast(Utils.parseRGB(color)))
? "color: #4a4a4a; "
: "color: #FFFFFF;")
.append("'>&nbsp;&nbsp;&nbsp;")
.append(" (#")
.append(color)
.append(")&nbsp;&nbsp;&nbsp;")
.append("</span>")
.toString();
}
} }

View file

@ -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.EntityDependency;
import ch.ethz.seb.sebserver.gbl.model.EntityKey; 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;
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.profile.WebServiceProfile;
import ch.ethz.seb.sebserver.gbl.util.Result; import ch.ethz.seb.sebserver.gbl.util.Result;
import ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.ClientGroupRecordDynamicSqlSupport; import ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.ClientGroupRecordDynamicSqlSupport;

View file

@ -601,11 +601,11 @@ public class ExamTemplateDAOImpl implements ExamTemplateDAO {
private void checkUniqueClientGroupName( private void checkUniqueClientGroupName(
final ClientGroupTemplate clientGroupTemplate, final ClientGroupTemplate clientGroupTemplate,
final Collection<ClientGroupTemplate> clinetGroups) { final Collection<ClientGroupTemplate> clietnGroups) {
// check unique name // check unique name
clinetGroups.stream() clietnGroups.stream()
.filter(it -> !Objects.equals(it, clientGroupTemplate) .filter(it -> !Objects.equals(it.id, clientGroupTemplate.id)
&& Objects.equals(it.name, clientGroupTemplate.name)) && Objects.equals(it.name, clientGroupTemplate.name))
.findAny() .findAny()
.ifPresent(it -> { .ifPresent(it -> {

View file

@ -13,14 +13,16 @@ import java.util.Set;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import org.apache.commons.lang3.BooleanUtils; import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.validation.FieldError; import org.springframework.validation.FieldError;
import ch.ethz.seb.sebserver.gbl.api.APIMessage; import ch.ethz.seb.sebserver.gbl.api.APIMessage;
import ch.ethz.seb.sebserver.gbl.api.APIMessage.APIMessageException; import ch.ethz.seb.sebserver.gbl.api.APIMessage.APIMessageException;
import ch.ethz.seb.sebserver.gbl.model.Domain; 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;
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;
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;
import ch.ethz.seb.sebserver.gbl.model.exam.Indicator.Threshold; import ch.ethz.seb.sebserver.gbl.model.exam.Indicator.Threshold;
import ch.ethz.seb.sebserver.gbl.model.exam.ProctoringServiceSettings; 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 * If a check fails, the methods throws a APIMessageException with a FieldError to notify the caller
* *
* @param clientGroup ClientGroup instance to check */ * @param clientGroup ClientGroup instance to check */
public static void checkClientGroupConsistency(final ClientGroupData clientGroup) { public static <T extends ClientGroupData> T checkClientGroupConsistency(final T clientGroup) {
final ClientGroupType type = clientGroup.getType(); final ClientGroupType type = clientGroup.getType();
if (type == null || type == ClientGroupType.NONE) { if (type == null || type == ClientGroupType.NONE) {
throw new APIMessageException(APIMessage.fieldValidationError( throw new APIMessageException(APIMessage.fieldValidationError(
new FieldError( new FieldError(
Domain.CLIENT_GROUP.TYPE_NAME, Domain.CLIENT_GROUP.TYPE_NAME,
Domain.CLIENT_GROUP.ATTR_TYPE, Domain.CLIENT_GROUP.ATTR_TYPE,
"clientGroup:type:mandatory"))); "clientGroup:type:notNull")));
} }
switch (type) { switch (type) {
@ -176,11 +178,13 @@ public interface ExamAdminService {
"clientGroup:type:typeInvalid"))); "clientGroup:type:typeInvalid")));
} }
} }
return clientGroup;
} }
static void checkIPRange(final String ipRangeStart, final String ipRangeEnd) { static void checkIPRange(final String ipRangeStart, final String ipRangeEnd) {
final long startIP = Utils.ipToLong(ipRangeStart); final long startIP = Utils.ipToLong(ipRangeStart);
if (startIP < 0) { if (StringUtils.isBlank(ipRangeStart) || startIP < 0) {
throw new APIMessageException(APIMessage.fieldValidationError( throw new APIMessageException(APIMessage.fieldValidationError(
new FieldError( new FieldError(
Domain.CLIENT_GROUP.TYPE_NAME, Domain.CLIENT_GROUP.TYPE_NAME,
@ -188,7 +192,7 @@ public interface ExamAdminService {
"clientGroup:ipRangeStart:invalidIP"))); "clientGroup:ipRangeStart:invalidIP")));
} }
final long endIP = Utils.ipToLong(ipRangeEnd); final long endIP = Utils.ipToLong(ipRangeEnd);
if (endIP < 0) { if (StringUtils.isBlank(ipRangeEnd) || endIP < 0) {
throw new APIMessageException(APIMessage.fieldValidationError( throw new APIMessageException(APIMessage.fieldValidationError(
new FieldError( new FieldError(
Domain.CLIENT_GROUP.TYPE_NAME, Domain.CLIENT_GROUP.TYPE_NAME,
@ -211,8 +215,14 @@ public interface ExamAdminService {
} }
static void checkClientOS(final String clientOS) { static void checkClientOS(final ClientOS clientOS) {
// TODO if (clientOS == null) {
throw new APIMessageException(APIMessage.fieldValidationError(
new FieldError(
Domain.CLIENT_GROUP.TYPE_NAME,
ClientGroupData.ATTR_CLIENT_OS,
"clientGroup:clientOS:notNull")));
}
} }
} }

View file

@ -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.ClientGroupDAO;
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.ExamDAO; 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.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.session.ExamSessionService;
import ch.ethz.seb.sebserver.webservice.servicelayer.validation.BeanValidationService; import ch.ethz.seb.sebserver.webservice.servicelayer.validation.BeanValidationService;
@ -88,15 +89,14 @@ public class ClientGroupController extends EntityController<ClientGroup, ClientG
@Override @Override
protected Result<ClientGroup> validForCreate(final ClientGroup entity) { protected Result<ClientGroup> validForCreate(final ClientGroup entity) {
// TODO check consistency!? return super.validForCreate(entity)
return super.validForCreate(entity); .map(ExamAdminService::checkClientGroupConsistency);
} }
@Override @Override
protected Result<ClientGroup> validForSave(final ClientGroup entity) { protected Result<ClientGroup> validForSave(final ClientGroup entity) {
// TODO check consistency!? return super.validForSave(entity)
return super.validForSave(entity); .map(ExamAdminService::checkClientGroupConsistency);
} }
@Override @Override

View file

@ -118,57 +118,6 @@ public class ExamAdministrationController extends EntityController<Exam, Exam> {
return ExamRecordDynamicSqlSupport.examRecord; return ExamRecordDynamicSqlSupport.examRecord;
} }
// @RequestMapping(
// method = RequestMethod.GET,
// consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE,
// produces = MediaType.APPLICATION_JSON_VALUE)
// @Override
// public Page<Exam> 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<String, String> 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<Exam> exams = this.examDAO
//// .allMatching(new FilterMap(
//// allRequestParams,
//// request.getQueryString()),
//// this::hasReadAccess)
//// .getOrThrow();
////
//// return this.paginationService.buildPageFromList(
//// pageNumber,
//// pageSize,
//// sort,
//// exams,
//// pageSort(sort));
// }
// }
@RequestMapping( @RequestMapping(
path = API.MODEL_ID_VAR_PATH_SEGMENT path = API.MODEL_ID_VAR_PATH_SEGMENT
+ API.EXAM_ADMINISTRATION_CHECK_IMPORTED_PATH_SEGMENT, + API.EXAM_ADMINISTRATION_CHECK_IMPORTED_PATH_SEGMENT,

View file

@ -337,7 +337,7 @@ public class ExamTemplateController extends EntityController<ExamTemplate, ExamT
null, null,
postMap.getLong(ClientGroupTemplate.ATTR_EXAM_TEMPLATE_ID), postMap.getLong(ClientGroupTemplate.ATTR_EXAM_TEMPLATE_ID),
postMap)) postMap))
.map(this::checkClientGroupConsistency) .map(ExamAdminService::checkClientGroupConsistency)
.flatMap(this.examTemplateDAO::createNewClientGroupTemplate) .flatMap(this.examTemplateDAO::createNewClientGroupTemplate)
.flatMap(this.userActivityLogDAO::logCreate) .flatMap(this.userActivityLogDAO::logCreate)
.getOrThrow(); .getOrThrow();
@ -359,7 +359,7 @@ public class ExamTemplateController extends EntityController<ExamTemplate, ExamT
this.checkModifyPrivilege(institutionId); this.checkModifyPrivilege(institutionId);
return this.beanValidationService return this.beanValidationService
.validateBean(modifyData) .validateBean(modifyData)
.map(this::checkClientGroupConsistency) .map(ExamAdminService::checkClientGroupConsistency)
.flatMap(this.examTemplateDAO::saveClientGroupTemplate) .flatMap(this.examTemplateDAO::saveClientGroupTemplate)
.flatMap(this.userActivityLogDAO::logModify) .flatMap(this.userActivityLogDAO::logModify)
.getOrThrow(); .getOrThrow();
@ -494,11 +494,6 @@ public class ExamTemplateController extends EntityController<ExamTemplate, ExamT
}; };
} }
private ClientGroupTemplate checkClientGroupConsistency(final ClientGroupTemplate clientGroupTemplate) {
ExamAdminService.checkClientGroupConsistency(clientGroupTemplate);
return clientGroupTemplate;
}
private IndicatorTemplate checkIndicatorConsistency(final IndicatorTemplate indicatorTemplate) { private IndicatorTemplate checkIndicatorConsistency(final IndicatorTemplate indicatorTemplate) {
ExamAdminService.checkThresholdConsistency(indicatorTemplate.thresholds); ExamAdminService.checkThresholdConsistency(indicatorTemplate.thresholds);
return indicatorTemplate; return indicatorTemplate;

View file

@ -80,6 +80,11 @@ sebserver.overall.activity.title.monitoring=Monitoring
sebserver.overall.batchaction.selected=Selected sebserver.overall.batchaction.selected=Selected
sebserver.overall.seb.os.type.NONE=Undefined
sebserver.overall.seb.os.type.WINDOWS=Windows
sebserver.overall.seb.os.type.MAC_OS=MacOS
sebserver.overall.seb.os.type.I_OS=iOS
################################ ################################
# Form validation and messages # Form validation and messages
################################ ################################
@ -595,7 +600,7 @@ sebserver.exam.status.ARCHIVED=Archived
sebserver.exam.status.CORRUPT_NO_LMS_CONNECTION=Corrupt (No LMS Connection) sebserver.exam.status.CORRUPT_NO_LMS_CONNECTION=Corrupt (No LMS Connection)
sebserver.exam.status.CORRUPT_INVALID_ID=Corrupt (Invalid Identifier) sebserver.exam.status.CORRUPT_INVALID_ID=Corrupt (Invalid Identifier)
sebserver.exam.configuration.list.actions= sebserver.exam.configuration.list.actions=&nbsp;
sebserver.exam.configuration.list.title=Exam Configuration sebserver.exam.configuration.list.title=Exam Configuration
sebserver.exam.configuration.list.title.tooltip=A list of all attached exam configuration for this exam sebserver.exam.configuration.list.title.tooltip=A list of all attached exam configuration for this exam
sebserver.exam.configuration.list.column.name=Name sebserver.exam.configuration.list.column.name=Name
@ -628,7 +633,7 @@ sebserver.exam.configuration.form.status.tooltip=The current status of the selec
sebserver.exam.configuration.form.encryptSecret.confirm=Confirm Password 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 encryption password if there is one
sebserver.exam.indicator.list.actions= sebserver.exam.indicator.list.actions=&nbsp;
sebserver.exam.indicator.list.title=Indicators sebserver.exam.indicator.list.title=Indicators
sebserver.exam.indicator.list.title.tooltip=A list of indicators that are shown on the exam monitoring view sebserver.exam.indicator.list.title.tooltip=A list of indicators that are shown on the exam monitoring view
sebserver.exam.indicator.list.column.type=Type sebserver.exam.indicator.list.column.type=Type
@ -640,6 +645,20 @@ sebserver.exam.indicator.list.column.thresholds.tooltip=The thresholds of the in
sebserver.exam.indicator.list.empty=There is currently no indicator defined for this exam. Please create a new one sebserver.exam.indicator.list.empty=There is currently no indicator defined for this exam. Please create a new one
sebserver.exam.indicator.list.pleaseSelect=At first please select an indicator from the list sebserver.exam.indicator.list.pleaseSelect=At first please select an indicator from the list
sebserver.exam.clientgroup.list.actions=&nbsp;
sebserver.exam.clientgroup.list.title=Client Groups
sebserver.exam.clientgroup.list.title.tooltip=A list of client groups that are shown on the exam monitoring view for the client connections that matches the specific group(s)
sebserver.exam.clientgroup.list.column.type=Type
sebserver.exam.clientgroup.list.column.type.tooltip=The type of the client group
sebserver.exam.clientgroup.list.column.name=Name
sebserver.exam.clientgroup.list.column.name.tooltip=The name of the client group
sebserver.exam.clientgroup.list.column.color=Color
sebserver.exam.clientgroup.list.column.color.tooltip=The color of the client group
sebserver.exam.clientgroup.list.column.data=Data
sebserver.exam.clientgroup.list.column.data.tooltip=The data of the client group that is specific to the type of the client group
sebserver.exam.clientgroup.list.empty=There is currently no client group defined for this exam.
sebserver.exam.clientgroup.list.pleaseSelect=At first please select a client group from the list
sebserver.exam.indicator.type.LAST_PING=Last Ping Time sebserver.exam.indicator.type.LAST_PING=Last Ping Time
sebserver.exam.indicator.type.ERROR_COUNT=Error-Log Counter sebserver.exam.indicator.type.ERROR_COUNT=Error-Log Counter
sebserver.exam.indicator.type.WARN_COUNT=Warning-Log Counter sebserver.exam.indicator.type.WARN_COUNT=Warning-Log Counter
@ -655,6 +674,7 @@ sebserver.exam.indicator.type.description.WLAN_STATUS=This indicator shows the p
sebserver.exam.clientgroup.type.IP_V4_RANGE=IP v4 Range sebserver.exam.clientgroup.type.IP_V4_RANGE=IP v4 Range
sebserver.exam.clientgroup.type.CLIENT_OS=SEB Client OS sebserver.exam.clientgroup.type.CLIENT_OS=SEB Client OS
sebserver.exam.clientgroup.type.description.NONE=No Client Group type is selected.<br/>Please select one.
sebserver.exam.clientgroup.type.description.IP_V4_RANGE=IP v4 Range TODO sebserver.exam.clientgroup.type.description.IP_V4_RANGE=IP v4 Range TODO
sebserver.exam.clientgroup.type.description.CLIENT_OS=SEB Client OS 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.list.delete=Delete Selected Indicator
sebserver.exam.indicator.action.save=Save 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=Indicator
sebserver.exam.indicator.form.title.subtitle= sebserver.exam.indicator.form.title.subtitle=
sebserver.exam.indicator.form.title.new=Add Indicator 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.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=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.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 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.name.tooltip=The name of the client group
sebserver.examtemplate.clientgroup.list.column.color=Color 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.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.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 sebserver.examtemplate.clientgroup.list.pleaseSelect=At first please select an client group from the list

Binary file not shown.

After

Width:  |  Height:  |  Size: 232 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 224 B

View file

@ -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.Page;
import ch.ethz.seb.sebserver.gbl.model.exam.Chapters; 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;
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;
import ch.ethz.seb.sebserver.gbl.model.exam.Exam.ExamStatus; import ch.ethz.seb.sebserver.gbl.model.exam.Exam.ExamStatus;
import ch.ethz.seb.sebserver.gbl.model.exam.Exam.ExamType; import ch.ethz.seb.sebserver.gbl.model.exam.Exam.ExamType;