SEBSERV-26 SEB client event logs
This commit is contained in:
parent
ccced32e1d
commit
47a0f37d9d
71 changed files with 1565 additions and 402 deletions
|
@ -131,6 +131,11 @@ public final class API {
|
|||
public static final String EXAM_MONITORING_SEB_CONNECTION_TOKEN_PATH_SEGMENT =
|
||||
"/{" + EXAM_API_SEB_CONNECTION_TOKEN + "}";
|
||||
|
||||
public static final String SEB_CLIENT_CONNECTION_ENDPOINT = "/seb-client-connection";
|
||||
|
||||
public static final String SEB_CLIENT_EVENT_ENDPOINT = "/seb-client-event";
|
||||
public static final String SEB_CLIENT_EVENT_SEARCH_PATH_SEGMENT = "/search";
|
||||
public static final String SEB_CLIENT_EVENT_EXTENDED_PAGE_ENDPOINT = SEB_CLIENT_EVENT_ENDPOINT
|
||||
+ SEB_CLIENT_EVENT_SEARCH_PATH_SEGMENT;
|
||||
|
||||
}
|
||||
|
|
|
@ -50,9 +50,19 @@ public interface Entity extends ModelIdAware {
|
|||
* @return EntityName instance created form given Entity */
|
||||
default EntityName toName() {
|
||||
return new EntityName(
|
||||
this.entityType(),
|
||||
this.getModelId(),
|
||||
this.entityType(),
|
||||
this.getName());
|
||||
}
|
||||
|
||||
/** This can be overwritten if an entity contains security sensitive data
|
||||
* Returns a representation of the entity that has no security sensitive data
|
||||
* and an be print out to user logs or error messages
|
||||
*
|
||||
* @return representation of the entity that has no security sensitive data */
|
||||
|
||||
default Entity printSecureCopy() {
|
||||
return this;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -13,12 +13,14 @@ import java.io.Serializable;
|
|||
import javax.validation.constraints.NotNull;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
import ch.ethz.seb.sebserver.gbl.api.EntityType;
|
||||
|
||||
/** A EntityKey uniquely identifies a domain entity within the SEB Server's domain model.
|
||||
* A EntityKey consists of the model identifier of a domain entity and the type of the entity. */
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
public class EntityKey implements ModelIdAware, Serializable {
|
||||
|
||||
private static final long serialVersionUID = -2368065921846821061L;
|
||||
|
|
|
@ -17,33 +17,30 @@ import ch.ethz.seb.sebserver.gbl.api.API;
|
|||
import ch.ethz.seb.sebserver.gbl.api.EntityType;
|
||||
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
public class EntityName implements ModelIdAware {
|
||||
public class EntityName extends EntityKey {
|
||||
|
||||
private static final long serialVersionUID = 9577137222563155L;
|
||||
|
||||
@JsonProperty(value = API.PARAM_ENTITY_TYPE, required = true)
|
||||
public final EntityType entityType;
|
||||
@JsonProperty(value = API.PARAM_MODEL_ID, required = true)
|
||||
public final String modelId;
|
||||
@JsonProperty(value = "name", required = true)
|
||||
public final String name;
|
||||
|
||||
@JsonCreator
|
||||
public EntityName(
|
||||
@JsonProperty(value = API.PARAM_ENTITY_TYPE, required = true) final EntityType entityType,
|
||||
@JsonProperty(value = API.PARAM_MODEL_ID, required = true) final String id,
|
||||
@JsonProperty(value = API.PARAM_ENTITY_TYPE, required = true) final EntityType entityType,
|
||||
@JsonProperty(value = "name", required = true) final String name) {
|
||||
|
||||
this.entityType = entityType;
|
||||
this.modelId = id;
|
||||
super(id, entityType);
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public EntityName(final EntityKey entityKey, final String name) {
|
||||
|
||||
this.entityType = entityKey.entityType;
|
||||
this.modelId = entityKey.modelId;
|
||||
super(entityKey.modelId, entityKey.entityType);
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public EntityType getEntityType() {
|
||||
return this.entityType;
|
||||
}
|
||||
|
@ -63,40 +60,6 @@ public class EntityName implements ModelIdAware {
|
|||
return new EntityKey(getModelId(), getEntityType());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
result = prime * result + ((this.entityType == null) ? 0 : this.entityType.hashCode());
|
||||
result = prime * result + ((this.modelId == null) ? 0 : this.modelId.hashCode());
|
||||
result = prime * result + ((this.name == null) ? 0 : this.name.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 EntityName other = (EntityName) obj;
|
||||
if (this.entityType != other.entityType)
|
||||
return false;
|
||||
if (this.modelId == null) {
|
||||
if (other.modelId != null)
|
||||
return false;
|
||||
} else if (!this.modelId.equals(other.modelId))
|
||||
return false;
|
||||
if (this.name == null) {
|
||||
if (other.name != null)
|
||||
return false;
|
||||
} else if (!this.name.equals(other.name))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
final StringBuilder builder = new StringBuilder();
|
||||
|
|
|
@ -21,6 +21,7 @@ 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.Activatable;
|
||||
|
@ -30,6 +31,23 @@ import ch.ethz.seb.sebserver.gbl.model.GrantEntity;
|
|||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
public final class Exam implements GrantEntity, Activatable {
|
||||
|
||||
public static final Exam EMPTY_EXAM = new Exam(
|
||||
-1L,
|
||||
-1L,
|
||||
-1L,
|
||||
Constants.EMPTY_NOTE,
|
||||
Constants.EMPTY_NOTE,
|
||||
Constants.EMPTY_NOTE,
|
||||
null,
|
||||
null,
|
||||
Constants.EMPTY_NOTE,
|
||||
ExamType.UNDEFINED,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
false);
|
||||
|
||||
// TODO make this a configurable exam attribute
|
||||
/** The number of hours to add at the start- and end-time of the exam
|
||||
* To add a expanded time-frame in which the exam state is running on SEB-Server side */
|
||||
|
|
|
@ -15,11 +15,13 @@ 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;
|
||||
import ch.ethz.seb.sebserver.gbl.model.Domain.CONFIGURATION_NODE;
|
||||
import ch.ethz.seb.sebserver.gbl.model.Domain.EXAM_CONFIGURATION_MAP;
|
||||
import ch.ethz.seb.sebserver.gbl.model.Entity;
|
||||
import ch.ethz.seb.sebserver.gbl.model.GrantEntity;
|
||||
import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationNode.ConfigurationStatus;
|
||||
|
||||
|
@ -143,10 +145,12 @@ public final class ExamConfigurationMap implements GrantEntity {
|
|||
return this.userNames;
|
||||
}
|
||||
|
||||
@JsonIgnore
|
||||
public CharSequence getEncryptSecret() {
|
||||
return this.encryptSecret;
|
||||
}
|
||||
|
||||
@JsonIgnore
|
||||
public CharSequence getConfirmEncryptSecret() {
|
||||
return this.confirmEncryptSecret;
|
||||
}
|
||||
|
@ -168,6 +172,21 @@ public final class ExamConfigurationMap implements GrantEntity {
|
|||
return this.configStatus;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Entity printSecureCopy() {
|
||||
return new ExamConfigurationMap(
|
||||
this.id,
|
||||
this.institutionId,
|
||||
this.examId,
|
||||
this.configurationNodeId,
|
||||
this.userNames,
|
||||
Constants.EMPTY_NOTE,
|
||||
Constants.EMPTY_NOTE,
|
||||
this.configName,
|
||||
this.configDescription,
|
||||
this.configStatus);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
final StringBuilder builder = new StringBuilder();
|
||||
|
|
|
@ -18,11 +18,13 @@ 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.Activatable;
|
||||
import ch.ethz.seb.sebserver.gbl.model.Domain.INSTITUTION;
|
||||
import ch.ethz.seb.sebserver.gbl.model.Domain.LMS_SETUP;
|
||||
import ch.ethz.seb.sebserver.gbl.model.Entity;
|
||||
import ch.ethz.seb.sebserver.gbl.model.EntityName;
|
||||
import ch.ethz.seb.sebserver.gbl.model.GrantEntity;
|
||||
|
||||
|
@ -161,6 +163,20 @@ public final class LmsSetup implements GrantEntity, Activatable {
|
|||
return this.active;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Entity printSecureCopy() {
|
||||
return new LmsSetup(
|
||||
this.id,
|
||||
this.institutionId,
|
||||
this.name,
|
||||
this.lmsType,
|
||||
this.lmsAuthName,
|
||||
Constants.EMPTY_NOTE,
|
||||
this.lmsApiUrl,
|
||||
Constants.EMPTY_NOTE,
|
||||
this.active);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
final StringBuilder builder = new StringBuilder();
|
||||
|
@ -174,8 +190,6 @@ public final class LmsSetup implements GrantEntity, Activatable {
|
|||
builder.append(this.lmsType);
|
||||
builder.append(", lmsAuthName=");
|
||||
builder.append(this.lmsAuthName);
|
||||
builder.append(", lmsAuthSecret=");
|
||||
builder.append(this.lmsAuthSecret);
|
||||
builder.append(", lmsApiUrl=");
|
||||
builder.append(this.lmsApiUrl);
|
||||
builder.append(", lmsRestApiToken=");
|
||||
|
@ -188,8 +202,8 @@ public final class LmsSetup implements GrantEntity, Activatable {
|
|||
|
||||
public static EntityName toName(final LmsSetup lmsSetup) {
|
||||
return new EntityName(
|
||||
EntityType.LMS_SETUP,
|
||||
String.valueOf(lmsSetup.id),
|
||||
EntityType.LMS_SETUP,
|
||||
lmsSetup.name);
|
||||
}
|
||||
|
||||
|
|
|
@ -15,14 +15,17 @@ import org.hibernate.validator.constraints.URL;
|
|||
import org.joda.time.DateTime;
|
||||
import org.joda.time.DateTimeZone;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
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.Activatable;
|
||||
import ch.ethz.seb.sebserver.gbl.model.Domain;
|
||||
import ch.ethz.seb.sebserver.gbl.model.Domain.SEB_CLIENT_CONFIGURATION;
|
||||
import ch.ethz.seb.sebserver.gbl.model.Entity;
|
||||
import ch.ethz.seb.sebserver.gbl.model.GrantEntity;
|
||||
|
||||
public final class SebClientConfig implements GrantEntity, Activatable {
|
||||
|
@ -60,6 +63,7 @@ public final class SebClientConfig implements GrantEntity, Activatable {
|
|||
@JsonProperty(SEB_CLIENT_CONFIGURATION.ATTR_ACTIVE)
|
||||
public final Boolean active;
|
||||
|
||||
@JsonCreator
|
||||
public SebClientConfig(
|
||||
@JsonProperty(SEB_CLIENT_CONFIGURATION.ATTR_ID) final Long id,
|
||||
@JsonProperty(SEB_CLIENT_CONFIGURATION.ATTR_INSTITUTION_ID) final Long institutionId,
|
||||
|
@ -130,10 +134,12 @@ public final class SebClientConfig implements GrantEntity, Activatable {
|
|||
return this.date;
|
||||
}
|
||||
|
||||
@JsonIgnore
|
||||
public CharSequence getEncryptSecret() {
|
||||
return this.encryptSecret;
|
||||
}
|
||||
|
||||
@JsonIgnore
|
||||
public CharSequence getConfirmEncryptSecret() {
|
||||
return this.confirmEncryptSecret;
|
||||
}
|
||||
|
@ -147,6 +153,19 @@ public final class SebClientConfig implements GrantEntity, Activatable {
|
|||
return this.active;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Entity printSecureCopy() {
|
||||
return new SebClientConfig(
|
||||
this.id,
|
||||
this.institutionId,
|
||||
this.name,
|
||||
this.fallbackStartURL,
|
||||
this.date,
|
||||
Constants.EMPTY_NOTE,
|
||||
Constants.EMPTY_NOTE,
|
||||
this.active);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
final StringBuilder builder = new StringBuilder();
|
||||
|
@ -160,10 +179,6 @@ public final class SebClientConfig implements GrantEntity, Activatable {
|
|||
builder.append(this.fallbackStartURL);
|
||||
builder.append(", date=");
|
||||
builder.append(this.date);
|
||||
builder.append(", encryptSecret=");
|
||||
builder.append(this.encryptSecret);
|
||||
builder.append(", confirmEncryptSecret=");
|
||||
builder.append(this.confirmEncryptSecret);
|
||||
builder.append(", active=");
|
||||
builder.append(this.active);
|
||||
builder.append("]");
|
||||
|
|
|
@ -19,9 +19,6 @@ import ch.ethz.seb.sebserver.gbl.model.GrantEntity;
|
|||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
public final class ClientConnection implements GrantEntity {
|
||||
|
||||
public static final String FILTER_ATTR_EXAM_ID = Domain.CLIENT_CONNECTION.ATTR_EXAM_ID;
|
||||
public static final String FILTER_ATTR_STATUS = Domain.CLIENT_CONNECTION.ATTR_STATUS;
|
||||
|
||||
public enum ConnectionStatus {
|
||||
UNDEFINED,
|
||||
CONNECTION_REQUESTED,
|
||||
|
@ -32,6 +29,20 @@ public final class ClientConnection implements GrantEntity {
|
|||
RELEASED
|
||||
}
|
||||
|
||||
public static final ClientConnection EMPTY_CLIENT_CONNECTION = new ClientConnection(
|
||||
-1L,
|
||||
-1L,
|
||||
-1L,
|
||||
ConnectionStatus.UNDEFINED,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null);
|
||||
|
||||
public static final String FILTER_ATTR_EXAM_ID = Domain.CLIENT_CONNECTION.ATTR_EXAM_ID;
|
||||
public static final String FILTER_ATTR_STATUS = Domain.CLIENT_CONNECTION.ATTR_STATUS;
|
||||
public static final String FILTER_ATTR_SESSION_ID = Domain.CLIENT_CONNECTION.ATTR_EXAM_USER_SESSION_IDENTIFER;
|
||||
|
||||
@JsonProperty(Domain.CLIENT_CONNECTION.ATTR_ID)
|
||||
public final Long id;
|
||||
|
||||
|
|
|
@ -21,7 +21,7 @@ import ch.ethz.seb.sebserver.gbl.util.Utils;
|
|||
import ch.ethz.seb.sebserver.webservice.datalayer.batis.model.ClientEventRecord;
|
||||
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
public final class ClientEvent implements Entity, IndicatorValueHolder {
|
||||
public class ClientEvent implements Entity, IndicatorValueHolder {
|
||||
|
||||
/** Adapt SEB API to SEB_SEB_Server API -> timestamp == clientTime */
|
||||
public static final String ATTR_TIMESTAMP = "timestamp";
|
||||
|
|
|
@ -0,0 +1,86 @@
|
|||
/*
|
||||
* Copyright (c) 2019 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.session;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
import ch.ethz.seb.sebserver.gbl.model.Domain;
|
||||
|
||||
public final class ExtendedClientEvent extends ClientEvent {
|
||||
|
||||
public static final String FILTER_ATTRIBUTE_EXAM = Domain.CLIENT_CONNECTION.ATTR_EXAM_ID;
|
||||
|
||||
@JsonProperty(Domain.CLIENT_CONNECTION.ATTR_INSTITUTION_ID)
|
||||
public final Long institutionId;
|
||||
|
||||
@JsonProperty(Domain.CLIENT_CONNECTION.ATTR_EXAM_ID)
|
||||
public final Long examId;
|
||||
|
||||
@JsonProperty(Domain.CLIENT_CONNECTION.ATTR_EXAM_USER_SESSION_IDENTIFER)
|
||||
public final String userSessionId;
|
||||
|
||||
@JsonCreator
|
||||
public ExtendedClientEvent(
|
||||
@JsonProperty(Domain.CLIENT_CONNECTION.ATTR_INSTITUTION_ID) final Long institutionId,
|
||||
@JsonProperty(Domain.CLIENT_CONNECTION.ATTR_EXAM_ID) final Long examId,
|
||||
@JsonProperty(Domain.CLIENT_CONNECTION.ATTR_EXAM_USER_SESSION_IDENTIFER) final String userSessionId,
|
||||
@JsonProperty(Domain.CLIENT_EVENT.ATTR_ID) final Long id,
|
||||
@JsonProperty(Domain.CLIENT_EVENT.ATTR_CONNECTION_ID) final Long connectionId,
|
||||
@JsonProperty(Domain.CLIENT_EVENT.ATTR_TYPE) final EventType eventType,
|
||||
@JsonProperty(ATTR_TIMESTAMP) final Long clientTime,
|
||||
@JsonProperty(Domain.CLIENT_EVENT.ATTR_SERVER_TIME) final Long serverTime,
|
||||
@JsonProperty(Domain.CLIENT_EVENT.ATTR_NUMERIC_VALUE) final Double numValue,
|
||||
@JsonProperty(Domain.CLIENT_EVENT.ATTR_TEXT) final String text) {
|
||||
|
||||
super(id, connectionId, eventType, clientTime, serverTime, numValue, text);
|
||||
|
||||
this.institutionId = institutionId;
|
||||
this.examId = examId;
|
||||
this.userSessionId = userSessionId;
|
||||
}
|
||||
|
||||
public Long getInstitutionId() {
|
||||
return this.institutionId;
|
||||
}
|
||||
|
||||
public Long getExamId() {
|
||||
return this.examId;
|
||||
}
|
||||
|
||||
public String getUserSessionId() {
|
||||
return this.userSessionId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
final StringBuilder builder = new StringBuilder();
|
||||
builder.append("ExtendedClientEvent [institutionId=");
|
||||
builder.append(this.institutionId);
|
||||
builder.append(", examId=");
|
||||
builder.append(this.examId);
|
||||
builder.append(", userSessionId=");
|
||||
builder.append(this.userSessionId);
|
||||
builder.append(", connectionId=");
|
||||
builder.append(this.connectionId);
|
||||
builder.append(", eventType=");
|
||||
builder.append(this.eventType);
|
||||
builder.append(", clientTime=");
|
||||
builder.append(this.clientTime);
|
||||
builder.append(", serverTime=");
|
||||
builder.append(this.serverTime);
|
||||
builder.append(", numValue=");
|
||||
builder.append(this.numValue);
|
||||
builder.append(", text=");
|
||||
builder.append(this.text);
|
||||
builder.append("]");
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
}
|
|
@ -14,6 +14,7 @@ import javax.validation.constraints.Size;
|
|||
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||
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.model.Domain.USER;
|
||||
import ch.ethz.seb.sebserver.gbl.model.Entity;
|
||||
|
@ -85,4 +86,13 @@ public class PasswordChange implements Entity {
|
|||
return "PasswordChange";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Entity printSecureCopy() {
|
||||
return new PasswordChange(
|
||||
this.userId,
|
||||
Constants.EMPTY_NOTE,
|
||||
Constants.EMPTY_NOTE,
|
||||
Constants.EMPTY_NOTE);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -405,5 +405,4 @@ public final class Utils {
|
|||
org.springframework.http.MediaType.APPLICATION_JSON_UTF8_VALUE);
|
||||
return headers;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -303,7 +303,7 @@ public class ExamForm implements TemplateComposer {
|
|||
.newAction(ActionDefinition.EXAM_CONFIGURATION_MODIFY_FROM_LIST)
|
||||
.create())
|
||||
|
||||
.compose(content);
|
||||
.compose(pageContext.copyOf(content));
|
||||
|
||||
final EntityKey configMapKey = (configurationTable.hasAnyContent())
|
||||
? configurationTable.getFirstRowData().getEntityKey()
|
||||
|
@ -369,7 +369,7 @@ public class ExamForm implements TemplateComposer {
|
|||
.withParentEntityKey(entityKey)
|
||||
.create())
|
||||
|
||||
.compose(content);
|
||||
.compose(pageContext.copyOf(content));
|
||||
|
||||
actionBuilder
|
||||
|
||||
|
|
|
@ -144,13 +144,13 @@ public class ExamList implements TemplateComposer {
|
|||
.withColumn(new ColumnDefinition<>(
|
||||
Domain.EXAM.ATTR_TYPE,
|
||||
COLUMN_TITLE_TYPE_KEY,
|
||||
this.resourceService::examTypeName)
|
||||
this.resourceService::localizedExamTypeName)
|
||||
.withFilter(this.typeFilter)
|
||||
.sortable())
|
||||
.withDefaultAction(actionBuilder
|
||||
.newAction(ActionDefinition.EXAM_VIEW_FROM_LIST)
|
||||
.create())
|
||||
.compose(content);
|
||||
.compose(pageContext.copyOf(content));
|
||||
|
||||
// propagate content actions to action-pane
|
||||
final GrantCheck userGrant = currentUser.grantCheck(EntityType.EXAM);
|
||||
|
|
|
@ -100,7 +100,7 @@ public class InstitutionList implements TemplateComposer {
|
|||
.withDefaultAction(pageActionBuilder
|
||||
.newAction(ActionDefinition.INSTITUTION_VIEW_FROM_LIST)
|
||||
.create())
|
||||
.compose(content);
|
||||
.compose(pageContext.copyOf(content));
|
||||
|
||||
// propagate content actions to action-pane
|
||||
final GrantCheck instGrant = this.currentUser.grantCheck(EntityType.INSTITUTION);
|
||||
|
|
|
@ -147,7 +147,7 @@ public class LmsSetupList implements TemplateComposer {
|
|||
.withDefaultAction(actionBuilder
|
||||
.newAction(ActionDefinition.LMS_SETUP_VIEW_FROM_LIST)
|
||||
.create())
|
||||
.compose(content);
|
||||
.compose(pageContext.copyOf(content));
|
||||
|
||||
// propagate content actions to action-pane
|
||||
final GrantCheck userGrant = currentUser.grantCheck(EntityType.LMS_SETUP);
|
||||
|
|
|
@ -169,10 +169,10 @@ public class MonitoringClientConnection implements TemplateComposer {
|
|||
.withRestCallAdapter(restCallBuilder -> restCallBuilder.withQueryParam(
|
||||
ClientEvent.FILTER_ATTR_CONECTION_ID,
|
||||
entityKey.modelId))
|
||||
.withColumn(new ColumnDefinition<>(
|
||||
.withColumn(new ColumnDefinition<ClientEvent>(
|
||||
Domain.CLIENT_EVENT.ATTR_TYPE,
|
||||
LIST_COLUMN_TYPE_KEY,
|
||||
this::getEventTypeName)
|
||||
this.resourceService::getEventTypeName)
|
||||
.withFilter(this.typeFilter)
|
||||
.sortable()
|
||||
.widthProportion(2))
|
||||
|
@ -202,7 +202,7 @@ public class MonitoringClientConnection implements TemplateComposer {
|
|||
.sortable()
|
||||
.widthProportion(1))
|
||||
|
||||
.compose(content);
|
||||
.compose(pageContext.copyOf(content));
|
||||
|
||||
this.pageService
|
||||
.pageActionBuilder(
|
||||
|
@ -216,10 +216,6 @@ public class MonitoringClientConnection implements TemplateComposer {
|
|||
|
||||
}
|
||||
|
||||
public String getEventTypeName(final ClientEvent clientEvent) {
|
||||
return this.resourceService.getEventTypeName(clientEvent.eventType);
|
||||
}
|
||||
|
||||
private final String getClientTime(final ClientEvent event) {
|
||||
if (event == null || event.getClientTime() == null) {
|
||||
return Constants.EMPTY_NOTE;
|
||||
|
|
|
@ -151,10 +151,6 @@ public class MonitoringRunningExam implements TemplateComposer {
|
|||
|
||||
return pageAction;
|
||||
})
|
||||
// .withSelect(
|
||||
// () -> clientTable.getSelection(),
|
||||
// PageAction::applySingleSelection,
|
||||
// EMPTY_SELECTION_TEXT_KEY)
|
||||
.publishIf(() -> currentUser.get().hasRole(UserRole.EXAM_SUPPORTER));
|
||||
}
|
||||
|
||||
|
|
|
@ -104,7 +104,7 @@ public class MonitoringRunningExamList implements TemplateComposer {
|
|||
.withColumn(new ColumnDefinition<>(
|
||||
Domain.EXAM.ATTR_TYPE,
|
||||
COLUMN_TITLE_TYPE_KEY,
|
||||
this.resourceService::examTypeName)
|
||||
this.resourceService::localizedExamTypeName)
|
||||
.withFilter(this.typeFilter)
|
||||
.sortable())
|
||||
.withColumn(new ColumnDefinition<>(
|
||||
|
@ -124,7 +124,7 @@ public class MonitoringRunningExamList implements TemplateComposer {
|
|||
.withDefaultAction(actionBuilder
|
||||
.newAction(ActionDefinition.MONITOR_EXAM_FROM_LIST)
|
||||
.create())
|
||||
.compose(content);
|
||||
.compose(pageContext.copyOf(content));
|
||||
|
||||
actionBuilder
|
||||
|
||||
|
|
|
@ -157,7 +157,7 @@ public class QuizDiscoveryList implements TemplateComposer {
|
|||
.withExec(action -> this.showDetails(action, t.getSelectedROWData()))
|
||||
.noEventPropagation()
|
||||
.create())
|
||||
.compose(content);
|
||||
.compose(pageContext.copyOf(content));
|
||||
|
||||
// propagate content actions to action-pane
|
||||
final GrantCheck lmsSetupGrant = currentUser.grantCheck(EntityType.LMS_SETUP);
|
||||
|
@ -221,7 +221,11 @@ public class QuizDiscoveryList implements TemplateComposer {
|
|||
}
|
||||
|
||||
private void createDetailsForm(final QuizData quizData, final PageContext pc) {
|
||||
this.pageService.formBuilder(pc, 3)
|
||||
|
||||
final Composite parent = pc.getParent();
|
||||
final Composite grid = this.widgetFactory.createPopupScrollComposite(parent);
|
||||
|
||||
this.pageService.formBuilder(pc.copyOf(grid), 3)
|
||||
.withEmptyCellSeparation(false)
|
||||
.readonly(true)
|
||||
.addField(FormBuilder.singleSelection(
|
||||
|
@ -251,7 +255,6 @@ public class QuizDiscoveryList implements TemplateComposer {
|
|||
QUIZ_DETAILS_URL_TEXT_KEY,
|
||||
quizData.startURL))
|
||||
.build();
|
||||
this.widgetFactory.labelSeparator(pc.getParent());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -151,7 +151,7 @@ public class SebClientConfigList implements TemplateComposer {
|
|||
.withDefaultAction(pageActionBuilder
|
||||
.newAction(ActionDefinition.SEB_CLIENT_CONFIG_VIEW_FROM_LIST)
|
||||
.create())
|
||||
.compose(content);
|
||||
.compose(pageContext.copyOf(content));
|
||||
|
||||
final GrantCheck clientConfigGrant = this.currentUser.grantCheck(EntityType.SEB_CLIENT_CONFIGURATION);
|
||||
|
||||
|
|
|
@ -8,26 +8,374 @@
|
|||
|
||||
package ch.ethz.seb.sebserver.gui.content;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.eclipse.swt.widgets.Composite;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
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.exam.Exam;
|
||||
import ch.ethz.seb.sebserver.gbl.model.exam.QuizData;
|
||||
import ch.ethz.seb.sebserver.gbl.model.session.ClientConnection;
|
||||
import ch.ethz.seb.sebserver.gbl.model.session.ClientEvent;
|
||||
import ch.ethz.seb.sebserver.gbl.model.session.ExtendedClientEvent;
|
||||
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.FormBuilder;
|
||||
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.PageService.PageActionBuilder;
|
||||
import ch.ethz.seb.sebserver.gui.service.page.TemplateComposer;
|
||||
import ch.ethz.seb.sebserver.gui.service.page.impl.ModalInputDialog;
|
||||
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.GetExam;
|
||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.logs.GetExtendedClientEventPage;
|
||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.session.GetClientConnection;
|
||||
import ch.ethz.seb.sebserver.gui.table.ColumnDefinition;
|
||||
import ch.ethz.seb.sebserver.gui.table.ColumnDefinition.TableFilterAttribute;
|
||||
import ch.ethz.seb.sebserver.gui.table.EntityTable;
|
||||
import ch.ethz.seb.sebserver.gui.table.TableFilter.CriteriaType;
|
||||
import ch.ethz.seb.sebserver.gui.widget.WidgetFactory;
|
||||
import ch.ethz.seb.sebserver.gui.widget.WidgetFactory.CustomVariant;
|
||||
|
||||
@Lazy
|
||||
@Component
|
||||
@GuiProfile
|
||||
public class SebClientLogs implements TemplateComposer {
|
||||
|
||||
public SebClientLogs() {
|
||||
// TODO Auto-generated constructor stub
|
||||
private static final Logger log = LoggerFactory.getLogger(SebClientLogs.class);
|
||||
|
||||
private static final LocTextKey DETAILS_TITLE_TEXT_KEY =
|
||||
new LocTextKey("sebserver.seblogs.details.title");
|
||||
private static final LocTextKey TITLE_TEXT_KEY =
|
||||
new LocTextKey("sebserver.seblogs.list.title");
|
||||
private static final LocTextKey EMPTY_TEXT_KEY =
|
||||
new LocTextKey("sebserver.seblogs.list.empty");
|
||||
|
||||
private static final LocTextKey EXAM_TEXT_KEY =
|
||||
new LocTextKey("sebserver.seblogs.list.column.exam");
|
||||
private static final LocTextKey CLIENT_SESSION_TEXT_KEY =
|
||||
new LocTextKey("sebserver.seblogs.list.column.client-session");
|
||||
private static final LocTextKey TYPE_TEXT_KEY =
|
||||
new LocTextKey("sebserver.seblogs.list.column.type");
|
||||
private static final LocTextKey TIME_TEXT_KEY =
|
||||
new LocTextKey("sebserver.seblogs.list.column.time");
|
||||
private static final LocTextKey VALUE_TEXT_KEY =
|
||||
new LocTextKey("sebserver.seblogs.list.column.value");
|
||||
|
||||
private static final LocTextKey DETAILS_EVENT_TILE_TEXT_KEY =
|
||||
new LocTextKey("sebserver.seblogs.details.event.title");
|
||||
private static final LocTextKey DETAILS_CONNECTION_TILE_TEXT_KEY =
|
||||
new LocTextKey("sebserver.seblogs.details.connection.title");
|
||||
private static final LocTextKey DETAILS_EXAM_TILE_TEXT_KEY =
|
||||
new LocTextKey("sebserver.seblogs.details.exam.title");
|
||||
|
||||
private static final LocTextKey FORM_TYPE_TEXT_KEY =
|
||||
new LocTextKey("sebserver.seblogs.form.column.type");
|
||||
private static final LocTextKey FORM_SERVERTIME_TEXT_KEY =
|
||||
new LocTextKey("sebserver.seblogs.form.column.server-time");
|
||||
private static final LocTextKey FORM_CLIENTTIME_TEXT_KEY =
|
||||
new LocTextKey("sebserver.seblogs.form.column.client-time");
|
||||
private static final LocTextKey FORM_VALUE_TEXT_KEY =
|
||||
new LocTextKey("sebserver.seblogs.form.column.value");
|
||||
private static final LocTextKey FORM_MESSAGE_TEXT_KEY =
|
||||
new LocTextKey("sebserver.seblogs.form.column.message");
|
||||
|
||||
private static final LocTextKey FORM_SESSION_ID_TEXT_KEY =
|
||||
new LocTextKey("sebserver.seblogs.form.column.connection.session-id");
|
||||
private static final LocTextKey FORM_ADDRESS_TEXT_KEY =
|
||||
new LocTextKey("sebserver.seblogs.form.column.connection.address");
|
||||
private static final LocTextKey FORM_TOKEN_TEXT_KEY =
|
||||
new LocTextKey("sebserver.seblogs.form.column.connection.token");
|
||||
private static final LocTextKey FORM_STATUS_TEXT_KEY =
|
||||
new LocTextKey("sebserver.seblogs.form.column.connection.status");
|
||||
|
||||
private static final LocTextKey FORM_EXAM_NAME_TEXT_KEY =
|
||||
new LocTextKey("sebserver.seblogs.form.column.exam.name");
|
||||
private static final LocTextKey FORM_DESC_TEXT_KEY =
|
||||
new LocTextKey("sebserver.seblogs.form.column.exam.description");
|
||||
private static final LocTextKey FORM_EXAM_TYPE_TEXT_KEY =
|
||||
new LocTextKey("sebserver.seblogs.form.column.exam.type");
|
||||
private static final LocTextKey FORM_START_TIME_TEXT_KEY =
|
||||
new LocTextKey("sebserver.seblogs.form.column.exam.startTime");
|
||||
private static final LocTextKey FORM_END_TIME_TEXT_KEY =
|
||||
new LocTextKey("sebserver.seblogs.form.column.exam.endTime");
|
||||
|
||||
private final static LocTextKey EMPTY_SELECTION_TEXT =
|
||||
new LocTextKey("sebserver.seblogs.info.pleaseSelect");
|
||||
|
||||
private final TableFilterAttribute examFilter;
|
||||
private final TableFilterAttribute clientSessionFilter;
|
||||
private final TableFilterAttribute eventTypeFilter;
|
||||
|
||||
private final PageService pageService;
|
||||
private final ResourceService resourceService;
|
||||
private final RestService restService;
|
||||
private final I18nSupport i18nSupport;
|
||||
private final WidgetFactory widgetFactory;
|
||||
private final int pageSize;
|
||||
|
||||
public SebClientLogs(
|
||||
final PageService pageService,
|
||||
@Value("${sebserver.gui.list.page.size:20}") final Integer pageSize) {
|
||||
|
||||
this.pageService = pageService;
|
||||
this.resourceService = pageService.getResourceService();
|
||||
this.restService = this.resourceService.getRestService();
|
||||
this.i18nSupport = this.resourceService.getI18nSupport();
|
||||
this.widgetFactory = pageService.getWidgetFactory();
|
||||
this.pageSize = pageSize;
|
||||
|
||||
this.examFilter = new TableFilterAttribute(
|
||||
CriteriaType.SINGLE_SELECTION,
|
||||
ExtendedClientEvent.FILTER_ATTRIBUTE_EXAM,
|
||||
this.resourceService::getExamResources);
|
||||
|
||||
this.clientSessionFilter = new TableFilterAttribute(
|
||||
CriteriaType.TEXT,
|
||||
ClientConnection.FILTER_ATTR_SESSION_ID);
|
||||
|
||||
this.eventTypeFilter = new TableFilterAttribute(
|
||||
CriteriaType.SINGLE_SELECTION,
|
||||
ClientEvent.FILTER_ATTR_TYPE,
|
||||
this.resourceService::clientEventTypeResources);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void compose(final PageContext pageContext) {
|
||||
// TODO Auto-generated method stub
|
||||
final WidgetFactory widgetFactory = this.pageService.getWidgetFactory();
|
||||
final RestService restService = this.resourceService.getRestService();
|
||||
// content page layout with title
|
||||
final Composite content = widgetFactory.defaultPageLayout(
|
||||
pageContext.getParent(),
|
||||
TITLE_TEXT_KEY);
|
||||
|
||||
final PageActionBuilder actionBuilder = this.pageService.pageActionBuilder(
|
||||
pageContext
|
||||
.clearEntityKeys()
|
||||
.clearAttributes());
|
||||
|
||||
// table
|
||||
final EntityTable<ExtendedClientEvent> table = this.pageService.entityTableBuilder(
|
||||
restService.getRestCall(GetExtendedClientEventPage.class))
|
||||
.withEmptyMessage(EMPTY_TEXT_KEY)
|
||||
.withPaging(this.pageSize)
|
||||
|
||||
.withColumn(new ColumnDefinition<>(
|
||||
Domain.CLIENT_CONNECTION.ATTR_EXAM_ID,
|
||||
EXAM_TEXT_KEY,
|
||||
examNameFunction())
|
||||
.withFilter(this.examFilter)
|
||||
.widthProportion(2))
|
||||
|
||||
.withColumn(new ColumnDefinition<>(
|
||||
Domain.CLIENT_CONNECTION.ATTR_EXAM_USER_SESSION_IDENTIFER,
|
||||
CLIENT_SESSION_TEXT_KEY,
|
||||
ExtendedClientEvent::getUserSessionId)
|
||||
.withFilter(this.clientSessionFilter)
|
||||
.sortable()
|
||||
.widthProportion(2))
|
||||
|
||||
.withColumn(new ColumnDefinition<ExtendedClientEvent>(
|
||||
Domain.CLIENT_EVENT.TYPE_NAME,
|
||||
TYPE_TEXT_KEY,
|
||||
this.resourceService::getEventTypeName)
|
||||
.withFilter(this.eventTypeFilter)
|
||||
.sortable()
|
||||
.widthProportion(1))
|
||||
|
||||
.withColumn(new ColumnDefinition<>(
|
||||
Domain.CLIENT_EVENT.ATTR_SERVER_TIME,
|
||||
TIME_TEXT_KEY,
|
||||
this::getEventTime)
|
||||
.withFilter(new TableFilterAttribute(
|
||||
CriteriaType.DATE_RANGE,
|
||||
ClientEvent.FILTER_ATTR_SERVER_TIME_FROM_TO,
|
||||
Utils.toDateTimeUTC(Utils.getMillisecondsNow())
|
||||
.minusYears(1)
|
||||
.toString()))
|
||||
.sortable()
|
||||
.widthProportion(2))
|
||||
|
||||
.withColumn(new ColumnDefinition<ExtendedClientEvent>(
|
||||
Domain.CLIENT_EVENT.ATTR_NUMERIC_VALUE,
|
||||
VALUE_TEXT_KEY,
|
||||
clientEvent -> (clientEvent.numValue != null)
|
||||
? String.valueOf(clientEvent.numValue)
|
||||
: Constants.EMPTY_NOTE)
|
||||
.widthProportion(1))
|
||||
|
||||
.withDefaultAction(t -> actionBuilder
|
||||
.newAction(ActionDefinition.LOGS_SEB_CLIENT_SHOW_DETAILS)
|
||||
.withExec(action -> this.showDetails(action, t.getSelectedROWData()))
|
||||
.noEventPropagation()
|
||||
.create())
|
||||
|
||||
.compose(pageContext.copyOf(content));
|
||||
|
||||
actionBuilder
|
||||
.newAction(ActionDefinition.LOGS_SEB_CLIENT_SHOW_DETAILS)
|
||||
.withSelect(
|
||||
table::getSelection,
|
||||
action -> this.showDetails(action, table.getSelectedROWData()),
|
||||
EMPTY_SELECTION_TEXT)
|
||||
.noEventPropagation()
|
||||
.publishIf(table::hasAnyContent);
|
||||
}
|
||||
|
||||
private PageAction showDetails(final PageAction action, final ExtendedClientEvent clientEvent) {
|
||||
action.getSingleSelection();
|
||||
|
||||
final ModalInputDialog<Void> dialog = new ModalInputDialog<>(
|
||||
action.pageContext().getParent().getShell(),
|
||||
this.widgetFactory);
|
||||
dialog.setDialogWidth(600);
|
||||
|
||||
dialog.open(
|
||||
DETAILS_TITLE_TEXT_KEY,
|
||||
action.pageContext(),
|
||||
pc -> createDetailsForm(clientEvent, pc));
|
||||
|
||||
return action;
|
||||
}
|
||||
|
||||
private void createDetailsForm(final ExtendedClientEvent clientEvent, final PageContext pc) {
|
||||
|
||||
final Composite parent = pc.getParent();
|
||||
final Composite content = this.widgetFactory.createPopupScrollComposite(parent);
|
||||
|
||||
// Event Details Title
|
||||
this.widgetFactory.labelLocalized(
|
||||
content,
|
||||
CustomVariant.TEXT_H3,
|
||||
DETAILS_EVENT_TILE_TEXT_KEY);
|
||||
|
||||
this.pageService.formBuilder(pc.copyOf(content), 3)
|
||||
.withEmptyCellSeparation(false)
|
||||
.readonly(true)
|
||||
.addField(FormBuilder.text(
|
||||
Domain.CLIENT_EVENT.TYPE_NAME,
|
||||
FORM_TYPE_TEXT_KEY,
|
||||
this.resourceService.getEventTypeName(clientEvent)))
|
||||
.addField(FormBuilder.text(
|
||||
Domain.CLIENT_EVENT.ATTR_CLIENT_TIME,
|
||||
FORM_CLIENTTIME_TEXT_KEY,
|
||||
this.i18nSupport.formatDisplayDateTime(clientEvent.clientTime)))
|
||||
.addField(FormBuilder.text(
|
||||
Domain.CLIENT_EVENT.ATTR_SERVER_TIME,
|
||||
FORM_SERVERTIME_TEXT_KEY,
|
||||
this.i18nSupport.formatDisplayDateTime(clientEvent.serverTime)))
|
||||
.addField(FormBuilder.text(
|
||||
Domain.CLIENT_EVENT.ATTR_NUMERIC_VALUE,
|
||||
FORM_VALUE_TEXT_KEY,
|
||||
(clientEvent.numValue != null)
|
||||
? String.valueOf(clientEvent.numValue)
|
||||
: Constants.EMPTY_NOTE))
|
||||
.addField(FormBuilder.text(
|
||||
Domain.CLIENT_EVENT.ATTR_TEXT,
|
||||
FORM_MESSAGE_TEXT_KEY,
|
||||
clientEvent.text)
|
||||
.asArea())
|
||||
.build();
|
||||
|
||||
// SEB Client Connection Title
|
||||
this.widgetFactory.labelLocalized(
|
||||
content,
|
||||
CustomVariant.TEXT_H3,
|
||||
DETAILS_CONNECTION_TILE_TEXT_KEY);
|
||||
|
||||
final ClientConnection connection = this.restService.getBuilder(GetClientConnection.class)
|
||||
.withURIVariable(API.PARAM_MODEL_ID, String.valueOf(clientEvent.connectionId))
|
||||
.call()
|
||||
.get(
|
||||
error -> log.error("Failed to get Exam for id {}", clientEvent.examId, error),
|
||||
() -> ClientConnection.EMPTY_CLIENT_CONNECTION);
|
||||
|
||||
this.pageService.formBuilder(pc.copyOf(content), 3)
|
||||
.withEmptyCellSeparation(false)
|
||||
.readonly(true)
|
||||
.addField(FormBuilder.text(
|
||||
Domain.CLIENT_CONNECTION.ATTR_EXAM_USER_SESSION_IDENTIFER,
|
||||
FORM_SESSION_ID_TEXT_KEY,
|
||||
connection.userSessionId))
|
||||
.addField(FormBuilder.text(
|
||||
Domain.CLIENT_CONNECTION.ATTR_CLIENT_ADDRESS,
|
||||
FORM_ADDRESS_TEXT_KEY,
|
||||
connection.clientAddress))
|
||||
.addField(FormBuilder.text(
|
||||
Domain.CLIENT_CONNECTION.ATTR_CONNECTION_TOKEN,
|
||||
FORM_TOKEN_TEXT_KEY,
|
||||
connection.connectionToken))
|
||||
.addField(FormBuilder.text(
|
||||
Domain.CLIENT_CONNECTION.ATTR_STATUS,
|
||||
FORM_STATUS_TEXT_KEY,
|
||||
this.resourceService.localizedClientConnectionStatusName(connection.status)))
|
||||
.build();
|
||||
|
||||
// Exam Details Title
|
||||
this.widgetFactory.labelLocalized(
|
||||
content,
|
||||
CustomVariant.TEXT_H3,
|
||||
DETAILS_EXAM_TILE_TEXT_KEY);
|
||||
|
||||
final Exam exam = this.restService.getBuilder(GetExam.class)
|
||||
.withURIVariable(API.PARAM_MODEL_ID, String.valueOf(clientEvent.examId))
|
||||
.call()
|
||||
.get(
|
||||
error -> log.error("Failed to get Exam for id {}", clientEvent.examId, error),
|
||||
() -> Exam.EMPTY_EXAM);
|
||||
|
||||
this.pageService.formBuilder(pc.copyOf(content), 3)
|
||||
.withEmptyCellSeparation(false)
|
||||
.readonly(true)
|
||||
.addField(FormBuilder.text(
|
||||
QuizData.QUIZ_ATTR_NAME,
|
||||
FORM_EXAM_NAME_TEXT_KEY,
|
||||
exam.name))
|
||||
.addField(FormBuilder.text(
|
||||
QuizData.QUIZ_ATTR_DESCRIPTION,
|
||||
FORM_DESC_TEXT_KEY,
|
||||
exam.description))
|
||||
.addField(FormBuilder.text(
|
||||
Domain.EXAM.ATTR_TYPE,
|
||||
FORM_EXAM_TYPE_TEXT_KEY,
|
||||
this.resourceService.localizedExamTypeName(exam)))
|
||||
.addField(FormBuilder.text(
|
||||
QuizData.QUIZ_ATTR_START_TIME,
|
||||
FORM_START_TIME_TEXT_KEY,
|
||||
this.i18nSupport.formatDisplayDateTime(exam.startTime)))
|
||||
.addField(FormBuilder.text(
|
||||
QuizData.QUIZ_ATTR_END_TIME,
|
||||
FORM_END_TIME_TEXT_KEY,
|
||||
this.i18nSupport.formatDisplayDateTime(exam.endTime)))
|
||||
.build();
|
||||
|
||||
}
|
||||
|
||||
private Function<ExtendedClientEvent, String> examNameFunction() {
|
||||
final Map<Long, String> examNameMapping = this.resourceService.getExamNameMapping();
|
||||
return event -> examNameMapping.get(event.examId);
|
||||
}
|
||||
|
||||
private final String getEventTime(final ExtendedClientEvent event) {
|
||||
if (event == null || event.serverTime == null) {
|
||||
return Constants.EMPTY_NOTE;
|
||||
}
|
||||
|
||||
return this.i18nSupport
|
||||
.formatDisplayDateTime(Utils.toDateTimeUTC(event.serverTime));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -137,7 +137,7 @@ public class SebExamConfigList implements TemplateComposer {
|
|||
.withDefaultAction(pageActionBuilder
|
||||
.newAction(ActionDefinition.SEB_EXAM_CONFIG_VIEW_PROP_FROM_LIST)
|
||||
.create())
|
||||
.compose(content);
|
||||
.compose(pageContext.copyOf(content));
|
||||
|
||||
final GrantCheck examConfigGrant = this.currentUser.grantCheck(EntityType.CONFIGURATION_NODE);
|
||||
pageActionBuilder
|
||||
|
|
|
@ -179,7 +179,7 @@ public class UserAccountList implements TemplateComposer {
|
|||
.withDefaultAction(actionBuilder
|
||||
.newAction(ActionDefinition.USER_ACCOUNT_VIEW_FROM_LIST)
|
||||
.create())
|
||||
.compose(content);
|
||||
.compose(pageContext.copyOf(content));
|
||||
|
||||
// propagate content actions to action-pane
|
||||
final GrantCheck userGrant = currentUser.grantCheck(EntityType.USER);
|
||||
|
|
|
@ -30,7 +30,7 @@ import ch.ethz.seb.sebserver.gui.service.page.TemplateComposer;
|
|||
import ch.ethz.seb.sebserver.gui.service.page.impl.ModalInputDialog;
|
||||
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.userlogs.GetUserLogPage;
|
||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.logs.GetUserLogPage;
|
||||
import ch.ethz.seb.sebserver.gui.table.ColumnDefinition;
|
||||
import ch.ethz.seb.sebserver.gui.table.ColumnDefinition.TableFilterAttribute;
|
||||
import ch.ethz.seb.sebserver.gui.table.EntityTable;
|
||||
|
@ -54,8 +54,10 @@ public class UserActivityLogs implements TemplateComposer {
|
|||
new LocTextKey("sebserver.userlogs.list.column.dateTime");
|
||||
private static final LocTextKey ACTIVITY_TEXT_KEY =
|
||||
new LocTextKey("sebserver.userlogs.list.column.activityType");
|
||||
private static final LocTextKey ENTITY_TEXT_KEY =
|
||||
private static final LocTextKey ENTITY_TYPE_TEXT_KEY =
|
||||
new LocTextKey("sebserver.userlogs.list.column.entityType");
|
||||
private static final LocTextKey ENTITY_ID_TEXT_KEY =
|
||||
new LocTextKey("sebserver.userlogs.list.column.entityId");
|
||||
private static final LocTextKey MESSAGE_TEXT_KEY =
|
||||
new LocTextKey("sebserver.userlogs.list.column.message");
|
||||
private final static LocTextKey EMPTY_SELECTION_TEXT =
|
||||
|
@ -73,12 +75,11 @@ public class UserActivityLogs implements TemplateComposer {
|
|||
|
||||
public UserActivityLogs(
|
||||
final PageService pageService,
|
||||
final ResourceService resourceService,
|
||||
@Value("${sebserver.gui.list.page.size:20}") final Integer pageSize) {
|
||||
|
||||
this.pageService = pageService;
|
||||
this.resourceService = resourceService;
|
||||
this.i18nSupport = resourceService.getI18nSupport();
|
||||
this.resourceService = pageService.getResourceService();
|
||||
this.i18nSupport = this.resourceService.getI18nSupport();
|
||||
this.widgetFactory = pageService.getWidgetFactory();
|
||||
this.pageSize = pageSize;
|
||||
|
||||
|
@ -133,7 +134,7 @@ public class UserActivityLogs implements TemplateComposer {
|
|||
|
||||
.withColumn(new ColumnDefinition<UserActivityLog>(
|
||||
Domain.USER_ACTIVITY_LOG.ATTR_ENTITY_ID,
|
||||
ENTITY_TEXT_KEY,
|
||||
ENTITY_TYPE_TEXT_KEY,
|
||||
this.resourceService::getEntityTypeName)
|
||||
.withFilter(this.entityFilter)
|
||||
.sortable())
|
||||
|
@ -156,7 +157,7 @@ public class UserActivityLogs implements TemplateComposer {
|
|||
.noEventPropagation()
|
||||
.create())
|
||||
|
||||
.compose(content);
|
||||
.compose(pageContext.copyOf(content));
|
||||
|
||||
actionBuilder
|
||||
.newAction(ActionDefinition.LOGS_USER_ACTIVITY_SHOW_DETAILS)
|
||||
|
@ -185,6 +186,7 @@ public class UserActivityLogs implements TemplateComposer {
|
|||
action.pageContext().getParent().getShell(),
|
||||
this.widgetFactory);
|
||||
|
||||
//dialog.setDialogHeight(400);
|
||||
dialog.open(
|
||||
DETAILS_TITLE_TEXT_KEY,
|
||||
action.pageContext(),
|
||||
|
@ -194,7 +196,11 @@ public class UserActivityLogs implements TemplateComposer {
|
|||
}
|
||||
|
||||
private void createDetailsForm(final UserActivityLog userActivityLog, final PageContext pc) {
|
||||
this.pageService.formBuilder(pc, 3)
|
||||
|
||||
final Composite parent = pc.getParent();
|
||||
final Composite grid = this.widgetFactory.createPopupScrollComposite(parent);
|
||||
|
||||
this.pageService.formBuilder(pc.copyOf(grid), 3)
|
||||
.withEmptyCellSeparation(false)
|
||||
.readonly(true)
|
||||
.addField(FormBuilder.text(
|
||||
|
@ -206,9 +212,13 @@ public class UserActivityLogs implements TemplateComposer {
|
|||
ACTIVITY_TEXT_KEY,
|
||||
this.resourceService.getUserActivityTypeName(userActivityLog)))
|
||||
.addField(FormBuilder.text(
|
||||
Domain.USER_ACTIVITY_LOG.ATTR_ENTITY_ID,
|
||||
ENTITY_TEXT_KEY,
|
||||
Domain.USER_ACTIVITY_LOG.ATTR_ENTITY_TYPE,
|
||||
ENTITY_TYPE_TEXT_KEY,
|
||||
this.resourceService.getEntityTypeName(userActivityLog)))
|
||||
.addField(FormBuilder.text(
|
||||
Domain.USER_ACTIVITY_LOG.ATTR_ENTITY_ID,
|
||||
ENTITY_ID_TEXT_KEY,
|
||||
userActivityLog.entityId))
|
||||
.addField(FormBuilder.text(
|
||||
Domain.USER_ACTIVITY_LOG.ATTR_TIMESTAMP,
|
||||
DATE_TEXT_KEY,
|
||||
|
@ -217,10 +227,9 @@ public class UserActivityLogs implements TemplateComposer {
|
|||
.addField(FormBuilder.text(
|
||||
Domain.USER_ACTIVITY_LOG.ATTR_MESSAGE,
|
||||
MESSAGE_TEXT_KEY,
|
||||
String.valueOf(userActivityLog.message).replace(",", ",\n"))
|
||||
String.valueOf(userActivityLog.message))
|
||||
.asArea())
|
||||
.build();
|
||||
this.widgetFactory.labelSeparator(pc.getParent());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -235,10 +235,9 @@ public class ActivitiesPane implements TemplateComposer {
|
|||
final boolean viewUserActivityLogs = this.currentUser.hasInstitutionalPrivilege(
|
||||
PrivilegeType.READ,
|
||||
EntityType.USER_ACTIVITY_LOG);
|
||||
final boolean viewSebClientLogs = false;
|
||||
// this.currentUser.hasInstitutionalPrivilege(
|
||||
// PrivilegeType.READ,
|
||||
// EntityType.EXAM);
|
||||
final boolean viewSebClientLogs = this.currentUser.hasInstitutionalPrivilege(
|
||||
PrivilegeType.READ,
|
||||
EntityType.EXAM);
|
||||
|
||||
TreeItem logRoot = null;
|
||||
if (viewUserActivityLogs && viewSebClientLogs) {
|
||||
|
@ -384,6 +383,9 @@ public class ActivitiesPane implements TemplateComposer {
|
|||
}
|
||||
|
||||
static final TreeItem findItemByActionDefinition(final TreeItem[] items, final PageState pageState) {
|
||||
if (pageState == null) {
|
||||
return null;
|
||||
}
|
||||
return findItemByActionDefinition(items, pageState.activityAnchor(), null);
|
||||
}
|
||||
|
||||
|
|
|
@ -200,6 +200,11 @@ public class FormBuilder {
|
|||
return new TextFieldBuilder(name, label, value);
|
||||
}
|
||||
|
||||
public static TextFieldBuilder text(final String name, final LocTextKey label,
|
||||
final Supplier<String> valueSupplier) {
|
||||
return new TextFieldBuilder(name, label, valueSupplier.get());
|
||||
}
|
||||
|
||||
public static SelectionFieldBuilder singleSelection(
|
||||
final String name,
|
||||
final LocTextKey label,
|
||||
|
|
|
@ -63,9 +63,9 @@ public final class TextFieldBuilder extends FieldBuilder<String> {
|
|||
? builder.widgetFactory.textAreaInput(fieldGrid)
|
||||
: builder.widgetFactory.textInput(fieldGrid, this.isPassword);
|
||||
|
||||
final GridData gridData = new GridData(SWT.FILL, SWT.FILL, true, false);
|
||||
if (this.isArea && !readonly) {
|
||||
gridData.heightHint = 50;
|
||||
final GridData gridData = new GridData(SWT.FILL, SWT.FILL, true, true);
|
||||
if (this.isArea) {
|
||||
gridData.minimumHeight = 35;
|
||||
}
|
||||
textInput.setLayoutData(gridData);
|
||||
if (StringUtils.isNoneBlank(this.value)) {
|
||||
|
|
|
@ -17,6 +17,7 @@ import java.util.List;
|
|||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
|
@ -37,6 +38,8 @@ import ch.ethz.seb.sebserver.gbl.model.institution.LmsSetup.LmsType;
|
|||
import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationNode;
|
||||
import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationNode.ConfigurationStatus;
|
||||
import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationNode.ConfigurationType;
|
||||
import ch.ethz.seb.sebserver.gbl.model.session.ClientConnection.ConnectionStatus;
|
||||
import ch.ethz.seb.sebserver.gbl.model.session.ClientEvent;
|
||||
import ch.ethz.seb.sebserver.gbl.model.session.ClientEvent.EventType;
|
||||
import ch.ethz.seb.sebserver.gbl.model.user.UserActivityLog;
|
||||
import ch.ethz.seb.sebserver.gbl.model.user.UserInfo;
|
||||
|
@ -49,6 +52,7 @@ 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.remote.webservice.api.RestService;
|
||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.GetExamConfigMappingNames;
|
||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.GetExamNames;
|
||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.institution.GetInstitutionNames;
|
||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.lmssetup.GetLmsSetupNames;
|
||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.useraccount.GetUserAccountNames;
|
||||
|
@ -75,6 +79,9 @@ public class ResourceService {
|
|||
EntityType.USER_ROLE,
|
||||
EntityType.WEBSERVICE_SERVER_INFO);
|
||||
|
||||
public static final EnumSet<EventType> CLIENT_EVENT_TYPE_EXCLUDE_MAP = EnumSet.of(
|
||||
EventType.LAST_PING);
|
||||
|
||||
public static final String EXAMCONFIG_STATUS_PREFIX = "sebserver.examconfig.status.";
|
||||
public static final String EXAM_TYPE_PREFIX = "sebserver.exam.type.";
|
||||
public static final String USERACCOUNT_ROLE_PREFIX = "sebserver.useraccount.role.";
|
||||
|
@ -83,6 +90,7 @@ public class ResourceService {
|
|||
public static final String CLIENT_EVENT_TYPE_PREFIX = "sebserver.monitoring.exam.connection.event.type.";
|
||||
public static final String USER_ACTIVITY_TYPE_PREFIX = "sebserver.overall.types.activityType.";
|
||||
public static final String ENTITY_TYPE_PREFIX = "sebserver.overall.types.entityType.";
|
||||
public static final String SEB_CONNECTION_STATUS_KEY_PREFIX = "sebserver.monitoring.exam.connection.status.";
|
||||
public static final LocTextKey ACTIVE_TEXT_KEY = new LocTextKey("sebserver.overall.status.active");
|
||||
public static final LocTextKey INACTIVE_TEXT_KEY = new LocTextKey("sebserver.overall.status.inactive");
|
||||
|
||||
|
@ -138,6 +146,7 @@ public class ResourceService {
|
|||
public List<Tuple<String>> clientEventTypeResources() {
|
||||
return Arrays.asList(EventType.values())
|
||||
.stream()
|
||||
.filter(Predicate.not(CLIENT_EVENT_TYPE_EXCLUDE_MAP::contains))
|
||||
.map(eventType -> new Tuple<>(
|
||||
eventType.name(),
|
||||
getEventTypeName(eventType)))
|
||||
|
@ -145,6 +154,13 @@ public class ResourceService {
|
|||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
public String getEventTypeName(final ClientEvent event) {
|
||||
if (event == null) {
|
||||
return getEventTypeName(EventType.UNKNOWN);
|
||||
}
|
||||
return getEventTypeName(event.eventType);
|
||||
}
|
||||
|
||||
public String getEventTypeName(final EventType eventType) {
|
||||
if (eventType == null) {
|
||||
return Constants.EMPTY_NOTE;
|
||||
|
@ -384,6 +400,51 @@ public class ResourceService {
|
|||
.getText(ResourceService.EXAMCONFIG_STATUS_PREFIX + config.configStatus.name());
|
||||
}
|
||||
|
||||
public String localizedClientConnectionStatusName(final ConnectionStatus status) {
|
||||
String name;
|
||||
if (status != null) {
|
||||
name = status.name();
|
||||
} else {
|
||||
name = ConnectionStatus.UNDEFINED.name();
|
||||
}
|
||||
return this.i18nSupport
|
||||
.getText(SEB_CONNECTION_STATUS_KEY_PREFIX + name, name);
|
||||
}
|
||||
|
||||
public String localizedExamTypeName(final Exam exam) {
|
||||
if (exam.type == null) {
|
||||
return Constants.EMPTY_NOTE;
|
||||
}
|
||||
|
||||
return this.i18nSupport
|
||||
.getText(ResourceService.EXAM_TYPE_PREFIX + exam.type.name());
|
||||
}
|
||||
|
||||
public List<Tuple<String>> getExamResources() {
|
||||
final UserInfo userInfo = this.currentUser.get();
|
||||
return this.restService.getBuilder(GetExamNames.class)
|
||||
.withQueryParam(Entity.FILTER_ATTR_INSTITUTION, String.valueOf(userInfo.getInstitutionId()))
|
||||
.call()
|
||||
.getOr(Collections.emptyList())
|
||||
.stream()
|
||||
.map(entityName -> new Tuple<>(entityName.modelId, entityName.name))
|
||||
.sorted(RESOURCE_COMPARATOR)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
public Map<Long, String> getExamNameMapping() {
|
||||
final UserInfo userInfo = this.currentUser.get();
|
||||
return this.restService.getBuilder(GetExamNames.class)
|
||||
.withQueryParam(Entity.FILTER_ATTR_INSTITUTION, String.valueOf(userInfo.getInstitutionId()))
|
||||
.call()
|
||||
.getOr(Collections.emptyList())
|
||||
.stream()
|
||||
.filter(k -> StringUtils.isNotBlank(k.modelId))
|
||||
.collect(Collectors.toMap(
|
||||
k -> Long.valueOf(k.modelId),
|
||||
k -> k.name));
|
||||
}
|
||||
|
||||
private Result<List<EntityName>> getExamConfigurationSelection() {
|
||||
return this.restService.getBuilder(GetExamConfigMappingNames.class)
|
||||
.withQueryParam(
|
||||
|
@ -398,13 +459,4 @@ public class ResourceService {
|
|||
.call();
|
||||
}
|
||||
|
||||
public String examTypeName(final Exam exam) {
|
||||
if (exam.type == null) {
|
||||
return Constants.EMPTY_NOTE;
|
||||
}
|
||||
|
||||
return this.i18nSupport
|
||||
.getText(ResourceService.EXAM_TYPE_PREFIX + exam.type.name());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -13,6 +13,8 @@ import java.util.Locale;
|
|||
|
||||
import org.joda.time.DateTime;
|
||||
|
||||
import ch.ethz.seb.sebserver.gbl.util.Utils;
|
||||
|
||||
public interface I18nSupport {
|
||||
|
||||
/** Get all supported languages as a collection of Locale
|
||||
|
@ -38,6 +40,18 @@ public interface I18nSupport {
|
|||
* @return date formatted date String to display */
|
||||
String formatDisplayDate(DateTime date);
|
||||
|
||||
/** Format a time-stamp (milliseconds) to a text format to display.
|
||||
* This uses the date-format defined by either the attribute 'sebserver.gui.date.displayformat'
|
||||
* or the Constants.DEFAULT_DISPLAY_DATE_FORMAT
|
||||
*
|
||||
* Adds time-zone information if the currents user time-zone is different form UTC
|
||||
*
|
||||
* @param date the DateTime instance
|
||||
* @return date formatted date String to display */
|
||||
default String formatDisplayDate(final Long timestamp) {
|
||||
return formatDisplayDate(Utils.toDateTimeUTC(timestamp));
|
||||
}
|
||||
|
||||
/** Format a DateTime to a text format to display.
|
||||
* This uses the date-format defined by either the attribute 'sebserver.gui.datetime.displayformat'
|
||||
* or the Constants.DEFAULT_DISPLAY_DATE_TIME_FORMAT
|
||||
|
@ -48,6 +62,18 @@ public interface I18nSupport {
|
|||
* @return date formatted date time String to display */
|
||||
String formatDisplayDateTime(DateTime date);
|
||||
|
||||
/** Format a time-stamp (milliseconds) to a text format to display.
|
||||
* This uses the date-format defined by either the attribute 'sebserver.gui.datetime.displayformat'
|
||||
* or the Constants.DEFAULT_DISPLAY_DATE_TIME_FORMAT
|
||||
*
|
||||
* Adds time-zone information if the currents user time-zone is different form UTC
|
||||
*
|
||||
* @param date the DateTime instance
|
||||
* @return date formatted date time String to display */
|
||||
default String formatDisplayDateTime(final Long timestamp) {
|
||||
return formatDisplayDateTime(Utils.toDateTimeUTC(timestamp));
|
||||
}
|
||||
|
||||
/** Format a DateTime to a text format to display.
|
||||
* This uses the date-format defined by either the attribute 'sebserver.gui.time.displayformat'
|
||||
* or the Constants.DEFAULT_DISPLAY_TIME_FORMAT
|
||||
|
@ -58,6 +84,18 @@ public interface I18nSupport {
|
|||
* @return date formatted time String to display */
|
||||
String formatDisplayTime(DateTime date);
|
||||
|
||||
/** Format a time-stamp (milliseconds) to a text format to display.
|
||||
* This uses the date-format defined by either the attribute 'sebserver.gui.time.displayformat'
|
||||
* or the Constants.DEFAULT_DISPLAY_TIME_FORMAT
|
||||
*
|
||||
* Adds time-zone information if the currents user time-zone is different form UTC
|
||||
*
|
||||
* @param date the DateTime instance
|
||||
* @return date formatted time String to display */
|
||||
default String formatDisplayTime(final Long timestamp) {
|
||||
return formatDisplayTime(Utils.toDateTimeUTC(timestamp));
|
||||
}
|
||||
|
||||
/** If the current user has another time zone then UTC this will return a tile suffix that describes
|
||||
* a date/time column title with adding (UTC|{usersTimeZone}) that can be added to the title.
|
||||
*
|
||||
|
|
|
@ -121,14 +121,14 @@ public interface PageService {
|
|||
* @param pageContext the PageContext on that the FormBuilder should work
|
||||
* @param rows the number of rows of the from
|
||||
* @return a FormBuilder instance for the given PageContext and with number of rows */
|
||||
FormBuilder formBuilder(final PageContext pageContext, final int rows);
|
||||
FormBuilder formBuilder(PageContext pageContext, int rows);
|
||||
|
||||
/** Get an new TableBuilder for specified page based RestCall.
|
||||
*
|
||||
* @param apiCall the SEB Server API RestCall that feeds the table with data
|
||||
* @param <T> the type of the Entity of the table
|
||||
* @return TableBuilder of specified type */
|
||||
<T extends Entity> TableBuilder<T> entityTableBuilder(final RestCall<Page<T>> apiCall);
|
||||
<T extends Entity> TableBuilder<T> entityTableBuilder(RestCall<Page<T>> apiCall);
|
||||
|
||||
/** Get a new PageActionBuilder for a given PageContext.
|
||||
*
|
||||
|
|
|
@ -13,7 +13,6 @@ import java.util.function.Supplier;
|
|||
|
||||
import org.eclipse.rap.rwt.RWT;
|
||||
import org.eclipse.swt.SWT;
|
||||
import org.eclipse.swt.graphics.Point;
|
||||
import org.eclipse.swt.graphics.Rectangle;
|
||||
import org.eclipse.swt.layout.GridData;
|
||||
import org.eclipse.swt.layout.GridLayout;
|
||||
|
@ -42,6 +41,7 @@ public class ModalInputDialog<T> extends Dialog {
|
|||
private final WidgetFactory widgetFactory;
|
||||
private int dialogWidth = 400;
|
||||
private int dialogHeight = 600;
|
||||
private final int buttonWidth = 100;
|
||||
|
||||
public ModalInputDialog(
|
||||
final Shell parent,
|
||||
|
@ -75,25 +75,21 @@ public class ModalInputDialog<T> extends Dialog {
|
|||
shell.setText(this.widgetFactory.getI18nSupport().getText(title));
|
||||
shell.setLayout(new GridLayout(2, true));
|
||||
final GridData gridData2 = new GridData(SWT.FILL, SWT.TOP, false, false);
|
||||
gridData2.widthHint = this.dialogWidth;
|
||||
//gridData2.heightHint = this.dialogHeight;
|
||||
shell.setLayoutData(gridData2);
|
||||
|
||||
final Composite main = new Composite(shell, SWT.NONE);
|
||||
main.setLayout(new GridLayout());
|
||||
final GridData gridData = new GridData(SWT.FILL, SWT.TOP, false, false);
|
||||
final GridData gridData = new GridData(SWT.FILL, SWT.FILL, true, true);
|
||||
gridData.horizontalSpan = 2;
|
||||
gridData.widthHint = this.dialogWidth;
|
||||
main.setLayoutData(gridData);
|
||||
|
||||
final Supplier<T> valueSuppier = contentComposer.compose(main);
|
||||
|
||||
final Point computeSize = main.computeSize(SWT.DEFAULT, SWT.DEFAULT);
|
||||
gridData.heightHint = (computeSize.y < this.dialogHeight) ? computeSize.y : this.dialogHeight;
|
||||
gridData.heightHint = calcDialogHeight(main);
|
||||
|
||||
final Button ok = this.widgetFactory.buttonLocalized(shell, OK_TEXT_KEY);
|
||||
GridData data = new GridData(GridData.HORIZONTAL_ALIGN_END);
|
||||
data.widthHint = 100;
|
||||
data.widthHint = this.buttonWidth;
|
||||
ok.setLayoutData(data);
|
||||
ok.addListener(SWT.Selection, event -> {
|
||||
if (valueSuppier != null) {
|
||||
|
@ -109,7 +105,7 @@ public class ModalInputDialog<T> extends Dialog {
|
|||
|
||||
final Button cancel = this.widgetFactory.buttonLocalized(shell, CANCEL_TEXT_KEY);
|
||||
data = new GridData(GridData.HORIZONTAL_ALIGN_BEGINNING);
|
||||
data.widthHint = 100;
|
||||
data.widthHint = this.buttonWidth;
|
||||
cancel.setLayoutData(data);
|
||||
cancel.addListener(SWT.Selection, event -> {
|
||||
if (cancelCallback != null) {
|
||||
|
@ -132,18 +128,22 @@ public class ModalInputDialog<T> extends Dialog {
|
|||
shell.setData(RWT.CUSTOM_VARIANT, CustomVariant.MESSAGE.key);
|
||||
shell.setText(this.widgetFactory.getI18nSupport().getText(title));
|
||||
shell.setLayout(new GridLayout());
|
||||
shell.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
|
||||
final GridData gridData2 = new GridData(SWT.FILL, SWT.TOP, true, true);
|
||||
|
||||
shell.setLayoutData(gridData2);
|
||||
|
||||
final Composite main = new Composite(shell, SWT.NONE);
|
||||
main.setLayout(new GridLayout());
|
||||
final GridData gridData = new GridData(SWT.FILL, SWT.TOP, true, true);
|
||||
gridData.widthHint = this.dialogWidth;
|
||||
main.setLayoutData(gridData);
|
||||
final PageContext internalPageContext = pageContext.copyOf(main);
|
||||
contentComposer.accept(internalPageContext);
|
||||
|
||||
contentComposer.accept(pageContext.copyOf(main));
|
||||
gridData.heightHint = calcDialogHeight(main);
|
||||
|
||||
final Button close = this.widgetFactory.buttonLocalized(shell, CLOSE_TEXT_KEY);
|
||||
final GridData data = new GridData(GridData.HORIZONTAL_ALIGN_CENTER);
|
||||
data.widthHint = 100;
|
||||
data.widthHint = this.buttonWidth;
|
||||
close.setLayoutData(data);
|
||||
close.addListener(SWT.Selection, event -> {
|
||||
shell.close();
|
||||
|
@ -164,4 +164,16 @@ public class ModalInputDialog<T> extends Dialog {
|
|||
shell.open();
|
||||
}
|
||||
|
||||
private int calcDialogHeight(final Composite main) {
|
||||
final int actualHeight = main.computeSize(SWT.DEFAULT, SWT.DEFAULT).y;
|
||||
final int displayHeight = main.getDisplay().getClientArea().height;
|
||||
final int availableHeight = (displayHeight < actualHeight + 100)
|
||||
? displayHeight - 100
|
||||
: actualHeight;
|
||||
final int height = (availableHeight > this.dialogHeight)
|
||||
? this.dialogHeight
|
||||
: availableHeight;
|
||||
return height;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -34,7 +34,8 @@ public class GetInstitution extends RestCall<Institution> {
|
|||
}),
|
||||
HttpMethod.GET,
|
||||
MediaType.APPLICATION_FORM_URLENCODED,
|
||||
API.INSTITUTION_ENDPOINT + API.MODEL_ID_VAR_PATH_SEGMENT);
|
||||
API.INSTITUTION_ENDPOINT
|
||||
+ API.MODEL_ID_VAR_PATH_SEGMENT);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,41 @@
|
|||
/*
|
||||
* Copyright (c) 2019 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.service.remote.webservice.api.logs;
|
||||
|
||||
import org.springframework.context.annotation.Lazy;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
|
||||
import ch.ethz.seb.sebserver.gbl.api.API;
|
||||
import ch.ethz.seb.sebserver.gbl.api.EntityType;
|
||||
import ch.ethz.seb.sebserver.gbl.model.Page;
|
||||
import ch.ethz.seb.sebserver.gbl.model.session.ExtendedClientEvent;
|
||||
import ch.ethz.seb.sebserver.gbl.profile.GuiProfile;
|
||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestCall;
|
||||
|
||||
@Lazy
|
||||
@Component
|
||||
@GuiProfile
|
||||
public class GetExtendedClientEventPage extends RestCall<Page<ExtendedClientEvent>> {
|
||||
|
||||
public GetExtendedClientEventPage() {
|
||||
super(new TypeKey<>(
|
||||
CallType.GET_PAGE,
|
||||
EntityType.CLIENT_EVENT,
|
||||
new TypeReference<Page<ExtendedClientEvent>>() {
|
||||
}),
|
||||
HttpMethod.GET,
|
||||
MediaType.APPLICATION_FORM_URLENCODED,
|
||||
API.SEB_CLIENT_EVENT_EXTENDED_PAGE_ENDPOINT);
|
||||
}
|
||||
|
||||
}
|
|
@ -6,7 +6,7 @@
|
|||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package ch.ethz.seb.sebserver.gui.service.remote.webservice.api.userlogs;
|
||||
package ch.ethz.seb.sebserver.gui.service.remote.webservice.api.logs;
|
||||
|
||||
import org.springframework.context.annotation.Lazy;
|
||||
import org.springframework.http.HttpMethod;
|
|
@ -0,0 +1,41 @@
|
|||
/*
|
||||
* Copyright (c) 2019 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.service.remote.webservice.api.session;
|
||||
|
||||
import org.springframework.context.annotation.Lazy;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
|
||||
import ch.ethz.seb.sebserver.gbl.api.API;
|
||||
import ch.ethz.seb.sebserver.gbl.api.EntityType;
|
||||
import ch.ethz.seb.sebserver.gbl.model.session.ClientConnection;
|
||||
import ch.ethz.seb.sebserver.gbl.profile.GuiProfile;
|
||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestCall;
|
||||
|
||||
@Lazy
|
||||
@Component
|
||||
@GuiProfile
|
||||
public class GetClientConnection extends RestCall<ClientConnection> {
|
||||
|
||||
public GetClientConnection() {
|
||||
super(new TypeKey<>(
|
||||
CallType.GET_SINGLE,
|
||||
EntityType.CLIENT_CONNECTION,
|
||||
new TypeReference<ClientConnection>() {
|
||||
}),
|
||||
HttpMethod.GET,
|
||||
MediaType.APPLICATION_FORM_URLENCODED,
|
||||
API.SEB_CLIENT_CONNECTION_ENDPOINT
|
||||
+ API.MODEL_ID_VAR_PATH_SEGMENT);
|
||||
}
|
||||
|
||||
}
|
|
@ -36,7 +36,8 @@ public class GetClientConnectionDataList extends RestCall<Collection<ClientConne
|
|||
}),
|
||||
HttpMethod.GET,
|
||||
MediaType.APPLICATION_FORM_URLENCODED,
|
||||
API.EXAM_MONITORING_ENDPOINT + API.EXAM_MONITORING_EXAM_ID_PATH_SEGMENT);
|
||||
API.EXAM_MONITORING_ENDPOINT
|
||||
+ API.EXAM_MONITORING_EXAM_ID_PATH_SEGMENT);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -36,8 +36,6 @@ public class ClientConnectionDetails {
|
|||
|
||||
private static final Logger log = LoggerFactory.getLogger(ClientConnectionDetails.class);
|
||||
|
||||
private final static String STATUS_LOC_TEXT_KEY_PREFIX = "sebserver.monitoring.connection.status.";
|
||||
|
||||
private final static LocTextKey EXAM_NAME_TEXT_KEY =
|
||||
new LocTextKey("sebserver.monitoring.connection.list.column.examname");
|
||||
private final static LocTextKey CONNECTION_ID_TEXT_KEY =
|
||||
|
@ -163,14 +161,10 @@ public class ClientConnectionDetails {
|
|||
}
|
||||
|
||||
String getStatusName() {
|
||||
String name;
|
||||
if (this.connectionData != null && this.connectionData.clientConnection.status != null) {
|
||||
name = this.connectionData.clientConnection.status.name();
|
||||
} else {
|
||||
name = ConnectionStatus.UNDEFINED.name();
|
||||
}
|
||||
return this.pageService.getI18nSupport()
|
||||
.getText(STATUS_LOC_TEXT_KEY_PREFIX + name, name);
|
||||
return this.pageService.getResourceService().localizedClientConnectionStatusName(
|
||||
(this.connectionData != null && this.connectionData.clientConnection != null)
|
||||
? this.connectionData.clientConnection.status
|
||||
: ConnectionStatus.UNDEFINED);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -122,9 +122,10 @@ public final class ColumnDefinition<ROW extends Entity> {
|
|||
public final String columnName;
|
||||
public final String initValue;
|
||||
public final Supplier<List<Tuple<String>>> resourceSupplier;
|
||||
public final Function<EntityTable<?>, List<Tuple<String>>> resourceFunction;
|
||||
|
||||
public TableFilterAttribute(final CriteriaType type, final String columnName) {
|
||||
this(type, columnName, "", null);
|
||||
this(type, columnName, "", (Supplier<List<Tuple<String>>>) null);
|
||||
}
|
||||
|
||||
public TableFilterAttribute(
|
||||
|
@ -140,7 +141,7 @@ public final class ColumnDefinition<ROW extends Entity> {
|
|||
final String columnName,
|
||||
final String initValue) {
|
||||
|
||||
this(type, columnName, initValue, null);
|
||||
this(type, columnName, initValue, (Supplier<List<Tuple<String>>>) null);
|
||||
}
|
||||
|
||||
public TableFilterAttribute(
|
||||
|
@ -153,6 +154,20 @@ public final class ColumnDefinition<ROW extends Entity> {
|
|||
this.columnName = columnName;
|
||||
this.initValue = initValue;
|
||||
this.resourceSupplier = resourceSupplier;
|
||||
this.resourceFunction = null;
|
||||
}
|
||||
|
||||
public TableFilterAttribute(
|
||||
final CriteriaType type,
|
||||
final String columnName,
|
||||
final String initValue,
|
||||
final Function<EntityTable<?>, List<Tuple<String>>> resourceFunction) {
|
||||
|
||||
this.type = type;
|
||||
this.columnName = columnName;
|
||||
this.initValue = initValue;
|
||||
this.resourceSupplier = null;
|
||||
this.resourceFunction = resourceFunction;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -46,6 +46,7 @@ import ch.ethz.seb.sebserver.gbl.model.PageSortOrder;
|
|||
import ch.ethz.seb.sebserver.gbl.util.Utils;
|
||||
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.PageMessageException;
|
||||
import ch.ethz.seb.sebserver.gui.service.page.PageService;
|
||||
import ch.ethz.seb.sebserver.gui.service.page.impl.PageAction;
|
||||
|
@ -66,6 +67,7 @@ public class EntityTable<ROW extends Entity> {
|
|||
final RestCall<Page<ROW>> restCall;
|
||||
final Function<RestCall<Page<ROW>>.RestCallBuilder, RestCall<Page<ROW>>.RestCallBuilder> restCallAdapter;
|
||||
final I18nSupport i18nSupport;
|
||||
final PageContext pageContext;
|
||||
|
||||
final List<ColumnDefinition<ROW>> columns;
|
||||
final LocTextKey emptyMessage;
|
||||
|
@ -84,7 +86,7 @@ public class EntityTable<ROW extends Entity> {
|
|||
|
||||
EntityTable(
|
||||
final int type,
|
||||
final Composite parent,
|
||||
final PageContext pageContext,
|
||||
final RestCall<Page<ROW>> restCall,
|
||||
final Function<RestCall<Page<ROW>>.RestCallBuilder, RestCall<Page<ROW>>.RestCallBuilder> restCallAdapter,
|
||||
final PageService pageService,
|
||||
|
@ -94,9 +96,10 @@ public class EntityTable<ROW extends Entity> {
|
|||
final Function<EntityTable<ROW>, PageAction> defaultActionFunction,
|
||||
final boolean hideNavigation) {
|
||||
|
||||
this.composite = new Composite(parent, type);
|
||||
this.composite = new Composite(pageContext.getParent(), type);
|
||||
this.pageService = pageService;
|
||||
this.i18nSupport = pageService.getI18nSupport();
|
||||
this.pageContext = pageContext;
|
||||
this.widgetFactory = pageService.getWidgetFactory();
|
||||
this.restCall = restCall;
|
||||
this.restCallAdapter = (restCallAdapter != null) ? restCallAdapter : Function.identity();
|
||||
|
@ -365,9 +368,7 @@ public class EntityTable<ROW extends Entity> {
|
|||
.call()
|
||||
.map(this::createTableRowsFromPage)
|
||||
.map(this.navigator::update)
|
||||
.onError(t -> {
|
||||
// TODO error handling
|
||||
});
|
||||
.onError(this.pageContext::notifyError);
|
||||
|
||||
this.composite.getParent().layout(true, true);
|
||||
PageService.updateScrolledComposite(this.composite);
|
||||
|
|
|
@ -15,11 +15,11 @@ import java.util.function.Function;
|
|||
import java.util.function.Supplier;
|
||||
|
||||
import org.eclipse.swt.SWT;
|
||||
import org.eclipse.swt.widgets.Composite;
|
||||
|
||||
import ch.ethz.seb.sebserver.gbl.model.Entity;
|
||||
import ch.ethz.seb.sebserver.gbl.model.Page;
|
||||
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.impl.PageAction;
|
||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestCall;
|
||||
|
@ -106,10 +106,10 @@ public class TableBuilder<ROW extends Entity> {
|
|||
return this;
|
||||
}
|
||||
|
||||
public EntityTable<ROW> compose(final Composite parent) {
|
||||
public EntityTable<ROW> compose(final PageContext pageContext) {
|
||||
return new EntityTable<>(
|
||||
this.type,
|
||||
parent,
|
||||
pageContext,
|
||||
this.restCall,
|
||||
this.restCallAdapter,
|
||||
this.pageService,
|
||||
|
|
|
@ -11,6 +11,7 @@ package ch.ethz.seb.sebserver.gui.table;
|
|||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
@ -29,6 +30,7 @@ import org.springframework.util.MultiValueMap;
|
|||
|
||||
import ch.ethz.seb.sebserver.gbl.Constants;
|
||||
import ch.ethz.seb.sebserver.gbl.model.Entity;
|
||||
import ch.ethz.seb.sebserver.gbl.util.Tuple;
|
||||
import ch.ethz.seb.sebserver.gui.service.i18n.LocTextKey;
|
||||
import ch.ethz.seb.sebserver.gui.table.ColumnDefinition.TableFilterAttribute;
|
||||
import ch.ethz.seb.sebserver.gui.widget.Selection;
|
||||
|
@ -302,11 +304,17 @@ public class TableFilter<ROW extends Entity> {
|
|||
final Composite innerComposite = createInnerComposite(parent);
|
||||
final GridData gridData = new GridData(SWT.FILL, SWT.END, true, true);
|
||||
|
||||
Supplier<List<Tuple<String>>> resourceSupplier = this.attribute.resourceSupplier;
|
||||
if (this.attribute.resourceFunction != null) {
|
||||
resourceSupplier = () -> this.attribute.resourceFunction.apply(TableFilter.this.entityTable);
|
||||
}
|
||||
|
||||
this.selector = TableFilter.this.entityTable.widgetFactory
|
||||
.selectionLocalized(
|
||||
ch.ethz.seb.sebserver.gui.widget.Selection.Type.SINGLE,
|
||||
innerComposite,
|
||||
this.attribute.resourceSupplier);
|
||||
resourceSupplier);
|
||||
|
||||
this.selector
|
||||
.adaptToControl()
|
||||
.setLayoutData(gridData);
|
||||
|
|
|
@ -47,6 +47,7 @@ import ch.ethz.seb.sebserver.gui.content.action.ActionDefinition;
|
|||
import ch.ethz.seb.sebserver.gui.service.i18n.I18nSupport;
|
||||
import ch.ethz.seb.sebserver.gui.service.i18n.LocTextKey;
|
||||
import ch.ethz.seb.sebserver.gui.service.i18n.PolyglotPageService;
|
||||
import ch.ethz.seb.sebserver.gui.service.page.PageService;
|
||||
import ch.ethz.seb.sebserver.gui.service.push.ServerPushService;
|
||||
|
||||
@Lazy
|
||||
|
@ -196,6 +197,23 @@ public class WidgetFactory {
|
|||
return grid;
|
||||
}
|
||||
|
||||
/** Use this to create a scrolled Composite for usual popup forms
|
||||
*
|
||||
* @param parent The parent Composite
|
||||
* @return the scrolled Composite to add the form content */
|
||||
public Composite createPopupScrollComposite(final Composite parent) {
|
||||
final Composite grid = PageService.createManagedVScrolledComposite(
|
||||
parent,
|
||||
scrolledComposite -> {
|
||||
final Composite g = new Composite(scrolledComposite, SWT.NONE);
|
||||
g.setLayout(new GridLayout());
|
||||
g.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false));
|
||||
return g;
|
||||
},
|
||||
false);
|
||||
return grid;
|
||||
}
|
||||
|
||||
public Button buttonLocalized(final Composite parent, final String locTextKey) {
|
||||
final Button button = new Button(parent, SWT.NONE);
|
||||
this.polyglotPageService.injectI18n(button, new LocTextKey(locTextKey));
|
||||
|
|
|
@ -8,8 +8,16 @@
|
|||
|
||||
package ch.ethz.seb.sebserver.webservice.datalayer.batis;
|
||||
|
||||
import static org.mybatis.dynamic.sql.SqlBuilder.equalTo;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import org.apache.ibatis.annotations.Arg;
|
||||
import org.apache.ibatis.annotations.ConstructorArgs;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.apache.ibatis.annotations.ResultType;
|
||||
import org.apache.ibatis.annotations.SelectProvider;
|
||||
import org.apache.ibatis.type.JdbcType;
|
||||
import org.mybatis.dynamic.sql.BasicColumn;
|
||||
import org.mybatis.dynamic.sql.SqlBuilder;
|
||||
import org.mybatis.dynamic.sql.select.MyBatis3SelectModelAdapter;
|
||||
|
@ -18,6 +26,7 @@ import org.mybatis.dynamic.sql.select.SelectDSL;
|
|||
import org.mybatis.dynamic.sql.select.render.SelectStatementProvider;
|
||||
import org.mybatis.dynamic.sql.util.SqlProviderAdapter;
|
||||
|
||||
import ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.ClientConnectionRecordDynamicSqlSupport;
|
||||
import ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.ClientEventRecordDynamicSqlSupport;
|
||||
|
||||
@Mapper
|
||||
|
@ -36,4 +45,86 @@ public interface ClientEventExtentionMapper {
|
|||
.from(ClientEventRecordDynamicSqlSupport.clientEventRecord);
|
||||
}
|
||||
|
||||
@SelectProvider(type = SqlProviderAdapter.class, method = "select")
|
||||
@ResultType(ClientEventExtentionMapper.ConnectionEventJoinRecord.class)
|
||||
@ConstructorArgs({
|
||||
@Arg(column = "id", javaType = Long.class, jdbcType = JdbcType.BIGINT, id = true),
|
||||
@Arg(column = "connection_id", javaType = Long.class, jdbcType = JdbcType.BIGINT),
|
||||
@Arg(column = "type", javaType = Integer.class, jdbcType = JdbcType.INTEGER),
|
||||
@Arg(column = "client_time", javaType = Long.class, jdbcType = JdbcType.BIGINT),
|
||||
@Arg(column = "server_time", javaType = Long.class, jdbcType = JdbcType.BIGINT),
|
||||
@Arg(column = "numeric_value", javaType = Double.class, jdbcType = JdbcType.DECIMAL),
|
||||
@Arg(column = "text", javaType = String.class, jdbcType = JdbcType.VARCHAR),
|
||||
|
||||
@Arg(column = "institution_id", javaType = Long.class, jdbcType = JdbcType.BIGINT),
|
||||
@Arg(column = "exam_id", javaType = Long.class, jdbcType = JdbcType.BIGINT),
|
||||
@Arg(column = "exam_user_session_identifer", javaType = String.class, jdbcType = JdbcType.VARCHAR)
|
||||
})
|
||||
Collection<ConnectionEventJoinRecord> selectMany(SelectStatementProvider select);
|
||||
|
||||
default QueryExpressionDSL<MyBatis3SelectModelAdapter<Collection<ConnectionEventJoinRecord>>>.JoinSpecificationFinisher selectByExample() {
|
||||
return SelectDSL.selectWithMapper(
|
||||
this::selectMany,
|
||||
|
||||
ClientEventRecordDynamicSqlSupport.id,
|
||||
ClientEventRecordDynamicSqlSupport.connectionId.as("connection_id"),
|
||||
ClientEventRecordDynamicSqlSupport.type,
|
||||
ClientEventRecordDynamicSqlSupport.clientTime.as("client_time"),
|
||||
ClientEventRecordDynamicSqlSupport.serverTime.as("server_time"),
|
||||
ClientEventRecordDynamicSqlSupport.numericValue.as("numeric_value"),
|
||||
ClientEventRecordDynamicSqlSupport.text,
|
||||
|
||||
ClientConnectionRecordDynamicSqlSupport.institutionId.as("institution_id"),
|
||||
ClientConnectionRecordDynamicSqlSupport.examId.as("exam_id"),
|
||||
ClientConnectionRecordDynamicSqlSupport.examUserSessionIdentifer.as("exam_user_session_identifer"))
|
||||
|
||||
.from(ClientEventRecordDynamicSqlSupport.clientEventRecord)
|
||||
|
||||
.leftJoin(ClientConnectionRecordDynamicSqlSupport.clientConnectionRecord)
|
||||
.on(
|
||||
ClientEventRecordDynamicSqlSupport.clientEventRecord.connectionId,
|
||||
equalTo(ClientConnectionRecordDynamicSqlSupport.clientConnectionRecord.id));
|
||||
}
|
||||
|
||||
public static final class ConnectionEventJoinRecord {
|
||||
|
||||
public final Long id;
|
||||
public final Long connection_id;
|
||||
public final Integer type;
|
||||
public final Long client_time;
|
||||
public final Long server_time;
|
||||
public final Double numeric_value;
|
||||
public final String text;
|
||||
|
||||
public final Long institution_id;
|
||||
public final Long exam_id;
|
||||
public final String exam_user_session_identifer;
|
||||
|
||||
protected ConnectionEventJoinRecord(
|
||||
final Long id,
|
||||
final Long connection_id,
|
||||
final Integer type,
|
||||
final Long client_time,
|
||||
final Long server_time,
|
||||
final Double numeric_value,
|
||||
final String text,
|
||||
|
||||
final Long institution_id,
|
||||
final Long exam_id,
|
||||
final String exam_user_session_identifer) {
|
||||
|
||||
this.id = id;
|
||||
this.connection_id = connection_id;
|
||||
this.type = type;
|
||||
this.client_time = client_time;
|
||||
this.server_time = server_time;
|
||||
this.numeric_value = numeric_value;
|
||||
this.text = text;
|
||||
|
||||
this.institution_id = institution_id;
|
||||
this.exam_id = exam_id;
|
||||
this.exam_user_session_identifer = exam_user_session_identifer;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -37,7 +37,7 @@ public interface PaginationService {
|
|||
|
||||
void setDefaultLimit();
|
||||
|
||||
void setDefaultLimit(final String sort, final SqlTable table);
|
||||
void setDefaultLimit(final String sort, final String tableName);
|
||||
|
||||
int getPageNumber(final Integer pageNumber);
|
||||
|
||||
|
|
|
@ -94,8 +94,8 @@ public class PaginationServiceImpl implements PaginationService {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void setDefaultLimit(final String sort, final SqlTable table) {
|
||||
setPagination(1, this.maxPageSize, sort, table);
|
||||
public void setDefaultLimit(final String sort, final String tableName) {
|
||||
setPagination(1, this.maxPageSize, sort, tableName);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -131,9 +131,9 @@ public class PaginationServiceImpl implements PaginationService {
|
|||
final Supplier<Result<Collection<T>>> delegate) {
|
||||
|
||||
return Result.tryCatch(() -> {
|
||||
final SqlTable table = SqlTable.of(tableName);
|
||||
//final SqlTable table = SqlTable.of(tableName);
|
||||
final com.github.pagehelper.Page<Object> page =
|
||||
setPagination(pageNumber, pageSize, sort, table);
|
||||
setPagination(pageNumber, pageSize, sort, tableName);
|
||||
|
||||
final Collection<T> list = delegate.get().getOrThrow();
|
||||
|
||||
|
@ -145,36 +145,36 @@ public class PaginationServiceImpl implements PaginationService {
|
|||
});
|
||||
}
|
||||
|
||||
private String verifySortColumnName(final String sort, final SqlTable table) {
|
||||
private String verifySortColumnName(final String sort, final String columnName) {
|
||||
|
||||
if (StringUtils.isBlank(sort)) {
|
||||
return this.defaultSortColumn.get(table.name());
|
||||
return this.defaultSortColumn.get(columnName);
|
||||
}
|
||||
|
||||
final Map<String, String> mapping = this.sortColumnMapping.get(table.name());
|
||||
final Map<String, String> mapping = this.sortColumnMapping.get(columnName);
|
||||
if (mapping != null) {
|
||||
final String sortColumn = PageSortOrder.decode(sort);
|
||||
if (StringUtils.isBlank(sortColumn)) {
|
||||
return this.defaultSortColumn.get(table.name());
|
||||
return this.defaultSortColumn.get(columnName);
|
||||
}
|
||||
return mapping.get(sortColumn);
|
||||
}
|
||||
|
||||
return this.defaultSortColumn.get(table.name());
|
||||
return this.defaultSortColumn.get(columnName);
|
||||
}
|
||||
|
||||
private com.github.pagehelper.Page<Object> setPagination(
|
||||
final Integer pageNumber,
|
||||
final Integer pageSize,
|
||||
final String sort,
|
||||
final SqlTable table) {
|
||||
final String sortMappingName) {
|
||||
|
||||
final com.github.pagehelper.Page<Object> startPage =
|
||||
PageHelper.startPage(getPageNumber(pageNumber), getPageSize(pageSize), true, true, false);
|
||||
|
||||
if (table != null && StringUtils.isNotBlank(sort)) {
|
||||
if (StringUtils.isNotBlank(sortMappingName) && StringUtils.isNotBlank(sort)) {
|
||||
final PageSortOrder sortOrder = PageSortOrder.getSortOrder(sort);
|
||||
final String sortColumnName = verifySortColumnName(sort, table);
|
||||
final String sortColumnName = verifySortColumnName(sort, sortMappingName);
|
||||
if (StringUtils.isNotBlank(sortColumnName)) {
|
||||
switch (sortOrder) {
|
||||
case DESCENDING: {
|
||||
|
|
|
@ -8,7 +8,10 @@
|
|||
|
||||
package ch.ethz.seb.sebserver.webservice.servicelayer.authorization;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import ch.ethz.seb.sebserver.gbl.api.EntityType;
|
||||
|
@ -233,17 +236,21 @@ public interface AuthorizationService {
|
|||
/** Checks if the current user has a specified role.
|
||||
* If not a PermissionDeniedException is thrown for the given EntityType
|
||||
*
|
||||
* @param role The UserRole to check
|
||||
* @param roles The UserRoles to check if any of them matches to current users roles
|
||||
* @param institution the institution identifier
|
||||
* @param type EntityType for PermissionDeniedException
|
||||
* @throws PermissionDeniedException if current user don't have the specified UserRole */
|
||||
default void checkRole(final UserRole role, final Long institution, final EntityType type) {
|
||||
default void checkRole(final Long institution, final EntityType type, final UserRole... roles) {
|
||||
final SEBServerUser currentUser = this
|
||||
.getUserService()
|
||||
.getCurrentUser();
|
||||
|
||||
final List<UserRole> rolesList = (roles == null)
|
||||
? Collections.emptyList()
|
||||
: Arrays.asList(roles);
|
||||
|
||||
if (!currentUser.institutionId().equals(institution) ||
|
||||
!currentUser.getUserRoles().contains(role)) {
|
||||
Collections.disjoint(currentUser.getUserRoles(), rolesList)) {
|
||||
|
||||
throw new PermissionDeniedException(
|
||||
type,
|
||||
|
|
|
@ -19,7 +19,10 @@ import java.util.stream.Collectors;
|
|||
import org.springframework.context.ApplicationEventPublisher;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
|
||||
import ch.ethz.seb.sebserver.gbl.api.EntityType;
|
||||
import ch.ethz.seb.sebserver.gbl.api.JSONMapper;
|
||||
import ch.ethz.seb.sebserver.gbl.model.EntityKey;
|
||||
import ch.ethz.seb.sebserver.gbl.model.EntityProcessingReport;
|
||||
import ch.ethz.seb.sebserver.gbl.model.user.UserLogActivityType;
|
||||
|
@ -34,11 +37,13 @@ public class BulkActionServiceImpl implements BulkActionService {
|
|||
private final Map<EntityType, BulkActionSupportDAO<?>> supporter;
|
||||
private final UserActivityLogDAO userActivityLogDAO;
|
||||
private final ApplicationEventPublisher applicationEventPublisher;
|
||||
private final JSONMapper jsonMapper;
|
||||
|
||||
public BulkActionServiceImpl(
|
||||
final Collection<BulkActionSupportDAO<?>> supporter,
|
||||
final UserActivityLogDAO userActivityLogDAO,
|
||||
final ApplicationEventPublisher applicationEventPublisher) {
|
||||
final ApplicationEventPublisher applicationEventPublisher,
|
||||
final JSONMapper jsonMapper) {
|
||||
|
||||
this.supporter = new HashMap<>();
|
||||
for (final BulkActionSupportDAO<?> support : supporter) {
|
||||
|
@ -46,6 +51,7 @@ public class BulkActionServiceImpl implements BulkActionService {
|
|||
}
|
||||
this.userActivityLogDAO = userActivityLogDAO;
|
||||
this.applicationEventPublisher = applicationEventPublisher;
|
||||
this.jsonMapper = jsonMapper;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -123,11 +129,12 @@ public class BulkActionServiceImpl implements BulkActionService {
|
|||
}
|
||||
|
||||
for (final EntityKey key : action.dependencies) {
|
||||
|
||||
this.userActivityLogDAO.log(
|
||||
activityType,
|
||||
key.entityType,
|
||||
key.modelId,
|
||||
"bulk action dependency");
|
||||
"Bulk Action - Dependency : " + toLogMessage(key));
|
||||
}
|
||||
|
||||
for (final EntityKey key : action.sources) {
|
||||
|
@ -135,10 +142,22 @@ public class BulkActionServiceImpl implements BulkActionService {
|
|||
activityType,
|
||||
key.entityType,
|
||||
key.modelId,
|
||||
"bulk action source");
|
||||
"Bulk Action - Source : " + toLogMessage(key));
|
||||
}
|
||||
}
|
||||
|
||||
private String toLogMessage(final EntityKey key) {
|
||||
String entityAsString;
|
||||
try {
|
||||
entityAsString = this.jsonMapper
|
||||
.writerWithDefaultPrettyPrinter()
|
||||
.writeValueAsString(key);
|
||||
} catch (final JsonProcessingException e) {
|
||||
entityAsString = key.toString();
|
||||
}
|
||||
return entityAsString;
|
||||
}
|
||||
|
||||
private List<BulkActionSupportDAO<?>> getDependancySupporter(final BulkAction action) {
|
||||
switch (action.type) {
|
||||
case ACTIVATE:
|
||||
|
|
|
@ -8,8 +8,17 @@
|
|||
|
||||
package ch.ethz.seb.sebserver.webservice.servicelayer.dao;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import ch.ethz.seb.sebserver.gbl.model.session.ClientEvent;
|
||||
import ch.ethz.seb.sebserver.gbl.model.session.ExtendedClientEvent;
|
||||
import ch.ethz.seb.sebserver.gbl.util.Result;
|
||||
|
||||
public interface ClientEventDAO extends EntityDAO<ClientEvent, ClientEvent> {
|
||||
|
||||
Result<Collection<ExtendedClientEvent>> allMatchingExtended(
|
||||
FilterMap filterMap,
|
||||
Predicate<ExtendedClientEvent> predicate);
|
||||
|
||||
}
|
||||
|
|
|
@ -88,8 +88,8 @@ public interface EntityDAO<T extends Entity, M extends ModelIdAware> {
|
|||
.getOrThrow()
|
||||
.stream()
|
||||
.map(entity -> new EntityName(
|
||||
entity.entityType(),
|
||||
entity.getModelId(),
|
||||
entity.entityType(),
|
||||
entity.getName()))
|
||||
.collect(Collectors.toList());
|
||||
});
|
||||
|
|
|
@ -30,6 +30,7 @@ import ch.ethz.seb.sebserver.gbl.model.sebconfig.SebClientConfig;
|
|||
import ch.ethz.seb.sebserver.gbl.model.session.ClientConnection;
|
||||
import ch.ethz.seb.sebserver.gbl.model.session.ClientEvent;
|
||||
import ch.ethz.seb.sebserver.gbl.model.session.ClientEvent.EventType;
|
||||
import ch.ethz.seb.sebserver.gbl.model.session.ExtendedClientEvent;
|
||||
import ch.ethz.seb.sebserver.gbl.model.user.UserActivityLog;
|
||||
import ch.ethz.seb.sebserver.gbl.model.user.UserInfo;
|
||||
import ch.ethz.seb.sebserver.gbl.util.Utils;
|
||||
|
@ -235,14 +236,14 @@ public class FilterMap extends POSTMapper {
|
|||
false);
|
||||
}
|
||||
|
||||
public Long getUserLogFrom(final String filterAttrFrom) {
|
||||
public Long getUserLogFrom() {
|
||||
return getFromToValue(
|
||||
UserActivityLog.FILTER_ATTR_FROM_TO,
|
||||
UserActivityLog.FILTER_ATTR_FROM,
|
||||
true);
|
||||
}
|
||||
|
||||
public Long getUserLofTo(final String filterAttrTo) {
|
||||
public Long getUserLofTo() {
|
||||
return getFromToValue(
|
||||
UserActivityLog.FILTER_ATTR_FROM_TO,
|
||||
UserActivityLog.FILTER_ATTR_TO,
|
||||
|
@ -253,6 +254,10 @@ public class FilterMap extends POSTMapper {
|
|||
return getSQLWildcard(ClientEvent.FILTER_ATTR_TEXT);
|
||||
}
|
||||
|
||||
public Long getClientEventExamId() {
|
||||
return getLong(ExtendedClientEvent.FILTER_ATTRIBUTE_EXAM);
|
||||
}
|
||||
|
||||
private Long getFromToValue(final String nameCombi, final String name, final boolean from) {
|
||||
final Long value = getFromToValue(nameCombi, from);
|
||||
if (value != null) {
|
||||
|
|
|
@ -29,6 +29,10 @@ public interface UserActivityLogDAO extends
|
|||
* @return Result of the Entity or referring to an Error id happened */
|
||||
public <E extends Entity> Result<E> logCreate(E entity);
|
||||
|
||||
public <E extends Entity> Result<E> logSaveToHistory(E entity);
|
||||
|
||||
public <E extends Entity> Result<E> logUndo(E entity);
|
||||
|
||||
/** Create a user activity log entry for the current user of activity type IMPORT
|
||||
*
|
||||
* @param entity the Entity
|
||||
|
@ -47,24 +51,6 @@ public interface UserActivityLogDAO extends
|
|||
* @return Result of the Entity or referring to an Error id happened */
|
||||
public <E extends Entity> Result<E> logModify(E entity);
|
||||
|
||||
/** Create a user activity log entry for the current user of activity type ACTIVATE
|
||||
*
|
||||
* @param entity the Entity
|
||||
* @return Result of the Entity or referring to an Error id happened */
|
||||
public <E extends Entity> Result<E> logActivate(E entity);
|
||||
|
||||
/** Create a user activity log entry for the current user of activity type DEACTIVATE
|
||||
*
|
||||
* @param entity the Entity
|
||||
* @return Result of the Entity or referring to an Error id happened */
|
||||
public <E extends Entity> Result<E> logDeactivate(E entity);
|
||||
|
||||
/** Create a user activity log entry for the current user of activity type DELETE
|
||||
*
|
||||
* @param entity the Entity
|
||||
* @return Result of the Entity or referring to an Error id happened */
|
||||
public <E extends Entity> Result<E> logDelete(E entity);
|
||||
|
||||
/** Creates a user activity log entry for the current user.
|
||||
*
|
||||
* @param activityType the activity type
|
||||
|
|
|
@ -25,10 +25,15 @@ import org.springframework.transaction.annotation.Transactional;
|
|||
|
||||
import ch.ethz.seb.sebserver.gbl.api.EntityType;
|
||||
import ch.ethz.seb.sebserver.gbl.model.EntityKey;
|
||||
import ch.ethz.seb.sebserver.gbl.model.session.ClientConnection;
|
||||
import ch.ethz.seb.sebserver.gbl.model.session.ClientEvent;
|
||||
import ch.ethz.seb.sebserver.gbl.model.session.ClientEvent.EventType;
|
||||
import ch.ethz.seb.sebserver.gbl.model.session.ExtendedClientEvent;
|
||||
import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile;
|
||||
import ch.ethz.seb.sebserver.gbl.util.Result;
|
||||
import ch.ethz.seb.sebserver.webservice.datalayer.batis.ClientEventExtentionMapper;
|
||||
import ch.ethz.seb.sebserver.webservice.datalayer.batis.ClientEventExtentionMapper.ConnectionEventJoinRecord;
|
||||
import ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.ClientConnectionRecordDynamicSqlSupport;
|
||||
import ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.ClientEventRecordDynamicSqlSupport;
|
||||
import ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.ClientEventRecordMapper;
|
||||
import ch.ethz.seb.sebserver.webservice.datalayer.batis.model.ClientEventRecord;
|
||||
|
@ -44,9 +49,14 @@ import ch.ethz.seb.sebserver.webservice.servicelayer.dao.TransactionHandler;
|
|||
public class ClientEventDAOImpl implements ClientEventDAO {
|
||||
|
||||
private final ClientEventRecordMapper clientEventRecordMapper;
|
||||
private final ClientEventExtentionMapper clientEventExtentionMapper;
|
||||
|
||||
protected ClientEventDAOImpl(
|
||||
final ClientEventRecordMapper clientEventRecordMapper,
|
||||
final ClientEventExtentionMapper clientEventExtentionMapper) {
|
||||
|
||||
protected ClientEventDAOImpl(final ClientEventRecordMapper clientEventRecordMapper) {
|
||||
this.clientEventRecordMapper = clientEventRecordMapper;
|
||||
this.clientEventExtentionMapper = clientEventExtentionMapper;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -100,7 +110,54 @@ public class ClientEventDAOImpl implements ClientEventDAO {
|
|||
.flatMap(DAOLoggingSupport::logAndSkipOnError)
|
||||
.filter(predicate)
|
||||
.collect(Collectors.toList()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Result<Collection<ExtendedClientEvent>> allMatchingExtended(
|
||||
final FilterMap filterMap,
|
||||
final Predicate<ExtendedClientEvent> predicate) {
|
||||
|
||||
return Result.tryCatch(() -> this.clientEventExtentionMapper.selectByExample()
|
||||
.where(
|
||||
ClientConnectionRecordDynamicSqlSupport.institutionId,
|
||||
isEqualToWhenPresent(filterMap.getInstitutionId()))
|
||||
.and(
|
||||
ClientConnectionRecordDynamicSqlSupport.examId,
|
||||
isEqualToWhenPresent(filterMap.getClientEventExamId()))
|
||||
.and(
|
||||
ClientConnectionRecordDynamicSqlSupport.examUserSessionIdentifer,
|
||||
SqlBuilder.isLikeWhenPresent(filterMap.getSQLWildcard(ClientConnection.FILTER_ATTR_SESSION_ID)))
|
||||
.and(
|
||||
ClientEventRecordDynamicSqlSupport.connectionId,
|
||||
isEqualToWhenPresent(filterMap.getClientEventConnectionId()))
|
||||
.and(
|
||||
ClientEventRecordDynamicSqlSupport.type,
|
||||
isEqualToWhenPresent(filterMap.getClientEventTypeId()))
|
||||
.and(
|
||||
ClientEventRecordDynamicSqlSupport.type,
|
||||
SqlBuilder.isNotEqualTo(EventType.LAST_PING.id))
|
||||
.and(
|
||||
ClientEventRecordDynamicSqlSupport.clientTime,
|
||||
SqlBuilder.isGreaterThanOrEqualToWhenPresent(filterMap.getClientEventClientTimeFrom()))
|
||||
.and(
|
||||
ClientEventRecordDynamicSqlSupport.clientTime,
|
||||
SqlBuilder.isLessThanOrEqualToWhenPresent(filterMap.getClientEventClientTimeTo()))
|
||||
.and(
|
||||
ClientEventRecordDynamicSqlSupport.serverTime,
|
||||
SqlBuilder.isGreaterThanOrEqualToWhenPresent(filterMap.getClientEventServerTimeFrom()))
|
||||
.and(
|
||||
ClientEventRecordDynamicSqlSupport.serverTime,
|
||||
SqlBuilder.isLessThanOrEqualToWhenPresent(filterMap.getClientEventServerTimeTo()))
|
||||
.and(
|
||||
ClientEventRecordDynamicSqlSupport.text,
|
||||
SqlBuilder.isLikeWhenPresent(filterMap.getClientEventText()))
|
||||
.build()
|
||||
.execute()
|
||||
.stream()
|
||||
.map(ClientEventDAOImpl::toDomainModelExtended)
|
||||
.flatMap(DAOLoggingSupport::logAndSkipOnError)
|
||||
.filter(predicate)
|
||||
.collect(Collectors.toList()));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -183,7 +240,23 @@ public class ClientEventDAOImpl implements ClientEventDAO {
|
|||
(numericValue != null) ? numericValue.doubleValue() : null,
|
||||
record.getText());
|
||||
});
|
||||
}
|
||||
|
||||
private static Result<ExtendedClientEvent> toDomainModelExtended(final ConnectionEventJoinRecord record) {
|
||||
return Result.tryCatch(() -> {
|
||||
|
||||
return new ExtendedClientEvent(
|
||||
record.institution_id,
|
||||
record.exam_id,
|
||||
record.exam_user_session_identifer,
|
||||
record.id,
|
||||
record.connection_id,
|
||||
(record.type != null) ? EventType.byId(record.type) : EventType.UNKNOWN,
|
||||
record.client_time,
|
||||
record.server_time,
|
||||
(record.numeric_value != null) ? record.numeric_value.doubleValue() : null,
|
||||
record.text);
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -29,8 +29,11 @@ import org.springframework.stereotype.Component;
|
|||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
|
||||
import ch.ethz.seb.sebserver.gbl.Constants;
|
||||
import ch.ethz.seb.sebserver.gbl.api.EntityType;
|
||||
import ch.ethz.seb.sebserver.gbl.api.JSONMapper;
|
||||
import ch.ethz.seb.sebserver.gbl.model.Entity;
|
||||
import ch.ethz.seb.sebserver.gbl.model.EntityKey;
|
||||
import ch.ethz.seb.sebserver.gbl.model.user.UserActivityLog;
|
||||
|
@ -59,15 +62,18 @@ public class UserActivityLogDAOImpl implements UserActivityLogDAO {
|
|||
private final UserActivityLogRecordMapper userLogRecordMapper;
|
||||
private final UserRecordMapper userRecordMapper;
|
||||
private final UserService userService;
|
||||
private final JSONMapper jsonMapper;
|
||||
|
||||
public UserActivityLogDAOImpl(
|
||||
final UserActivityLogRecordMapper userLogRecordMapper,
|
||||
final UserRecordMapper userRecordMapper,
|
||||
final UserService userService) {
|
||||
final UserService userService,
|
||||
final JSONMapper jsonMapper) {
|
||||
|
||||
this.userLogRecordMapper = userLogRecordMapper;
|
||||
this.userRecordMapper = userRecordMapper;
|
||||
this.userService = userService;
|
||||
this.jsonMapper = jsonMapper;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -81,6 +87,18 @@ public class UserActivityLogDAOImpl implements UserActivityLogDAO {
|
|||
return log(UserLogActivityType.CREATE, entity);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public <E extends Entity> Result<E> logSaveToHistory(final E entity) {
|
||||
return log(UserLogActivityType.MODIFY, entity, "SEB Exam Configuration : Save To History");
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public <E extends Entity> Result<E> logUndo(final E entity) {
|
||||
return log(UserLogActivityType.MODIFY, entity, "SEB Exam Configuration : Undo");
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public <E extends Entity> Result<E> logImport(final E entity) {
|
||||
|
@ -99,24 +117,6 @@ public class UserActivityLogDAOImpl implements UserActivityLogDAO {
|
|||
return log(UserLogActivityType.MODIFY, entity);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public <E extends Entity> Result<E> logActivate(final E entity) {
|
||||
return log(UserLogActivityType.ACTIVATE, entity);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public <E extends Entity> Result<E> logDeactivate(final E entity) {
|
||||
return log(UserLogActivityType.DEACTIVATE, entity);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public <E extends Entity> Result<E> logDelete(final E entity) {
|
||||
return log(UserLogActivityType.DELETE, entity);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public <E extends Entity> Result<E> log(
|
||||
|
@ -130,7 +130,7 @@ public class UserActivityLogDAOImpl implements UserActivityLogDAO {
|
|||
@Override
|
||||
@Transactional
|
||||
public <E extends Entity> Result<E> log(final UserLogActivityType activityType, final E entity) {
|
||||
return log(this.userService.getCurrentUser(), activityType, entity, null);
|
||||
return log(this.userService.getCurrentUser(), activityType, entity, toMessage(entity));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -273,8 +273,8 @@ public class UserActivityLogDAOImpl implements UserActivityLogDAO {
|
|||
return all(
|
||||
filterMap.getInstitutionId(),
|
||||
filterMap.getString(UserActivityLog.FILTER_ATTR_USER),
|
||||
filterMap.getUserLogFrom(UserActivityLog.FILTER_ATTR_FROM),
|
||||
filterMap.getUserLofTo(UserActivityLog.FILTER_ATTR_TO),
|
||||
filterMap.getUserLogFrom(),
|
||||
filterMap.getUserLofTo(),
|
||||
filterMap.getString(UserActivityLog.FILTER_ATTR_ACTIVITY_TYPES),
|
||||
filterMap.getString(UserActivityLog.FILTER_ATTR_ENTITY_TYPES),
|
||||
predicate);
|
||||
|
@ -312,7 +312,7 @@ public class UserActivityLogDAOImpl implements UserActivityLogDAO {
|
|||
SqlBuilder.equalTo(UserActivityLogRecordDynamicSqlSupport.userUuid))
|
||||
.where(
|
||||
UserRecordDynamicSqlSupport.institutionId,
|
||||
SqlBuilder.isEqualTo(institutionId))
|
||||
SqlBuilder.isEqualToWhenPresent(institutionId))
|
||||
.and(
|
||||
UserActivityLogRecordDynamicSqlSupport.userUuid,
|
||||
SqlBuilder.isEqualToWhenPresent(userId))
|
||||
|
@ -480,4 +480,20 @@ public class UserActivityLogDAOImpl implements UserActivityLogDAO {
|
|||
});
|
||||
}
|
||||
|
||||
private String toMessage(final Entity entity) {
|
||||
if (entity == null) {
|
||||
return Constants.EMPTY_NOTE;
|
||||
}
|
||||
|
||||
String entityAsString;
|
||||
try {
|
||||
entityAsString = entity.getName() + " = " + this.jsonMapper
|
||||
.writerWithDefaultPrettyPrinter()
|
||||
.writeValueAsString(entity.printSecureCopy());
|
||||
} catch (final JsonProcessingException e) {
|
||||
entityAsString = entity.toString();
|
||||
}
|
||||
return entityAsString;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -142,13 +142,13 @@ public class AsyncBatchEventSaveStrategy implements EventHandlingStrategy {
|
|||
.forEach(clientEventMapper::insert);
|
||||
return null;
|
||||
});
|
||||
|
||||
sqlSessionTemplate.flushStatements();
|
||||
} else {
|
||||
sleepTime += 100;
|
||||
}
|
||||
} catch (final Exception e) {
|
||||
log.error("unexpected Error while trying to batch store client-events: ", e);
|
||||
} finally {
|
||||
sqlSessionTemplate.flushStatements();
|
||||
}
|
||||
|
||||
try {
|
||||
|
|
|
@ -18,7 +18,7 @@ import ch.ethz.seb.sebserver.gbl.api.API;
|
|||
import ch.ethz.seb.sebserver.gbl.api.API.BulkActionType;
|
||||
import ch.ethz.seb.sebserver.gbl.api.EntityType;
|
||||
import ch.ethz.seb.sebserver.gbl.model.Entity;
|
||||
import ch.ethz.seb.sebserver.gbl.model.EntityKey;
|
||||
import ch.ethz.seb.sebserver.gbl.model.EntityName;
|
||||
import ch.ethz.seb.sebserver.gbl.model.EntityProcessingReport;
|
||||
import ch.ethz.seb.sebserver.gbl.model.GrantEntity;
|
||||
import ch.ethz.seb.sebserver.gbl.model.Page;
|
||||
|
@ -136,15 +136,14 @@ public abstract class ActivatableEntityController<T extends GrantEntity, M exten
|
|||
|
||||
private Result<EntityProcessingReport> setActive(final String modelId, final boolean active) {
|
||||
final EntityType entityType = this.entityDAO.entityType();
|
||||
final BulkAction bulkAction = new BulkAction(
|
||||
(active) ? BulkActionType.ACTIVATE : BulkActionType.DEACTIVATE,
|
||||
entityType,
|
||||
new EntityKey(modelId, entityType));
|
||||
|
||||
return this.entityDAO.byModelId(modelId)
|
||||
.flatMap(this.authorization::checkWrite)
|
||||
.flatMap(this::validForActivation)
|
||||
.flatMap(entity -> this.bulkActionService.createReport(bulkAction));
|
||||
.flatMap(entity -> this.bulkActionService.createReport(new BulkAction(
|
||||
(active) ? BulkActionType.ACTIVATE : BulkActionType.DEACTIVATE,
|
||||
entityType,
|
||||
new EntityName(modelId, entityType, entity.getName()))));
|
||||
}
|
||||
|
||||
protected Result<T> validForActivation(final T entity) {
|
||||
|
|
|
@ -0,0 +1,89 @@
|
|||
/*
|
||||
* Copyright (c) 2019 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.webservice.weblayer.api;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import org.mybatis.dynamic.sql.SqlTable;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import ch.ethz.seb.sebserver.gbl.api.API;
|
||||
import ch.ethz.seb.sebserver.gbl.api.API.BulkActionType;
|
||||
import ch.ethz.seb.sebserver.gbl.api.EntityType;
|
||||
import ch.ethz.seb.sebserver.gbl.model.EntityKey;
|
||||
import ch.ethz.seb.sebserver.gbl.model.session.ClientConnection;
|
||||
import ch.ethz.seb.sebserver.gbl.model.user.UserRole;
|
||||
import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile;
|
||||
import ch.ethz.seb.sebserver.gbl.util.Result;
|
||||
import ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.ClientConnectionRecordDynamicSqlSupport;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.PaginationService;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.authorization.AuthorizationService;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.bulkaction.BulkActionService;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.ClientConnectionDAO;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.UserActivityLogDAO;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.validation.BeanValidationService;
|
||||
|
||||
@WebServiceProfile
|
||||
@RestController
|
||||
@RequestMapping("${sebserver.webservice.api.admin.endpoint}" + API.SEB_CLIENT_CONNECTION_ENDPOINT)
|
||||
public class ClientConnectionController extends ReadonlyEntityController<ClientConnection, ClientConnection> {
|
||||
|
||||
protected ClientConnectionController(
|
||||
final AuthorizationService authorization,
|
||||
final BulkActionService bulkActionService,
|
||||
final ClientConnectionDAO clientConnectionDAO,
|
||||
final UserActivityLogDAO userActivityLogDAO,
|
||||
final PaginationService paginationService,
|
||||
final BeanValidationService beanValidationService) {
|
||||
|
||||
super(authorization,
|
||||
bulkActionService,
|
||||
clientConnectionDAO,
|
||||
userActivityLogDAO,
|
||||
paginationService,
|
||||
beanValidationService);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<EntityKey> getDependencies(final String modelId, final BulkActionType bulkActionType) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected SqlTable getSQLTableOfEntity() {
|
||||
return ClientConnectionRecordDynamicSqlSupport.clientConnectionRecord;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void checkReadPrivilege(final Long institutionId) {
|
||||
checkRead(institutionId);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Result<ClientConnection> checkReadAccess(final ClientConnection entity) {
|
||||
return Result.tryCatch(() -> {
|
||||
checkRead(entity.institutionId);
|
||||
return entity;
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean hasReadAccess(final ClientConnection entity) {
|
||||
return checkReadAccess(entity).hasValue();
|
||||
}
|
||||
|
||||
private void checkRead(final Long institution) {
|
||||
this.authorization.checkRole(
|
||||
institution,
|
||||
EntityType.CLIENT_EVENT,
|
||||
UserRole.EXAM_ADMIN,
|
||||
UserRole.EXAM_SUPPORTER);
|
||||
}
|
||||
}
|
|
@ -10,40 +10,44 @@ package ch.ethz.seb.sebserver.webservice.weblayer.api;
|
|||
|
||||
import java.util.Collection;
|
||||
|
||||
import javax.validation.Valid;
|
||||
|
||||
import org.mybatis.dynamic.sql.SqlTable;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMethod;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import ch.ethz.seb.sebserver.gbl.api.API;
|
||||
import ch.ethz.seb.sebserver.gbl.api.API.BulkActionType;
|
||||
import ch.ethz.seb.sebserver.gbl.api.EntityType;
|
||||
import ch.ethz.seb.sebserver.gbl.api.POSTMapper;
|
||||
import ch.ethz.seb.sebserver.gbl.api.authorization.PrivilegeType;
|
||||
import ch.ethz.seb.sebserver.gbl.model.EntityKey;
|
||||
import ch.ethz.seb.sebserver.gbl.model.Page;
|
||||
import ch.ethz.seb.sebserver.gbl.model.session.ClientConnection;
|
||||
import ch.ethz.seb.sebserver.gbl.model.session.ClientEvent;
|
||||
import ch.ethz.seb.sebserver.gbl.model.session.ExtendedClientEvent;
|
||||
import ch.ethz.seb.sebserver.gbl.model.user.UserRole;
|
||||
import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile;
|
||||
import ch.ethz.seb.sebserver.gbl.util.Result;
|
||||
import ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.ClientEventRecordDynamicSqlSupport;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.PaginationService;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.authorization.AuthorizationService;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.authorization.PermissionDeniedException;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.authorization.UserService;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.bulkaction.BulkActionService;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.ClientConnectionDAO;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.ClientEventDAO;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.FilterMap;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.UserActivityLogDAO;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.validation.BeanValidationService;
|
||||
|
||||
@WebServiceProfile
|
||||
@RestController
|
||||
@RequestMapping("${sebserver.webservice.api.admin.endpoint}" + API.SEB_CLIENT_EVENT_ENDPOINT)
|
||||
public class ClientEventController extends EntityController<ClientEvent, ClientEvent> {
|
||||
public class ClientEventController extends ReadonlyEntityController<ClientEvent, ClientEvent> {
|
||||
|
||||
private final ClientConnectionDAO clientConnectionDAO;
|
||||
private final ClientEventDAO clientEventDAO;
|
||||
|
||||
protected ClientEventController(
|
||||
final AuthorizationService authorization,
|
||||
|
@ -62,16 +66,42 @@ public class ClientEventController extends EntityController<ClientEvent, ClientE
|
|||
beanValidationService);
|
||||
|
||||
this.clientConnectionDAO = clientConnectionDAO;
|
||||
this.clientEventDAO = entityDAO;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClientEvent create(final MultiValueMap<String, String> allRequestParams, final Long institutionId) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
@RequestMapping(
|
||||
path = API.SEB_CLIENT_EVENT_SEARCH_PATH_SEGMENT,
|
||||
method = RequestMethod.GET,
|
||||
consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE,
|
||||
produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
|
||||
public Page<ExtendedClientEvent> getExtendedPage(
|
||||
@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) {
|
||||
|
||||
@Override
|
||||
public ClientEvent savePut(@Valid final ClientEvent modifyData) {
|
||||
throw new UnsupportedOperationException();
|
||||
// at least current user must have read access for specified entity type within its own institution
|
||||
checkReadPrivilege(institutionId);
|
||||
|
||||
final FilterMap filterMap = new FilterMap(allRequestParams);
|
||||
|
||||
// if current user has no read access for specified entity type within other institution
|
||||
// then the current users institutionId is put as a SQL filter criteria attribute to extends query performance
|
||||
if (!this.authorization.hasGrant(PrivilegeType.READ, getGrantEntityType())) {
|
||||
filterMap.putIfAbsent(API.PARAM_INSTITUTION_ID, String.valueOf(institutionId));
|
||||
}
|
||||
|
||||
return this.paginationService.getPage(
|
||||
pageNumber,
|
||||
pageSize,
|
||||
sort,
|
||||
getSQLTableOfEntity().name(),
|
||||
() -> this.clientEventDAO.allMatchingExtended(filterMap, this::hasReadAccess))
|
||||
.getOrThrow();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -79,11 +109,6 @@ public class ClientEventController extends EntityController<ClientEvent, ClientE
|
|||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ClientEvent createNew(final POSTMapper postParams) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected SqlTable getSQLTableOfEntity() {
|
||||
return ClientEventRecordDynamicSqlSupport.clientEventRecord;
|
||||
|
@ -107,38 +132,6 @@ public class ClientEventController extends EntityController<ClientEvent, ClientE
|
|||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void checkModifyPrivilege(final Long institutionId) {
|
||||
throw new PermissionDeniedException(
|
||||
EntityType.CLIENT_EVENT,
|
||||
PrivilegeType.MODIFY,
|
||||
this.authorization.getUserService().getCurrentUser().uuid());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Result<ClientEvent> checkModifyAccess(final ClientEvent entity) {
|
||||
throw new PermissionDeniedException(
|
||||
EntityType.CLIENT_EVENT,
|
||||
PrivilegeType.MODIFY,
|
||||
this.authorization.getUserService().getCurrentUser().uuid());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Result<ClientEvent> checkWriteAccess(final ClientEvent entity) {
|
||||
throw new PermissionDeniedException(
|
||||
EntityType.CLIENT_EVENT,
|
||||
PrivilegeType.WRITE,
|
||||
this.authorization.getUserService().getCurrentUser().uuid());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Result<ClientEvent> checkCreateAccess(final ClientEvent entity) {
|
||||
throw new PermissionDeniedException(
|
||||
EntityType.CLIENT_EVENT,
|
||||
PrivilegeType.WRITE,
|
||||
this.authorization.getUserService().getCurrentUser().uuid());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean hasReadAccess(final ClientEvent entity) {
|
||||
return true;
|
||||
|
@ -146,9 +139,10 @@ public class ClientEventController extends EntityController<ClientEvent, ClientE
|
|||
|
||||
private void checkRead(final Long institution) {
|
||||
this.authorization.checkRole(
|
||||
UserRole.EXAM_SUPPORTER,
|
||||
institution,
|
||||
EntityType.CLIENT_EVENT);
|
||||
EntityType.CLIENT_EVENT,
|
||||
UserRole.EXAM_ADMIN,
|
||||
UserRole.EXAM_SUPPORTER);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -12,7 +12,6 @@ import java.util.Collection;
|
|||
|
||||
import org.mybatis.dynamic.sql.SqlTable;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMethod;
|
||||
|
@ -21,9 +20,7 @@ import org.springframework.web.bind.annotation.RestController;
|
|||
|
||||
import ch.ethz.seb.sebserver.gbl.api.API;
|
||||
import ch.ethz.seb.sebserver.gbl.api.API.BulkActionType;
|
||||
import ch.ethz.seb.sebserver.gbl.api.POSTMapper;
|
||||
import ch.ethz.seb.sebserver.gbl.model.EntityKey;
|
||||
import ch.ethz.seb.sebserver.gbl.model.EntityProcessingReport;
|
||||
import ch.ethz.seb.sebserver.gbl.model.sebconfig.Configuration;
|
||||
import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile;
|
||||
import ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.ConfigurationRecordDynamicSqlSupport;
|
||||
|
@ -37,7 +34,7 @@ import ch.ethz.seb.sebserver.webservice.servicelayer.validation.BeanValidationSe
|
|||
@WebServiceProfile
|
||||
@RestController
|
||||
@RequestMapping("${sebserver.webservice.api.admin.endpoint}" + API.CONFIGURATION_ENDPOINT)
|
||||
public class ConfigurationController extends EntityController<Configuration, Configuration> {
|
||||
public class ConfigurationController extends ReadonlyEntityController<Configuration, Configuration> {
|
||||
|
||||
private final ConfigurationDAO configurationDAO;
|
||||
|
||||
|
@ -69,6 +66,7 @@ public class ConfigurationController extends EntityController<Configuration, Con
|
|||
return this.entityDAO.byModelId(modelId)
|
||||
.flatMap(this::checkModifyAccess)
|
||||
.flatMap(config -> this.configurationDAO.saveToHistory(config.configurationNodeId))
|
||||
.flatMap(this.userActivityLogDAO::logSaveToHistory)
|
||||
.getOrThrow();
|
||||
}
|
||||
|
||||
|
@ -82,6 +80,7 @@ public class ConfigurationController extends EntityController<Configuration, Con
|
|||
return this.entityDAO.byModelId(modelId)
|
||||
.flatMap(this::checkModifyAccess)
|
||||
.flatMap(config -> this.configurationDAO.undo(config.configurationNodeId))
|
||||
.flatMap(this.userActivityLogDAO::logUndo)
|
||||
.getOrThrow();
|
||||
}
|
||||
|
||||
|
@ -105,21 +104,6 @@ public class ConfigurationController extends EntityController<Configuration, Con
|
|||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Configuration create(final MultiValueMap<String, String> allRequestParams, final Long institutionId) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public EntityProcessingReport hardDelete(final String modelId) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Configuration createNew(final POSTMapper postParams) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected SqlTable getSQLTableOfEntity() {
|
||||
return ConfigurationRecordDynamicSqlSupport.configurationRecord;
|
||||
|
|
|
@ -140,7 +140,7 @@ public class ConfigurationNodeController extends EntityController<ConfigurationN
|
|||
|
||||
this.entityDAO.byPK(modelId)
|
||||
.flatMap(this.authorization::checkRead)
|
||||
.map(this.userActivityLogDAO::logExport);
|
||||
.flatMap(this.userActivityLogDAO::logExport);
|
||||
|
||||
final ServletOutputStream outputStream = response.getOutputStream();
|
||||
|
||||
|
|
|
@ -69,6 +69,12 @@ public class ConfigurationValueController extends EntityController<Configuration
|
|||
this.sebExamConfigService = sebExamConfigService;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Result<ConfigurationValue> logModify(final ConfigurationValue entity) {
|
||||
// Skip the modify logging for each individual ConfigurationValue
|
||||
return Result.of(entity);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ConfigurationValue createNew(final POSTMapper postParams) {
|
||||
final Long institutionId = postParams.getLong(API.PARAM_INSTITUTION_ID);
|
||||
|
|
|
@ -279,7 +279,7 @@ public abstract class EntityController<T extends Entity, M extends Entity> {
|
|||
return this.checkCreateAccess(requestModel)
|
||||
.flatMap(this::validForCreate)
|
||||
.flatMap(this.entityDAO::createNew)
|
||||
.flatMap(this.userActivityLogDAO::logCreate)
|
||||
.flatMap(this::logCreate)
|
||||
.flatMap(this::notifyCreated)
|
||||
.getOrThrow();
|
||||
}
|
||||
|
@ -297,7 +297,7 @@ public abstract class EntityController<T extends Entity, M extends Entity> {
|
|||
return this.checkModifyAccess(modifyData)
|
||||
.flatMap(this::validForSave)
|
||||
.flatMap(this.entityDAO::save)
|
||||
.flatMap(this.userActivityLogDAO::logModify)
|
||||
.flatMap(this::logModify)
|
||||
.flatMap(this::notifySaved)
|
||||
.getOrThrow();
|
||||
}
|
||||
|
@ -311,15 +311,16 @@ public abstract class EntityController<T extends Entity, M extends Entity> {
|
|||
method = RequestMethod.DELETE,
|
||||
produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
|
||||
public EntityProcessingReport hardDelete(@PathVariable final String modelId) {
|
||||
|
||||
final EntityType entityType = this.entityDAO.entityType();
|
||||
final BulkAction bulkAction = new BulkAction(
|
||||
BulkActionType.HARD_DELETE,
|
||||
entityType,
|
||||
new EntityKey(modelId, entityType));
|
||||
|
||||
return this.entityDAO.byModelId(modelId)
|
||||
.flatMap(this::checkWriteAccess)
|
||||
.flatMap(entity -> this.bulkActionService.createReport(bulkAction))
|
||||
.flatMap(entity -> this.bulkActionService.createReport(new BulkAction(
|
||||
BulkActionType.HARD_DELETE,
|
||||
entityType,
|
||||
new EntityName(modelId, entityType, entity.getName()))))
|
||||
.flatMap(this::logBulkAction)
|
||||
.getOrThrow();
|
||||
}
|
||||
|
||||
|
@ -451,6 +452,33 @@ public abstract class EntityController<T extends Entity, M extends Entity> {
|
|||
return this.entityDAO.entityType();
|
||||
}
|
||||
|
||||
/** Makes a CREATE user activity log for the specified entity.
|
||||
* This may be overwritten if the create user activity log should be skipped.
|
||||
*
|
||||
* @param entity
|
||||
* @return Result of entity */
|
||||
protected Result<T> logCreate(final T entity) {
|
||||
return this.userActivityLogDAO.logCreate(entity);
|
||||
}
|
||||
|
||||
/** Makes a MODIFY user activity log for the specified entity.
|
||||
* This may be overwritten if the create user activity log should be skipped.
|
||||
*
|
||||
* @param entity
|
||||
* @return */
|
||||
protected Result<T> logModify(final T entity) {
|
||||
return this.userActivityLogDAO.logModify(entity);
|
||||
}
|
||||
|
||||
/** Makes user activity log for a bulk action.
|
||||
*
|
||||
* @param bulkActionReport the EntityProcessingReport
|
||||
* @return Result of entity */
|
||||
protected Result<EntityProcessingReport> logBulkAction(final EntityProcessingReport bulkActionReport) {
|
||||
// TODO
|
||||
return Result.of(bulkActionReport);
|
||||
}
|
||||
|
||||
/** Implements the creation of a new entity from the post parameters given within the POSTMapper
|
||||
*
|
||||
* @param postParams contains all post parameter from request
|
||||
|
|
|
@ -100,9 +100,9 @@ public class ExamMonitoringController {
|
|||
@RequestParam final MultiValueMap<String, String> allRequestParams) {
|
||||
|
||||
this.authorization.checkRole(
|
||||
UserRole.EXAM_SUPPORTER,
|
||||
institutionId,
|
||||
EntityType.EXAM);
|
||||
EntityType.EXAM,
|
||||
UserRole.EXAM_SUPPORTER);
|
||||
|
||||
final FilterMap filterMap = new FilterMap(allRequestParams);
|
||||
|
||||
|
@ -136,9 +136,9 @@ public class ExamMonitoringController {
|
|||
@PathVariable(name = API.EXAM_API_PARAM_EXAM_ID, required = true) final Long examId) {
|
||||
|
||||
this.authorization.checkRole(
|
||||
UserRole.EXAM_SUPPORTER,
|
||||
institutionId,
|
||||
EntityType.EXAM);
|
||||
EntityType.EXAM,
|
||||
UserRole.EXAM_SUPPORTER);
|
||||
|
||||
return this.examSessionService
|
||||
.getConnectionData(examId)
|
||||
|
@ -159,9 +159,9 @@ public class ExamMonitoringController {
|
|||
@PathVariable(name = API.EXAM_API_SEB_CONNECTION_TOKEN, required = true) final String connectionToken) {
|
||||
|
||||
this.authorization.checkRole(
|
||||
UserRole.EXAM_SUPPORTER,
|
||||
institutionId,
|
||||
EntityType.EXAM);
|
||||
EntityType.EXAM,
|
||||
UserRole.EXAM_SUPPORTER);
|
||||
|
||||
return this.examSessionService
|
||||
.getConnectionData(connectionToken)
|
||||
|
|
|
@ -0,0 +1,102 @@
|
|||
/*
|
||||
* Copyright (c) 2019 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.webservice.weblayer.api;
|
||||
|
||||
import javax.validation.Valid;
|
||||
|
||||
import org.springframework.util.MultiValueMap;
|
||||
|
||||
import ch.ethz.seb.sebserver.gbl.api.EntityType;
|
||||
import ch.ethz.seb.sebserver.gbl.api.POSTMapper;
|
||||
import ch.ethz.seb.sebserver.gbl.api.authorization.PrivilegeType;
|
||||
import ch.ethz.seb.sebserver.gbl.model.Entity;
|
||||
import ch.ethz.seb.sebserver.gbl.model.EntityProcessingReport;
|
||||
import ch.ethz.seb.sebserver.gbl.util.Result;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.PaginationService;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.authorization.AuthorizationService;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.authorization.PermissionDeniedException;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.bulkaction.BulkActionService;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.EntityDAO;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.UserActivityLogDAO;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.validation.BeanValidationService;
|
||||
|
||||
public abstract class ReadonlyEntityController<T extends Entity, M extends Entity> extends EntityController<T, M> {
|
||||
|
||||
private static final String ONLY_READ_ACCESS = "Only read requests available for this entity";
|
||||
|
||||
protected ReadonlyEntityController(
|
||||
final AuthorizationService authorization,
|
||||
final BulkActionService bulkActionService,
|
||||
final EntityDAO<T, M> entityDAO,
|
||||
final UserActivityLogDAO userActivityLogDAO,
|
||||
final PaginationService paginationService,
|
||||
final BeanValidationService beanValidationService) {
|
||||
|
||||
super(
|
||||
authorization,
|
||||
bulkActionService,
|
||||
entityDAO,
|
||||
userActivityLogDAO,
|
||||
paginationService,
|
||||
beanValidationService);
|
||||
}
|
||||
|
||||
@Override
|
||||
public T savePut(@Valid final T modifyData) {
|
||||
throw new UnsupportedOperationException(ONLY_READ_ACCESS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public T create(final MultiValueMap<String, String> allRequestParams, final Long institutionId) {
|
||||
throw new UnsupportedOperationException(ONLY_READ_ACCESS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public EntityProcessingReport hardDelete(final String modelId) {
|
||||
throw new UnsupportedOperationException(ONLY_READ_ACCESS);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected M createNew(final POSTMapper postParams) {
|
||||
throw new UnsupportedOperationException(ONLY_READ_ACCESS);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void checkModifyPrivilege(final Long institutionId) {
|
||||
throw new PermissionDeniedException(
|
||||
EntityType.CLIENT_EVENT,
|
||||
PrivilegeType.MODIFY,
|
||||
this.authorization.getUserService().getCurrentUser().uuid());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Result<T> checkModifyAccess(final T entity) {
|
||||
throw new PermissionDeniedException(
|
||||
EntityType.CLIENT_EVENT,
|
||||
PrivilegeType.MODIFY,
|
||||
this.authorization.getUserService().getCurrentUser().uuid());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Result<T> checkWriteAccess(final T entity) {
|
||||
throw new PermissionDeniedException(
|
||||
EntityType.CLIENT_EVENT,
|
||||
PrivilegeType.WRITE,
|
||||
this.authorization.getUserService().getCurrentUser().uuid());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Result<M> checkCreateAccess(final M entity) {
|
||||
throw new PermissionDeniedException(
|
||||
EntityType.CLIENT_EVENT,
|
||||
PrivilegeType.WRITE,
|
||||
this.authorization.getUserService().getCurrentUser().uuid());
|
||||
}
|
||||
|
||||
}
|
|
@ -8,105 +8,72 @@
|
|||
|
||||
package ch.ethz.seb.sebserver.webservice.weblayer.api;
|
||||
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
import org.springframework.web.bind.WebDataBinder;
|
||||
import org.springframework.web.bind.annotation.InitBinder;
|
||||
import org.mybatis.dynamic.sql.SqlTable;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMethod;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import ch.ethz.seb.sebserver.gbl.api.API;
|
||||
import ch.ethz.seb.sebserver.gbl.api.EntityType;
|
||||
import ch.ethz.seb.sebserver.gbl.api.authorization.PrivilegeType;
|
||||
import ch.ethz.seb.sebserver.gbl.model.Page;
|
||||
import ch.ethz.seb.sebserver.gbl.model.user.UserActivityLog;
|
||||
import ch.ethz.seb.sebserver.gbl.model.user.UserRole;
|
||||
import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile;
|
||||
import ch.ethz.seb.sebserver.gbl.util.Result;
|
||||
import ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.UserActivityLogRecordDynamicSqlSupport;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.PaginationService;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.authorization.AuthorizationService;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.authorization.UserService;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.FilterMap;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.bulkaction.BulkActionService;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.EntityDAO;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.UserActivityLogDAO;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.validation.BeanValidationService;
|
||||
|
||||
@WebServiceProfile
|
||||
@RestController
|
||||
@RequestMapping("${sebserver.webservice.api.admin.endpoint}" + API.USER_ACTIVITY_LOG_ENDPOINT)
|
||||
public class UserActivityLogController {
|
||||
public class UserActivityLogController extends ReadonlyEntityController<UserActivityLog, UserActivityLog> {
|
||||
|
||||
private final UserActivityLogDAO userActivityLogDAO;
|
||||
private final AuthorizationService authorization;
|
||||
private final PaginationService paginationService;
|
||||
|
||||
public UserActivityLogController(
|
||||
final UserActivityLogDAO userActivityLogDAO,
|
||||
protected UserActivityLogController(
|
||||
final AuthorizationService authorization,
|
||||
final PaginationService paginationService) {
|
||||
final BulkActionService bulkActionService,
|
||||
final EntityDAO<UserActivityLog, UserActivityLog> entityDAO,
|
||||
final UserActivityLogDAO userActivityLogDAO,
|
||||
final PaginationService paginationService,
|
||||
final BeanValidationService beanValidationService) {
|
||||
|
||||
this.userActivityLogDAO = userActivityLogDAO;
|
||||
this.authorization = authorization;
|
||||
this.paginationService = paginationService;
|
||||
super(
|
||||
authorization,
|
||||
bulkActionService,
|
||||
entityDAO,
|
||||
userActivityLogDAO,
|
||||
paginationService,
|
||||
beanValidationService);
|
||||
}
|
||||
|
||||
@InitBinder
|
||||
public void initBinder(final WebDataBinder binder) throws Exception {
|
||||
this.authorization
|
||||
.getUserService()
|
||||
.addUsersInstitutionDefaultPropertySupport(binder);
|
||||
@Override
|
||||
protected void checkReadPrivilege(final Long institutionId) {
|
||||
checkRead(institutionId);
|
||||
}
|
||||
|
||||
/** Rest endpoint to get a Page UserActivityLog.
|
||||
*
|
||||
* GET /{api}/{entity-type-endpoint-name}
|
||||
*
|
||||
* GET /admin-api/v1/useractivity
|
||||
* GET /admin-api/v1/useractivity?page_number=2&page_size=10&sort=-name
|
||||
* GET /admin-api/v1/useractivity?name=seb&active=true
|
||||
*
|
||||
* @param institutionId The institution identifier of the request.
|
||||
* Default is the institution identifier of the institution of the current user
|
||||
* @param pageNumber the number of the page that is requested
|
||||
* @param pageSize the size of the page that is requested
|
||||
* @param sort the sort parameter to sort the list of entities before paging
|
||||
* the sort parameter is the name of the entity-model attribute to sort with a leading '-' sign for
|
||||
* descending sort order
|
||||
* @param allRequestParams a MultiValueMap of all request parameter that is used for filtering
|
||||
* @return Page of domain-model-entities of specified type */
|
||||
@RequestMapping(
|
||||
method = RequestMethod.GET,
|
||||
consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE,
|
||||
produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
|
||||
public Page<UserActivityLog> 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) {
|
||||
|
||||
// at least current user must have read access for specified entity type within its own institution
|
||||
checkBaseReadPrivilege(institutionId);
|
||||
|
||||
final FilterMap filterMap = new FilterMap(allRequestParams);
|
||||
filterMap.putIfAbsent(API.PARAM_INSTITUTION_ID, String.valueOf(institutionId));
|
||||
|
||||
return this.paginationService.<UserActivityLog> getPage(
|
||||
pageNumber,
|
||||
pageSize,
|
||||
sort,
|
||||
UserActivityLogRecordDynamicSqlSupport.userActivityLogRecord.name(),
|
||||
() -> this.userActivityLogDAO.allMatching(filterMap))
|
||||
.getOrThrow();
|
||||
@Override
|
||||
protected Result<UserActivityLog> checkReadAccess(final UserActivityLog entity) {
|
||||
return Result.of(entity);
|
||||
}
|
||||
|
||||
private void checkBaseReadPrivilege(final Long institutionId) {
|
||||
this.authorization.check(
|
||||
PrivilegeType.READ,
|
||||
@Override
|
||||
protected boolean hasReadAccess(final UserActivityLog entity) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected SqlTable getSQLTableOfEntity() {
|
||||
return UserActivityLogRecordDynamicSqlSupport.userActivityLogRecord;
|
||||
}
|
||||
|
||||
private void checkRead(final Long institution) {
|
||||
this.authorization.checkRole(
|
||||
institution,
|
||||
EntityType.USER_ACTIVITY_LOG,
|
||||
institutionId);
|
||||
UserRole.SEB_SERVER_ADMIN,
|
||||
UserRole.INSTITUTIONAL_ADMIN);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
<Logger name="ch.ethz.seb.sebserver.gui" level="INFO" additivity="true" />
|
||||
<Logger name="ch.ethz.seb.sebserver.webservice" level="INFO" additivity="true" />
|
||||
<Logger name="org.apache.ibatis.datasource" level="INFO" additivity="true" />
|
||||
<Logger name="org.mybatis" level="DEBUG" additivity="true" />
|
||||
<Logger name="org.mybatis.generator" level="INFO" additivity="true" />
|
||||
<Logger name="org.springframework.boot" level="INFO" additivity="true" />
|
||||
<Logger name="org.springframework.security" level="INFO" additivity="true" />
|
||||
|
@ -32,7 +33,7 @@
|
|||
<Logger name="org.springframework.web" level="INFO" additivity="true" />
|
||||
<Logger name="org.springframework.security.oauth2" level="INFO" additivity="true" />
|
||||
|
||||
<Logger name="ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig" level="DEBUG" additivity="true" />
|
||||
<Logger name="ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig" level="INFO" additivity="true" />
|
||||
<Logger name="ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.impl.SebExamConfigServiceImpl" level="TRACE" additivity="true" />
|
||||
|
||||
</springProfile>
|
||||
|
|
|
@ -941,6 +941,14 @@ sebserver.monitoring.exam.connection.event.type.WARN_LOG=Warn
|
|||
sebserver.monitoring.exam.connection.event.type.ERROR_LOG=Error
|
||||
sebserver.monitoring.exam.connection.event.type.LAST_PING=Last Ping
|
||||
|
||||
sebserver.monitoring.exam.connection.status.UNDEFINED=Undefined
|
||||
sebserver.monitoring.exam.connection.status.CONNECTION_REQUESTED=Connection Requested,
|
||||
sebserver.monitoring.exam.connection.status.AUTHENTICATED=Authenticated,
|
||||
sebserver.monitoring.exam.connection.status.ESTABLISHED=Established,
|
||||
sebserver.monitoring.exam.connection.status.CLOSED=Closed,
|
||||
sebserver.monitoring.exam.connection.status.ABORTED=Aborted,
|
||||
sebserver.monitoring.exam.connection.status.RELEASED=Released
|
||||
|
||||
################################
|
||||
# Logs
|
||||
################################
|
||||
|
@ -956,7 +964,8 @@ sebserver.userlogs.list.column.institution=Institution
|
|||
sebserver.userlogs.list.column.user=User
|
||||
sebserver.userlogs.list.column.dateTime=Date
|
||||
sebserver.userlogs.list.column.activityType=User Activity
|
||||
sebserver.userlogs.list.column.entityType=Entity
|
||||
sebserver.userlogs.list.column.entityType=Entity Type
|
||||
sebserver.userlogs.list.column.entityId=Entity-ID
|
||||
sebserver.userlogs.list.column.message=Message
|
||||
|
||||
sebserver.userlogs.details.title=User Activity Log Details
|
||||
|
@ -969,3 +978,32 @@ sebserver.seblogs.list.title=SEB Client Logs
|
|||
sebserver.seblogs.list.actions=Selected Log
|
||||
sebserver.seblogs.list.empty=No SEB client logs has been found. Please adapt or clear the filter
|
||||
|
||||
sebserver.seblogs.list.column.exam=Exam
|
||||
sebserver.seblogs.list.column.client-session=User Session-ID
|
||||
sebserver.seblogs.list.column.type=Event Type
|
||||
sebserver.seblogs.list.column.time=Event Time
|
||||
sebserver.seblogs.list.column.value=Value
|
||||
|
||||
sebserver.seblogs.details.title=SEB Client Log Details
|
||||
sebserver.seblogs.details.event.title=Event
|
||||
sebserver.seblogs.details.connection.title=SEB Connection Details
|
||||
sebserver.seblogs.details.exam.title=Exam Details
|
||||
|
||||
sebserver.seblogs.form.column.client-session=Session-ID
|
||||
sebserver.seblogs.form.column.type=Event Type
|
||||
sebserver.seblogs.form.column.server-time=Server Time
|
||||
sebserver.seblogs.form.column.client-time=SEB Client Time
|
||||
sebserver.seblogs.form.column.value=Value
|
||||
sebserver.seblogs.form.column.message=Message
|
||||
|
||||
sebserver.seblogs.form.column.connection.session-id=User Session-ID
|
||||
sebserver.seblogs.form.column.connection.address=SEB Client Address
|
||||
sebserver.seblogs.form.column.connection.token=SEB Connection Token
|
||||
sebserver.seblogs.form.column.connection.status=Connection Status
|
||||
|
||||
sebserver.seblogs.form.column.exam.name=Name
|
||||
sebserver.seblogs.form.column.exam.description=Description
|
||||
sebserver.seblogs.form.column.exam.type=Type
|
||||
sebserver.seblogs.form.column.exam.startTime=Start Time
|
||||
sebserver.seblogs.form.column.exam.endTime=End Time
|
||||
|
||||
|
|
|
@ -663,7 +663,6 @@ TabItem:selected:hover:first {
|
|||
}
|
||||
|
||||
|
||||
|
||||
Widget-ToolTip {
|
||||
padding: 1px 3px 2px 3px;
|
||||
background-color: #82be1e;
|
||||
|
|
|
@ -58,7 +58,7 @@ public class InstitutionTest {
|
|||
|
||||
json = jsonMapper.writeValueAsString(namesList);
|
||||
assertEquals(
|
||||
"[{\"entityType\":\"INSTITUTION\",\"modelId\":\"1\",\"name\":\"InstOne\"},{\"entityType\":\"INSTITUTION\",\"modelId\":\"2\",\"name\":\"InstTwo\"},{\"entityType\":\"INSTITUTION\",\"modelId\":\"3\",\"name\":\"InstThree\"}]",
|
||||
"[{\"modelId\":\"1\",\"entityType\":\"INSTITUTION\",\"name\":\"InstOne\"},{\"modelId\":\"2\",\"entityType\":\"INSTITUTION\",\"name\":\"InstTwo\"},{\"modelId\":\"3\",\"entityType\":\"INSTITUTION\",\"name\":\"InstThree\"}]",
|
||||
json);
|
||||
}
|
||||
|
||||
|
|
|
@ -41,12 +41,12 @@ public class UserActivityLogAPITest extends AdministrationAPIIntegrationTester {
|
|||
});
|
||||
|
||||
assertNotNull(logs);
|
||||
assertTrue(2 == logs.content.size());
|
||||
assertTrue(5 == logs.content.size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getAllAsSEBAdminForUser() throws Exception {
|
||||
final String token = getSebAdminAccess();
|
||||
public void getAllAsInstAdmin2ForUser() throws Exception {
|
||||
final String token = getAdminInstitution2Access();
|
||||
// for a user in another institution, the institution has to be defined
|
||||
Page<UserActivityLog> logs = this.jsonMapper.readValue(
|
||||
this.mockMvc
|
||||
|
@ -63,7 +63,7 @@ public class UserActivityLogAPITest extends AdministrationAPIIntegrationTester {
|
|||
|
||||
// for a user in the same institution no institution is needed
|
||||
logs = this.jsonMapper.readValue(
|
||||
this.mockMvc.perform(get(this.endpoint + API.USER_ACTIVITY_LOG_ENDPOINT + "?user=user2")
|
||||
this.mockMvc.perform(get(this.endpoint + API.USER_ACTIVITY_LOG_ENDPOINT + "?user=user3")
|
||||
.header("Authorization", "Bearer " + token)
|
||||
.header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_FORM_URLENCODED_VALUE))
|
||||
.andExpect(status().isOk())
|
||||
|
@ -76,7 +76,7 @@ public class UserActivityLogAPITest extends AdministrationAPIIntegrationTester {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void getAllAsSEBAdminInTimeRange() throws Exception {
|
||||
public void getAllAsInst2AdminInTimeRange() throws Exception {
|
||||
final DateTime zeroDate = DateTime.parse("1970-01-01T00:00:00Z", Constants.STANDARD_DATE_TIME_FORMATTER);
|
||||
assertEquals("0", String.valueOf(zeroDate.getMillis()));
|
||||
final String sec2 = zeroDate.plus(1000).toString(Constants.STANDARD_DATE_TIME_FORMATTER);
|
||||
|
@ -84,7 +84,7 @@ public class UserActivityLogAPITest extends AdministrationAPIIntegrationTester {
|
|||
final String sec5 = zeroDate.plus(5000).toString(Constants.STANDARD_DATE_TIME_FORMATTER);
|
||||
final String sec6 = zeroDate.plus(6000).toString(Constants.STANDARD_DATE_TIME_FORMATTER);
|
||||
|
||||
final String token = getSebAdminAccess();
|
||||
final String token = getAdminInstitution2Access();
|
||||
Page<UserActivityLog> logs = this.jsonMapper.readValue(
|
||||
this.mockMvc.perform(
|
||||
get(this.endpoint + API.USER_ACTIVITY_LOG_ENDPOINT + "?institutionId=2&from=" + sec2)
|
||||
|
@ -149,9 +149,23 @@ public class UserActivityLogAPITest extends AdministrationAPIIntegrationTester {
|
|||
public void getAllAsSEBAdminForActivityType() throws Exception {
|
||||
final String token = getSebAdminAccess();
|
||||
Page<UserActivityLog> logs = this.jsonMapper.readValue(
|
||||
this.mockMvc.perform(get(this.endpoint + API.USER_ACTIVITY_LOG_ENDPOINT + "?activity_types=CREATE")
|
||||
.header("Authorization", "Bearer " + token)
|
||||
.header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_FORM_URLENCODED_VALUE))
|
||||
this.mockMvc.perform(
|
||||
get(this.endpoint + API.USER_ACTIVITY_LOG_ENDPOINT + "?activity_types=CREATE")
|
||||
.header("Authorization", "Bearer " + token)
|
||||
.header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_FORM_URLENCODED_VALUE))
|
||||
.andExpect(status().isOk())
|
||||
.andReturn().getResponse().getContentAsString(),
|
||||
new TypeReference<Page<UserActivityLog>>() {
|
||||
});
|
||||
|
||||
assertNotNull(logs);
|
||||
assertTrue(3 == logs.content.size());
|
||||
|
||||
logs = this.jsonMapper.readValue(
|
||||
this.mockMvc.perform(
|
||||
get(this.endpoint + API.USER_ACTIVITY_LOG_ENDPOINT + "?institutionId=1&activity_types=CREATE")
|
||||
.header("Authorization", "Bearer " + token)
|
||||
.header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_FORM_URLENCODED_VALUE))
|
||||
.andExpect(status().isOk())
|
||||
.andReturn().getResponse().getContentAsString(),
|
||||
new TypeReference<Page<UserActivityLog>>() {
|
||||
|
@ -164,7 +178,7 @@ public class UserActivityLogAPITest extends AdministrationAPIIntegrationTester {
|
|||
this.mockMvc
|
||||
.perform(
|
||||
get(this.endpoint + API.USER_ACTIVITY_LOG_ENDPOINT
|
||||
+ "?activity_types=CREATE,MODIFY")
|
||||
+ "?institutionId=1&activity_types=CREATE,MODIFY")
|
||||
.header("Authorization", "Bearer " + token)
|
||||
.header(HttpHeaders.CONTENT_TYPE,
|
||||
MediaType.APPLICATION_FORM_URLENCODED_VALUE))
|
||||
|
@ -177,12 +191,13 @@ public class UserActivityLogAPITest extends AdministrationAPIIntegrationTester {
|
|||
assertTrue(2 == logs.content.size());
|
||||
|
||||
// for other institution (2)
|
||||
final String adminInstitution2Access = getAdminInstitution2Access();
|
||||
logs = this.jsonMapper.readValue(
|
||||
this.mockMvc
|
||||
.perform(
|
||||
get(this.endpoint + API.USER_ACTIVITY_LOG_ENDPOINT
|
||||
+ "?institutionId=2&activity_types=CREATE,MODIFY")
|
||||
.header("Authorization", "Bearer " + token)
|
||||
.header("Authorization", "Bearer " + adminInstitution2Access)
|
||||
.header(HttpHeaders.CONTENT_TYPE,
|
||||
MediaType.APPLICATION_FORM_URLENCODED_VALUE))
|
||||
.andExpect(status().isOk())
|
||||
|
@ -199,9 +214,10 @@ public class UserActivityLogAPITest extends AdministrationAPIIntegrationTester {
|
|||
final String token = getSebAdminAccess();
|
||||
Page<UserActivityLog> logs = this.jsonMapper.readValue(
|
||||
this.mockMvc
|
||||
.perform(get(this.endpoint + API.USER_ACTIVITY_LOG_ENDPOINT + "?entity_types=INSTITUTION")
|
||||
.header("Authorization", "Bearer " + token)
|
||||
.header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_FORM_URLENCODED_VALUE))
|
||||
.perform(get(this.endpoint + API.USER_ACTIVITY_LOG_ENDPOINT
|
||||
+ "?institutionId=1&entity_types=INSTITUTION")
|
||||
.header("Authorization", "Bearer " + token)
|
||||
.header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_FORM_URLENCODED_VALUE))
|
||||
.andExpect(status().isOk())
|
||||
.andReturn().getResponse().getContentAsString(),
|
||||
new TypeReference<Page<UserActivityLog>>() {
|
||||
|
@ -214,7 +230,7 @@ public class UserActivityLogAPITest extends AdministrationAPIIntegrationTester {
|
|||
this.mockMvc
|
||||
.perform(
|
||||
get(this.endpoint + API.USER_ACTIVITY_LOG_ENDPOINT
|
||||
+ "?entity_types=INSTITUTION,EXAM")
|
||||
+ "?institutionId=1&entity_types=INSTITUTION,EXAM")
|
||||
.header("Authorization", "Bearer " + token)
|
||||
.header(HttpHeaders.CONTENT_TYPE,
|
||||
MediaType.APPLICATION_FORM_URLENCODED_VALUE))
|
||||
|
@ -226,12 +242,13 @@ public class UserActivityLogAPITest extends AdministrationAPIIntegrationTester {
|
|||
assertNotNull(logs);
|
||||
assertTrue(2 == logs.content.size());
|
||||
|
||||
final String adminInstitution2Access = getAdminInstitution2Access();
|
||||
logs = this.jsonMapper.readValue(
|
||||
this.mockMvc
|
||||
.perform(
|
||||
get(this.endpoint + API.USER_ACTIVITY_LOG_ENDPOINT
|
||||
+ "?entity_types=INSTITUTION,EXAM&institutionId=2")
|
||||
.header("Authorization", "Bearer " + token)
|
||||
.header("Authorization", "Bearer " + adminInstitution2Access)
|
||||
.header(HttpHeaders.CONTENT_TYPE,
|
||||
MediaType.APPLICATION_FORM_URLENCODED_VALUE))
|
||||
.andExpect(status().isOk())
|
||||
|
|
Loading…
Add table
Reference in a new issue