SEBSERV-163 validation
This commit is contained in:
parent
c431279214
commit
800494b1b7
18 changed files with 762 additions and 46 deletions
|
@ -250,6 +250,10 @@ public class APIMessage implements Serializable {
|
|||
this.apiMessages = Arrays.asList(apiMessage);
|
||||
}
|
||||
|
||||
public APIMessageException(final APIMessage... apiMessage) {
|
||||
this.apiMessages = Arrays.asList(apiMessage);
|
||||
}
|
||||
|
||||
public APIMessageException(final ErrorMessage errorMessage) {
|
||||
super(errorMessage.systemMessage);
|
||||
this.apiMessages = Arrays.asList(errorMessage.of());
|
||||
|
|
|
@ -11,21 +11,28 @@ package ch.ethz.seb.sebserver.gbl.model.exam;
|
|||
import javax.validation.constraints.NotNull;
|
||||
import javax.validation.constraints.Size;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
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.Entity;
|
||||
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
public class ClientGroup implements Entity {
|
||||
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
|
||||
}
|
||||
|
@ -52,8 +59,14 @@ public class ClientGroup implements Entity {
|
|||
@JsonProperty(CLIENT_GROUP.ATTR_ICON)
|
||||
public final String icon;
|
||||
|
||||
@JsonProperty(CLIENT_GROUP.ATTR_DATA)
|
||||
public final String data;
|
||||
@JsonProperty(ATTR_IP_RANGE_START)
|
||||
public final String ipRangeStart;
|
||||
|
||||
@JsonProperty(ATTR_IP_RANGE_END)
|
||||
public final String ipRangeEnd;
|
||||
|
||||
@JsonProperty(ATTR_CLIENT_OS)
|
||||
public final String clientOS;
|
||||
|
||||
@JsonCreator
|
||||
public ClientGroup(
|
||||
|
@ -63,7 +76,9 @@ public class ClientGroup implements Entity {
|
|||
@JsonProperty(CLIENT_GROUP.ATTR_TYPE) final ClientGroupType type,
|
||||
@JsonProperty(CLIENT_GROUP.ATTR_COLOR) final String color,
|
||||
@JsonProperty(CLIENT_GROUP.ATTR_ICON) final String icon,
|
||||
@JsonProperty(CLIENT_GROUP.ATTR_DATA) final String data) {
|
||||
@JsonProperty(ATTR_IP_RANGE_START) final String ipRangeStart,
|
||||
@JsonProperty(ATTR_IP_RANGE_END) final String ipRangeEnd,
|
||||
@JsonProperty(ATTR_CLIENT_OS) final String clientOS) {
|
||||
|
||||
super();
|
||||
this.id = id;
|
||||
|
@ -72,7 +87,48 @@ public class ClientGroup implements Entity {
|
|||
this.type = type;
|
||||
this.color = color;
|
||||
this.icon = icon;
|
||||
this.data = data;
|
||||
this.ipRangeStart = ipRangeStart;
|
||||
this.ipRangeEnd = ipRangeEnd;
|
||||
this.clientOS = clientOS;
|
||||
}
|
||||
|
||||
public ClientGroup(
|
||||
final Long id,
|
||||
final Long examId,
|
||||
final String name,
|
||||
final ClientGroupType type,
|
||||
final String color,
|
||||
final String icon,
|
||||
final String data) {
|
||||
|
||||
super();
|
||||
this.id = id;
|
||||
this.examId = examId;
|
||||
this.name = name;
|
||||
this.type = type;
|
||||
this.color = color;
|
||||
this.icon = icon;
|
||||
|
||||
switch (type) {
|
||||
case IP_V4_RANGE: {
|
||||
final String[] split = StringUtils.split(data, Constants.EMBEDDED_LIST_SEPARATOR);
|
||||
this.ipRangeStart = split[0];
|
||||
this.ipRangeEnd = split[1];
|
||||
this.clientOS = null;
|
||||
break;
|
||||
}
|
||||
case CLIENT_OS: {
|
||||
this.ipRangeStart = null;
|
||||
this.ipRangeEnd = null;
|
||||
this.clientOS = data;
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
this.ipRangeStart = null;
|
||||
this.ipRangeEnd = null;
|
||||
this.clientOS = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public ClientGroup(final Long examId, final POSTMapper postParams) {
|
||||
|
@ -82,7 +138,9 @@ public class ClientGroup implements Entity {
|
|||
this.type = postParams.getEnum(CLIENT_GROUP.ATTR_TYPE, ClientGroupType.class);
|
||||
this.color = postParams.getString(CLIENT_GROUP.ATTR_COLOR);
|
||||
this.icon = postParams.getString(CLIENT_GROUP.ATTR_ICON);
|
||||
this.data = postParams.getString(CLIENT_GROUP.ATTR_DATA);
|
||||
this.ipRangeStart = postParams.getString(ATTR_IP_RANGE_START);
|
||||
this.ipRangeEnd = postParams.getString(ATTR_IP_RANGE_END);
|
||||
this.clientOS = postParams.getString(ATTR_CLIENT_OS);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -100,6 +158,7 @@ public class ClientGroup implements Entity {
|
|||
return this.name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long getId() {
|
||||
return this.id;
|
||||
}
|
||||
|
@ -108,20 +167,49 @@ public class ClientGroup implements Entity {
|
|||
return this.examId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClientGroupType getType() {
|
||||
return this.type;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getColor() {
|
||||
return this.color;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getIcon() {
|
||||
return this.icon;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getIpRangeStart() {
|
||||
return this.ipRangeStart;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getIpRangeEnd() {
|
||||
return this.ipRangeEnd;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getClientOS() {
|
||||
return this.clientOS;
|
||||
}
|
||||
|
||||
@JsonIgnore
|
||||
public String getData() {
|
||||
return this.data;
|
||||
switch (this.type) {
|
||||
case IP_V4_RANGE: {
|
||||
return this.ipRangeStart + Constants.EMBEDDED_LIST_SEPARATOR + this.ipRangeEnd;
|
||||
}
|
||||
case CLIENT_OS: {
|
||||
return this.clientOS;
|
||||
}
|
||||
default: {
|
||||
return StringUtils.EMPTY;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -139,10 +227,42 @@ public class ClientGroup implements Entity {
|
|||
builder.append(this.color);
|
||||
builder.append(", icon=");
|
||||
builder.append(this.icon);
|
||||
builder.append(", data=");
|
||||
builder.append(this.data);
|
||||
builder.append(", ipRangeStart=");
|
||||
builder.append(this.ipRangeStart);
|
||||
builder.append(", ipRangeEnd=");
|
||||
builder.append(this.ipRangeEnd);
|
||||
builder.append(", clientOS=");
|
||||
builder.append(this.clientOS);
|
||||
builder.append("]");
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
result = prime * result + ((this.id == null) ? 0 : this.id.hashCode());
|
||||
result = prime * result + ((this.type == null) ? 0 : this.type.hashCode());
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object obj) {
|
||||
if (this == obj)
|
||||
return true;
|
||||
if (obj == null)
|
||||
return false;
|
||||
if (getClass() != obj.getClass())
|
||||
return false;
|
||||
final ClientGroup other = (ClientGroup) obj;
|
||||
if (this.id == null) {
|
||||
if (other.id != null)
|
||||
return false;
|
||||
} else if (!this.id.equals(other.id))
|
||||
return false;
|
||||
if (this.type != other.type)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
/*
|
||||
* 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.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 {
|
||||
|
||||
Long getId();
|
||||
|
||||
ClientGroupType getType();
|
||||
|
||||
String getColor();
|
||||
|
||||
String getIcon();
|
||||
|
||||
String getIpRangeStart();
|
||||
|
||||
String getIpRangeEnd();
|
||||
|
||||
String getClientOS();
|
||||
}
|
|
@ -11,18 +11,21 @@ package ch.ethz.seb.sebserver.gbl.model.exam;
|
|||
import javax.validation.constraints.NotNull;
|
||||
import javax.validation.constraints.Size;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
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.Entity;
|
||||
import ch.ethz.seb.sebserver.gbl.model.exam.ClientGroup.ClientGroupType;
|
||||
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
public class ClientGroupTemplate implements Entity {
|
||||
public class ClientGroupTemplate implements ClientGroupData {
|
||||
|
||||
public static final String ATTR_EXAM_TEMPLATE_ID = "examTemplateId";
|
||||
|
||||
|
@ -47,8 +50,14 @@ public class ClientGroupTemplate implements Entity {
|
|||
@JsonProperty(CLIENT_GROUP.ATTR_ICON)
|
||||
public final String icon;
|
||||
|
||||
@JsonProperty(CLIENT_GROUP.ATTR_DATA)
|
||||
public final String data;
|
||||
@JsonProperty(ClientGroup.ATTR_IP_RANGE_START)
|
||||
public final String ipRangeStart;
|
||||
|
||||
@JsonProperty(ClientGroup.ATTR_IP_RANGE_END)
|
||||
public final String ipRangeEnd;
|
||||
|
||||
@JsonProperty(ClientGroup.ATTR_CLIENT_OS)
|
||||
public final String clientOS;
|
||||
|
||||
@JsonCreator
|
||||
public ClientGroupTemplate(
|
||||
|
@ -58,7 +67,9 @@ public class ClientGroupTemplate implements Entity {
|
|||
@JsonProperty(CLIENT_GROUP.ATTR_TYPE) final ClientGroupType type,
|
||||
@JsonProperty(CLIENT_GROUP.ATTR_COLOR) final String color,
|
||||
@JsonProperty(CLIENT_GROUP.ATTR_ICON) final String icon,
|
||||
@JsonProperty(CLIENT_GROUP.ATTR_DATA) final String data) {
|
||||
@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) {
|
||||
|
||||
super();
|
||||
this.id = id;
|
||||
|
@ -67,7 +78,9 @@ public class ClientGroupTemplate implements Entity {
|
|||
this.type = type;
|
||||
this.color = color;
|
||||
this.icon = icon;
|
||||
this.data = data;
|
||||
this.ipRangeStart = ipRangeStart;
|
||||
this.ipRangeEnd = ipRangeEnd;
|
||||
this.clientOS = clientOS;
|
||||
}
|
||||
|
||||
public ClientGroupTemplate(final Long id, final Long examTemplateId, final POSTMapper postParams) {
|
||||
|
@ -78,7 +91,9 @@ public class ClientGroupTemplate implements Entity {
|
|||
this.type = postParams.getEnum(CLIENT_GROUP.ATTR_TYPE, ClientGroupType.class);
|
||||
this.color = postParams.getString(CLIENT_GROUP.ATTR_COLOR);
|
||||
this.icon = postParams.getString(CLIENT_GROUP.ATTR_ICON);
|
||||
this.data = postParams.getString(CLIENT_GROUP.ATTR_DATA);
|
||||
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);
|
||||
}
|
||||
|
||||
public ClientGroupTemplate(final Long id, final ClientGroupTemplate other) {
|
||||
|
@ -89,7 +104,9 @@ public class ClientGroupTemplate implements Entity {
|
|||
this.type = other.type;
|
||||
this.color = other.color;
|
||||
this.icon = other.icon;
|
||||
this.data = other.data;
|
||||
this.ipRangeStart = other.ipRangeStart;
|
||||
this.ipRangeEnd = other.ipRangeEnd;
|
||||
this.clientOS = other.clientOS;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -107,34 +124,64 @@ public class ClientGroupTemplate implements Entity {
|
|||
return this.name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long getId() {
|
||||
return this.id;
|
||||
}
|
||||
|
||||
public Long getExamTempateId() {
|
||||
return this.examTemplateId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClientGroupType getType() {
|
||||
return this.type;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getColor() {
|
||||
return this.color;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getIcon() {
|
||||
return this.icon;
|
||||
}
|
||||
|
||||
public Long getExamTemplateId() {
|
||||
return this.examTemplateId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getIpRangeStart() {
|
||||
return this.ipRangeStart;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getIpRangeEnd() {
|
||||
return this.ipRangeEnd;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getClientOS() {
|
||||
return this.clientOS;
|
||||
}
|
||||
|
||||
@JsonIgnore
|
||||
public String getData() {
|
||||
return this.data;
|
||||
switch (this.type) {
|
||||
case IP_V4_RANGE: {
|
||||
return this.ipRangeStart + Constants.EMBEDDED_LIST_SEPARATOR + this.ipRangeEnd;
|
||||
}
|
||||
case CLIENT_OS: {
|
||||
return this.clientOS;
|
||||
}
|
||||
default: {
|
||||
return StringUtils.EMPTY;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
final StringBuilder builder = new StringBuilder();
|
||||
builder.append("ClientGroup [id=");
|
||||
builder.append("ClientGroupTemplate [id=");
|
||||
builder.append(this.id);
|
||||
builder.append(", examTemplateId=");
|
||||
builder.append(this.examTemplateId);
|
||||
|
@ -146,8 +193,12 @@ public class ClientGroupTemplate implements Entity {
|
|||
builder.append(this.color);
|
||||
builder.append(", icon=");
|
||||
builder.append(this.icon);
|
||||
builder.append(", data=");
|
||||
builder.append(this.data);
|
||||
builder.append(", ipRangeStart=");
|
||||
builder.append(this.ipRangeStart);
|
||||
builder.append(", ipRangeEnd=");
|
||||
builder.append(this.ipRangeEnd);
|
||||
builder.append(", clientOS=");
|
||||
builder.append(this.clientOS);
|
||||
builder.append("]");
|
||||
return builder.toString();
|
||||
}
|
||||
|
|
|
@ -8,13 +8,11 @@
|
|||
|
||||
package ch.ethz.seb.sebserver.gbl.monitoring;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.context.annotation.Lazy;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import ch.ethz.seb.sebserver.gbl.Constants;
|
||||
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.session.ClientConnection;
|
||||
|
@ -34,15 +32,14 @@ public class IPv4RangeClientGroupMatcher implements ClientGroupConnectionMatcher
|
|||
@Override
|
||||
public boolean isInGroup(final ClientConnection clientConnection, final ClientGroup group) {
|
||||
try {
|
||||
final String[] split = StringUtils.split(group.data, Constants.LIST_SEPARATOR);
|
||||
|
||||
final long startIPAddress = Utils.ipToLong(split[0]);
|
||||
final long endIPAddress = Utils.ipToLong(split[1]);
|
||||
final long startIPAddress = Utils.ipToLong(group.ipRangeStart);
|
||||
final long endIPAddress = Utils.ipToLong(group.ipRangeEnd);
|
||||
final long inputIPAddress = Utils.ipToLong(clientConnection.clientAddress);
|
||||
|
||||
return (inputIPAddress >= startIPAddress && inputIPAddress <= endIPAddress);
|
||||
} catch (final Exception e) {
|
||||
log.error("Failed to verify ip range for group: {} connection: {}", group, clientConnection, e);
|
||||
log.error("Failed to verify IP range for group: {} connection: {}", group, clientConnection, e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,6 +20,7 @@ public enum ActionCategory {
|
|||
EXAM_LIST(new LocTextKey("sebserver.exam.list.actions"), 1),
|
||||
EXAM_TEMPLATE_LIST(new LocTextKey("sebserver.examtemplate.list.actions"), 1),
|
||||
INDICATOR_TEMPLATE_LIST(new LocTextKey("sebserver.examtemplate.indicator.list.actions"), 1),
|
||||
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),
|
||||
SEB_CLIENT_CONFIG_LIST(new LocTextKey("sebserver.clientconfig.list.actions"), 1),
|
||||
|
|
|
@ -469,6 +469,32 @@ public enum ActionDefinition {
|
|||
PageStateDefinitionImpl.EXAM_TEMPLATE_VIEW,
|
||||
ActionCategory.FORM),
|
||||
|
||||
CLIENT_GROUP_TEMPLATE_NEW(
|
||||
new LocTextKey("sebserver.examtemplate.clientgroup.action.list.new"),
|
||||
ImageIcon.CLIENT_GROUP,
|
||||
PageStateDefinitionImpl.CLIENT_GROUP_TEMPLATE_EDIT,
|
||||
ActionCategory.CLIENT_GROUP_TEMPLATE_LIST),
|
||||
CLIENT_GROUP_TEMPLATE_MODIFY_FROM_LIST(
|
||||
new LocTextKey("sebserver.examtemplate.clientgroup.action.list.modify"),
|
||||
ImageIcon.EDIT,
|
||||
PageStateDefinitionImpl.CLIENT_GROUP_TEMPLATE_EDIT,
|
||||
ActionCategory.CLIENT_GROUP_TEMPLATE_LIST),
|
||||
CLIENT_GROUP_TEMPLATE_DELETE_FROM_LIST(
|
||||
new LocTextKey("sebserver.examtemplate.clientgroup.action.list.delete"),
|
||||
ImageIcon.DELETE,
|
||||
PageStateDefinitionImpl.EXAM_TEMPLATE_VIEW,
|
||||
ActionCategory.CLIENT_GROUP_TEMPLATE_LIST),
|
||||
CLIENT_GROUP_TEMPLATE_SAVE(
|
||||
new LocTextKey("sebserver.examtemplate.clientgroup.action.save"),
|
||||
ImageIcon.SAVE,
|
||||
PageStateDefinitionImpl.EXAM_TEMPLATE_VIEW,
|
||||
ActionCategory.FORM),
|
||||
CLIENT_GROUP_TEMPLATE_CANCEL_MODIFY(
|
||||
new LocTextKey("sebserver.overall.action.modify.cancel"),
|
||||
ImageIcon.CANCEL,
|
||||
PageStateDefinitionImpl.EXAM_TEMPLATE_VIEW,
|
||||
ActionCategory.FORM),
|
||||
|
||||
SEB_CLIENT_CONFIG_LIST(
|
||||
new LocTextKey("sebserver.clientconfig.list.title"),
|
||||
PageStateDefinitionImpl.SEB_CLIENT_CONFIG_LIST),
|
||||
|
|
|
@ -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.ClientGroupTemplateForm;
|
||||
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.ExamTemplateForm;
|
||||
|
@ -70,6 +71,7 @@ public enum PageStateDefinitionImpl implements PageStateDefinition {
|
|||
EXAM_TEMPLATE_VIEW(Type.LIST_VIEW, ExamTemplateForm.class, ActivityDefinition.EXAM_TEMPLATE),
|
||||
EXAM_TEMPLATE_EDIT(Type.FORM_EDIT, ExamTemplateForm.class, ActivityDefinition.EXAM_TEMPLATE),
|
||||
INDICATOR_TEMPLATE_EDIT(Type.FORM_EDIT, IndicatorTemplateForm.class, ActivityDefinition.EXAM_TEMPLATE),
|
||||
CLIENT_GROUP_TEMPLATE_EDIT(Type.FORM_EDIT, ClientGroupTemplateForm.class, ActivityDefinition.EXAM_TEMPLATE),
|
||||
|
||||
SEB_CLIENT_CONFIG_LIST(Type.LIST_VIEW, SEBClientConfigList.class, ActivityDefinition.SEB_CLIENT_CONFIG),
|
||||
SEB_CLIENT_CONFIG_VIEW(Type.FORM_VIEW, SEBClientConfigForm.class, ActivityDefinition.SEB_CLIENT_CONFIG),
|
||||
|
|
|
@ -0,0 +1,234 @@
|
|||
/*
|
||||
* 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.ClientGroup.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;
|
||||
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.clientgroup.GetClientGroupTemplate;
|
||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.clientgroup.NewClientGroupTemplate;
|
||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.clientgroup.SaveClientGroupTemplate;
|
||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.template.GetExamTemplate;
|
||||
import ch.ethz.seb.sebserver.gui.widget.WidgetFactory;
|
||||
|
||||
@Lazy
|
||||
@Component
|
||||
@GuiProfile
|
||||
public class ClientGroupTemplateForm 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 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;
|
||||
|
||||
public ClientGroupTemplateForm(
|
||||
final PageService pageService,
|
||||
final ResourceService resourceService,
|
||||
final I18nSupport i18nSupport) {
|
||||
|
||||
super();
|
||||
this.pageService = pageService;
|
||||
this.resourceService = resourceService;
|
||||
this.i18nSupport = i18nSupport;
|
||||
}
|
||||
|
||||
@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 ExamTemplate examTemplate = restService
|
||||
.getBuilder(GetExamTemplate.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 ClientGroupTemplate clientGroupTemplate = (isNew)
|
||||
? new ClientGroupTemplate(null, Long.parseLong(parentEntityKey.modelId),
|
||||
null, null, null, null, null, null, null)
|
||||
: restService
|
||||
.getBuilder(GetClientGroupTemplate.class)
|
||||
.withURIVariable(API.PARAM_PARENT_MODEL_ID, parentEntityKey.modelId)
|
||||
.withURIVariable(API.PARAM_MODEL_ID, entityKey.modelId)
|
||||
.call()
|
||||
.onError(error -> pageContext.notifyLoadError(EntityType.CLIENT_GROUP, error))
|
||||
.getOrThrow();
|
||||
|
||||
final boolean typeSet = clientGroupTemplate.type != null;
|
||||
final String typeDescription = (typeSet)
|
||||
? Utils.formatLineBreaks(
|
||||
this.i18nSupport.getText(CLIENT_GROUP_TYPE_DESC_PREFIX + clientGroupTemplate.type.name()))
|
||||
: Constants.EMPTY_NOTE;
|
||||
|
||||
// new PageContext with actual EntityKey
|
||||
final PageContext formContext = pageContext.withEntityKey(clientGroupTemplate.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<ClientGroupTemplate> formHandle = this.pageService.formBuilder(
|
||||
formContext.copyOf(content))
|
||||
.readonly(isReadonly)
|
||||
.putStaticValueIf(() -> !isNew,
|
||||
Domain.CLIENT_GROUP.ATTR_ID,
|
||||
clientGroupTemplate.getModelId())
|
||||
.putStaticValue(
|
||||
Domain.EXAM.ATTR_INSTITUTION_ID,
|
||||
String.valueOf(examTemplate.getInstitutionId()))
|
||||
.putStaticValue(
|
||||
IndicatorTemplate.ATTR_EXAM_TEMPLATE_ID,
|
||||
parentEntityKey.getModelId())
|
||||
|
||||
.addField(FormBuilder.text(
|
||||
Domain.EXAM_TEMPLATE.ATTR_NAME,
|
||||
FORM_EXAM_TEXT_KEY,
|
||||
examTemplate.name)
|
||||
.readonly(true))
|
||||
.addField(FormBuilder.text(
|
||||
Domain.CLIENT_GROUP.ATTR_NAME,
|
||||
FORM_NAME_TEXT_KEY,
|
||||
clientGroupTemplate.name)
|
||||
.mandatory(!isReadonly))
|
||||
|
||||
.addField(FormBuilder.colorSelection(
|
||||
Domain.CLIENT_GROUP.ATTR_COLOR,
|
||||
FORM_COLOR_TEXT_KEY,
|
||||
clientGroupTemplate.color)
|
||||
.withEmptyCellSeparation(false))
|
||||
|
||||
.addField(FormBuilder.singleSelection(
|
||||
Domain.CLIENT_GROUP.ATTR_TYPE,
|
||||
FORM_TYPE_TEXT_KEY,
|
||||
(clientGroupTemplate.type != null) ? clientGroupTemplate.type.name() : null,
|
||||
this.resourceService::clientGroupTypeResources)
|
||||
.withSelectionListener(this::updateForm)
|
||||
.mandatory(!isReadonly))
|
||||
|
||||
.addField(FormBuilder.text(
|
||||
TYPE_DESCRIPTION_FIELD_NAME,
|
||||
FORM_DESC_TEXT_KEY,
|
||||
typeDescription)
|
||||
.asArea()
|
||||
//.asHTML(true)
|
||||
.readonly(true))
|
||||
|
||||
.addField(FormBuilder.text(
|
||||
ClientGroup.ATTR_IP_RANGE_START,
|
||||
FORM_IP_START_KEY,
|
||||
clientGroupTemplate::getIpRangeStart)
|
||||
.visibleIf(clientGroupTemplate.type != null
|
||||
&& clientGroupTemplate.type == ClientGroupType.IP_V4_RANGE))
|
||||
|
||||
.addField(FormBuilder.text(
|
||||
ClientGroup.ATTR_IP_RANGE_END,
|
||||
FORM_IP_END_KEY,
|
||||
clientGroupTemplate::getIpRangeEnd)
|
||||
.visibleIf(clientGroupTemplate.type != null
|
||||
&& clientGroupTemplate.type == ClientGroupType.IP_V4_RANGE))
|
||||
|
||||
.buildFor((isNew)
|
||||
? restService.getRestCall(NewClientGroupTemplate.class)
|
||||
: restService.getRestCall(SaveClientGroupTemplate.class));
|
||||
|
||||
// propagate content actions to action-pane
|
||||
this.pageService.pageActionBuilder(formContext.clearEntityKeys())
|
||||
|
||||
.newAction(ActionDefinition.CLIENT_GROUP_TEMPLATE_SAVE)
|
||||
.withEntityKey(parentEntityKey)
|
||||
.withExec(formHandle::processFormSave)
|
||||
.ignoreMoveAwayFromEdit()
|
||||
.publishIf(() -> !isReadonly)
|
||||
|
||||
.newAction(ActionDefinition.CLIENT_GROUP_TEMPLATE_CANCEL_MODIFY)
|
||||
.withEntityKey(parentEntityKey)
|
||||
.withExec(this.pageService.backToCurrentFunction())
|
||||
.publishIf(() -> !isReadonly);
|
||||
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -20,6 +20,7 @@ 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.ClientGroupTemplate;
|
||||
import ch.ethz.seb.sebserver.gbl.model.exam.Exam;
|
||||
import ch.ethz.seb.sebserver.gbl.model.exam.ExamTemplate;
|
||||
import ch.ethz.seb.sebserver.gbl.model.exam.IndicatorTemplate;
|
||||
|
@ -37,6 +38,8 @@ 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.DeleteClientGroupTemplate;
|
||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.clientgroup.GetClientGroupTemplatePage;
|
||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.indicator.DeleteIndicatorTemplate;
|
||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.indicator.GetIndicatorTemplatePage;
|
||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.template.DeleteExamTemplate;
|
||||
|
@ -88,6 +91,21 @@ public class ExamTemplateForm implements TemplateComposer {
|
|||
private static final LocTextKey INDICATOR_EMPTY_LIST_MESSAGE =
|
||||
new LocTextKey("sebserver.examtemplate.indicator.list.empty");
|
||||
|
||||
private final static LocTextKey CLIENT_GROUP_LIST_TITLE_KEY =
|
||||
new LocTextKey("sebserver.examtemplate.clientgroup.list.title");
|
||||
private final static LocTextKey CLIENT_GROUP_LIST_TITLE_TOOLTIP_KEY =
|
||||
new LocTextKey("sebserver.examtemplate.clientgroup.list.title" + Constants.TOOLTIP_TEXT_KEY_SUFFIX);
|
||||
private final static LocTextKey CLIENT_GROUP_TYPE_COLUMN_KEY =
|
||||
new LocTextKey("sebserver.examtemplate.clientgroup.list.column.type");
|
||||
private final static LocTextKey CLIENT_GROUP_NAME_COLUMN_KEY =
|
||||
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_EMPTY_SELECTION_TEXT_KEY =
|
||||
new LocTextKey("sebserver.examtemplate.clientgroup.list.pleaseSelect");
|
||||
private static final LocTextKey CLIENT_GROUP_EMPTY_LIST_MESSAGE =
|
||||
new LocTextKey("sebserver.examtemplate.clientgroup.list.empty");
|
||||
|
||||
private static final LocTextKey EXAM_TEMPLATE_DELETE_CONFIRM =
|
||||
new LocTextKey("sebserver.examtemplate.form.action.delete.confirm");
|
||||
|
||||
|
@ -307,6 +325,74 @@ public class ExamTemplateForm implements TemplateComposer {
|
|||
.newAction(ActionDefinition.INDICATOR_TEMPLATE_NEW)
|
||||
.withParentEntityKey(entityKey)
|
||||
.publishIf(() -> userGrantCheck.m());
|
||||
|
||||
// List of Client Groups
|
||||
this.widgetFactory.addFormSubContextHeader(
|
||||
content,
|
||||
CLIENT_GROUP_LIST_TITLE_KEY,
|
||||
CLIENT_GROUP_LIST_TITLE_TOOLTIP_KEY);
|
||||
|
||||
final EntityTable<ClientGroupTemplate> clientGroupTable =
|
||||
this.pageService
|
||||
.entityTableBuilder(this.restService.getRestCall(GetClientGroupTemplatePage.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,
|
||||
ClientGroupTemplate::getName)
|
||||
.widthProportion(2))
|
||||
.withColumn(new ColumnDefinition<>(
|
||||
Domain.CLIENT_GROUP.ATTR_TYPE,
|
||||
CLIENT_GROUP_TYPE_COLUMN_KEY,
|
||||
this::clientGroupTypeName)
|
||||
.widthProportion(1))
|
||||
.withColumn(new ColumnDefinition<>(
|
||||
Domain.CLIENT_GROUP.ATTR_COLOR,
|
||||
CLIENT_GROUP_COLOR_COLUMN_KEY,
|
||||
ClientGroupTemplate::getColor)
|
||||
.asMarkup()
|
||||
.widthProportion(4))
|
||||
.withDefaultActionIf(
|
||||
() -> userGrantCheck.m(),
|
||||
() -> actionBuilder
|
||||
.newAction(ActionDefinition.CLIENT_GROUP_TEMPLATE_MODIFY_FROM_LIST)
|
||||
.withParentEntityKey(entityKey)
|
||||
.create())
|
||||
|
||||
.withSelectionListener(this.pageService.getSelectionPublisher(
|
||||
pageContext,
|
||||
ActionDefinition.CLIENT_GROUP_TEMPLATE_MODIFY_FROM_LIST,
|
||||
ActionDefinition.CLIENT_GROUP_TEMPLATE_DELETE_FROM_LIST))
|
||||
|
||||
.compose(pageContext.copyOf(content));
|
||||
|
||||
actionBuilder
|
||||
|
||||
.newAction(ActionDefinition.CLIENT_GROUP_TEMPLATE_MODIFY_FROM_LIST)
|
||||
.withParentEntityKey(entityKey)
|
||||
.withSelect(
|
||||
indicatorTable::getMultiSelection,
|
||||
PageAction::applySingleSelectionAsEntityKey,
|
||||
CLIENT_GROUP_EMPTY_SELECTION_TEXT_KEY)
|
||||
.publishIf(() -> userGrantCheck.m() && clientGroupTable.hasAnyContent(), false)
|
||||
|
||||
.newAction(ActionDefinition.CLIENT_GROUP_TEMPLATE_DELETE_FROM_LIST)
|
||||
.withEntityKey(entityKey)
|
||||
.withSelect(
|
||||
indicatorTable::getMultiSelection,
|
||||
this::deleteSelectedClientGroup,
|
||||
CLIENT_GROUP_EMPTY_SELECTION_TEXT_KEY)
|
||||
.publishIf(() -> userGrantCheck.m() && clientGroupTable.hasAnyContent(), false)
|
||||
|
||||
.newAction(ActionDefinition.CLIENT_GROUP_TEMPLATE_NEW)
|
||||
.withParentEntityKey(entityKey)
|
||||
.publishIf(() -> userGrantCheck.m());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -329,6 +415,17 @@ public class ExamTemplateForm implements TemplateComposer {
|
|||
return action;
|
||||
}
|
||||
|
||||
private PageAction deleteSelectedClientGroup(final PageAction action) {
|
||||
final EntityKey entityKey = action.getEntityKey();
|
||||
final EntityKey indicatorKey = action.getSingleSelection();
|
||||
this.resourceService.getRestService()
|
||||
.getBuilder(DeleteClientGroupTemplate.class)
|
||||
.withURIVariable(API.PARAM_PARENT_MODEL_ID, entityKey.modelId)
|
||||
.withURIVariable(API.PARAM_MODEL_ID, indicatorKey.modelId)
|
||||
.call();
|
||||
return action;
|
||||
}
|
||||
|
||||
private String indicatorTypeName(final IndicatorTemplate indicator) {
|
||||
if (indicator.type == null) {
|
||||
return Constants.EMPTY_NOTE;
|
||||
|
@ -338,4 +435,13 @@ public class ExamTemplateForm implements TemplateComposer {
|
|||
.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());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -36,6 +36,7 @@ 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.Exam;
|
||||
import ch.ethz.seb.sebserver.gbl.model.exam.Exam.ExamStatus;
|
||||
import ch.ethz.seb.sebserver.gbl.model.exam.Exam.ExamType;
|
||||
|
@ -123,6 +124,7 @@ public class ResourceService {
|
|||
public static final String EXAM_TYPE_PREFIX = "sebserver.exam.type.";
|
||||
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 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.";
|
||||
|
@ -260,6 +262,19 @@ public class ResourceService {
|
|||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
public List<Tuple<String>> clientGroupTypeResources() {
|
||||
return Arrays.stream(ClientGroupType.values())
|
||||
.filter(type -> type != ClientGroupType.NONE)
|
||||
.map(type -> new Tuple3<>(
|
||||
type.name(),
|
||||
this.i18nSupport.getText(EXAM_CLIENT_GROUP_TYPE_PREFIX + type.name(), type.name()),
|
||||
Utils.formatLineBreaks(this.i18nSupport.getText(
|
||||
EXAM_CLIENT_GROUP_TYPE_PREFIX + type.name() + Constants.TOOLTIP_TEXT_KEY_SUFFIX,
|
||||
StringUtils.EMPTY))))
|
||||
.sorted(RESOURCE_COMPARATOR_TUPLE_3)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
public List<Tuple<String>> examConfigurationSelectionResources() {
|
||||
return getExamConfigurationSelection()
|
||||
.getOr(Collections.emptyList())
|
||||
|
|
|
@ -130,6 +130,7 @@ public class WidgetFactory {
|
|||
INSTITUTION("institution.png"),
|
||||
LMS_SETUP("lmssetup.png"),
|
||||
INDICATOR("indicator.png"),
|
||||
CLIENT_GROUP("indicator.png"),
|
||||
TEMPLATE("template.png"),
|
||||
DISABLE("disable.png"),
|
||||
SEND_QUIT("send-quit.png"),
|
||||
|
|
|
@ -129,7 +129,7 @@ public class ClientGroupDAOImpl implements ClientGroupDAO {
|
|||
data.type.name(),
|
||||
data.color,
|
||||
data.icon,
|
||||
data.data);
|
||||
data.getData());
|
||||
|
||||
this.clientGroupRecordMapper.insert(newRecord);
|
||||
|
||||
|
@ -151,7 +151,7 @@ public class ClientGroupDAOImpl implements ClientGroupDAO {
|
|||
data.type.name(),
|
||||
data.color,
|
||||
data.icon,
|
||||
data.data);
|
||||
data.getData());
|
||||
|
||||
this.clientGroupRecordMapper.updateByPrimaryKeySelective(newRecord);
|
||||
return this.clientGroupRecordMapper.selectByPrimaryKey(data.id);
|
||||
|
|
|
@ -18,10 +18,14 @@ 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.Exam;
|
||||
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.util.Result;
|
||||
import ch.ethz.seb.sebserver.gbl.util.Utils;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.session.ExamProctoringService;
|
||||
|
||||
public interface ExamAdminService {
|
||||
|
@ -139,4 +143,76 @@ public interface ExamAdminService {
|
|||
}
|
||||
}
|
||||
|
||||
/** Used to check client group consistency for a given ClientGroup.
|
||||
* Checks if correct entries for specific type
|
||||
*
|
||||
* 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) {
|
||||
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")));
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
case IP_V4_RANGE: {
|
||||
checkIPRange(clientGroup.getIpRangeStart(), clientGroup.getIpRangeEnd());
|
||||
break;
|
||||
}
|
||||
case CLIENT_OS: {
|
||||
checkClientOS(clientGroup.getClientOS());
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
throw new APIMessageException(APIMessage.fieldValidationError(
|
||||
new FieldError(
|
||||
Domain.CLIENT_GROUP.TYPE_NAME,
|
||||
Domain.CLIENT_GROUP.ATTR_TYPE,
|
||||
"clientGroup:type:typeInvalid")));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void checkIPRange(final String ipRangeStart, final String ipRangeEnd) {
|
||||
final long startIP = Utils.ipToLong(ipRangeStart);
|
||||
if (startIP < 0) {
|
||||
throw new APIMessageException(APIMessage.fieldValidationError(
|
||||
new FieldError(
|
||||
Domain.CLIENT_GROUP.TYPE_NAME,
|
||||
ClientGroup.ATTR_IP_RANGE_START,
|
||||
"clientGroup:ipRangeStart:invalidIP")));
|
||||
}
|
||||
final long endIP = Utils.ipToLong(ipRangeEnd);
|
||||
if (endIP < 0) {
|
||||
throw new APIMessageException(APIMessage.fieldValidationError(
|
||||
new FieldError(
|
||||
Domain.CLIENT_GROUP.TYPE_NAME,
|
||||
ClientGroup.ATTR_IP_RANGE_END,
|
||||
"clientGroup:ipRangeEnd:invalidIP")));
|
||||
}
|
||||
|
||||
if (endIP <= startIP) {
|
||||
throw new APIMessageException(APIMessage.fieldValidationError(
|
||||
new FieldError(
|
||||
Domain.CLIENT_GROUP.TYPE_NAME,
|
||||
ClientGroup.ATTR_IP_RANGE_START,
|
||||
"clientGroup:ipRangeStart:invalidIPRange")),
|
||||
APIMessage.fieldValidationError(
|
||||
new FieldError(
|
||||
Domain.CLIENT_GROUP.TYPE_NAME,
|
||||
ClientGroup.ATTR_IP_RANGE_END,
|
||||
"clientGroup:ipRangeEnd:invalidIPRange")));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static void checkClientOS(final String clientOS) {
|
||||
// TODO
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -363,7 +363,9 @@ public class ExamTemplateServiceImpl implements ExamTemplateService {
|
|||
template.type,
|
||||
template.color,
|
||||
template.icon,
|
||||
template.data))
|
||||
template.ipRangeStart,
|
||||
template.ipRangeEnd,
|
||||
template.clientOS))
|
||||
.onError(
|
||||
error -> log.error("Failed to automatically create client group from template: {} for exam: {}",
|
||||
template,
|
||||
|
|
|
@ -192,10 +192,7 @@ public class ExamTemplateController extends EntityController<ExamTemplate, ExamT
|
|||
null,
|
||||
postMap.getLong(IndicatorTemplate.ATTR_EXAM_TEMPLATE_ID),
|
||||
postMap))
|
||||
.map(indicator -> {
|
||||
ExamAdminService.checkThresholdConsistency(indicator.thresholds);
|
||||
return indicator;
|
||||
})
|
||||
.map(this::checkIndicatorConsistency)
|
||||
.flatMap(this.examTemplateDAO::createNewIndicatorTemplate)
|
||||
.flatMap(this.userActivityLogDAO::logCreate)
|
||||
.getOrThrow();
|
||||
|
@ -217,10 +214,7 @@ public class ExamTemplateController extends EntityController<ExamTemplate, ExamT
|
|||
this.checkModifyPrivilege(institutionId);
|
||||
return this.beanValidationService
|
||||
.validateBean(modifyData)
|
||||
.map(indicator -> {
|
||||
ExamAdminService.checkThresholdConsistency(indicator.thresholds);
|
||||
return indicator;
|
||||
})
|
||||
.map(this::checkIndicatorConsistency)
|
||||
.flatMap(this.examTemplateDAO::saveIndicatorTemplate)
|
||||
.flatMap(this.userActivityLogDAO::logModify)
|
||||
.getOrThrow();
|
||||
|
@ -343,6 +337,7 @@ public class ExamTemplateController extends EntityController<ExamTemplate, ExamT
|
|||
null,
|
||||
postMap.getLong(ClientGroupTemplate.ATTR_EXAM_TEMPLATE_ID),
|
||||
postMap))
|
||||
.map(this::checkClientGroupConsistency)
|
||||
.flatMap(this.examTemplateDAO::createNewClientGroupTemplate)
|
||||
.flatMap(this.userActivityLogDAO::logCreate)
|
||||
.getOrThrow();
|
||||
|
@ -364,6 +359,7 @@ public class ExamTemplateController extends EntityController<ExamTemplate, ExamT
|
|||
this.checkModifyPrivilege(institutionId);
|
||||
return this.beanValidationService
|
||||
.validateBean(modifyData)
|
||||
.map(this::checkClientGroupConsistency)
|
||||
.flatMap(this.examTemplateDAO::saveClientGroupTemplate)
|
||||
.flatMap(this.userActivityLogDAO::logModify)
|
||||
.getOrThrow();
|
||||
|
@ -498,4 +494,14 @@ public class ExamTemplateController extends EntityController<ExamTemplate, ExamT
|
|||
};
|
||||
}
|
||||
|
||||
private ClientGroupTemplate checkClientGroupConsistency(final ClientGroupTemplate clientGroupTemplate) {
|
||||
ExamAdminService.checkClientGroupConsistency(clientGroupTemplate);
|
||||
return clientGroupTemplate;
|
||||
}
|
||||
|
||||
private IndicatorTemplate checkIndicatorConsistency(final IndicatorTemplate indicatorTemplate) {
|
||||
ExamAdminService.checkThresholdConsistency(indicatorTemplate.thresholds);
|
||||
return indicatorTemplate;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -100,9 +100,12 @@ sebserver.form.validation.fieldError.exists=This name already exists. Please cho
|
|||
sebserver.form.validation.fieldError.email=Invalid mail address
|
||||
sebserver.form.validation.fieldError.serverNotAvailable=No service seems to be available within the given URL
|
||||
sebserver.form.validation.fieldError.url.invalid=Invalid URL. The given URL cannot be reached.
|
||||
sebserver.form.validation.fieldError.typeInvalid=This type is not implemented yet and cannot be used.
|
||||
sebserver.form.validation.fieldError.url.noservice=The expected service is not available within the given URL and API access.
|
||||
sebserver.form.validation.fieldError.thresholdDuplicate=There are duplicate threshold values.
|
||||
sebserver.form.validation.fieldError.thresholdEmpty=There are missing values or colors for the threshold declaration
|
||||
sebserver.form.validation.fieldError.invalidIP=Invalid IP v4. Please enter a valid IP-address (v4)
|
||||
sebserver.form.validation.fieldError.invalidIPRange=Invalid IP-address range.
|
||||
sebserver.error.unexpected=Unexpected Error
|
||||
sebserver.page.message=Information
|
||||
sebserver.dialog.confirm.title=Confirmation
|
||||
|
@ -650,6 +653,11 @@ sebserver.exam.indicator.type.description.INFO_COUNT=This indicator shows the nu
|
|||
sebserver.exam.indicator.type.description.BATTERY_STATUS=This indicator shows the percentage of the battery load level of a SEB Client.
|
||||
sebserver.exam.indicator.type.description.WLAN_STATUS=This indicator shows the percentage of the WiFi connection status of a SEB Client.
|
||||
|
||||
sebserver.exam.clientgroup.type.IP_V4_RANGE=IP v4 Range
|
||||
sebserver.exam.clientgroup.type.CLIENT_OS=SEB Client OS
|
||||
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.indicator.info.pleaseSelect=At first please select an indicator from the list
|
||||
|
||||
sebserver.exam.indicator.action.list.new=Add Indicator
|
||||
|
@ -677,6 +685,26 @@ sebserver.exam.indicator.form.thresholds=Thresholds
|
|||
sebserver.exam.indicator.form.thresholds.tooltip=A list of value / color pairs that defines the thresholds of the indicator.<br/><br/>On the exam monitoring view a cell of the indicator is displayed in the specified color when the defined threshold value is reached
|
||||
sebserver.exam.indicator.thresholds.select.color=Please select a color
|
||||
|
||||
sebserver.exam.clientgroup.form.title=Client Group
|
||||
sebserver.exam.clientgroup.form.title.subtitle=
|
||||
sebserver.exam.clientgroup.form.title.new=Add Client Group
|
||||
sebserver.exam.clientgroup.form.exam=Exam
|
||||
sebserver.exam.clientgroup.form.exam.tooltip=The exam this client group belongs to
|
||||
sebserver.exam.clientgroup.form.name=Name
|
||||
sebserver.exam.clientgroup.form.name.tooltip=The name of the client group.<br/><br/>This name is also displayed in the column cell of in the exam monitoring
|
||||
sebserver.exam.clientgroup.form.type=Type
|
||||
sebserver.exam.clientgroup.form.type.tooltip=The type of the client group<br/><br/>There are only a set of defined client group types to choose from.<br/>Choose one to see a detailed description for each client group type below.
|
||||
sebserver.exam.clientgroup.form.description=Type Description
|
||||
sebserver.exam.clientgroup.form.description.tooltip=A detailed description of the selected client group type
|
||||
sebserver.exam.clientgroup.form.color=Color
|
||||
sebserver.exam.clientgroup.form.color.tooltip=The color that is displayed on the exam monitoring for this client group
|
||||
sebserver.exam.clientgroup.form.color.action=Please select a color
|
||||
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.indicator.thresholds.list.title=Thresholds
|
||||
sebserver.exam.indicator.thresholds.list.value=Value
|
||||
sebserver.exam.indicator.thresholds.list.value.tooltip=The threshold value
|
||||
|
@ -1748,7 +1776,7 @@ sebserver.examtemplate.form.action.edit=Edit Exam Template
|
|||
sebserver.examtemplate.form.action.delete=Delete Exam Template
|
||||
sebserver.examtemplate.form.action.delete.confirm=Are you sure to delete this exam template?<br/><br/>Please note that a reference from a exam that uses this template will also be deleted but the exam itself is not affected.
|
||||
|
||||
sebserver.examtemplate.indicator.list.actions=
|
||||
sebserver.examtemplate.indicator.list.actions=
|
||||
sebserver.examtemplate.indicator.list.title=Indicators
|
||||
sebserver.examtemplate.indicator.list.title.tooltip=A list of indicators that will automatically be created when importing a exam with this template
|
||||
sebserver.examtemplate.indicator.list.column.type=Type
|
||||
|
@ -1760,12 +1788,28 @@ sebserver.examtemplate.indicator.list.column.thresholds.tooltip=The thresholds o
|
|||
sebserver.examtemplate.indicator.list.empty=There is currently no indicator defined for this exam template. Please create a new one
|
||||
sebserver.examtemplate.indicator.list.pleaseSelect=At first please select an indicator from the list
|
||||
|
||||
sebserver.examtemplate.clientgroup.list.actions=
|
||||
sebserver.examtemplate.clientgroup.list.title=Client Groups
|
||||
sebserver.examtemplate.clientgroup.list.title.tooltip=A list of client groups that will automatically be created when importing a exam with this template
|
||||
sebserver.examtemplate.clientgroup.list.column.type=Type
|
||||
sebserver.examtemplate.clientgroup.list.column.type.tooltip=The type of the client group
|
||||
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.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.indicator.action.save=Save Indicator
|
||||
sebserver.examtemplate.indicator.list.actions=
|
||||
sebserver.examtemplate.indicator.action.list.new=Add Indicator
|
||||
sebserver.examtemplate.indicator.action.list.modify=Edit Indicator
|
||||
sebserver.examtemplate.indicator.action.list.delete=Delete Indicator
|
||||
|
||||
sebserver.examtemplate.clientgroup.action.save=Save Client Group
|
||||
sebserver.examtemplate.clientgroup.action.list.new=Add Client Group
|
||||
sebserver.examtemplate.clientgroup.action.list.modify=Edit Client Group
|
||||
sebserver.examtemplate.clientgroup.action.list.delete=Delete Client Group
|
||||
|
||||
sebserver.examtemplate.proctoring.actions.open=Proctoring Settings
|
||||
|
||||
################################
|
||||
|
|
|
@ -1086,7 +1086,9 @@ public class UseCasesIntegrationTest extends GuiIntegrationTest {
|
|||
newClientGroup.type,
|
||||
"000002",
|
||||
newClientGroup.icon,
|
||||
newClientGroup.data);
|
||||
newClientGroup.ipRangeStart,
|
||||
newClientGroup.ipRangeEnd,
|
||||
newClientGroup.clientOS);
|
||||
|
||||
final Result<ClientGroup> savedClientGroupResult = restService
|
||||
.getBuilder(SaveClientGroup.class)
|
||||
|
|
Loading…
Reference in a new issue