From 25485b1fd4deca38425fc75d30b462a700b5a7e9 Mon Sep 17 00:00:00 2001 From: anhefti Date: Thu, 13 Feb 2020 16:46:36 +0100 Subject: [PATCH] GUI improvements --- .../ch/ethz/seb/sebserver/gbl/Constants.java | 273 +-- .../sebserver/gbl/model/user/UserInfo.java | 721 ++++---- .../seb/sebserver/gbl/model/user/UserMod.java | 569 +++--- .../ch/ethz/seb/sebserver/gbl/util/Tuple.java | 149 +- .../ethz/seb/sebserver/gbl/util/Tuple3.java | 41 + .../content/ConfigTemplateAttributeForm.java | 510 +++--- .../gui/content/ConfigTemplateForm.java | 710 ++++---- .../gui/content/ConfigTemplateList.java | 314 ++-- .../seb/sebserver/gui/content/ExamForm.java | 1524 ++++++++--------- .../gui/content/InstitutionForm.java | 339 ++-- .../gui/content/InstitutionList.java | 316 ++-- .../gui/content/SebClientConfigForm.java | 416 ++--- .../gui/content/SebClientConfigList.java | 381 +++-- .../gui/content/SebExamConfigList.java | 352 ++-- .../gui/content/SebExamConfigPropForm.java | 836 ++++----- .../content/SebExamConfigSettingsForm.java | 477 +++--- .../UserAccountChangePasswordForm.java | 290 ++-- .../gui/content/UserAccountForm.java | 570 +++--- .../gui/content/UserAccountList.java | 486 +++--- .../gui/content/UserActivityLogs.java | 555 +++--- .../gui/content/action/ActionDefinition.java | 1362 +++++++-------- .../activity/PageStateDefinitionImpl.java | 275 ++- .../gui/service/ResourceService.java | 1315 +++++++------- .../gui/service/i18n/I18nSupport.java | 367 ++-- .../i18n/impl/PolyglotPageServiceImpl.java | 590 ++++--- .../gui/widget/MultiSelectionCheckbox.java | 275 +-- src/main/resources/messages.properties | 68 +- .../resources/static/images/mandatory.png | Bin 678 -> 1703 bytes 28 files changed, 7160 insertions(+), 6921 deletions(-) create mode 100644 src/main/java/ch/ethz/seb/sebserver/gbl/util/Tuple3.java diff --git a/src/main/java/ch/ethz/seb/sebserver/gbl/Constants.java b/src/main/java/ch/ethz/seb/sebserver/gbl/Constants.java index b1ec8883..09d4551a 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gbl/Constants.java +++ b/src/main/java/ch/ethz/seb/sebserver/gbl/Constants.java @@ -1,136 +1,137 @@ -/* - * Copyright (c) 2018 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; - -import java.util.Collection; - -import org.eclipse.swt.graphics.RGB; -import org.joda.time.format.DateTimeFormat; -import org.joda.time.format.DateTimeFormatter; -import org.springframework.core.ParameterizedTypeReference; - -import com.fasterxml.jackson.core.type.TypeReference; - -import ch.ethz.seb.sebserver.gbl.api.APIMessage; -import ch.ethz.seb.sebserver.gbl.api.authorization.Privilege; - -/** Global Constants used in SEB Server web-service as well as in web-gui component */ -public final class Constants { - - public static final String DEFAULT_LANG_CODE = "en"; - public static final String DEFAULT_TIME_ZONE_CODE = "UTC"; - - public static final int SEB_FILE_HEADER_SIZE = 4; - public static final int JN_CRYPTOR_ITERATIONS = 10000; - public static final int JN_CRYPTOR_VERSION_HEADER_SIZE = 1; - - public static final String TRUE_STRING = Boolean.TRUE.toString(); - public static final String FALSE_STRING = Boolean.FALSE.toString(); - - public static final long SECOND_IN_MILLIS = 1000; - public static final long MINUTE_IN_MILLIS = 60 * SECOND_IN_MILLIS; - public static final long HOUR_IN_MILLIS = 60 * MINUTE_IN_MILLIS; - public static final long DAY_IN_MILLIS = 24 * HOUR_IN_MILLIS; - - public static final Character CARRIAGE_RETURN = '\n'; - public static final Character CURLY_BRACE_OPEN = '{'; - public static final Character CURLY_BRACE_CLOSE = '}'; - public static final Character SQUARE_BRACE_OPEN = '['; - public static final Character SQUARE_BRACE_CLOSE = ']'; - public static final Character COLON = ':'; - public static final Character SEMICOLON = ';'; - public static final Character PERCENTAGE = '%'; - public static final Character SLASH = '/'; - public static final Character BACKSLASH = '\\'; - public static final Character QUOTE = '\''; - public static final Character DOUBLE_QUOTE = '"'; - public static final Character COMMA = ','; - public static final Character PIPE = '|'; - public static final Character AMPERSAND = '&'; - public static final Character EQUALITY_SIGN = '='; - public static final Character LIST_SEPARATOR_CHAR = COMMA; - public static final Character COMPLEX_VALUE_SEPARATOR = COLON; - - public static final String NULL = "null"; - public static final String PERCENTAGE_STRING = Constants.PERCENTAGE.toString(); - public static final String LIST_SEPARATOR = COMMA.toString(); - public static final String EMBEDDED_LIST_SEPARATOR = PIPE.toString(); - public static final String NO_NAME = "NONE"; - public static final String EMPTY_NOTE = "--"; - public static final String FORM_URL_ENCODED_SEPARATOR = AMPERSAND.toString(); - public static final String FORM_URL_ENCODED_NAME_VALUE_SEPARATOR = EQUALITY_SIGN.toString(); - public static final String URL_PORT_SEPARATOR = COLON.toString(); - public static final String URL_ADDRESS_SEPARATOR = COLON.toString() + SLASH.toString() + SLASH.toString(); - public static final String URL_PATH_SEPARATOR = SLASH.toString(); - - public static final String DEFAULT_DATE_TIME_FORMAT = "yyyy-MM-dd'T'HH:mm:ss'Z'"; - public static final String TIME_ZONE_OFFSET_TAIL_FORMAT = "|ZZ"; - - //public static final String DEFAULT_DIPLAY_DATE_TIME_FORMAT = "MM-dd-yyyy HH:mm:ss"; - public static final String DEFAULT_DISPLAY_DATE_FORMAT = "MM-dd-yyyy"; - public static final String DEFAULT_TIME_FORMAT = "HH:mm:ss"; - - public static final DateTimeFormatter STANDARD_DATE_TIME_FORMATTER = DateTimeFormat - .forPattern(DEFAULT_DATE_TIME_FORMAT) - .withZoneUTC(); - - public static final String XML_VERSION_HEADER = - ""; - public static final String XML_DOCTYPE_HEADER = - ""; - public static final String XML_PLIST_START_V1 = - ""; - public static final String XML_PLIST_END = - ""; - public static final String XML_DICT_START = - ""; - public static final String XML_DICT_END = - ""; - - public static final String XML_PLIST_NAME = "plist"; - public static final String XML_PLIST_DICT_NAME = "dict"; - public static final String XML_PLIST_ARRAY_NAME = "array"; - public static final String XML_PLIST_KEY_NAME = "key"; - public static final String XML_PLIST_BOOLEAN_TRUE = "true"; - public static final String XML_PLIST_BOOLEAN_FALSE = "false"; - public static final String XML_PLIST_STRING = "string"; - public static final String XML_PLIST_DATA = "data"; - public static final String XML_PLIST_INTEGER = "integer"; - - public static final String OAUTH2_GRANT_TYPE_PASSWORD = "password"; - public static final String OAUTH2_CLIENT_SECRET = "client_secret"; - public static final String OAUTH2_GRANT_TYPE_REFRESH_TOKEN = "refresh_token"; - public static final String OAUTH2_GRANT_TYPE_CLIENT_CREDENTIALS = "client_credentials"; - public static final String OAUTH2_SCOPE_READ = "read"; - public static final String OAUTH2_SCOPE_WRITE = "write"; - - public static final int RWT_MOUSE_BUTTON_1 = 1; - public static final int RWT_MOUSE_BUTTON_2 = 2; - public static final int RWT_MOUSE_BUTTON_3 = 3; - - public static final int GZIP_HEADER_LENGTH = 4; - public static final int GZIP_ID1 = 0x1F; - public static final int GZIP_ID2 = 0x8B; - public static final int GZIP_CM = 8; - - public static final RGB WHITE_RGB = new RGB(255, 255, 255); - public static final RGB BLACK_RGB = new RGB(0, 0, 0); - - public static final TypeReference> TYPE_REFERENCE_API_MESSAGE = - new TypeReferenceAPIMessage(); - public static final ParameterizedTypeReference> TYPE_REFERENCE_PRIVILEGES = - new TypeReferencePrivileges(); - - public static final class TypeReferenceAPIMessage extends TypeReference> { - } - - public static final class TypeReferencePrivileges extends ParameterizedTypeReference> { - } - -} +/* + * Copyright (c) 2018 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; + +import java.util.Collection; + +import org.eclipse.swt.graphics.RGB; +import org.joda.time.format.DateTimeFormat; +import org.joda.time.format.DateTimeFormatter; +import org.springframework.core.ParameterizedTypeReference; + +import com.fasterxml.jackson.core.type.TypeReference; + +import ch.ethz.seb.sebserver.gbl.api.APIMessage; +import ch.ethz.seb.sebserver.gbl.api.authorization.Privilege; + +/** Global Constants used in SEB Server web-service as well as in web-gui component */ +public final class Constants { + + public static final String DEFAULT_LANG_CODE = "en"; + public static final String DEFAULT_TIME_ZONE_CODE = "UTC"; + public static final String TOOLTIP_TEXT_KEY_SUFFIX = ".tooltip"; + + public static final int SEB_FILE_HEADER_SIZE = 4; + public static final int JN_CRYPTOR_ITERATIONS = 10000; + public static final int JN_CRYPTOR_VERSION_HEADER_SIZE = 1; + + public static final String TRUE_STRING = Boolean.TRUE.toString(); + public static final String FALSE_STRING = Boolean.FALSE.toString(); + + public static final long SECOND_IN_MILLIS = 1000; + public static final long MINUTE_IN_MILLIS = 60 * SECOND_IN_MILLIS; + public static final long HOUR_IN_MILLIS = 60 * MINUTE_IN_MILLIS; + public static final long DAY_IN_MILLIS = 24 * HOUR_IN_MILLIS; + + public static final Character CARRIAGE_RETURN = '\n'; + public static final Character CURLY_BRACE_OPEN = '{'; + public static final Character CURLY_BRACE_CLOSE = '}'; + public static final Character SQUARE_BRACE_OPEN = '['; + public static final Character SQUARE_BRACE_CLOSE = ']'; + public static final Character COLON = ':'; + public static final Character SEMICOLON = ';'; + public static final Character PERCENTAGE = '%'; + public static final Character SLASH = '/'; + public static final Character BACKSLASH = '\\'; + public static final Character QUOTE = '\''; + public static final Character DOUBLE_QUOTE = '"'; + public static final Character COMMA = ','; + public static final Character PIPE = '|'; + public static final Character AMPERSAND = '&'; + public static final Character EQUALITY_SIGN = '='; + public static final Character LIST_SEPARATOR_CHAR = COMMA; + public static final Character COMPLEX_VALUE_SEPARATOR = COLON; + + public static final String NULL = "null"; + public static final String PERCENTAGE_STRING = Constants.PERCENTAGE.toString(); + public static final String LIST_SEPARATOR = COMMA.toString(); + public static final String EMBEDDED_LIST_SEPARATOR = PIPE.toString(); + public static final String NO_NAME = "NONE"; + public static final String EMPTY_NOTE = "--"; + public static final String FORM_URL_ENCODED_SEPARATOR = AMPERSAND.toString(); + public static final String FORM_URL_ENCODED_NAME_VALUE_SEPARATOR = EQUALITY_SIGN.toString(); + public static final String URL_PORT_SEPARATOR = COLON.toString(); + public static final String URL_ADDRESS_SEPARATOR = COLON.toString() + SLASH.toString() + SLASH.toString(); + public static final String URL_PATH_SEPARATOR = SLASH.toString(); + + public static final String DEFAULT_DATE_TIME_FORMAT = "yyyy-MM-dd'T'HH:mm:ss'Z'"; + public static final String TIME_ZONE_OFFSET_TAIL_FORMAT = "|ZZ"; + + //public static final String DEFAULT_DIPLAY_DATE_TIME_FORMAT = "MM-dd-yyyy HH:mm:ss"; + public static final String DEFAULT_DISPLAY_DATE_FORMAT = "MM-dd-yyyy"; + public static final String DEFAULT_TIME_FORMAT = "HH:mm:ss"; + + public static final DateTimeFormatter STANDARD_DATE_TIME_FORMATTER = DateTimeFormat + .forPattern(DEFAULT_DATE_TIME_FORMAT) + .withZoneUTC(); + + public static final String XML_VERSION_HEADER = + ""; + public static final String XML_DOCTYPE_HEADER = + ""; + public static final String XML_PLIST_START_V1 = + ""; + public static final String XML_PLIST_END = + ""; + public static final String XML_DICT_START = + ""; + public static final String XML_DICT_END = + ""; + + public static final String XML_PLIST_NAME = "plist"; + public static final String XML_PLIST_DICT_NAME = "dict"; + public static final String XML_PLIST_ARRAY_NAME = "array"; + public static final String XML_PLIST_KEY_NAME = "key"; + public static final String XML_PLIST_BOOLEAN_TRUE = "true"; + public static final String XML_PLIST_BOOLEAN_FALSE = "false"; + public static final String XML_PLIST_STRING = "string"; + public static final String XML_PLIST_DATA = "data"; + public static final String XML_PLIST_INTEGER = "integer"; + + public static final String OAUTH2_GRANT_TYPE_PASSWORD = "password"; + public static final String OAUTH2_CLIENT_SECRET = "client_secret"; + public static final String OAUTH2_GRANT_TYPE_REFRESH_TOKEN = "refresh_token"; + public static final String OAUTH2_GRANT_TYPE_CLIENT_CREDENTIALS = "client_credentials"; + public static final String OAUTH2_SCOPE_READ = "read"; + public static final String OAUTH2_SCOPE_WRITE = "write"; + + public static final int RWT_MOUSE_BUTTON_1 = 1; + public static final int RWT_MOUSE_BUTTON_2 = 2; + public static final int RWT_MOUSE_BUTTON_3 = 3; + + public static final int GZIP_HEADER_LENGTH = 4; + public static final int GZIP_ID1 = 0x1F; + public static final int GZIP_ID2 = 0x8B; + public static final int GZIP_CM = 8; + + public static final RGB WHITE_RGB = new RGB(255, 255, 255); + public static final RGB BLACK_RGB = new RGB(0, 0, 0); + + public static final TypeReference> TYPE_REFERENCE_API_MESSAGE = + new TypeReferenceAPIMessage(); + public static final ParameterizedTypeReference> TYPE_REFERENCE_PRIVILEGES = + new TypeReferencePrivileges(); + + public static final class TypeReferenceAPIMessage extends TypeReference> { + } + + public static final class TypeReferencePrivileges extends ParameterizedTypeReference> { + } + +} diff --git a/src/main/java/ch/ethz/seb/sebserver/gbl/model/user/UserInfo.java b/src/main/java/ch/ethz/seb/sebserver/gbl/model/user/UserInfo.java index a312d606..f828a0a5 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gbl/model/user/UserInfo.java +++ b/src/main/java/ch/ethz/seb/sebserver/gbl/model/user/UserInfo.java @@ -1,360 +1,361 @@ -/* - * Copyright (c) 2018 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.user; - -import java.io.Serializable; -import java.util.Arrays; -import java.util.EnumSet; -import java.util.HashSet; -import java.util.Locale; -import java.util.Set; -import java.util.stream.Collectors; - -import javax.validation.constraints.Email; -import javax.validation.constraints.NotEmpty; -import javax.validation.constraints.NotNull; -import javax.validation.constraints.Size; - -import org.apache.commons.lang3.BooleanUtils; -import org.apache.commons.lang3.StringUtils; -import org.joda.time.DateTime; -import org.joda.time.DateTimeZone; -import org.springframework.util.CollectionUtils; - -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonIgnore; -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonProperty; - -import ch.ethz.seb.sebserver.gbl.api.EntityType; -import ch.ethz.seb.sebserver.gbl.model.Domain.USER; -import ch.ethz.seb.sebserver.gbl.model.Domain.USER_ROLE; -import ch.ethz.seb.sebserver.gbl.model.EntityKey; -import ch.ethz.seb.sebserver.gbl.util.Utils; - -/** The user info domain model contains primary user information - * - * This domain model is annotated and fully serializable and deserializable - * to and from JSON within the Jackson library. - * - * This domain model is immutable and thread-save */ -@JsonIgnoreProperties(ignoreUnknown = true) -public final class UserInfo implements UserAccount, Serializable { - - private static final long serialVersionUID = 2526446136264377808L; - - public static final String FILTER_ATTR_USER_NAME = "username"; - public static final String FILTER_ATTR_EMAIL = "email"; - public static final String FILTER_ATTR_LANGUAGE = "language"; - public static final String FILTER_ATTR_ROLE = "role"; - - /** The user's UUID */ - @JsonProperty(USER.ATTR_UUID) - public final String uuid; - - /** The foreign key identifier to the institution where the User belongs to */ - @NotNull - @JsonProperty(USER.ATTR_INSTITUTION_ID) - public final Long institutionId; - - @JsonProperty(USER.ATTR_CREATION_DATE) - public final DateTime creationDate; - - /** First name of the user */ - @NotNull(message = "user:name:notNull") - @Size(min = 3, max = 255, message = "user:name:size:{min}:{max}:${validatedValue}") - @JsonProperty(USER.ATTR_NAME) - public final String name; - - /** Surname of the user */ - @Size(max = 255, message = "user:surname:size:{min}:{max}:${validatedValue}") - @JsonProperty(USER.ATTR_SURNAME) - public final String surname; - - /** The internal user name */ - @NotNull(message = "user:username:notNull") - @Size(min = 3, max = 255, message = "user:username:size:{min}:{max}:${validatedValue}") - @JsonProperty(USER.ATTR_USERNAME) - public final String username; - - /** E-mail address of the user */ - @Email(message = "user:email:email:_:_:${validatedValue}") - @JsonProperty(USER.ATTR_EMAIL) - public final String email; - - /** Indicates whether this user is still active or not */ - @NotNull - @JsonProperty(USER.ATTR_ACTIVE) - public final Boolean active; - - /** The users locale */ - @NotNull(message = "user:language:notNull") - @JsonProperty(USER.ATTR_LANGUAGE) - public final Locale language; - - /** The users time zone */ - @NotNull(message = "user:timeZone:notNull") - @JsonProperty(USER.ATTR_TIMEZONE) - public final DateTimeZone timeZone; - - /** The users roles in a unmodifiable set. Is never null */ - @NotNull(message = "user:userRoles:notNull") - @NotEmpty(message = "user:userRoles:notNull") - @JsonProperty(USER_ROLE.REFERENCE_NAME) - public final Set roles; - - @JsonCreator - public UserInfo( - @JsonProperty(USER.ATTR_UUID) final String uuid, - @JsonProperty(USER.ATTR_INSTITUTION_ID) final Long institutionId, - @JsonProperty(USER.ATTR_CREATION_DATE) final DateTime creationDate, - @JsonProperty(USER.ATTR_NAME) final String name, - @JsonProperty(USER.ATTR_SURNAME) final String surname, - @JsonProperty(USER.ATTR_USERNAME) final String username, - @JsonProperty(USER.ATTR_EMAIL) final String email, - @JsonProperty(USER.ATTR_ACTIVE) final Boolean active, - @JsonProperty(USER.ATTR_LANGUAGE) final Locale language, - @JsonProperty(USER.ATTR_TIMEZONE) final DateTimeZone timeZone, - @JsonProperty(USER_ROLE.REFERENCE_NAME) final Set roles) { - - this.uuid = uuid; - this.institutionId = institutionId; - this.creationDate = creationDate; - this.name = name; - this.surname = surname; - this.username = username; - this.email = email; - this.active = BooleanUtils.isTrue(active); - this.language = language; - this.timeZone = timeZone; - this.roles = Utils.immutableSetOf(roles); - } - - @Override - public EntityType entityType() { - return EntityType.USER; - } - - @Override - public String getModelId() { - return this.uuid; - } - - public String getUuid() { - return this.uuid; - } - - @Override - public DateTime getCreationDate() { - return this.creationDate; - } - - @Override - public Long getInstitutionId() { - return this.institutionId; - } - - @Override - public String getOwnerId() { - return this.uuid; - } - - @Override - public String getName() { - return this.name; - } - - @Override - public String getSurname() { - return this.surname; - } - - @Override - public String getUsername() { - return this.username; - } - - @Override - public String getEmail() { - return this.email; - } - - @Override - public Boolean getActive() { - return this.active; - } - - @Override - public boolean isActive() { - return this.active; - } - - @Override - public Locale getLanguage() { - return this.language; - } - - @Override - public DateTimeZone getTimeZone() { - return this.timeZone; - } - - @Override - public Set getRoles() { - return this.roles; - } - - @Override - @JsonIgnore - public EnumSet getUserRoles() { - return EnumSet.copyOf( - getRoles().stream() - .map(r -> UserRole.valueOf(r)) - .collect(Collectors.toList())); - } - - public boolean hasRole(final UserRole userRole) { - if (userRole == null) { - return false; - } - return this.roles.contains(userRole.name()); - } - - public boolean hasAnyRole(final UserRole... userRole) { - if (userRole == null) { - return false; - } - return CollectionUtils.containsAny(getUserRoles(), Arrays.asList(userRole)); - } - - @JsonIgnore - @Override - public EntityKey getEntityKey() { - if (StringUtils.isBlank(this.uuid)) { - return null; - } - return new EntityKey(this.uuid, entityType()); - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((this.uuid == null) ? 0 : this.uuid.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 UserInfo other = (UserInfo) obj; - if (this.uuid == null) { - if (other.uuid != null) - return false; - } else if (!this.uuid.equals(other.uuid)) - return false; - return true; - } - - @Override - public String toString() { - final StringBuilder builder = new StringBuilder(); - builder.append("UserInfo [uuid="); - builder.append(this.uuid); - builder.append(", institutionId="); - builder.append(this.institutionId); - builder.append(", creationDate="); - builder.append(this.creationDate); - builder.append(", name="); - builder.append(this.name); - builder.append(", surname="); - builder.append(this.surname); - builder.append(", username="); - builder.append(this.username); - builder.append(", email="); - builder.append(this.email); - builder.append(", active="); - builder.append(this.active); - builder.append(", language="); - builder.append(this.language); - builder.append(", timeZone="); - builder.append(this.timeZone); - builder.append(", roles="); - builder.append(this.roles); - builder.append("]"); - return builder.toString(); - } - - /** Use this to create a copy of a given UserInfo instance. - * - * @param userInfo UserInfo instance to copy - * @return copied UserInfo instance */ - public static final UserInfo of(final UserInfo userInfo) { - return new UserInfo( - userInfo.getUuid(), - userInfo.getInstitutionId(), - userInfo.creationDate, - userInfo.getName(), - userInfo.getUsername(), - userInfo.getSurname(), - userInfo.getEmail(), - userInfo.getActive(), - userInfo.getLanguage(), - userInfo.getTimeZone(), - userInfo.roles); - } - - /** Use this to create a copy of a given UserInfo by overriding available arguments. - * - * @param userInfo UserInfo instance to copy - * @param name new name or null if the name of given userInfo should be taken - * @param surname new surname or null if the name of given userInfo should be taken - * @param username new username or null if the username of given userInfo should be taken - * @param email new email or null if the email of given userInfo should be taken - * @param language new language or null if the language of given userInfo should be taken - * @param timeZone new timeZone or null if the timeZone of given userInfo should be taken - * @param roles new timeZone or null if the roles of given userInfo should be taken - * @return copied UserInfo instance with the given attributes */ - public static final UserInfo of( - final UserInfo userInfo, - final String name, - final String username, - final String surname, - final String email, - final Locale language, - final DateTimeZone timeZone, - final String... roles) { - - return new UserInfo( - userInfo.getUuid(), - userInfo.getInstitutionId(), - userInfo.creationDate, - (name != null) ? name : userInfo.getName(), - (surname != null) ? surname : userInfo.getSurname(), - (username != null) ? username : userInfo.getUsername(), - (email != null) ? email : userInfo.getEmail(), - userInfo.getActive(), - (language != null) ? language : userInfo.getLanguage(), - (timeZone != null) ? timeZone : userInfo.getTimeZone(), - (roles != null) ? new HashSet<>(Arrays.asList(roles)) : userInfo.roles); - } - - public static final UserInfo withEMail(final UserInfo userInfo, final String email) { - return of(userInfo, null, null, null, email, null, null, (String[]) null); - } - - public static final UserInfo withRoles(final UserInfo userInfo, final String... roles) { - return of(userInfo, null, null, null, null, null, null, roles); - } -} +/* + * Copyright (c) 2018 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.user; + +import java.io.Serializable; +import java.util.Arrays; +import java.util.EnumSet; +import java.util.HashSet; +import java.util.Locale; +import java.util.Set; +import java.util.stream.Collectors; + +import javax.validation.constraints.Email; +import javax.validation.constraints.NotEmpty; +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Size; + +import org.apache.commons.lang3.BooleanUtils; +import org.apache.commons.lang3.StringUtils; +import org.joda.time.DateTime; +import org.joda.time.DateTimeZone; +import org.springframework.util.CollectionUtils; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; + +import ch.ethz.seb.sebserver.gbl.api.EntityType; +import ch.ethz.seb.sebserver.gbl.model.Domain.USER; +import ch.ethz.seb.sebserver.gbl.model.Domain.USER_ROLE; +import ch.ethz.seb.sebserver.gbl.model.EntityKey; +import ch.ethz.seb.sebserver.gbl.util.Utils; + +/** The user info domain model contains primary user information + * + * This domain model is annotated and fully serializable and deserializable + * to and from JSON within the Jackson library. + * + * This domain model is immutable and thread-save */ +@JsonIgnoreProperties(ignoreUnknown = true) +public final class UserInfo implements UserAccount, Serializable { + + private static final long serialVersionUID = 2526446136264377808L; + + public static final String FILTER_ATTR_USER_NAME = "username"; + public static final String FILTER_ATTR_EMAIL = "email"; + public static final String FILTER_ATTR_LANGUAGE = "language"; + public static final String FILTER_ATTR_ROLE = "role"; + + /** The user's UUID */ + @JsonProperty(USER.ATTR_UUID) + public final String uuid; + + /** The foreign key identifier to the institution where the User belongs to */ + @NotNull(message = "user:institutionId:notNull") + @JsonProperty(USER.ATTR_INSTITUTION_ID) + public final Long institutionId; + + @JsonProperty(USER.ATTR_CREATION_DATE) + public final DateTime creationDate; + + /** First name of the user */ + @NotNull(message = "user:name:notNull") + @Size(max = 255, message = "user:name:size:{min}:{max}:${validatedValue}") + @JsonProperty(USER.ATTR_NAME) + public final String name; + + /** Surname of the user */ + @NotNull(message = "user:surname:notNull") + @Size(max = 255, message = "user:surname:size:{min}:{max}:${validatedValue}") + @JsonProperty(USER.ATTR_SURNAME) + public final String surname; + + /** The internal user name */ + @NotNull(message = "user:username:notNull") + @Size(min = 3, max = 255, message = "user:username:size:{min}:{max}:${validatedValue}") + @JsonProperty(USER.ATTR_USERNAME) + public final String username; + + /** E-mail address of the user */ + @Email(message = "user:email:email:_:_:${validatedValue}") + @JsonProperty(USER.ATTR_EMAIL) + public final String email; + + /** Indicates whether this user is still active or not */ + @NotNull + @JsonProperty(USER.ATTR_ACTIVE) + public final Boolean active; + + /** The users locale */ + @NotNull(message = "user:language:notNull") + @JsonProperty(USER.ATTR_LANGUAGE) + public final Locale language; + + /** The users time zone */ + @NotNull(message = "user:timeZone:notNull") + @JsonProperty(USER.ATTR_TIMEZONE) + public final DateTimeZone timeZone; + + /** The users roles in a unmodifiable set. Is never null */ + @NotNull(message = "user:userRoles:notNull") + @NotEmpty(message = "user:userRoles:notNull") + @JsonProperty(USER_ROLE.REFERENCE_NAME) + public final Set roles; + + @JsonCreator + public UserInfo( + @JsonProperty(USER.ATTR_UUID) final String uuid, + @JsonProperty(USER.ATTR_INSTITUTION_ID) final Long institutionId, + @JsonProperty(USER.ATTR_CREATION_DATE) final DateTime creationDate, + @JsonProperty(USER.ATTR_NAME) final String name, + @JsonProperty(USER.ATTR_SURNAME) final String surname, + @JsonProperty(USER.ATTR_USERNAME) final String username, + @JsonProperty(USER.ATTR_EMAIL) final String email, + @JsonProperty(USER.ATTR_ACTIVE) final Boolean active, + @JsonProperty(USER.ATTR_LANGUAGE) final Locale language, + @JsonProperty(USER.ATTR_TIMEZONE) final DateTimeZone timeZone, + @JsonProperty(USER_ROLE.REFERENCE_NAME) final Set roles) { + + this.uuid = uuid; + this.institutionId = institutionId; + this.creationDate = creationDate; + this.name = name; + this.surname = surname; + this.username = username; + this.email = email; + this.active = BooleanUtils.isTrue(active); + this.language = language; + this.timeZone = timeZone; + this.roles = Utils.immutableSetOf(roles); + } + + @Override + public EntityType entityType() { + return EntityType.USER; + } + + @Override + public String getModelId() { + return this.uuid; + } + + public String getUuid() { + return this.uuid; + } + + @Override + public DateTime getCreationDate() { + return this.creationDate; + } + + @Override + public Long getInstitutionId() { + return this.institutionId; + } + + @Override + public String getOwnerId() { + return this.uuid; + } + + @Override + public String getName() { + return this.name; + } + + @Override + public String getSurname() { + return this.surname; + } + + @Override + public String getUsername() { + return this.username; + } + + @Override + public String getEmail() { + return this.email; + } + + @Override + public Boolean getActive() { + return this.active; + } + + @Override + public boolean isActive() { + return this.active; + } + + @Override + public Locale getLanguage() { + return this.language; + } + + @Override + public DateTimeZone getTimeZone() { + return this.timeZone; + } + + @Override + public Set getRoles() { + return this.roles; + } + + @Override + @JsonIgnore + public EnumSet getUserRoles() { + return EnumSet.copyOf( + getRoles().stream() + .map(UserRole::valueOf) + .collect(Collectors.toList())); + } + + public boolean hasRole(final UserRole userRole) { + if (userRole == null) { + return false; + } + return this.roles.contains(userRole.name()); + } + + public boolean hasAnyRole(final UserRole... userRole) { + if (userRole == null) { + return false; + } + return CollectionUtils.containsAny(getUserRoles(), Arrays.asList(userRole)); + } + + @JsonIgnore + @Override + public EntityKey getEntityKey() { + if (StringUtils.isBlank(this.uuid)) { + return null; + } + return new EntityKey(this.uuid, entityType()); + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((this.uuid == null) ? 0 : this.uuid.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 UserInfo other = (UserInfo) obj; + if (this.uuid == null) { + if (other.uuid != null) + return false; + } else if (!this.uuid.equals(other.uuid)) + return false; + return true; + } + + @Override + public String toString() { + final StringBuilder builder = new StringBuilder(); + builder.append("UserInfo [uuid="); + builder.append(this.uuid); + builder.append(", institutionId="); + builder.append(this.institutionId); + builder.append(", creationDate="); + builder.append(this.creationDate); + builder.append(", name="); + builder.append(this.name); + builder.append(", surname="); + builder.append(this.surname); + builder.append(", username="); + builder.append(this.username); + builder.append(", email="); + builder.append(this.email); + builder.append(", active="); + builder.append(this.active); + builder.append(", language="); + builder.append(this.language); + builder.append(", timeZone="); + builder.append(this.timeZone); + builder.append(", roles="); + builder.append(this.roles); + builder.append("]"); + return builder.toString(); + } + + /** Use this to create a copy of a given UserInfo instance. + * + * @param userInfo UserInfo instance to copy + * @return copied UserInfo instance */ + public static UserInfo of(final UserInfo userInfo) { + return new UserInfo( + userInfo.getUuid(), + userInfo.getInstitutionId(), + userInfo.creationDate, + userInfo.getName(), + userInfo.getUsername(), + userInfo.getSurname(), + userInfo.getEmail(), + userInfo.getActive(), + userInfo.getLanguage(), + userInfo.getTimeZone(), + userInfo.roles); + } + + /** Use this to create a copy of a given UserInfo by overriding available arguments. + * + * @param userInfo UserInfo instance to copy + * @param name new name or null if the name of given userInfo should be taken + * @param surname new surname or null if the name of given userInfo should be taken + * @param username new username or null if the username of given userInfo should be taken + * @param email new email or null if the email of given userInfo should be taken + * @param language new language or null if the language of given userInfo should be taken + * @param timeZone new timeZone or null if the timeZone of given userInfo should be taken + * @param roles new timeZone or null if the roles of given userInfo should be taken + * @return copied UserInfo instance with the given attributes */ + public static UserInfo of( + final UserInfo userInfo, + final String name, + final String username, + final String surname, + final String email, + final Locale language, + final DateTimeZone timeZone, + final String... roles) { + + return new UserInfo( + userInfo.getUuid(), + userInfo.getInstitutionId(), + userInfo.creationDate, + (name != null) ? name : userInfo.getName(), + (surname != null) ? surname : userInfo.getSurname(), + (username != null) ? username : userInfo.getUsername(), + (email != null) ? email : userInfo.getEmail(), + userInfo.getActive(), + (language != null) ? language : userInfo.getLanguage(), + (timeZone != null) ? timeZone : userInfo.getTimeZone(), + (roles != null) ? new HashSet<>(Arrays.asList(roles)) : userInfo.roles); + } + + public static UserInfo withEMail(final UserInfo userInfo, final String email) { + return of(userInfo, null, null, null, email, null, null, (String[]) null); + } + + public static UserInfo withRoles(final UserInfo userInfo, final String... roles) { + return of(userInfo, null, null, null, null, null, null, roles); + } +} diff --git a/src/main/java/ch/ethz/seb/sebserver/gbl/model/user/UserMod.java b/src/main/java/ch/ethz/seb/sebserver/gbl/model/user/UserMod.java index e18cd318..18f75ef1 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gbl/model/user/UserMod.java +++ b/src/main/java/ch/ethz/seb/sebserver/gbl/model/user/UserMod.java @@ -1,284 +1,285 @@ -/* - * Copyright (c) 2018 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.user; - -import java.util.Collections; -import java.util.EnumSet; -import java.util.List; -import java.util.Locale; -import java.util.Set; -import java.util.UUID; -import java.util.stream.Collectors; - -import javax.validation.constraints.Email; -import javax.validation.constraints.NotEmpty; -import javax.validation.constraints.NotNull; -import javax.validation.constraints.Size; - -import org.apache.commons.lang3.StringUtils; -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.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonProperty; - -import ch.ethz.seb.sebserver.gbl.api.EntityType; -import ch.ethz.seb.sebserver.gbl.api.POSTMapper; -import ch.ethz.seb.sebserver.gbl.model.Domain.USER; -import ch.ethz.seb.sebserver.gbl.model.Domain.USER_ROLE; -import ch.ethz.seb.sebserver.gbl.model.EntityKey; - -@JsonIgnoreProperties(ignoreUnknown = true) -public final class UserMod implements UserAccount { - - @JsonProperty(USER.ATTR_UUID) - public final String uuid; - - /** The foreign key identifier to the institution where the User belongs to */ - @NotNull(message = "user:institutionId:notNull") - @JsonProperty(USER.ATTR_INSTITUTION_ID) - public final Long institutionId; - - /** first (or full) name of the user */ - @NotNull(message = "user:name:notNull") - @Size(min = 3, max = 255, message = "user:name:size:{min}:{max}:${validatedValue}") - @JsonProperty(USER.ATTR_NAME) - public final String name; - - /** surname of the user */ - @Size(max = 255, message = "user:surname:size:{min}:{max}:${validatedValue}") - @JsonProperty(USER.ATTR_SURNAME) - public final String surname; - - /** The internal user name */ - @NotNull(message = "user:username:notNull") - @Size(min = 3, max = 255, message = "user:username:size:{min}:{max}:${validatedValue}") - @JsonProperty(USER.ATTR_USERNAME) - public final String username; - - /** E-mail address of the user */ - @Email(message = "user:email:email:_:_:${validatedValue}") - @JsonProperty(USER.ATTR_EMAIL) - public final String email; - - /** The users locale */ - @NotNull(message = "user:language:notNull") - @JsonProperty(USER.ATTR_LANGUAGE) - public final Locale language; - - /** The users time zone */ - @NotNull(message = "user:timeZone:notNull") - @JsonProperty(USER.ATTR_TIMEZONE) - public final DateTimeZone timeZone; - - /** The users roles in a unmodifiable set */ - @NotNull(message = "user:userRoles:notNull") - @NotEmpty(message = "user:userRoles:notNull") - @JsonProperty(USER_ROLE.REFERENCE_NAME) - public final Set roles; - - @NotNull(message = "user:newPassword:notNull") - @Size(min = 8, max = 255, message = "user:newPassword:size:{min}:{max}:${validatedValue}") - @JsonProperty(PasswordChange.ATTR_NAME_NEW_PASSWORD) - private final CharSequence newPassword; - - @NotNull(message = "user:confirmNewPassword:notNull") - @JsonProperty(PasswordChange.ATTR_NAME_CONFIRM_NEW_PASSWORD) - private final CharSequence confirmNewPassword; - - @JsonCreator - public UserMod( - @JsonProperty(USER.ATTR_UUID) final String uuid, - @JsonProperty(USER.ATTR_INSTITUTION_ID) final Long institutionId, - @JsonProperty(USER.ATTR_NAME) final String name, - @JsonProperty(USER.ATTR_SURNAME) final String surname, - @JsonProperty(USER.ATTR_USERNAME) final String username, - @JsonProperty(PasswordChange.ATTR_NAME_NEW_PASSWORD) final CharSequence newPassword, - @JsonProperty(PasswordChange.ATTR_NAME_CONFIRM_NEW_PASSWORD) final CharSequence confirmNewPassword, - @JsonProperty(USER.ATTR_EMAIL) final String email, - @JsonProperty(USER.ATTR_LANGUAGE) final Locale language, - @JsonProperty(USER.ATTR_TIMEZONE) final DateTimeZone timeZone, - @JsonProperty(USER_ROLE.REFERENCE_NAME) final Set roles) { - - this.uuid = uuid; - this.institutionId = institutionId; - this.newPassword = newPassword; - this.confirmNewPassword = confirmNewPassword; - this.name = name; - this.surname = surname; - this.username = username; - this.email = email; - this.language = (language != null) ? language : Locale.ENGLISH; - this.timeZone = (timeZone != null) ? timeZone : DateTimeZone.UTC; - this.roles = (roles != null) - ? Collections.unmodifiableSet(roles) - : Collections.emptySet(); - } - - public UserMod(final String modelId, final POSTMapper postAttrMapper) { - this.uuid = modelId; - this.institutionId = postAttrMapper.getLong(USER.ATTR_INSTITUTION_ID); - this.newPassword = postAttrMapper.getString(PasswordChange.ATTR_NAME_NEW_PASSWORD); - this.confirmNewPassword = postAttrMapper.getString(PasswordChange.ATTR_NAME_CONFIRM_NEW_PASSWORD); - this.name = postAttrMapper.getString(USER.ATTR_NAME); - this.surname = postAttrMapper.getString(USER.ATTR_SURNAME); - this.username = postAttrMapper.getString(USER.ATTR_USERNAME); - this.email = postAttrMapper.getString(USER.ATTR_EMAIL); - this.language = postAttrMapper.getLocale(USER.ATTR_LANGUAGE); - this.timeZone = postAttrMapper.getDateTimeZone(USER.ATTR_TIMEZONE); - this.roles = postAttrMapper.getStringSet(USER_ROLE.REFERENCE_NAME); - } - - @Override - public String getModelId() { - return this.uuid; - } - - @Override - public EntityType entityType() { - return EntityType.USER; - } - - @Override - public Long getInstitutionId() { - return this.institutionId; - } - - @Override - public DateTime getCreationDate() { - return null; - } - - @Override - public String getOwnerId() { - return this.uuid; - } - - public CharSequence getNewPassword() { - return this.newPassword; - } - - @Override - public String getName() { - return this.name; - } - - @Override - public String getSurname() { - return this.surname; - } - - @Override - public String getUsername() { - return this.username; - } - - @Override - public String getEmail() { - return this.email; - } - - @Override - public Locale getLanguage() { - return this.language; - } - - @Override - public DateTimeZone getTimeZone() { - return this.timeZone; - } - - @Override - public Set getRoles() { - return this.roles; - } - - @Override - @JsonIgnore - public EnumSet getUserRoles() { - final List roles = getRoles() - .stream() - .map(r -> UserRole.valueOf(r)) - .collect(Collectors.toList()); - if (roles.isEmpty()) { - return EnumSet.noneOf(UserRole.class); - } - return EnumSet.copyOf(roles); - } - - public CharSequence getRetypedNewPassword() { - return this.confirmNewPassword; - } - - public boolean passwordChangeRequest() { - return this.newPassword != null; - } - - public boolean newPasswordMatch() { - return passwordChangeRequest() && this.newPassword.equals(this.confirmNewPassword); - } - - @Override - public Boolean getActive() { - return false; - } - - @Override - public boolean isActive() { - return false; - } - - @JsonIgnore - @Override - public EntityKey getEntityKey() { - if (StringUtils.isBlank(this.uuid)) { - return null; - } - return new EntityKey(this.uuid, entityType()); - } - - @Override - public String toString() { - final StringBuilder builder = new StringBuilder(); - builder.append("UserMod [uuid="); - builder.append(this.uuid); - builder.append(", institutionId="); - builder.append(this.institutionId); - builder.append(", name="); - builder.append(this.name); - builder.append(", surname="); - builder.append(this.surname); - builder.append(", username="); - builder.append(this.username); - builder.append(", email="); - builder.append(this.email); - builder.append(", language="); - builder.append(this.language); - builder.append(", timeZone="); - builder.append(this.timeZone); - builder.append(", roles="); - builder.append(this.roles); - builder.append(", newPassword="); - builder.append(this.newPassword); - builder.append(", confirmNewPassword="); - builder.append(this.confirmNewPassword); - builder.append("]"); - return builder.toString(); - } - - public static UserMod createNew(final Long institutionId) { - return new UserMod( - UUID.randomUUID().toString(), - institutionId, - null, null, null, null, null, null, null, null, null); - } - -} +/* + * Copyright (c) 2018 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.user; + +import java.util.Collections; +import java.util.EnumSet; +import java.util.List; +import java.util.Locale; +import java.util.Set; +import java.util.UUID; +import java.util.stream.Collectors; + +import javax.validation.constraints.Email; +import javax.validation.constraints.NotEmpty; +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Size; + +import org.apache.commons.lang3.StringUtils; +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.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; + +import ch.ethz.seb.sebserver.gbl.api.EntityType; +import ch.ethz.seb.sebserver.gbl.api.POSTMapper; +import ch.ethz.seb.sebserver.gbl.model.Domain.USER; +import ch.ethz.seb.sebserver.gbl.model.Domain.USER_ROLE; +import ch.ethz.seb.sebserver.gbl.model.EntityKey; + +@JsonIgnoreProperties(ignoreUnknown = true) +public final class UserMod implements UserAccount { + + @JsonProperty(USER.ATTR_UUID) + public final String uuid; + + /** The foreign key identifier to the institution where the User belongs to */ + @NotNull(message = "user:institutionId:notNull") + @JsonProperty(USER.ATTR_INSTITUTION_ID) + public final Long institutionId; + + /** first (or full) name of the user */ + @NotNull(message = "user:name:notNull") + @Size(max = 255, message = "user:name:size:{min}:{max}:${validatedValue}") + @JsonProperty(USER.ATTR_NAME) + public final String name; + + /** surname of the user */ + @NotNull(message = "user:surname:notNull") + @Size(max = 255, message = "user:surname:size:{min}:{max}:${validatedValue}") + @JsonProperty(USER.ATTR_SURNAME) + public final String surname; + + /** The internal user name */ + @NotNull(message = "user:username:notNull") + @Size(min = 3, max = 255, message = "user:username:size:{min}:{max}:${validatedValue}") + @JsonProperty(USER.ATTR_USERNAME) + public final String username; + + /** E-mail address of the user */ + @Email(message = "user:email:email:_:_:${validatedValue}") + @JsonProperty(USER.ATTR_EMAIL) + public final String email; + + /** The users locale */ + @NotNull(message = "user:language:notNull") + @JsonProperty(USER.ATTR_LANGUAGE) + public final Locale language; + + /** The users time zone */ + @NotNull(message = "user:timeZone:notNull") + @JsonProperty(USER.ATTR_TIMEZONE) + public final DateTimeZone timeZone; + + /** The users roles in a unmodifiable set */ + @NotNull(message = "user:userRoles:notNull") + @NotEmpty(message = "user:userRoles:notNull") + @JsonProperty(USER_ROLE.REFERENCE_NAME) + public final Set roles; + + @NotNull(message = "user:newPassword:notNull") + @Size(min = 8, max = 255, message = "user:newPassword:size:{min}:{max}:${validatedValue}") + @JsonProperty(PasswordChange.ATTR_NAME_NEW_PASSWORD) + private final CharSequence newPassword; + + @NotNull(message = "user:confirmNewPassword:notNull") + @JsonProperty(PasswordChange.ATTR_NAME_CONFIRM_NEW_PASSWORD) + private final CharSequence confirmNewPassword; + + @JsonCreator + public UserMod( + @JsonProperty(USER.ATTR_UUID) final String uuid, + @JsonProperty(USER.ATTR_INSTITUTION_ID) final Long institutionId, + @JsonProperty(USER.ATTR_NAME) final String name, + @JsonProperty(USER.ATTR_SURNAME) final String surname, + @JsonProperty(USER.ATTR_USERNAME) final String username, + @JsonProperty(PasswordChange.ATTR_NAME_NEW_PASSWORD) final CharSequence newPassword, + @JsonProperty(PasswordChange.ATTR_NAME_CONFIRM_NEW_PASSWORD) final CharSequence confirmNewPassword, + @JsonProperty(USER.ATTR_EMAIL) final String email, + @JsonProperty(USER.ATTR_LANGUAGE) final Locale language, + @JsonProperty(USER.ATTR_TIMEZONE) final DateTimeZone timeZone, + @JsonProperty(USER_ROLE.REFERENCE_NAME) final Set roles) { + + this.uuid = uuid; + this.institutionId = institutionId; + this.newPassword = newPassword; + this.confirmNewPassword = confirmNewPassword; + this.name = name; + this.surname = surname; + this.username = username; + this.email = email; + this.language = (language != null) ? language : Locale.ENGLISH; + this.timeZone = (timeZone != null) ? timeZone : DateTimeZone.UTC; + this.roles = (roles != null) + ? Collections.unmodifiableSet(roles) + : Collections.emptySet(); + } + + public UserMod(final String modelId, final POSTMapper postAttrMapper) { + this.uuid = modelId; + this.institutionId = postAttrMapper.getLong(USER.ATTR_INSTITUTION_ID); + this.newPassword = postAttrMapper.getString(PasswordChange.ATTR_NAME_NEW_PASSWORD); + this.confirmNewPassword = postAttrMapper.getString(PasswordChange.ATTR_NAME_CONFIRM_NEW_PASSWORD); + this.name = postAttrMapper.getString(USER.ATTR_NAME); + this.surname = postAttrMapper.getString(USER.ATTR_SURNAME); + this.username = postAttrMapper.getString(USER.ATTR_USERNAME); + this.email = postAttrMapper.getString(USER.ATTR_EMAIL); + this.language = postAttrMapper.getLocale(USER.ATTR_LANGUAGE); + this.timeZone = postAttrMapper.getDateTimeZone(USER.ATTR_TIMEZONE); + this.roles = postAttrMapper.getStringSet(USER_ROLE.REFERENCE_NAME); + } + + @Override + public String getModelId() { + return this.uuid; + } + + @Override + public EntityType entityType() { + return EntityType.USER; + } + + @Override + public Long getInstitutionId() { + return this.institutionId; + } + + @Override + public DateTime getCreationDate() { + return null; + } + + @Override + public String getOwnerId() { + return this.uuid; + } + + public CharSequence getNewPassword() { + return this.newPassword; + } + + @Override + public String getName() { + return this.name; + } + + @Override + public String getSurname() { + return this.surname; + } + + @Override + public String getUsername() { + return this.username; + } + + @Override + public String getEmail() { + return this.email; + } + + @Override + public Locale getLanguage() { + return this.language; + } + + @Override + public DateTimeZone getTimeZone() { + return this.timeZone; + } + + @Override + public Set getRoles() { + return this.roles; + } + + @Override + @JsonIgnore + public EnumSet getUserRoles() { + final List roles = getRoles() + .stream() + .map(r -> UserRole.valueOf(r)) + .collect(Collectors.toList()); + if (roles.isEmpty()) { + return EnumSet.noneOf(UserRole.class); + } + return EnumSet.copyOf(roles); + } + + public CharSequence getRetypedNewPassword() { + return this.confirmNewPassword; + } + + public boolean passwordChangeRequest() { + return this.newPassword != null; + } + + public boolean newPasswordMatch() { + return passwordChangeRequest() && this.newPassword.equals(this.confirmNewPassword); + } + + @Override + public Boolean getActive() { + return false; + } + + @Override + public boolean isActive() { + return false; + } + + @JsonIgnore + @Override + public EntityKey getEntityKey() { + if (StringUtils.isBlank(this.uuid)) { + return null; + } + return new EntityKey(this.uuid, entityType()); + } + + @Override + public String toString() { + final StringBuilder builder = new StringBuilder(); + builder.append("UserMod [uuid="); + builder.append(this.uuid); + builder.append(", institutionId="); + builder.append(this.institutionId); + builder.append(", name="); + builder.append(this.name); + builder.append(", surname="); + builder.append(this.surname); + builder.append(", username="); + builder.append(this.username); + builder.append(", email="); + builder.append(this.email); + builder.append(", language="); + builder.append(this.language); + builder.append(", timeZone="); + builder.append(this.timeZone); + builder.append(", roles="); + builder.append(this.roles); + builder.append(", newPassword="); + builder.append(this.newPassword); + builder.append(", confirmNewPassword="); + builder.append(this.confirmNewPassword); + builder.append("]"); + return builder.toString(); + } + + public static UserMod createNew(final Long institutionId) { + return new UserMod( + UUID.randomUUID().toString(), + institutionId, + null, null, null, null, null, null, null, null, null); + } + +} diff --git a/src/main/java/ch/ethz/seb/sebserver/gbl/util/Tuple.java b/src/main/java/ch/ethz/seb/sebserver/gbl/util/Tuple.java index 61dab764..7f2e67d5 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gbl/util/Tuple.java +++ b/src/main/java/ch/ethz/seb/sebserver/gbl/util/Tuple.java @@ -1,70 +1,79 @@ -/* - * Copyright (c) 2018 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.util; - -/** A tuple of two elements of the same type */ -public class Tuple { - - /** The first element of the tuple */ - public final T _1; - /** The second element of the tuple */ - public final T _2; - - public Tuple(final T _1, final T _2) { - super(); - this._1 = _1; - this._2 = _2; - } - - public T get_1() { - return this._1; - } - - public T get_2() { - return this._2; - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((this._1 == null) ? 0 : this._1.hashCode()); - result = prime * result + ((this._2 == null) ? 0 : this._2.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; - @SuppressWarnings("rawtypes") - final Tuple other = (Tuple) obj; - if (this._1 == null) { - if (other._1 != null) - return false; - } else if (!this._1.equals(other._1)) - return false; - if (this._2 == null) { - if (other._2 != null) - return false; - } else if (!this._2.equals(other._2)) - return false; - return true; - } - - @Override - public String toString() { - return "( " + this._1 + ", " + this._2 + ")"; - } - -} +/* + * Copyright (c) 2018 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.util; + +/** A tuple of two elements of the same type */ +public class Tuple { + + /** The first element of the tuple */ + public final T _1; + /** The second element of the tuple */ + public final T _2; + + public Tuple(final T _1, final T _2) { + super(); + this._1 = _1; + this._2 = _2; + } + + public T get_1() { + return this._1; + } + + public T get_2() { + return this._2; + } + + @SuppressWarnings("unchecked") + public > TT adaptTo(Class type) { + if (type.equals(this.getClass())) { + return (TT) this; + } + + return null; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((this._1 == null) ? 0 : this._1.hashCode()); + result = prime * result + ((this._2 == null) ? 0 : this._2.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; + @SuppressWarnings("rawtypes") + final Tuple other = (Tuple) obj; + if (this._1 == null) { + if (other._1 != null) + return false; + } else if (!this._1.equals(other._1)) + return false; + if (this._2 == null) { + if (other._2 != null) + return false; + } else if (!this._2.equals(other._2)) + return false; + return true; + } + + @Override + public String toString() { + return "( " + this._1 + ", " + this._2 + ")"; + } + +} diff --git a/src/main/java/ch/ethz/seb/sebserver/gbl/util/Tuple3.java b/src/main/java/ch/ethz/seb/sebserver/gbl/util/Tuple3.java new file mode 100644 index 00000000..f244cec2 --- /dev/null +++ b/src/main/java/ch/ethz/seb/sebserver/gbl/util/Tuple3.java @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2020 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.util; + +import java.util.Objects; + +/** A tuple of three elements of the same type */ +public class Tuple3 extends Tuple { + + /** The third element of the tuple */ + public final T _3; + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + if (!super.equals(o)) return false; + Tuple3 tuple3 = (Tuple3) o; + return Objects.equals(_3, tuple3._3); + } + + @Override + public int hashCode() { + return Objects.hash(super.hashCode(), _3); + } + + public Tuple3(T _1, T _2, T _3) { + super(_1, _2); + this._3 = _3; + } + + public T get_3() { + return _3; + } +} diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/content/ConfigTemplateAttributeForm.java b/src/main/java/ch/ethz/seb/sebserver/gui/content/ConfigTemplateAttributeForm.java index 09ac97fd..b92eeb3e 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/content/ConfigTemplateAttributeForm.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/content/ConfigTemplateAttributeForm.java @@ -1,253 +1,257 @@ -/* - * 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.content; - -import java.util.Arrays; - -import org.eclipse.swt.SWT; -import org.eclipse.swt.layout.GridData; -import org.eclipse.swt.layout.GridLayout; -import org.eclipse.swt.widgets.Composite; -import org.springframework.context.annotation.Lazy; -import org.springframework.stereotype.Component; - -import ch.ethz.seb.sebserver.gbl.Constants; -import ch.ethz.seb.sebserver.gbl.api.API; -import ch.ethz.seb.sebserver.gbl.api.EntityType; -import ch.ethz.seb.sebserver.gbl.model.Domain; -import ch.ethz.seb.sebserver.gbl.model.EntityKey; -import ch.ethz.seb.sebserver.gbl.model.sebconfig.Configuration; -import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationNode; -import ch.ethz.seb.sebserver.gbl.model.sebconfig.Orientation; -import ch.ethz.seb.sebserver.gbl.model.sebconfig.TemplateAttribute; -import ch.ethz.seb.sebserver.gbl.model.sebconfig.TitleOrientation; -import ch.ethz.seb.sebserver.gbl.model.sebconfig.View; -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.examconfig.ExamConfigurationService; -import ch.ethz.seb.sebserver.gui.service.examconfig.InputField; -import ch.ethz.seb.sebserver.gui.service.examconfig.InputFieldBuilder; -import ch.ethz.seb.sebserver.gui.service.examconfig.impl.AttributeMapping; -import ch.ethz.seb.sebserver.gui.service.examconfig.impl.ViewContext; -import ch.ethz.seb.sebserver.gui.service.i18n.LocTextKey; -import ch.ethz.seb.sebserver.gui.service.page.PageContext; -import ch.ethz.seb.sebserver.gui.service.page.PageService; -import ch.ethz.seb.sebserver.gui.service.page.TemplateComposer; -import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestService; -import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.seb.examconfig.GetConfigurations; -import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.seb.examconfig.GetExamConfigNode; -import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.seb.examconfig.GetTemplateAttribute; -import ch.ethz.seb.sebserver.gui.service.remote.webservice.auth.CurrentUser; -import ch.ethz.seb.sebserver.gui.service.remote.webservice.auth.CurrentUser.EntityGrantCheck; -import ch.ethz.seb.sebserver.gui.widget.WidgetFactory; -import ch.ethz.seb.sebserver.gui.widget.WidgetFactory.CustomVariant; - -@Lazy -@Component -@GuiProfile -public class ConfigTemplateAttributeForm implements TemplateComposer { - - private static final LocTextKey FORM_TITLE = - new LocTextKey("sebserver.configtemplate.attr.form.title"); - private static final LocTextKey FORM_NAME_TEXT_KEY = - new LocTextKey("sebserver.configtemplate.attr.form.name"); - private static final LocTextKey FORM_TYPE_TEXT_KEY = - new LocTextKey("sebserver.configtemplate.attr.form.type"); - private static final LocTextKey FORM_VIEW_TEXT_KEY = - new LocTextKey("sebserver.configtemplate.attr.form.view"); - private static final LocTextKey FORM_GROUP_TEXT_KEY = - new LocTextKey("sebserver.configtemplate.attr.form.group"); - private static final LocTextKey FORM_VALUE_TEXT_KEY = - new LocTextKey("sebserver.configtemplate.attr.form.value"); - - private final PageService pageService; - private final RestService restService; - private final CurrentUser currentUser; - private final ResourceService resourceService; - private final ExamConfigurationService examConfigurationService; - - protected ConfigTemplateAttributeForm( - final PageService pageService, - final RestService restService, - final CurrentUser currentUser, - final ExamConfigurationService examConfigurationService) { - - this.pageService = pageService; - this.restService = restService; - this.currentUser = currentUser; - this.resourceService = pageService.getResourceService(); - this.examConfigurationService = examConfigurationService; - - } - - @Override - public void compose(final PageContext pageContext) { - final WidgetFactory widgetFactory = this.pageService.getWidgetFactory(); - - final EntityKey attributeKey = pageContext.getEntityKey(); - final EntityKey templateKey = pageContext.getParentEntityKey(); - final Long templateId = Long.valueOf(templateKey.modelId); - - try { - - final ConfigurationNode template = this.restService - .getBuilder(GetExamConfigNode.class) - .withURIVariable(API.PARAM_MODEL_ID, templateKey.modelId) - .call() - .onError(error -> pageContext.notifyLoadError(EntityType.CONFIGURATION_NODE, error)) - .getOrThrow(); - - final EntityGrantCheck entityGrant = this.currentUser.entityGrantCheck(template); - final boolean modifyGrant = entityGrant.m(); - - // the attribute - final TemplateAttribute attribute = this.restService.getBuilder(GetTemplateAttribute.class) - .withURIVariable(API.PARAM_PARENT_MODEL_ID, templateKey.modelId) - .withURIVariable(API.PARAM_MODEL_ID, attributeKey.modelId) - .call() - .onError(error -> pageContext.notifyLoadError(EntityType.CONFIGURATION_NODE, error)) - .getOrThrow(); - - // the follow-up configuration - final Configuration configuration = this.restService.getBuilder(GetConfigurations.class) - .withQueryParam(Configuration.FILTER_ATTR_CONFIGURATION_NODE_ID, templateKey.getModelId()) - .withQueryParam(Configuration.FILTER_ATTR_FOLLOWUP, Constants.TRUE_STRING) - .call() - .map(Utils::toSingleton) - .onError(error -> pageContext.notifyLoadError(EntityType.CONFIGURATION, error)) - .getOrThrow(); - - // the default page layout with title - final Composite content = widgetFactory.defaultPageLayout( - pageContext.getParent(), - FORM_TITLE); - - final PageContext formContext = pageContext.copyOf(content); - - final boolean hasView = attribute.getOrientation() != null; - - this.pageService.formBuilder(formContext) - .readonly(true) // TODO change this for next version - .addField(FormBuilder.text( - Domain.CONFIGURATION_ATTRIBUTE.ATTR_NAME, - FORM_NAME_TEXT_KEY, - attribute::getName)) - .addField(FormBuilder.text( - Domain.CONFIGURATION_ATTRIBUTE.ATTR_TYPE, - FORM_TYPE_TEXT_KEY, - () -> this.resourceService.getAttributeTypeName(attribute))) - .addFieldIf( - () -> hasView, - () -> FormBuilder.singleSelection( - Domain.ORIENTATION.ATTR_VIEW_ID, - FORM_VIEW_TEXT_KEY, - attribute.getViewModelId(), - () -> this.resourceService.getViewResources(templateKey.modelId))) - .addFieldIf( - () -> hasView, - () -> FormBuilder.text( - Domain.ORIENTATION.ATTR_GROUP_ID, - FORM_GROUP_TEXT_KEY, - attribute.getGroupId())) - .build(); - - final Composite valSpace = new Composite(content, SWT.NONE); - valSpace.setLayout(new GridLayout()); - valSpace.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false)); - - widgetFactory.labelLocalized( - valSpace, - CustomVariant.TEXT_H2, - FORM_VALUE_TEXT_KEY); - - widgetFactory.labelSeparator(valSpace); - - final Composite grid = new Composite(valSpace, SWT.NONE); - grid.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false)); - grid.setLayout(new GridLayout(6, true)); - - final PageContext valueContext = formContext.copyOf(grid); - - final Orientation defaultOrientation = getDefaultOrientation(attribute); - final AttributeMapping attributeMapping = this.examConfigurationService - .getAttributes(attribute, defaultOrientation) - .getOrThrow(); - final ViewContext viewContext = this.examConfigurationService.createViewContext( - valueContext, - configuration, - new View(-1L, "template", 10, 0, templateId), - attributeMapping, - 1, false); - - final InputFieldBuilder inputFieldBuilder = this.examConfigurationService.getInputFieldBuilder( - attribute.getConfigAttribute(), - defaultOrientation); - - final InputField createInputField = inputFieldBuilder.createInputField( - grid, - attribute.getConfigAttribute(), - viewContext); - - viewContext.registerInputField(createInputField); - - this.examConfigurationService.initInputFieldValues( - configuration.id, - Arrays.asList(viewContext)); - - this.pageService.pageActionBuilder(formContext.clearEntityKeys()) - - .newAction(ActionDefinition.SEB_EXAM_CONFIG_TEMPLATE_ATTR_FORM_SET_DEFAULT) - .withEntityKey(attributeKey) - .withParentEntityKey(templateKey) - .withExec(this.examConfigurationService::resetToDefaults) - .ignoreMoveAwayFromEdit() - .publishIf(() -> modifyGrant) - - .newAction(ActionDefinition.SEB_EXAM_CONFIG_TEMPLATE_ATTR_REMOVE_VIEW) - .withEntityKey(attributeKey) - .withParentEntityKey(templateKey) - .withExec(this.examConfigurationService::removeFromView) - .ignoreMoveAwayFromEdit() - .publishIf(() -> modifyGrant && hasView) - - .newAction(ActionDefinition.SEB_EXAM_CONFIG_TEMPLATE_ATTR_ATTACH_DEFAULT_VIEW) - .withEntityKey(attributeKey) - .withParentEntityKey(templateKey) - .withExec(this.examConfigurationService::attachToDefaultView) - .ignoreMoveAwayFromEdit() - .publishIf(() -> modifyGrant && !hasView) - - .newAction(ActionDefinition.SEB_EXAM_CONFIG_TEMPLATE_ATTR_FORM_EDIT_TEMPLATE) - .withEntityKey(templateKey) - .ignoreMoveAwayFromEdit() - .publish(); - - } catch (final Exception e) { - pageContext.notifyUnexpectedError(e); - } - } - - private Orientation getDefaultOrientation(final TemplateAttribute attribute) { - return new Orientation( - -1L, - attribute.getConfigAttribute().id, - attribute.templateId, - null, - null, - 0, - 0, - 2, - 1, - TitleOrientation.NONE); - } - -} +/* + * 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.content; + +import java.util.Collections; + +import org.apache.commons.lang3.StringUtils; +import org.eclipse.swt.SWT; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Composite; +import org.springframework.context.annotation.Lazy; +import org.springframework.stereotype.Component; + +import ch.ethz.seb.sebserver.gbl.Constants; +import ch.ethz.seb.sebserver.gbl.api.API; +import ch.ethz.seb.sebserver.gbl.api.EntityType; +import ch.ethz.seb.sebserver.gbl.model.Domain; +import ch.ethz.seb.sebserver.gbl.model.EntityKey; +import ch.ethz.seb.sebserver.gbl.model.sebconfig.Configuration; +import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationNode; +import ch.ethz.seb.sebserver.gbl.model.sebconfig.Orientation; +import ch.ethz.seb.sebserver.gbl.model.sebconfig.TemplateAttribute; +import ch.ethz.seb.sebserver.gbl.model.sebconfig.TitleOrientation; +import ch.ethz.seb.sebserver.gbl.model.sebconfig.View; +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.examconfig.ExamConfigurationService; +import ch.ethz.seb.sebserver.gui.service.examconfig.InputField; +import ch.ethz.seb.sebserver.gui.service.examconfig.InputFieldBuilder; +import ch.ethz.seb.sebserver.gui.service.examconfig.impl.AttributeMapping; +import ch.ethz.seb.sebserver.gui.service.examconfig.impl.ViewContext; +import ch.ethz.seb.sebserver.gui.service.i18n.LocTextKey; +import ch.ethz.seb.sebserver.gui.service.page.PageContext; +import ch.ethz.seb.sebserver.gui.service.page.PageService; +import ch.ethz.seb.sebserver.gui.service.page.TemplateComposer; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestService; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.seb.examconfig.GetConfigurations; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.seb.examconfig.GetExamConfigNode; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.seb.examconfig.GetTemplateAttribute; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.auth.CurrentUser; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.auth.CurrentUser.EntityGrantCheck; +import ch.ethz.seb.sebserver.gui.widget.WidgetFactory; +import ch.ethz.seb.sebserver.gui.widget.WidgetFactory.CustomVariant; + +@Lazy +@Component +@GuiProfile +public class ConfigTemplateAttributeForm implements TemplateComposer { + + private static final LocTextKey FORM_TITLE = + new LocTextKey("sebserver.configtemplate.attr.form.title"); + private static final LocTextKey FORM_NAME_TEXT_KEY = + new LocTextKey("sebserver.configtemplate.attr.form.name"); + private static final LocTextKey FORM_TYPE_TEXT_KEY = + new LocTextKey("sebserver.configtemplate.attr.form.type"); + private static final LocTextKey FORM_VIEW_TEXT_KEY = + new LocTextKey("sebserver.configtemplate.attr.form.view"); + private static final LocTextKey FORM_GROUP_TEXT_KEY = + new LocTextKey("sebserver.configtemplate.attr.form.group"); + private static final LocTextKey FORM_VALUE_TEXT_KEY = + new LocTextKey("sebserver.configtemplate.attr.form.value"); + private static final LocTextKey FORM_VALUE_TOOLTIP_TEXT_KEY = + new LocTextKey("sebserver.configtemplate.attr.form.value" + Constants.TOOLTIP_TEXT_KEY_SUFFIX); + + private final PageService pageService; + private final RestService restService; + private final CurrentUser currentUser; + private final ResourceService resourceService; + private final ExamConfigurationService examConfigurationService; + + protected ConfigTemplateAttributeForm( + final PageService pageService, + final RestService restService, + final CurrentUser currentUser, + final ExamConfigurationService examConfigurationService) { + + this.pageService = pageService; + this.restService = restService; + this.currentUser = currentUser; + this.resourceService = pageService.getResourceService(); + this.examConfigurationService = examConfigurationService; + + } + + @Override + public void compose(final PageContext pageContext) { + final WidgetFactory widgetFactory = this.pageService.getWidgetFactory(); + + final EntityKey attributeKey = pageContext.getEntityKey(); + final EntityKey templateKey = pageContext.getParentEntityKey(); + final Long templateId = Long.valueOf(templateKey.modelId); + + try { + + final ConfigurationNode template = this.restService + .getBuilder(GetExamConfigNode.class) + .withURIVariable(API.PARAM_MODEL_ID, templateKey.modelId) + .call() + .onError(error -> pageContext.notifyLoadError(EntityType.CONFIGURATION_NODE, error)) + .getOrThrow(); + + final EntityGrantCheck entityGrant = this.currentUser.entityGrantCheck(template); + final boolean modifyGrant = entityGrant.m(); + + // the attribute + final TemplateAttribute attribute = this.restService.getBuilder(GetTemplateAttribute.class) + .withURIVariable(API.PARAM_PARENT_MODEL_ID, templateKey.modelId) + .withURIVariable(API.PARAM_MODEL_ID, attributeKey.modelId) + .call() + .onError(error -> pageContext.notifyLoadError(EntityType.CONFIGURATION_NODE, error)) + .getOrThrow(); + + // the follow-up configuration + final Configuration configuration = this.restService.getBuilder(GetConfigurations.class) + .withQueryParam(Configuration.FILTER_ATTR_CONFIGURATION_NODE_ID, templateKey.getModelId()) + .withQueryParam(Configuration.FILTER_ATTR_FOLLOWUP, Constants.TRUE_STRING) + .call() + .map(Utils::toSingleton) + .onError(error -> pageContext.notifyLoadError(EntityType.CONFIGURATION, error)) + .getOrThrow(); + + // the default page layout with title + final Composite content = widgetFactory.defaultPageLayout( + pageContext.getParent(), + FORM_TITLE); + + final PageContext formContext = pageContext.copyOf(content); + + final boolean hasView = attribute.getOrientation() != null; + + this.pageService.formBuilder(formContext) + .readonly(true) // TODO change this for next version + .addField(FormBuilder.text( + Domain.CONFIGURATION_ATTRIBUTE.ATTR_NAME, + FORM_NAME_TEXT_KEY, + attribute::getName)) + .addField(FormBuilder.text( + Domain.CONFIGURATION_ATTRIBUTE.ATTR_TYPE, + FORM_TYPE_TEXT_KEY, + () -> this.resourceService.getAttributeTypeName(attribute))) + .addFieldIf( + () -> hasView, + () -> FormBuilder.singleSelection( + Domain.ORIENTATION.ATTR_VIEW_ID, + FORM_VIEW_TEXT_KEY, + attribute.getViewModelId(), + () -> this.resourceService.getViewResources(templateKey.modelId))) + .addFieldIf( + () -> hasView, + () -> FormBuilder.text( + Domain.ORIENTATION.ATTR_GROUP_ID, + FORM_GROUP_TEXT_KEY, + attribute.getGroupId())) + .build(); + + final Composite valSpace = new Composite(content, SWT.NONE); + valSpace.setLayout(new GridLayout()); + valSpace.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false)); + + widgetFactory.label(content, StringUtils.EMPTY); + widgetFactory.labelLocalized( + valSpace, + CustomVariant.TEXT_H3, + FORM_VALUE_TEXT_KEY, + FORM_VALUE_TOOLTIP_TEXT_KEY); + widgetFactory.labelSeparator(valSpace); + + final Composite grid = new Composite(valSpace, SWT.NONE); + grid.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false)); + grid.setLayout(new GridLayout(6, true)); + + final PageContext valueContext = formContext.copyOf(grid); + + final Orientation defaultOrientation = getDefaultOrientation(attribute); + final AttributeMapping attributeMapping = this.examConfigurationService + .getAttributes(attribute, defaultOrientation) + .getOrThrow(); + final ViewContext viewContext = this.examConfigurationService.createViewContext( + valueContext, + configuration, + new View(-1L, "template", 10, 0, templateId), + attributeMapping, + 1, false); + + final InputFieldBuilder inputFieldBuilder = this.examConfigurationService.getInputFieldBuilder( + attribute.getConfigAttribute(), + defaultOrientation); + + final InputField createInputField = inputFieldBuilder.createInputField( + grid, + attribute.getConfigAttribute(), + viewContext); + + viewContext.registerInputField(createInputField); + + this.examConfigurationService.initInputFieldValues( + configuration.id, + Collections.singletonList(viewContext)); + + this.pageService.pageActionBuilder(formContext.clearEntityKeys()) + + .newAction(ActionDefinition.SEB_EXAM_CONFIG_TEMPLATE_ATTR_FORM_SET_DEFAULT) + .withEntityKey(attributeKey) + .withParentEntityKey(templateKey) + .withExec(this.examConfigurationService::resetToDefaults) + .ignoreMoveAwayFromEdit() + .publishIf(() -> modifyGrant) + + .newAction(ActionDefinition.SEB_EXAM_CONFIG_TEMPLATE_ATTR_REMOVE_VIEW) + .withEntityKey(attributeKey) + .withParentEntityKey(templateKey) + .withExec(this.examConfigurationService::removeFromView) + .ignoreMoveAwayFromEdit() + .publishIf(() -> modifyGrant && hasView) + + .newAction(ActionDefinition.SEB_EXAM_CONFIG_TEMPLATE_ATTR_ATTACH_DEFAULT_VIEW) + .withEntityKey(attributeKey) + .withParentEntityKey(templateKey) + .withExec(this.examConfigurationService::attachToDefaultView) + .ignoreMoveAwayFromEdit() + .publishIf(() -> modifyGrant && !hasView) + + .newAction(ActionDefinition.SEB_EXAM_CONFIG_TEMPLATE_ATTR_FORM_EDIT_TEMPLATE) + .withEntityKey(templateKey) + .ignoreMoveAwayFromEdit() + .publish(); + + } catch (final Exception e) { + pageContext.notifyUnexpectedError(e); + } + } + + private Orientation getDefaultOrientation(final TemplateAttribute attribute) { + return new Orientation( + -1L, + attribute.getConfigAttribute().id, + attribute.templateId, + null, + null, + 0, + 0, + 2, + 1, + TitleOrientation.NONE); + } + +} diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/content/ConfigTemplateForm.java b/src/main/java/ch/ethz/seb/sebserver/gui/content/ConfigTemplateForm.java index 4d0535ec..39ca82f6 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/content/ConfigTemplateForm.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/content/ConfigTemplateForm.java @@ -1,348 +1,362 @@ -/* - * 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.content; - -import org.apache.commons.lang3.StringUtils; -import org.eclipse.swt.widgets.Composite; -import org.springframework.context.annotation.Lazy; -import org.springframework.stereotype.Component; - -import ch.ethz.seb.sebserver.gbl.Constants; -import ch.ethz.seb.sebserver.gbl.api.API; -import ch.ethz.seb.sebserver.gbl.api.EntityType; -import ch.ethz.seb.sebserver.gbl.model.Domain; -import ch.ethz.seb.sebserver.gbl.model.EntityKey; -import ch.ethz.seb.sebserver.gbl.model.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.sebconfig.TemplateAttribute; -import ch.ethz.seb.sebserver.gbl.model.user.UserInfo; -import ch.ethz.seb.sebserver.gbl.profile.GuiProfile; -import ch.ethz.seb.sebserver.gui.content.action.ActionDefinition; -import ch.ethz.seb.sebserver.gui.form.FormBuilder; -import ch.ethz.seb.sebserver.gui.form.FormHandle; -import ch.ethz.seb.sebserver.gui.service.ResourceService; -import ch.ethz.seb.sebserver.gui.service.examconfig.ExamConfigurationService; -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.PageAction; -import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestService; -import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.seb.examconfig.GetExamConfigNode; -import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.seb.examconfig.GetTemplateAttributePage; -import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.seb.examconfig.NewExamConfig; -import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.seb.examconfig.SaveExamConfig; -import ch.ethz.seb.sebserver.gui.service.remote.webservice.auth.CurrentUser; -import ch.ethz.seb.sebserver.gui.service.remote.webservice.auth.CurrentUser.EntityGrantCheck; -import ch.ethz.seb.sebserver.gui.table.ColumnDefinition; -import ch.ethz.seb.sebserver.gui.table.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; - -@Lazy -@Component -@GuiProfile -public class ConfigTemplateForm implements TemplateComposer { - - private static final LocTextKey FORM_TITLE_NEW = - new LocTextKey("sebserver.configtemplate.form.title.new"); - private static final LocTextKey FORM_TITLE = - new LocTextKey("sebserver.configtemplate.form.title"); - private static final LocTextKey FORM_NAME_TEXT_KEY = - new LocTextKey("sebserver.configtemplate.form.name"); - private static final LocTextKey FORM_DESCRIPTION_TEXT_KEY = - new LocTextKey("sebserver.configtemplate.form.description"); - private static final LocTextKey ATTRIBUTES_LIST_TITLE_TEXT_KEY = - new LocTextKey("sebserver.configtemplate.attrs.list.title"); - private static final LocTextKey ATTRIBUTES_LIST_NAME_TEXT_KEY = - new LocTextKey("sebserver.configtemplate.attrs.list.name"); - private static final LocTextKey ATTRIBUTES_LIST_VIEW_TEXT_KEY = - new LocTextKey("sebserver.configtemplate.attrs.list.view"); - private static final LocTextKey ATTRIBUTES_LIST_GROUP_TEXT_KEY = - new LocTextKey("sebserver.configtemplate.attrs.list.group"); - private static final LocTextKey ATTRIBUTES_LIST_TYPE_TEXT_KEY = - new LocTextKey("sebserver.configtemplate.attrs.list.type"); - private static final LocTextKey EMPTY_ATTRIBUTE_SELECTION_TEXT_KEY = - new LocTextKey("sebserver.configtemplate.attr.info.pleaseSelect"); - - private final PageService pageService; - private final RestService restService; - private final CurrentUser currentUser; - private final I18nSupport i18nSupport; - private final ResourceService resourceService; - private final ExamConfigurationService examConfigurationService; - - private final TableFilterAttribute nameFilter = - new TableFilterAttribute(CriteriaType.TEXT, TemplateAttribute.FILTER_ATTR_NAME); - private final TableFilterAttribute groupFilter = - new TableFilterAttribute(CriteriaType.TEXT, TemplateAttribute.FILTER_ATTR_GROUP); - - protected ConfigTemplateForm( - final PageService pageService, - final RestService restService, - final CurrentUser currentUser, - final ExamConfigurationService examConfigurationService) { - - this.pageService = pageService; - this.restService = restService; - this.currentUser = currentUser; - this.i18nSupport = pageService.getI18nSupport(); - this.resourceService = pageService.getResourceService(); - this.examConfigurationService = examConfigurationService; - - } - - @Override - public void compose(final PageContext pageContext) { - - final WidgetFactory widgetFactory = this.pageService.getWidgetFactory(); - final ResourceService resourceService = this.pageService.getResourceService(); - - final UserInfo user = this.currentUser.get(); - final EntityKey entityKey = pageContext.getEntityKey(); - final boolean isNew = entityKey == null; - - // get data or create new. Handle error if happen - final ConfigurationNode examConfig = (isNew) - ? ConfigurationNode.createNewTemplate(user.institutionId) - : this.restService - .getBuilder(GetExamConfigNode.class) - .withURIVariable(API.PARAM_MODEL_ID, entityKey.modelId) - .call() - .onError(error -> pageContext.notifyLoadError(EntityType.CONFIGURATION_NODE, error)) - .getOrThrow(); - - final EntityGrantCheck entityGrant = this.currentUser.entityGrantCheck(examConfig); - final boolean writeGrant = entityGrant.w(); - final boolean modifyGrant = entityGrant.m(); - final boolean isReadonly = pageContext.isReadonly(); - - // new PageContext with actual EntityKey - final PageContext formContext = pageContext - .withEntityKey(examConfig.getEntityKey()); - - // the default page layout with interactive title - final LocTextKey titleKey = (isNew) - ? FORM_TITLE_NEW - : FORM_TITLE; - final Composite content = widgetFactory.defaultPageLayout( - formContext.getParent(), - titleKey); - - // The SebClientConfig form - final FormHandle formHandle = this.pageService.formBuilder( - formContext.copyOf(content)) - .readonly(isReadonly) - .putStaticValueIf(() -> !isNew, - Domain.CONFIGURATION_NODE.ATTR_ID, - examConfig.getModelId()) - .putStaticValue( - Domain.CONFIGURATION_NODE.ATTR_INSTITUTION_ID, - String.valueOf(examConfig.getInstitutionId())) - .putStaticValue( - Domain.CONFIGURATION_NODE.ATTR_TYPE, - ConfigurationType.TEMPLATE.name()) - .putStaticValue( - Domain.CONFIGURATION_NODE.ATTR_STATUS, - ConfigurationStatus.IN_USE.name()) - .addField(FormBuilder.text( - Domain.CONFIGURATION_NODE.ATTR_NAME, - FORM_NAME_TEXT_KEY, - examConfig.name)) - .addField(FormBuilder.text( - Domain.CONFIGURATION_NODE.ATTR_DESCRIPTION, - FORM_DESCRIPTION_TEXT_KEY, - examConfig.description) - .asArea()) - - .buildFor((isNew) - ? this.restService.getRestCall(NewExamConfig.class) - : this.restService.getRestCall(SaveExamConfig.class)); - - final PageActionBuilder pageActionBuilder = this.pageService - .pageActionBuilder(formContext.clearEntityKeys()); - - if (isReadonly) { - - widgetFactory.label(content, ""); - widgetFactory.labelLocalizedTitle( - content, - ATTRIBUTES_LIST_TITLE_TEXT_KEY); - - final TableFilterAttribute viewFilter = new TableFilterAttribute( - CriteriaType.SINGLE_SELECTION, - TemplateAttribute.FILTER_ATTR_VIEW, - () -> this.resourceService.getViewResources(entityKey.modelId)); - final TableFilterAttribute typeFilter = new TableFilterAttribute( - CriteriaType.SINGLE_SELECTION, - TemplateAttribute.FILTER_ATTR_TYPE, - () -> this.resourceService.getAttributeTypeResources()); - - final EntityTable attrTable = - this.pageService.entityTableBuilder( - Domain.CONFIGURATION_NODE.TYPE_NAME + "_Template", - this.restService.getRestCall(GetTemplateAttributePage.class)) - .withRestCallAdapter(restCall -> restCall.withURIVariable( - API.PARAM_PARENT_MODEL_ID, - entityKey.modelId)) - .withPaging(15) - .withColumn(new ColumnDefinition<>( - Domain.CONFIGURATION_ATTRIBUTE.ATTR_NAME, - ATTRIBUTES_LIST_NAME_TEXT_KEY, - this::getAttributeName) - .withFilter(this.nameFilter) - .sortable() - .widthProportion(3)) - .withColumn(new ColumnDefinition( - Domain.CONFIGURATION_ATTRIBUTE.ATTR_TYPE, - ATTRIBUTES_LIST_TYPE_TEXT_KEY, - resourceService::getAttributeTypeName) - .withFilter(typeFilter) - .sortable() - .widthProportion(1)) - .withColumn(new ColumnDefinition<>( - Domain.ORIENTATION.ATTR_VIEW_ID, - ATTRIBUTES_LIST_VIEW_TEXT_KEY, - resourceService.getViewNameFunction(entityKey.modelId)) - .withFilter(viewFilter) - .sortable() - .widthProportion(1)) - .withColumn(new ColumnDefinition<>( - Domain.ORIENTATION.ATTR_GROUP_ID, - ATTRIBUTES_LIST_GROUP_TEXT_KEY, - TemplateAttribute::getGroupId) - .withFilter(this.groupFilter) - .sortable() - .widthProportion(1)) - .withDefaultAction(pageActionBuilder - .newAction(ActionDefinition.SEB_EXAM_CONFIG_TEMPLATE_ATTR_EDIT) - .withParentEntityKey(entityKey) - .create()) - .compose(pageContext.copyOf(content)); - - pageActionBuilder - - .newAction(ActionDefinition.SEB_EXAM_CONFIG_TEMPLATE_ATTR_EDIT) - .withParentEntityKey(entityKey) - .withSelect( - attrTable::getSelection, - PageAction::applySingleSelectionAsEntityKey, - EMPTY_ATTRIBUTE_SELECTION_TEXT_KEY) - .publishIf(() -> attrTable.hasAnyContent()) - - .newAction(ActionDefinition.SEB_EXAM_CONFIG_TEMPLATE_ATTR_SET_DEFAULT) - .withParentEntityKey(entityKey) - .withSelect( - attrTable::getSelection, - action -> this.resetToDefaults(action, attrTable), - EMPTY_ATTRIBUTE_SELECTION_TEXT_KEY) - .noEventPropagation() - .publishIf(() -> attrTable.hasAnyContent()) - - .newAction(ActionDefinition.SEB_EXAM_CONFIG_TEMPLATE_ATTR_LIST_REMOVE_VIEW) - .withParentEntityKey(entityKey) - .withSelect( - attrTable::getSelection, - action -> this.removeFormView(action, attrTable), - EMPTY_ATTRIBUTE_SELECTION_TEXT_KEY) - .noEventPropagation() - .publishIf(() -> attrTable.hasAnyContent()) - - .newAction(ActionDefinition.SEB_EXAM_CONFIG_TEMPLATE_ATTR_LIST_ATTACH_DEFAULT_VIEW) - .withParentEntityKey(entityKey) - .withSelect( - attrTable::getSelection, - action -> this.attachView(action, attrTable), - EMPTY_ATTRIBUTE_SELECTION_TEXT_KEY) - .noEventPropagation() - .publishIf(() -> attrTable.hasAnyContent()); - } - - pageActionBuilder - - .newAction(ActionDefinition.SEB_EXAM_CONFIG_TEMPLATE_NEW) - .publishIf(() -> writeGrant && isReadonly) - - .newAction(ActionDefinition.SEB_EXAM_CONFIG_TEMPLATE_MODIFY) - .withEntityKey(entityKey) - .publishIf(() -> modifyGrant && isReadonly) - - .newAction(ActionDefinition.SEB_EXAM_CONFIG_TEMPLATE_CREATE_CONFIG) - .withEntityKey(entityKey) - .withExec(SebExamConfigCreationPopup.configCreationFunction( - this.pageService, - pageContext - .withAttribute( - PageContext.AttributeKeys.CREATE_FROM_TEMPLATE, - Constants.TRUE_STRING) - .withAttribute( - PageContext.AttributeKeys.COPY_AS_TEMPLATE, - Constants.FALSE_STRING))) - .publishIf(() -> modifyGrant && isReadonly) - - .newAction(ActionDefinition.SEB_EXAM_CONFIG_TEMPLATE_SAVE) - .withEntityKey(entityKey) - .withExec(formHandle::processFormSave) - .ignoreMoveAwayFromEdit() - .publishIf(() -> !isReadonly) - - .newAction(ActionDefinition.SEB_EXAM_CONFIG_TEMPLATE_CANCEL_MODIFY) - .withEntityKey(entityKey) - .withExec(this.pageService.backToCurrentFunction()) - .publishIf(() -> !isReadonly); - - } - - private String getAttributeName(final TemplateAttribute attribute) { - - final String name = this.i18nSupport.getText( - ExamConfigurationService.ATTRIBUTE_LABEL_LOC_TEXT_PREFIX + attribute.getName(), - ""); - if (StringUtils.isNotBlank(name)) { - return attribute.getName() + " (" + name + ")"; - } else { - return attribute.getName(); - } - } - - private final PageAction resetToDefaults( - final PageAction action, - final EntityTable attrTable) { - - final PageAction resetToDefaults = this.examConfigurationService.resetToDefaults(action); - // reload the list - attrTable.applyFilter(); - return resetToDefaults; - } - - private final PageAction removeFormView( - final PageAction action, - final EntityTable attrTable) { - - final PageAction removeFormView = this.examConfigurationService.removeFromView(action); - // reload the list - attrTable.applyFilter(); - return removeFormView; - } - - private final PageAction attachView( - final PageAction action, - final EntityTable attrTable) { - - final PageAction attachView = this.examConfigurationService.attachToDefaultView(action); - // reload the list - attrTable.applyFilter(); - return attachView; - } - -} +/* + * 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.content; + +import org.apache.commons.lang3.StringUtils; +import org.eclipse.swt.widgets.Composite; +import org.springframework.context.annotation.Lazy; +import org.springframework.stereotype.Component; + +import ch.ethz.seb.sebserver.gbl.Constants; +import ch.ethz.seb.sebserver.gbl.api.API; +import ch.ethz.seb.sebserver.gbl.api.EntityType; +import ch.ethz.seb.sebserver.gbl.model.Domain; +import ch.ethz.seb.sebserver.gbl.model.EntityKey; +import ch.ethz.seb.sebserver.gbl.model.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.sebconfig.TemplateAttribute; +import ch.ethz.seb.sebserver.gbl.model.user.UserInfo; +import ch.ethz.seb.sebserver.gbl.profile.GuiProfile; +import ch.ethz.seb.sebserver.gui.content.action.ActionDefinition; +import ch.ethz.seb.sebserver.gui.form.FormBuilder; +import ch.ethz.seb.sebserver.gui.form.FormHandle; +import ch.ethz.seb.sebserver.gui.service.ResourceService; +import ch.ethz.seb.sebserver.gui.service.examconfig.ExamConfigurationService; +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.PageAction; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestService; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.seb.examconfig.GetExamConfigNode; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.seb.examconfig.GetTemplateAttributePage; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.seb.examconfig.NewExamConfig; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.seb.examconfig.SaveExamConfig; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.auth.CurrentUser; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.auth.CurrentUser.EntityGrantCheck; +import ch.ethz.seb.sebserver.gui.table.ColumnDefinition; +import ch.ethz.seb.sebserver.gui.table.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; + +@Lazy +@Component +@GuiProfile +public class ConfigTemplateForm implements TemplateComposer { + + private static final LocTextKey FORM_TITLE_NEW = + new LocTextKey("sebserver.configtemplate.form.title.new"); + private static final LocTextKey FORM_TITLE = + new LocTextKey("sebserver.configtemplate.form.title"); + private static final LocTextKey FORM_NAME_TEXT_KEY = + new LocTextKey("sebserver.configtemplate.form.name"); + private static final LocTextKey FORM_DESCRIPTION_TEXT_KEY = + new LocTextKey("sebserver.configtemplate.form.description"); + private static final LocTextKey ATTRIBUTES_LIST_TITLE_TEXT_KEY = + new LocTextKey("sebserver.configtemplate.attrs.list.title"); + private static final LocTextKey ATTRIBUTES_LIST_TITLE_TOOLTIP_TEXT_KEY = + new LocTextKey("sebserver.configtemplate.attrs.list.title" + Constants.TOOLTIP_TEXT_KEY_SUFFIX); + private static final LocTextKey ATTRIBUTES_LIST_NAME_TEXT_KEY = + new LocTextKey("sebserver.configtemplate.attrs.list.name"); + private static final LocTextKey ATTRIBUTES_LIST_VIEW_TEXT_KEY = + new LocTextKey("sebserver.configtemplate.attrs.list.view"); + private static final LocTextKey ATTRIBUTES_LIST_GROUP_TEXT_KEY = + new LocTextKey("sebserver.configtemplate.attrs.list.group"); + private static final LocTextKey ATTRIBUTES_LIST_TYPE_TEXT_KEY = + new LocTextKey("sebserver.configtemplate.attrs.list.type"); + private static final LocTextKey EMPTY_ATTRIBUTE_SELECTION_TEXT_KEY = + new LocTextKey("sebserver.configtemplate.attr.info.pleaseSelect"); + + private final PageService pageService; + private final RestService restService; + private final CurrentUser currentUser; + private final I18nSupport i18nSupport; + private final ResourceService resourceService; + private final ExamConfigurationService examConfigurationService; + + private final TableFilterAttribute nameFilter = + new TableFilterAttribute(CriteriaType.TEXT, TemplateAttribute.FILTER_ATTR_NAME); + private final TableFilterAttribute groupFilter = + new TableFilterAttribute(CriteriaType.TEXT, TemplateAttribute.FILTER_ATTR_GROUP); + + protected ConfigTemplateForm( + final PageService pageService, + final RestService restService, + final CurrentUser currentUser, + final ExamConfigurationService examConfigurationService) { + + this.pageService = pageService; + this.restService = restService; + this.currentUser = currentUser; + this.i18nSupport = pageService.getI18nSupport(); + this.resourceService = pageService.getResourceService(); + this.examConfigurationService = examConfigurationService; + + } + + @Override + public void compose(final PageContext pageContext) { + + final WidgetFactory widgetFactory = this.pageService.getWidgetFactory(); + final ResourceService resourceService = this.pageService.getResourceService(); + + final UserInfo user = this.currentUser.get(); + final EntityKey entityKey = pageContext.getEntityKey(); + final boolean isNew = entityKey == null; + + // get data or create new. Handle error if happen + final ConfigurationNode examConfig = (isNew) + ? ConfigurationNode.createNewTemplate(user.institutionId) + : this.restService + .getBuilder(GetExamConfigNode.class) + .withURIVariable(API.PARAM_MODEL_ID, entityKey.modelId) + .call() + .onError(error -> pageContext.notifyLoadError(EntityType.CONFIGURATION_NODE, error)) + .getOrThrow(); + + final EntityGrantCheck entityGrant = this.currentUser.entityGrantCheck(examConfig); + final boolean writeGrant = entityGrant.w(); + final boolean modifyGrant = entityGrant.m(); + final boolean isReadonly = pageContext.isReadonly(); + + // new PageContext with actual EntityKey + final PageContext formContext = pageContext + .withEntityKey(examConfig.getEntityKey()); + + // the default page layout with interactive title + final LocTextKey titleKey = (isNew) + ? FORM_TITLE_NEW + : FORM_TITLE; + final Composite content = widgetFactory.defaultPageLayout( + formContext.getParent(), + titleKey); + + // The SebClientConfig form + final FormHandle formHandle = this.pageService.formBuilder( + formContext.copyOf(content)) + .readonly(isReadonly) + .putStaticValueIf(() -> !isNew, + Domain.CONFIGURATION_NODE.ATTR_ID, + examConfig.getModelId()) + .putStaticValue( + Domain.CONFIGURATION_NODE.ATTR_INSTITUTION_ID, + String.valueOf(examConfig.getInstitutionId())) + .putStaticValue( + Domain.CONFIGURATION_NODE.ATTR_TYPE, + ConfigurationType.TEMPLATE.name()) + .putStaticValue( + Domain.CONFIGURATION_NODE.ATTR_STATUS, + ConfigurationStatus.IN_USE.name()) + .addField(FormBuilder.text( + Domain.CONFIGURATION_NODE.ATTR_NAME, + FORM_NAME_TEXT_KEY, + examConfig.name) + .mandatory(!isReadonly)) + .addField(FormBuilder.text( + Domain.CONFIGURATION_NODE.ATTR_DESCRIPTION, + FORM_DESCRIPTION_TEXT_KEY, + examConfig.description) + .asArea()) + + .buildFor((isNew) + ? this.restService.getRestCall(NewExamConfig.class) + : this.restService.getRestCall(SaveExamConfig.class)); + + final PageActionBuilder pageActionBuilder = this.pageService + .pageActionBuilder(formContext.clearEntityKeys()); + + if (isReadonly) { + + widgetFactory.label(content, StringUtils.EMPTY); + widgetFactory.labelLocalized( + content, + WidgetFactory.CustomVariant.TEXT_H3, + ATTRIBUTES_LIST_TITLE_TEXT_KEY, + ATTRIBUTES_LIST_TITLE_TOOLTIP_TEXT_KEY); + widgetFactory.labelSeparator(content); + + final TableFilterAttribute viewFilter = new TableFilterAttribute( + CriteriaType.SINGLE_SELECTION, + TemplateAttribute.FILTER_ATTR_VIEW, + () -> this.resourceService.getViewResources(entityKey.modelId)); + final TableFilterAttribute typeFilter = new TableFilterAttribute( + CriteriaType.SINGLE_SELECTION, + TemplateAttribute.FILTER_ATTR_TYPE, + this.resourceService::getAttributeTypeResources); + + final EntityTable attrTable = + this.pageService.entityTableBuilder( + Domain.CONFIGURATION_NODE.TYPE_NAME + "_Template", + this.restService.getRestCall(GetTemplateAttributePage.class)) + .withRestCallAdapter(restCall -> restCall.withURIVariable( + API.PARAM_PARENT_MODEL_ID, + entityKey.modelId)) + .withPaging(15) + .withColumn(new ColumnDefinition<>( + Domain.CONFIGURATION_ATTRIBUTE.ATTR_NAME, + ATTRIBUTES_LIST_NAME_TEXT_KEY, + this::getAttributeName) + .withFilter(this.nameFilter) + .sortable() + .widthProportion(3)) + .withColumn(new ColumnDefinition( + Domain.CONFIGURATION_ATTRIBUTE.ATTR_TYPE, + ATTRIBUTES_LIST_TYPE_TEXT_KEY, + resourceService::getAttributeTypeName) + .withFilter(typeFilter) + .sortable() + .widthProportion(1)) + .withColumn(new ColumnDefinition<>( + Domain.ORIENTATION.ATTR_VIEW_ID, + ATTRIBUTES_LIST_VIEW_TEXT_KEY, + resourceService.getViewNameFunction(entityKey.modelId)) + .withFilter(viewFilter) + .sortable() + .widthProportion(1)) + .withColumn(new ColumnDefinition<>( + Domain.ORIENTATION.ATTR_GROUP_ID, + ATTRIBUTES_LIST_GROUP_TEXT_KEY, + TemplateAttribute::getGroupId) + .withFilter(this.groupFilter) + .sortable() + .widthProportion(1)) + .withDefaultAction(pageActionBuilder + .newAction(ActionDefinition.SEB_EXAM_CONFIG_TEMPLATE_ATTR_EDIT) + .withParentEntityKey(entityKey) + .create()) + + .withSelectionListener(this.pageService.getSelectionPublisher( + pageContext, + ActionDefinition.SEB_EXAM_CONFIG_TEMPLATE_ATTR_EDIT, + ActionDefinition.SEB_EXAM_CONFIG_TEMPLATE_ATTR_SET_DEFAULT, + ActionDefinition.SEB_EXAM_CONFIG_TEMPLATE_ATTR_LIST_REMOVE_VIEW, + ActionDefinition.SEB_EXAM_CONFIG_TEMPLATE_ATTR_LIST_ATTACH_DEFAULT_VIEW)) + + .compose(pageContext.copyOf(content)); + + pageActionBuilder + + .newAction(ActionDefinition.SEB_EXAM_CONFIG_TEMPLATE_ATTR_EDIT) + .withParentEntityKey(entityKey) + .withSelect( + attrTable::getSelection, + PageAction::applySingleSelectionAsEntityKey, + EMPTY_ATTRIBUTE_SELECTION_TEXT_KEY) + .publishIf(attrTable::hasAnyContent, false) + + .newAction(ActionDefinition.SEB_EXAM_CONFIG_TEMPLATE_ATTR_SET_DEFAULT) + .withParentEntityKey(entityKey) + .withSelect( + attrTable::getSelection, + action -> this.resetToDefaults(action, attrTable), + EMPTY_ATTRIBUTE_SELECTION_TEXT_KEY) + .noEventPropagation() + .publishIf(attrTable::hasAnyContent, false) + + .newAction(ActionDefinition.SEB_EXAM_CONFIG_TEMPLATE_ATTR_LIST_REMOVE_VIEW) + .withParentEntityKey(entityKey) + .withSelect( + attrTable::getSelection, + action -> this.removeFormView(action, attrTable), + EMPTY_ATTRIBUTE_SELECTION_TEXT_KEY) + .noEventPropagation() + .publishIf(attrTable::hasAnyContent, false) + + .newAction(ActionDefinition.SEB_EXAM_CONFIG_TEMPLATE_ATTR_LIST_ATTACH_DEFAULT_VIEW) + .withParentEntityKey(entityKey) + .withSelect( + attrTable::getSelection, + action -> this.attachView(action, attrTable), + EMPTY_ATTRIBUTE_SELECTION_TEXT_KEY) + .noEventPropagation() + .publishIf(attrTable::hasAnyContent, false); + } + + pageActionBuilder + + .newAction(ActionDefinition.SEB_EXAM_CONFIG_TEMPLATE_NEW) + .publishIf(() -> writeGrant && isReadonly) + + .newAction(ActionDefinition.SEB_EXAM_CONFIG_TEMPLATE_MODIFY) + .withEntityKey(entityKey) + .publishIf(() -> modifyGrant && isReadonly) + + .newAction(ActionDefinition.SEB_EXAM_CONFIG_TEMPLATE_CREATE_CONFIG) + .withEntityKey(entityKey) + .withExec(SebExamConfigCreationPopup.configCreationFunction( + this.pageService, + pageContext + .withAttribute( + PageContext.AttributeKeys.CREATE_FROM_TEMPLATE, + Constants.TRUE_STRING) + .withAttribute( + PageContext.AttributeKeys.COPY_AS_TEMPLATE, + Constants.FALSE_STRING))) + .publishIf(() -> modifyGrant && isReadonly) + + .newAction(ActionDefinition.SEB_EXAM_CONFIG_TEMPLATE_SAVE) + .withEntityKey(entityKey) + .withExec(formHandle::processFormSave) + .ignoreMoveAwayFromEdit() + .publishIf(() -> !isReadonly) + + .newAction(ActionDefinition.SEB_EXAM_CONFIG_TEMPLATE_CANCEL_MODIFY) + .withEntityKey(entityKey) + .withExec(this.pageService.backToCurrentFunction()) + .publishIf(() -> !isReadonly); + + } + + private String getAttributeName(final TemplateAttribute attribute) { + + final String name = this.i18nSupport.getText( + ExamConfigurationService.ATTRIBUTE_LABEL_LOC_TEXT_PREFIX + attribute.getName(), + ""); + if (StringUtils.isNotBlank(name)) { + return attribute.getName() + " (" + name + ")"; + } else { + return attribute.getName(); + } + } + + private PageAction resetToDefaults( + final PageAction action, + final EntityTable attrTable) { + + final PageAction resetToDefaults = this.examConfigurationService.resetToDefaults(action); + // reload the list + attrTable.applyFilter(); + return resetToDefaults; + } + + private PageAction removeFormView( + final PageAction action, + final EntityTable attrTable) { + + final PageAction removeFormView = this.examConfigurationService.removeFromView(action); + // reload the list + attrTable.applyFilter(); + return removeFormView; + } + + private PageAction attachView( + final PageAction action, + final EntityTable attrTable) { + + final PageAction attachView = this.examConfigurationService.attachToDefaultView(action); + // reload the list + attrTable.applyFilter(); + return attachView; + } + +} diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/content/ConfigTemplateList.java b/src/main/java/ch/ethz/seb/sebserver/gui/content/ConfigTemplateList.java index 5694776d..674dab8f 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/content/ConfigTemplateList.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/content/ConfigTemplateList.java @@ -1,155 +1,159 @@ -/* - * Copyright (c) 2020 ETH Zürich, Educational Development and Technology (LET) - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. - */ - -package ch.ethz.seb.sebserver.gui.content; - -import org.eclipse.swt.widgets.Composite; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.context.annotation.Lazy; -import org.springframework.stereotype.Component; - -import ch.ethz.seb.sebserver.gbl.api.EntityType; -import ch.ethz.seb.sebserver.gbl.model.Domain; -import ch.ethz.seb.sebserver.gbl.model.Entity; -import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationNode; -import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationNode.ConfigurationType; -import ch.ethz.seb.sebserver.gbl.model.user.UserRole; -import ch.ethz.seb.sebserver.gbl.profile.GuiProfile; -import ch.ethz.seb.sebserver.gui.content.action.ActionDefinition; -import ch.ethz.seb.sebserver.gui.service.ResourceService; -import ch.ethz.seb.sebserver.gui.service.i18n.LocTextKey; -import ch.ethz.seb.sebserver.gui.service.page.PageContext; -import ch.ethz.seb.sebserver.gui.service.page.PageService; -import ch.ethz.seb.sebserver.gui.service.page.PageService.PageActionBuilder; -import ch.ethz.seb.sebserver.gui.service.page.TemplateComposer; -import ch.ethz.seb.sebserver.gui.service.page.impl.PageAction; -import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestService; -import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.seb.examconfig.GetExamConfigNodePage; -import ch.ethz.seb.sebserver.gui.service.remote.webservice.auth.CurrentUser; -import ch.ethz.seb.sebserver.gui.service.remote.webservice.auth.CurrentUser.GrantCheck; -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; - -@Lazy -@Component -@GuiProfile -public class ConfigTemplateList implements TemplateComposer { - - private static final LocTextKey NO_MODIFY_PRIVILEGE_ON_OTHER_INSTITUION = - new LocTextKey("sebserver.examconfig.list.action.no.modify.privilege"); - private static final LocTextKey TITLE_TEMPLATE_TEXT_KEY = - new LocTextKey("sebserver.configtemplate.list.title"); - private static final LocTextKey EMPTY_TEMPLATE_LIST_TEXT_KEY = - new LocTextKey("sebserver.configtemplate.list.empty"); - private static final LocTextKey INSTITUTION_TEXT_KEY = - new LocTextKey("sebserver.examconfig.list.column.institution"); - private static final LocTextKey NAME_TEXT_KEY = - new LocTextKey("sebserver.examconfig.list.column.name"); - private static final LocTextKey DESCRIPTION_TEXT_KEY = - new LocTextKey("sebserver.examconfig.list.column.description"); - private static final LocTextKey EMPTY_TEMPLATE_SELECTION_TEXT_KEY = - new LocTextKey("sebserver.configtemplate.info.pleaseSelect"); - - private final PageService pageService; - private final RestService restService; - private final CurrentUser currentUser; - private final ResourceService resourceService; - private final int pageSize; - - private final TableFilterAttribute institutionFilter; - private final TableFilterAttribute nameFilter = - new TableFilterAttribute(CriteriaType.TEXT, Entity.FILTER_ATTR_NAME); - private final TableFilterAttribute descFilter = - new TableFilterAttribute(CriteriaType.TEXT, ConfigurationNode.FILTER_ATTR_DESCRIPTION); - - protected ConfigTemplateList( - final PageService pageService, - final RestService restService, - final CurrentUser currentUser, - @Value("${sebserver.gui.list.page.size:20}") final Integer pageSize) { - - this.pageService = pageService; - this.restService = restService; - this.currentUser = currentUser; - this.resourceService = pageService.getResourceService(); - this.pageSize = pageSize; - - this.institutionFilter = new TableFilterAttribute( - CriteriaType.SINGLE_SELECTION, - Entity.FILTER_ATTR_INSTITUTION, - this.resourceService::institutionResource); - } - - @Override - public void compose(final PageContext pageContext) { - final WidgetFactory widgetFactory = this.pageService.getWidgetFactory(); - final Composite content = widgetFactory.defaultPageLayout( - pageContext.getParent(), - TITLE_TEMPLATE_TEXT_KEY); - - final boolean isSEBAdmin = this.currentUser.get().hasRole(UserRole.SEB_SERVER_ADMIN); - final PageActionBuilder pageActionBuilder = - this.pageService.pageActionBuilder(pageContext.clearEntityKeys()); - - final EntityTable templateTable = - this.pageService.entityTableBuilder( - TITLE_TEMPLATE_TEXT_KEY.name, - this.restService.getRestCall(GetExamConfigNodePage.class)) - .withStaticFilter( - Domain.CONFIGURATION_NODE.ATTR_TYPE, - ConfigurationType.TEMPLATE.name()) - .withEmptyMessage(EMPTY_TEMPLATE_LIST_TEXT_KEY) - .withPaging(this.pageSize) - .withColumnIf( - () -> isSEBAdmin, - () -> new ColumnDefinition<>( - Domain.LMS_SETUP.ATTR_INSTITUTION_ID, - INSTITUTION_TEXT_KEY, - this.resourceService::localizedExamConfigInstitutionName) - .withFilter(this.institutionFilter) - .sortable()) - .withColumn(new ColumnDefinition<>( - Domain.CONFIGURATION_NODE.ATTR_NAME, - NAME_TEXT_KEY, - ConfigurationNode::getName) - .withFilter(this.nameFilter) - .sortable()) - .withColumn(new ColumnDefinition<>( - Domain.CONFIGURATION_NODE.ATTR_DESCRIPTION, - DESCRIPTION_TEXT_KEY, - ConfigurationNode::getDescription) - .withFilter(this.descFilter) - .sortable()) - .withDefaultAction(pageActionBuilder - .newAction(ActionDefinition.SEB_EXAM_CONFIG_TEMPLATE_VIEW_FROM_LIST) - .create()) - .compose(pageContext.copyOf(content)); - - final GrantCheck examConfigGrant = this.currentUser.grantCheck(EntityType.CONFIGURATION_NODE); - pageActionBuilder - // Exam Configuration template actions... - .newAction(ActionDefinition.SEB_EXAM_CONFIG_TEMPLATE_NEW) - .publishIf(examConfigGrant::iw) - - .newAction(ActionDefinition.SEB_EXAM_CONFIG_TEMPLATE_VIEW_FROM_LIST) - .withSelect(templateTable::getSelection, PageAction::applySingleSelectionAsEntityKey, - EMPTY_TEMPLATE_SELECTION_TEXT_KEY) - .publishIf(() -> templateTable.hasAnyContent()) - - .newAction(ActionDefinition.SEB_EXAM_CONFIG_TEMPLATE_MODIFY_FROM_LIST) - .withSelect( - templateTable.getGrantedSelection(this.currentUser, NO_MODIFY_PRIVILEGE_ON_OTHER_INSTITUION), - PageAction::applySingleSelectionAsEntityKey, EMPTY_TEMPLATE_SELECTION_TEXT_KEY) - .publishIf(() -> examConfigGrant.im() && templateTable.hasAnyContent()); - - } - -} +/* + * Copyright (c) 2020 ETH Zürich, Educational Development and Technology (LET) + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +package ch.ethz.seb.sebserver.gui.content; + +import org.eclipse.swt.widgets.Composite; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Lazy; +import org.springframework.stereotype.Component; + +import ch.ethz.seb.sebserver.gbl.api.EntityType; +import ch.ethz.seb.sebserver.gbl.model.Domain; +import ch.ethz.seb.sebserver.gbl.model.Entity; +import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationNode; +import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationNode.ConfigurationType; +import ch.ethz.seb.sebserver.gbl.model.user.UserRole; +import ch.ethz.seb.sebserver.gbl.profile.GuiProfile; +import ch.ethz.seb.sebserver.gui.content.action.ActionDefinition; +import ch.ethz.seb.sebserver.gui.service.ResourceService; +import ch.ethz.seb.sebserver.gui.service.i18n.LocTextKey; +import ch.ethz.seb.sebserver.gui.service.page.PageContext; +import ch.ethz.seb.sebserver.gui.service.page.PageService; +import ch.ethz.seb.sebserver.gui.service.page.PageService.PageActionBuilder; +import ch.ethz.seb.sebserver.gui.service.page.TemplateComposer; +import ch.ethz.seb.sebserver.gui.service.page.impl.PageAction; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestService; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.seb.examconfig.GetExamConfigNodePage; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.auth.CurrentUser; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.auth.CurrentUser.GrantCheck; +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; + +@Lazy +@Component +@GuiProfile +public class ConfigTemplateList implements TemplateComposer { + + private static final LocTextKey NO_MODIFY_PRIVILEGE_ON_OTHER_INSTITUTION = + new LocTextKey("sebserver.examconfig.list.action.no.modify.privilege"); + private static final LocTextKey TITLE_TEMPLATE_TEXT_KEY = + new LocTextKey("sebserver.configtemplate.list.title"); + private static final LocTextKey EMPTY_TEMPLATE_LIST_TEXT_KEY = + new LocTextKey("sebserver.configtemplate.list.empty"); + private static final LocTextKey INSTITUTION_TEXT_KEY = + new LocTextKey("sebserver.examconfig.list.column.institution"); + private static final LocTextKey NAME_TEXT_KEY = + new LocTextKey("sebserver.examconfig.list.column.name"); + private static final LocTextKey DESCRIPTION_TEXT_KEY = + new LocTextKey("sebserver.examconfig.list.column.description"); + private static final LocTextKey EMPTY_TEMPLATE_SELECTION_TEXT_KEY = + new LocTextKey("sebserver.configtemplate.info.pleaseSelect"); + + private final PageService pageService; + private final RestService restService; + private final CurrentUser currentUser; + private final ResourceService resourceService; + private final int pageSize; + + private final TableFilterAttribute institutionFilter; + private final TableFilterAttribute nameFilter = + new TableFilterAttribute(CriteriaType.TEXT, Entity.FILTER_ATTR_NAME); + private final TableFilterAttribute descFilter = + new TableFilterAttribute(CriteriaType.TEXT, ConfigurationNode.FILTER_ATTR_DESCRIPTION); + + protected ConfigTemplateList( + final PageService pageService, + final RestService restService, + final CurrentUser currentUser, + @Value("${sebserver.gui.list.page.size:20}") final Integer pageSize) { + + this.pageService = pageService; + this.restService = restService; + this.currentUser = currentUser; + this.resourceService = pageService.getResourceService(); + this.pageSize = pageSize; + + this.institutionFilter = new TableFilterAttribute( + CriteriaType.SINGLE_SELECTION, + Entity.FILTER_ATTR_INSTITUTION, + this.resourceService::institutionResource); + } + + @Override + public void compose(final PageContext pageContext) { + final WidgetFactory widgetFactory = this.pageService.getWidgetFactory(); + final Composite content = widgetFactory.defaultPageLayout( + pageContext.getParent(), + TITLE_TEMPLATE_TEXT_KEY); + + final boolean isSEBAdmin = this.currentUser.get().hasRole(UserRole.SEB_SERVER_ADMIN); + final PageActionBuilder pageActionBuilder = + this.pageService.pageActionBuilder(pageContext.clearEntityKeys()); + + final EntityTable templateTable = + this.pageService.entityTableBuilder( + TITLE_TEMPLATE_TEXT_KEY.name, + this.restService.getRestCall(GetExamConfigNodePage.class)) + .withStaticFilter( + Domain.CONFIGURATION_NODE.ATTR_TYPE, + ConfigurationType.TEMPLATE.name()) + .withEmptyMessage(EMPTY_TEMPLATE_LIST_TEXT_KEY) + .withPaging(this.pageSize) + .withColumnIf( + () -> isSEBAdmin, + () -> new ColumnDefinition<>( + Domain.LMS_SETUP.ATTR_INSTITUTION_ID, + INSTITUTION_TEXT_KEY, + this.resourceService::localizedExamConfigInstitutionName) + .withFilter(this.institutionFilter) + .sortable()) + .withColumn(new ColumnDefinition<>( + Domain.CONFIGURATION_NODE.ATTR_NAME, + NAME_TEXT_KEY, + ConfigurationNode::getName) + .withFilter(this.nameFilter) + .sortable()) + .withColumn(new ColumnDefinition<>( + Domain.CONFIGURATION_NODE.ATTR_DESCRIPTION, + DESCRIPTION_TEXT_KEY, + ConfigurationNode::getDescription) + .withFilter(this.descFilter) + .sortable()) + .withDefaultAction(pageActionBuilder + .newAction(ActionDefinition.SEB_EXAM_CONFIG_TEMPLATE_VIEW_FROM_LIST) + .create()) + + .withSelectionListener(this.pageService.getSelectionPublisher( + pageContext, + ActionDefinition.SEB_EXAM_CONFIG_TEMPLATE_VIEW_FROM_LIST, + ActionDefinition.SEB_EXAM_CONFIG_TEMPLATE_MODIFY_FROM_LIST)) + + .compose(pageContext.copyOf(content)); + + final GrantCheck examConfigGrant = this.currentUser.grantCheck(EntityType.CONFIGURATION_NODE); + pageActionBuilder + // Exam Configuration template actions... + .newAction(ActionDefinition.SEB_EXAM_CONFIG_TEMPLATE_NEW) + .publishIf(examConfigGrant::iw) + + .newAction(ActionDefinition.SEB_EXAM_CONFIG_TEMPLATE_VIEW_FROM_LIST) + .withSelect(templateTable::getSelection, PageAction::applySingleSelectionAsEntityKey, + EMPTY_TEMPLATE_SELECTION_TEXT_KEY) + .publishIf(templateTable::hasAnyContent, false) + + .newAction(ActionDefinition.SEB_EXAM_CONFIG_TEMPLATE_MODIFY_FROM_LIST) + .withSelect( + templateTable.getGrantedSelection(this.currentUser, NO_MODIFY_PRIVILEGE_ON_OTHER_INSTITUTION), + PageAction::applySingleSelectionAsEntityKey, EMPTY_TEMPLATE_SELECTION_TEXT_KEY) + .publishIf(() -> examConfigGrant.im() && templateTable.hasAnyContent(), false); + } +} diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/content/ExamForm.java b/src/main/java/ch/ethz/seb/sebserver/gui/content/ExamForm.java index c84df4ef..583c1213 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/content/ExamForm.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/content/ExamForm.java @@ -1,762 +1,762 @@ -/* - * 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.content; - -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; -import java.util.function.BooleanSupplier; -import java.util.function.Function; -import java.util.function.Supplier; - -import org.apache.commons.lang3.BooleanUtils; -import org.apache.tomcat.util.buf.StringUtils; -import org.eclipse.rap.rwt.RWT; -import org.eclipse.rap.rwt.client.service.UrlLauncher; -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.api.APIMessage; -import ch.ethz.seb.sebserver.gbl.api.EntityType; -import ch.ethz.seb.sebserver.gbl.model.Domain; -import ch.ethz.seb.sebserver.gbl.model.EntityKey; -import ch.ethz.seb.sebserver.gbl.model.exam.Exam; -import ch.ethz.seb.sebserver.gbl.model.exam.Exam.ExamStatus; -import ch.ethz.seb.sebserver.gbl.model.exam.ExamConfigurationMap; -import ch.ethz.seb.sebserver.gbl.model.exam.Indicator; -import ch.ethz.seb.sebserver.gbl.model.exam.QuizData; -import ch.ethz.seb.sebserver.gbl.model.institution.LmsSetup.Features; -import ch.ethz.seb.sebserver.gbl.model.user.UserRole; -import ch.ethz.seb.sebserver.gbl.profile.GuiProfile; -import ch.ethz.seb.sebserver.gbl.util.Result; -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.form.FormHandle; -import ch.ethz.seb.sebserver.gui.service.ResourceService; -import ch.ethz.seb.sebserver.gui.service.i18n.I18nSupport; -import ch.ethz.seb.sebserver.gui.service.i18n.LocTextKey; -import ch.ethz.seb.sebserver.gui.service.page.PageContext; -import ch.ethz.seb.sebserver.gui.service.page.PageContext.AttributeKeys; -import ch.ethz.seb.sebserver.gui.service.page.PageService; -import ch.ethz.seb.sebserver.gui.service.page.PageService.PageActionBuilder; -import ch.ethz.seb.sebserver.gui.service.page.TemplateComposer; -import ch.ethz.seb.sebserver.gui.service.page.event.ActionEvent; -import ch.ethz.seb.sebserver.gui.service.page.impl.PageAction; -import ch.ethz.seb.sebserver.gui.service.remote.download.DownloadService; -import ch.ethz.seb.sebserver.gui.service.remote.download.SebExamConfigDownload; -import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestService; -import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.CheckExamConsistency; -import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.DeleteExamConfigMapping; -import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.DeleteIndicator; -import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.GetExam; -import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.GetExamConfigMappingsPage; -import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.GetIndicatorPage; -import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.SaveExam; -import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.lmssetup.GetLmsSetup; -import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.quiz.GetQuizData; -import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.quiz.ImportAsExam; -import ch.ethz.seb.sebserver.gui.service.remote.webservice.auth.CurrentUser; -import ch.ethz.seb.sebserver.gui.service.remote.webservice.auth.CurrentUser.EntityGrantCheck; -import ch.ethz.seb.sebserver.gui.table.ColumnDefinition; -import ch.ethz.seb.sebserver.gui.table.EntityTable; -import ch.ethz.seb.sebserver.gui.widget.WidgetFactory; -import ch.ethz.seb.sebserver.gui.widget.WidgetFactory.CustomVariant; - -@Lazy -@Component -@GuiProfile -public class ExamForm implements TemplateComposer { - - private static final Logger log = LoggerFactory.getLogger(ExamForm.class); - - public static final LocTextKey EXAM_FORM_TITLE_KEY = - new LocTextKey("sebserver.exam.form.title"); - public static final LocTextKey EXAM_FORM_TITLE_IMPORT_KEY = - new LocTextKey("sebserver.exam.form.title.import"); - private static final LocTextKey CONFIG_EMPTY_LIST_MESSAGE = - new LocTextKey("sebserver.exam.configuration.list.empty"); - private static final LocTextKey INDICATOR_EMPTY_LIST_MESSAGE = - new LocTextKey("sebserver.exam.indicator.list.empty"); - private static final LocTextKey FORM_SUPPORTER_TEXT_KEY = - new LocTextKey("sebserver.exam.form.supporter"); - private static final LocTextKey FORM_STATUS_TEXT_KEY = - new LocTextKey("sebserver.exam.form.status"); - private static final LocTextKey FORM_TYPE_TEXT_KEY = - new LocTextKey("sebserver.exam.form.type"); - private static final LocTextKey FORM_ENDTIME_TEXT_KEY = - new LocTextKey("sebserver.exam.form.endtime"); - private static final LocTextKey FORM_STARTTIME_TEXT_KEY = - new LocTextKey("sebserver.exam.form.starttime"); - private static final LocTextKey FORM_DESCRIPTION_TEXT_KEY = - new LocTextKey("sebserver.exam.form.description"); - private static final LocTextKey FORM_NAME_TEXT_KEY = - new LocTextKey("sebserver.exam.form.name"); - private static final LocTextKey FORM_QUIZID_TEXT_KEY = - new LocTextKey("sebserver.exam.form.quizid"); - private static final LocTextKey FORM_QUIZ_URL_TEXT_KEY = - new LocTextKey("sebserver.exam.form.quizurl"); - private static final LocTextKey FORM_LMSSETUP_TEXT_KEY = - new LocTextKey("sebserver.exam.form.lmssetup"); - - private final static LocTextKey CONFIG_LIST_TITLE_KEY = - new LocTextKey("sebserver.exam.configuration.list.title"); - private final static LocTextKey CONFIG_NAME_COLUMN_KEY = - new LocTextKey("sebserver.exam.configuration.list.column.name"); - private final static LocTextKey CONFIG_DESCRIPTION_COLUMN_KEY = - new LocTextKey("sebserver.exam.configuration.list.column.description"); - private final static LocTextKey CONFIG_STATUS_COLUMN_KEY = - new LocTextKey("sebserver.exam.configuration.list.column.status"); - private final static LocTextKey CONFIG_EMPTY_SELECTION_TEXT_KEY = - new LocTextKey("sebserver.exam.configuration.list.pleaseSelect"); - - private final static LocTextKey INDICATOR_LIST_TITLE_KEY = - new LocTextKey("sebserver.exam.indicator.list.title"); - private final static LocTextKey INDICATOR_TYPE_COLUMN_KEY = - new LocTextKey("sebserver.exam.indicator.list.column.type"); - private final static LocTextKey INDICATOR_NAME_COLUMN_KEY = - new LocTextKey("sebserver.exam.indicator.list.column.name"); - private final static LocTextKey INDICATOR_THRESHOLD_COLUMN_KEY = - new LocTextKey("sebserver.exam.indicator.list.column.thresholds"); - private final static LocTextKey INDICATOR_EMPTY_SELECTION_TEXT_KEY = - new LocTextKey("sebserver.exam.indicator.list.pleaseSelect"); - - private final static LocTextKey CONSISTENCY_MESSAGE_TITLE = - new LocTextKey("sebserver.exam.consistency.title"); - private final static LocTextKey CONSISTENCY_MESSAGE_MISSING_SUPPORTER = - new LocTextKey("sebserver.exam.consistency.missing-supporter"); - private final static LocTextKey CONSISTENCY_MESSAGE_MISSING_INDICATOR = - new LocTextKey("sebserver.exam.consistency.missing-indicator"); - private final static LocTextKey CONSISTENCY_MESSAGE_MISSING_CONFIG = - new LocTextKey("sebserver.exam.consistency.missing-config"); - private final static LocTextKey CONSISTENCY_MESSAGE_MISSING_SEB_RESTRICTION = - new LocTextKey("sebserver.exam.consistency.missing-seb-restriction"); - - private final Map consistencyMessageMapping; - - private final static LocTextKey CONFIRM_MESSAGE_REMOVE_CONFIG = - new LocTextKey("sebserver.exam.confirm.remove-config"); - - private final PageService pageService; - private final ResourceService resourceService; - private final DownloadService downloadService; - private final String downloadFileName; - private final WidgetFactory widgetFactory; - private final RestService restService; - - protected ExamForm( - final PageService pageService, - final ResourceService resourceService, - final DownloadService downloadService, - @Value("${sebserver.gui.seb.exam.config.download.filename}") final String downloadFileName) { - - this.pageService = pageService; - this.resourceService = resourceService; - this.downloadService = downloadService; - this.downloadFileName = downloadFileName; - this.widgetFactory = pageService.getWidgetFactory(); - this.restService = this.resourceService.getRestService(); - - this.consistencyMessageMapping = new HashMap<>(); - this.consistencyMessageMapping.put( - APIMessage.ErrorMessage.EXAM_CONSISTANCY_VALIDATION_SUPPORTER.messageCode, - CONSISTENCY_MESSAGE_MISSING_SUPPORTER); - this.consistencyMessageMapping.put( - APIMessage.ErrorMessage.EXAM_CONSISTANCY_VALIDATION_INDICATOR.messageCode, - CONSISTENCY_MESSAGE_MISSING_INDICATOR); - this.consistencyMessageMapping.put( - APIMessage.ErrorMessage.EXAM_CONSISTANCY_VALIDATION_CONFIG.messageCode, - CONSISTENCY_MESSAGE_MISSING_CONFIG); - this.consistencyMessageMapping.put( - APIMessage.ErrorMessage.EXAM_CONSISTANCY_VALIDATION_SEB_RESTRICTION.messageCode, - CONSISTENCY_MESSAGE_MISSING_SEB_RESTRICTION); - } - - @Override - public void compose(final PageContext pageContext) { - final CurrentUser currentUser = this.resourceService.getCurrentUser(); - - final I18nSupport i18nSupport = this.resourceService.getI18nSupport(); - final EntityKey entityKey = pageContext.getEntityKey(); - final boolean readonly = pageContext.isReadonly(); - final boolean importFromQuizData = BooleanUtils.toBoolean( - pageContext.getAttribute(AttributeKeys.IMPORT_FROM_QUIZ_DATA)); - - // get or create model data - final Exam exam = (importFromQuizData - ? createExamFromQuizData(pageContext) - : getExistingExam(pageContext)) - .onError(error -> pageContext.notifyLoadError(EntityType.EXAM, error)) - .getOrThrow(); - - // new PageContext with actual EntityKey - final PageContext formContext = pageContext.withEntityKey(exam.getEntityKey()); - - // check exam consistency and inform the user if needed - if (readonly) { - this.restService.getBuilder(CheckExamConsistency.class) - .withURIVariable(API.PARAM_MODEL_ID, entityKey.modelId) - .call() - .ifPresent(result -> showConsistencyChecks(result, formContext.getParent())); - } - - // the default page layout with title - final LocTextKey titleKey = importFromQuizData - ? EXAM_FORM_TITLE_IMPORT_KEY - : EXAM_FORM_TITLE_KEY; - final Composite content = this.widgetFactory.defaultPageLayout( - formContext.getParent(), - titleKey); - - final BooleanSupplier isNew = () -> importFromQuizData; - final BooleanSupplier isNotNew = () -> !isNew.getAsBoolean(); - final EntityGrantCheck userGrantCheck = currentUser.entityGrantCheck(exam); - final boolean modifyGrant = userGrantCheck.m(); - final ExamStatus examStatus = exam.getStatus(); - final boolean isExamRunning = examStatus == ExamStatus.RUNNING; - final boolean editable = examStatus == ExamStatus.UP_COMING - || examStatus == ExamStatus.RUNNING - && currentUser.get().hasRole(UserRole.EXAM_ADMIN); - final boolean sebRestrictionAvailable = testSebRestrictionAPI(exam); - - // The Exam form - final FormHandle formHandle = this.pageService.formBuilder( - formContext.copyOf(content), 8) - .withDefaultSpanLabel(1) - .withDefaultSpanInput(4) - .withDefaultSpanEmptyCell(3) - .readonly(readonly) - .putStaticValueIf(isNotNew, - Domain.EXAM.ATTR_ID, - exam.getModelId()) - .putStaticValue( - Domain.EXAM.ATTR_INSTITUTION_ID, - String.valueOf(exam.getInstitutionId())) - .putStaticValueIf(isNotNew, - Domain.EXAM.ATTR_LMS_SETUP_ID, - String.valueOf(exam.lmsSetupId)) - .putStaticValueIf(isNew, - QuizData.QUIZ_ATTR_LMS_SETUP_ID, - String.valueOf(exam.lmsSetupId)) - .putStaticValueIf(isNotNew, - Domain.EXAM.ATTR_EXTERNAL_ID, - exam.externalId) - .putStaticValueIf(isNew, - QuizData.QUIZ_ATTR_ID, - exam.externalId) - - .addField(FormBuilder.text( - QuizData.QUIZ_ATTR_NAME, - FORM_NAME_TEXT_KEY, - exam.name) - .readonly(true) - .withInputSpan(3) - .withEmptyCellSeparation(false)) - - .addField(FormBuilder.singleSelection( - Domain.EXAM.ATTR_LMS_SETUP_ID, - FORM_LMSSETUP_TEXT_KEY, - String.valueOf(exam.lmsSetupId), - this.resourceService::lmsSetupResource) - .readonly(true) - .withInputSpan(3) - .withEmptyCellSeparation(false)) - - .addField(FormBuilder.text( - QuizData.QUIZ_ATTR_START_TIME, - FORM_STARTTIME_TEXT_KEY, - i18nSupport.formatDisplayDateWithTimeZone(exam.startTime)) - .readonly(true) - .withInputSpan(3) - .withEmptyCellSeparation(false)) - .addField(FormBuilder.text( - QuizData.QUIZ_ATTR_END_TIME, - FORM_ENDTIME_TEXT_KEY, - i18nSupport.formatDisplayDateWithTimeZone(exam.endTime)) - .readonly(true) - .withInputSpan(3) - .withEmptyCellSeparation(false)) - - .addField(FormBuilder.text( - Domain.EXAM.ATTR_EXTERNAL_ID, - FORM_QUIZID_TEXT_KEY, - exam.externalId) - .readonly(true) - .withEmptyCellSeparation(false)) - .addField(FormBuilder.text( - QuizData.QUIZ_ATTR_START_URL, - FORM_QUIZ_URL_TEXT_KEY, - exam.startURL) - .readonly(true) - .withInputSpan(7)) - - .addField(FormBuilder.text( - QuizData.QUIZ_ATTR_DESCRIPTION, - FORM_DESCRIPTION_TEXT_KEY, - exam.description) - .asHTML() - .readonly(true) - .withInputSpan(6) - .withEmptyCellSeparation(false)) - - .addField(FormBuilder.text( - Domain.EXAM.ATTR_STATUS + "_display", - FORM_STATUS_TEXT_KEY, - i18nSupport.getText(new LocTextKey("sebserver.exam.status." + examStatus.name()))) - .readonly(true) - .withLabelSpan(2) - .withInputSpan(4) - .withEmptyCellSpan(1)) - .addField(FormBuilder.singleSelection( - Domain.EXAM.ATTR_TYPE, - FORM_TYPE_TEXT_KEY, - String.valueOf(exam.type), - this.resourceService::examTypeResources) - .withLabelSpan(2) - .withInputSpan(4) - .withEmptyCellSpan(2)) - - .addField(FormBuilder.multiComboSelection( - Domain.EXAM.ATTR_SUPPORTER, - FORM_SUPPORTER_TEXT_KEY, - StringUtils.join(exam.supporter, Constants.LIST_SEPARATOR_CHAR), - this.resourceService::examSupporterResources) - .withLabelSpan(2) - .withInputSpan(4) - .withEmptyCellSpan(2)) - - .buildFor(importFromQuizData - ? this.restService.getRestCall(ImportAsExam.class) - : this.restService.getRestCall(SaveExam.class)); - - final PageActionBuilder actionBuilder = this.pageService.pageActionBuilder(formContext - .clearEntityKeys() - .removeAttribute(AttributeKeys.IMPORT_FROM_QUIZ_DATA)); - // propagate content actions to action-pane - actionBuilder - - .newAction(ActionDefinition.EXAM_MODIFY) - .withEntityKey(entityKey) - .publishIf(() -> modifyGrant && readonly && editable) - - .newAction(ActionDefinition.EXAM_SAVE) - .withExec(action -> (importFromQuizData) - ? importExam(action, formHandle, sebRestrictionAvailable && exam.status == ExamStatus.RUNNING) - : formHandle.processFormSave(action)) - .ignoreMoveAwayFromEdit() - .publishIf(() -> !readonly && modifyGrant) - - .newAction(ActionDefinition.EXAM_CANCEL_MODIFY) - .withEntityKey(entityKey) - .withAttribute(AttributeKeys.IMPORT_FROM_QUIZ_DATA, String.valueOf(importFromQuizData)) - .withExec(this.cancelModifyFunction()) - .publishIf(() -> !readonly) - - .newAction(ActionDefinition.EXAM_MODIFY_SEB_RESTRICTION_DETAILS) - .withEntityKey(entityKey) - .withExec(ExamSebRestrictionSettings.settingsFunction(this.pageService)) - .withAttribute( - ExamSebRestrictionSettings.PAGE_CONTEXT_ATTR_LMS_TYPE, - this.restService.getBuilder(GetLmsSetup.class) - .withURIVariable(API.PARAM_MODEL_ID, String.valueOf(exam.lmsSetupId)) - .call() - .getOrThrow().lmsType.name()) - .noEventPropagation() - .publishIf(() -> sebRestrictionAvailable && readonly) - - .newAction(ActionDefinition.EXAM_ENABLE_SEB_RESTRICTION) - .withEntityKey(entityKey) - .withExec(action -> ExamSebRestrictionSettings.setSebRestriction(action, true, this.restService)) - .publishIf(() -> sebRestrictionAvailable && readonly && BooleanUtils.isFalse(exam.lmsSebRestriction)) - - .newAction(ActionDefinition.EXAM_DISABLE_SEB_RESTRICTION) - .withEntityKey(entityKey) - .withExec(action -> ExamSebRestrictionSettings.setSebRestriction(action, false, this.restService)) - .publishIf(() -> sebRestrictionAvailable && readonly && BooleanUtils.isTrue(exam.lmsSebRestriction)); - - // additional data in read-only view - if (readonly && !importFromQuizData) { - - // List of SEB Configuration - this.widgetFactory.label(content, ""); - this.widgetFactory.labelLocalized( - content, - CustomVariant.TEXT_H3, - CONFIG_LIST_TITLE_KEY); - this.widgetFactory.labelSeparator(content); - - final EntityTable configurationTable = - this.pageService.entityTableBuilder(this.restService.getRestCall(GetExamConfigMappingsPage.class)) - .withRestCallAdapter(builder -> builder.withQueryParam( - ExamConfigurationMap.FILTER_ATTR_EXAM_ID, - entityKey.modelId)) - .withEmptyMessage(CONFIG_EMPTY_LIST_MESSAGE) - .withPaging(1) - .hideNavigation() - .withColumn(new ColumnDefinition<>( - Domain.CONFIGURATION_NODE.ATTR_NAME, - CONFIG_NAME_COLUMN_KEY, - ExamConfigurationMap::getConfigName) - .widthProportion(2)) - .withColumn(new ColumnDefinition<>( - Domain.CONFIGURATION_NODE.ATTR_DESCRIPTION, - CONFIG_DESCRIPTION_COLUMN_KEY, - ExamConfigurationMap::getConfigDescription) - .widthProportion(4)) - .withColumn(new ColumnDefinition( - Domain.CONFIGURATION_NODE.ATTR_STATUS, - CONFIG_STATUS_COLUMN_KEY, - this.resourceService::localizedExamConfigStatusName) - .widthProportion(1)) - .withDefaultActionIf( - () -> modifyGrant, - this::viewExamConfigPageAction) - - .compose(pageContext.copyOf(content)); - - final EntityKey configMapKey = (configurationTable.hasAnyContent()) - ? new EntityKey( - configurationTable.getFirstRowData().configurationNodeId, - EntityType.CONFIGURATION_NODE) - : null; - - actionBuilder - - .newAction(ActionDefinition.EXAM_CONFIGURATION_NEW) - .withParentEntityKey(entityKey) - .withExec(ExamToConfigBindingForm.bindFunction(this.pageService)) - .noEventPropagation() - .publishIf(() -> modifyGrant && editable && !configurationTable.hasAnyContent()) - - .newAction(ActionDefinition.EXAM_CONFIGURATION_EXAM_CONFIG_VIEW_PROP) - .withParentEntityKey(entityKey) - .withEntityKey(configMapKey) - .publishIf(() -> modifyGrant && configurationTable.hasAnyContent()) - - .newAction(ActionDefinition.EXAM_CONFIGURATION_DELETE_FROM_LIST) - .withEntityKey(entityKey) - .withSelect( - getConfigMappingSelection(configurationTable), - this::deleteExamConfigMapping, - CONFIG_EMPTY_SELECTION_TEXT_KEY) - .withConfirm(() -> { - if (isExamRunning) { - return CONFIRM_MESSAGE_REMOVE_CONFIG; - } - return null; - }) - .publishIf(() -> modifyGrant && configurationTable.hasAnyContent() && editable) - - .newAction(ActionDefinition.EXAM_CONFIGURATION_EXPORT) - .withParentEntityKey(entityKey) - .withSelect( - getConfigSelection(configurationTable), - this::downloadExamConfigAction, - CONFIG_EMPTY_SELECTION_TEXT_KEY) - .noEventPropagation() - .publishIf(() -> userGrantCheck.r() && configurationTable.hasAnyContent()) - - .newAction(ActionDefinition.EXAM_CONFIGURATION_GET_CONFIG_KEY) - .withSelect( - getConfigSelection(configurationTable), - this::getExamConfigKey, - CONFIG_EMPTY_SELECTION_TEXT_KEY) - .noEventPropagation() - .publishIf(() -> userGrantCheck.r() && configurationTable.hasAnyContent()); - - // List of Indicators - this.widgetFactory.label(content, ""); - this.widgetFactory.labelLocalized( - content, - CustomVariant.TEXT_H3, - INDICATOR_LIST_TITLE_KEY); - this.widgetFactory.labelSeparator(content); - - final EntityTable indicatorTable = - this.pageService.entityTableBuilder(this.restService.getRestCall(GetIndicatorPage.class)) - .withRestCallAdapter(builder -> builder.withQueryParam( - Indicator.FILTER_ATTR_EXAM_ID, - entityKey.modelId)) - .withEmptyMessage(INDICATOR_EMPTY_LIST_MESSAGE) - .withMarkup() - .withPaging(5) - .hideNavigation() - .withColumn(new ColumnDefinition<>( - Domain.INDICATOR.ATTR_NAME, - INDICATOR_NAME_COLUMN_KEY, - Indicator::getName) - .widthProportion(2)) - .withColumn(new ColumnDefinition<>( - Domain.INDICATOR.ATTR_TYPE, - INDICATOR_TYPE_COLUMN_KEY, - this::indicatorTypeName) - .widthProportion(1)) - .withColumn(new ColumnDefinition<>( - Domain.THRESHOLD.REFERENCE_NAME, - INDICATOR_THRESHOLD_COLUMN_KEY, - ExamForm::thresholdsValue) - .asMarkup() - .widthProportion(4)) - .withDefaultActionIf( - () -> modifyGrant, - () -> actionBuilder - .newAction(ActionDefinition.EXAM_INDICATOR_MODIFY_FROM_LIST) - .withParentEntityKey(entityKey) - .create()) - - .compose(pageContext.copyOf(content)); - - actionBuilder - - .newAction(ActionDefinition.EXAM_INDICATOR_NEW) - .withParentEntityKey(entityKey) - .publishIf(() -> modifyGrant) - - .newAction(ActionDefinition.EXAM_INDICATOR_MODIFY_FROM_LIST) - .withParentEntityKey(entityKey) - .withSelect( - indicatorTable::getSelection, - PageAction::applySingleSelectionAsEntityKey, - INDICATOR_EMPTY_SELECTION_TEXT_KEY) - .publishIf(() -> modifyGrant && indicatorTable.hasAnyContent()) - - .newAction(ActionDefinition.EXAM_INDICATOR_DELETE_FROM_LIST) - .withEntityKey(entityKey) - .withSelect( - indicatorTable::getSelection, - this::deleteSelectedIndicator, - INDICATOR_EMPTY_SELECTION_TEXT_KEY) - .publishIf(() -> modifyGrant && indicatorTable.hasAnyContent()); - } - } - - private PageAction importExam( - final PageAction action, - final FormHandle formHandle, - final boolean applySebRestriction) { - - // process normal save first - final PageAction processFormSave = formHandle.processFormSave(action); - - // when okay and the exam sebRestriction is true - if (applySebRestriction) { - ExamSebRestrictionSettings.setSebRestriction( - processFormSave, - true, - this.restService, - t -> log.error("Failed to intially restrict the course for SEB on LMS: {}", t.getMessage())); - } - - return processFormSave; - } - - private boolean testSebRestrictionAPI(final Exam exam) { - return this.restService.getBuilder(GetLmsSetup.class) - .withURIVariable(API.PARAM_MODEL_ID, String.valueOf(exam.lmsSetupId)) - .call() - .onError(t -> log.error("Failed to check SEB restriction API: ", t)) - .map(lmsSetup -> lmsSetup.lmsType.features.contains(Features.SEB_RESTICTION)) - .getOr(false); - } - - private void showConsistencyChecks(final Collection result, final Composite parent) { - if (result == null || result.isEmpty()) { - return; - } - - final Composite warningPanel = this.widgetFactory.createWarningPanel(parent); - this.widgetFactory.labelLocalized( - warningPanel, - CustomVariant.TITLE_LABEL, - CONSISTENCY_MESSAGE_TITLE); - - result - .stream() - .map(message -> this.consistencyMessageMapping.get(message.messageCode)) - .filter(message -> message != null) - .forEach(message -> this.widgetFactory.labelLocalized( - warningPanel, - CustomVariant.MESSAGE, - message)); - } - - private PageAction viewExamConfigPageAction(final EntityTable table) { - - return this.pageService.pageActionBuilder(table.getPageContext() - .clearEntityKeys() - .removeAttribute(AttributeKeys.IMPORT_FROM_QUIZ_DATA)) - .newAction(ActionDefinition.EXAM_CONFIGURATION_EXAM_CONFIG_VIEW_PROP) - .withSelectionSupplier(() -> { - final ExamConfigurationMap selectedROWData = table.getSingleSelectedROWData(); - final HashSet result = new HashSet<>(); - if (selectedROWData != null) { - result.add(new EntityKey( - selectedROWData.configurationNodeId, - EntityType.CONFIGURATION_NODE)); - } - return result; - }) - .create(); - } - - private PageAction downloadExamConfigAction(final PageAction action) { - final UrlLauncher urlLauncher = RWT.getClient().getService(UrlLauncher.class); - final EntityKey selection = action.getSingleSelection(); - if (selection != null) { - final String downloadURL = this.downloadService.createDownloadURL( - selection.modelId, - action.pageContext().getParentEntityKey().modelId, - SebExamConfigDownload.class, - this.downloadFileName); - urlLauncher.openURL(downloadURL); - } - return action; - } - - private Supplier> getConfigMappingSelection( - final EntityTable configurationTable) { - return () -> { - final ExamConfigurationMap firstRowData = configurationTable.getFirstRowData(); - if (firstRowData == null) { - return Collections.emptySet(); - } else { - return new HashSet<>(Arrays.asList(firstRowData.getEntityKey())); - } - }; - } - - private Supplier> getConfigSelection(final EntityTable configurationTable) { - return () -> { - final ExamConfigurationMap firstRowData = configurationTable.getFirstRowData(); - if (firstRowData == null) { - return Collections.emptySet(); - } else { - return new HashSet<>(Arrays.asList(new EntityKey( - firstRowData.configurationNodeId, - EntityType.CONFIGURATION_NODE))); - } - }; - } - - private PageAction deleteSelectedIndicator(final PageAction action) { - final EntityKey indicatorKey = action.getSingleSelection(); - this.resourceService.getRestService() - .getBuilder(DeleteIndicator.class) - .withURIVariable(API.PARAM_MODEL_ID, indicatorKey.modelId) - .call(); - return action; - } - - private PageAction getExamConfigKey(final PageAction action) { - final EntityKey examConfigMappingKey = action.getSingleSelection(); - if (examConfigMappingKey != null) { - action.withEntityKey(examConfigMappingKey); - return SebExamConfigPropForm - .getConfigKeyFunction(this.pageService) - .apply(action); - } - - return action; - } - - private PageAction deleteExamConfigMapping(final PageAction action) { - final EntityKey examConfigMappingKey = action.getSingleSelection(); - this.resourceService.getRestService() - .getBuilder(DeleteExamConfigMapping.class) - .withURIVariable(API.PARAM_MODEL_ID, examConfigMappingKey.modelId) - .call() - .onError(error -> action.pageContext().notifyRemoveError(EntityType.EXAM_CONFIGURATION_MAP, error)); - return action; - } - - private Result getExistingExam(final PageContext pageContext) { - final EntityKey entityKey = pageContext.getEntityKey(); - return this.restService.getBuilder(GetExam.class) - .withURIVariable(API.PARAM_MODEL_ID, entityKey.modelId) - .call(); - } - - private Result createExamFromQuizData(final PageContext pageContext) { - final EntityKey entityKey = pageContext.getEntityKey(); - final EntityKey parentEntityKey = pageContext.getParentEntityKey(); - return this.restService.getBuilder(GetQuizData.class) - .withURIVariable(API.PARAM_MODEL_ID, entityKey.modelId) - .withQueryParam(QuizData.QUIZ_ATTR_LMS_SETUP_ID, parentEntityKey.modelId) - .call() - .map(quizzData -> new Exam(quizzData)) - .onError(error -> pageContext.notifyLoadError(EntityType.EXAM, error)); - } - - private String indicatorTypeName(final Indicator indicator) { - if (indicator.type == null) { - return Constants.EMPTY_NOTE; - } - - return this.resourceService.getI18nSupport() - .getText(ResourceService.EXAM_INDICATOR_TYPE_PREFIX + indicator.type.name()); - } - - private static String thresholdsValue(final Indicator indicator) { - if (indicator.thresholds.isEmpty()) { - return Constants.EMPTY_NOTE; - } - - final StringBuilder builder = indicator.thresholds - .stream() - .reduce( - new StringBuilder(), - (sb, threshold) -> sb - .append("") - .append(Indicator.getDisplayValue(indicator.type, threshold.value)) - .append(" (") - .append(threshold.color) - .append(")") - .append("") - .append(" | "), - (sb1, sb2) -> sb1.append(sb2)); - builder.delete(builder.length() - 3, builder.length() - 1); - return builder.toString(); - } - - private Function cancelModifyFunction() { - final Function backToCurrentFunction = this.pageService.backToCurrentFunction(); - return action -> { - final boolean importFromQuizData = BooleanUtils.toBoolean( - - action.pageContext().getAttribute(AttributeKeys.IMPORT_FROM_QUIZ_DATA)); - if (importFromQuizData) { - final PageActionBuilder actionBuilder = this.pageService.pageActionBuilder(action.pageContext()); - final PageAction activityHomeAction = actionBuilder - .newAction(ActionDefinition.QUIZ_DISCOVERY_VIEW_LIST) - .create(); - this.pageService.firePageEvent(new ActionEvent(activityHomeAction), action.pageContext()); - return activityHomeAction; - } - - return backToCurrentFunction.apply(action); - }; - } - -} +/* + * 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.content; + +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.function.BooleanSupplier; +import java.util.function.Function; +import java.util.function.Supplier; + +import org.apache.commons.lang3.BooleanUtils; +import org.apache.commons.lang3.StringUtils; +import org.eclipse.rap.rwt.RWT; +import org.eclipse.rap.rwt.client.service.UrlLauncher; +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.api.APIMessage; +import ch.ethz.seb.sebserver.gbl.api.EntityType; +import ch.ethz.seb.sebserver.gbl.model.Domain; +import ch.ethz.seb.sebserver.gbl.model.EntityKey; +import ch.ethz.seb.sebserver.gbl.model.exam.Exam; +import ch.ethz.seb.sebserver.gbl.model.exam.Exam.ExamStatus; +import ch.ethz.seb.sebserver.gbl.model.exam.ExamConfigurationMap; +import ch.ethz.seb.sebserver.gbl.model.exam.Indicator; +import ch.ethz.seb.sebserver.gbl.model.exam.QuizData; +import ch.ethz.seb.sebserver.gbl.model.institution.LmsSetup.Features; +import ch.ethz.seb.sebserver.gbl.model.user.UserRole; +import ch.ethz.seb.sebserver.gbl.profile.GuiProfile; +import ch.ethz.seb.sebserver.gbl.util.Result; +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.form.FormHandle; +import ch.ethz.seb.sebserver.gui.service.ResourceService; +import ch.ethz.seb.sebserver.gui.service.i18n.I18nSupport; +import ch.ethz.seb.sebserver.gui.service.i18n.LocTextKey; +import ch.ethz.seb.sebserver.gui.service.page.PageContext; +import ch.ethz.seb.sebserver.gui.service.page.PageContext.AttributeKeys; +import ch.ethz.seb.sebserver.gui.service.page.PageService; +import ch.ethz.seb.sebserver.gui.service.page.PageService.PageActionBuilder; +import ch.ethz.seb.sebserver.gui.service.page.TemplateComposer; +import ch.ethz.seb.sebserver.gui.service.page.event.ActionEvent; +import ch.ethz.seb.sebserver.gui.service.page.impl.PageAction; +import ch.ethz.seb.sebserver.gui.service.remote.download.DownloadService; +import ch.ethz.seb.sebserver.gui.service.remote.download.SebExamConfigDownload; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestService; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.CheckExamConsistency; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.DeleteExamConfigMapping; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.DeleteIndicator; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.GetExam; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.GetExamConfigMappingsPage; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.GetIndicatorPage; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.SaveExam; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.lmssetup.GetLmsSetup; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.quiz.GetQuizData; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.quiz.ImportAsExam; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.auth.CurrentUser; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.auth.CurrentUser.EntityGrantCheck; +import ch.ethz.seb.sebserver.gui.table.ColumnDefinition; +import ch.ethz.seb.sebserver.gui.table.EntityTable; +import ch.ethz.seb.sebserver.gui.widget.WidgetFactory; +import ch.ethz.seb.sebserver.gui.widget.WidgetFactory.CustomVariant; + +@Lazy +@Component +@GuiProfile +public class ExamForm implements TemplateComposer { + + private static final Logger log = LoggerFactory.getLogger(ExamForm.class); + + public static final LocTextKey EXAM_FORM_TITLE_KEY = + new LocTextKey("sebserver.exam.form.title"); + public static final LocTextKey EXAM_FORM_TITLE_IMPORT_KEY = + new LocTextKey("sebserver.exam.form.title.import"); + private static final LocTextKey CONFIG_EMPTY_LIST_MESSAGE = + new LocTextKey("sebserver.exam.configuration.list.empty"); + private static final LocTextKey INDICATOR_EMPTY_LIST_MESSAGE = + new LocTextKey("sebserver.exam.indicator.list.empty"); + private static final LocTextKey FORM_SUPPORTER_TEXT_KEY = + new LocTextKey("sebserver.exam.form.supporter"); + private static final LocTextKey FORM_STATUS_TEXT_KEY = + new LocTextKey("sebserver.exam.form.status"); + private static final LocTextKey FORM_TYPE_TEXT_KEY = + new LocTextKey("sebserver.exam.form.type"); + private static final LocTextKey FORM_ENDTIME_TEXT_KEY = + new LocTextKey("sebserver.exam.form.endtime"); + private static final LocTextKey FORM_STARTTIME_TEXT_KEY = + new LocTextKey("sebserver.exam.form.starttime"); + private static final LocTextKey FORM_DESCRIPTION_TEXT_KEY = + new LocTextKey("sebserver.exam.form.description"); + private static final LocTextKey FORM_NAME_TEXT_KEY = + new LocTextKey("sebserver.exam.form.name"); + private static final LocTextKey FORM_QUIZID_TEXT_KEY = + new LocTextKey("sebserver.exam.form.quizid"); + private static final LocTextKey FORM_QUIZ_URL_TEXT_KEY = + new LocTextKey("sebserver.exam.form.quizurl"); + private static final LocTextKey FORM_LMSSETUP_TEXT_KEY = + new LocTextKey("sebserver.exam.form.lmssetup"); + + private final static LocTextKey CONFIG_LIST_TITLE_KEY = + new LocTextKey("sebserver.exam.configuration.list.title"); + private final static LocTextKey CONFIG_NAME_COLUMN_KEY = + new LocTextKey("sebserver.exam.configuration.list.column.name"); + private final static LocTextKey CONFIG_DESCRIPTION_COLUMN_KEY = + new LocTextKey("sebserver.exam.configuration.list.column.description"); + private final static LocTextKey CONFIG_STATUS_COLUMN_KEY = + new LocTextKey("sebserver.exam.configuration.list.column.status"); + private final static LocTextKey CONFIG_EMPTY_SELECTION_TEXT_KEY = + new LocTextKey("sebserver.exam.configuration.list.pleaseSelect"); + + private final static LocTextKey INDICATOR_LIST_TITLE_KEY = + new LocTextKey("sebserver.exam.indicator.list.title"); + private final static LocTextKey INDICATOR_TYPE_COLUMN_KEY = + new LocTextKey("sebserver.exam.indicator.list.column.type"); + private final static LocTextKey INDICATOR_NAME_COLUMN_KEY = + new LocTextKey("sebserver.exam.indicator.list.column.name"); + private final static LocTextKey INDICATOR_THRESHOLD_COLUMN_KEY = + new LocTextKey("sebserver.exam.indicator.list.column.thresholds"); + private final static LocTextKey INDICATOR_EMPTY_SELECTION_TEXT_KEY = + new LocTextKey("sebserver.exam.indicator.list.pleaseSelect"); + + private final static LocTextKey CONSISTENCY_MESSAGE_TITLE = + new LocTextKey("sebserver.exam.consistency.title"); + private final static LocTextKey CONSISTENCY_MESSAGE_MISSING_SUPPORTER = + new LocTextKey("sebserver.exam.consistency.missing-supporter"); + private final static LocTextKey CONSISTENCY_MESSAGE_MISSING_INDICATOR = + new LocTextKey("sebserver.exam.consistency.missing-indicator"); + private final static LocTextKey CONSISTENCY_MESSAGE_MISSING_CONFIG = + new LocTextKey("sebserver.exam.consistency.missing-config"); + private final static LocTextKey CONSISTENCY_MESSAGE_MISSING_SEB_RESTRICTION = + new LocTextKey("sebserver.exam.consistency.missing-seb-restriction"); + + private final Map consistencyMessageMapping; + + private final static LocTextKey CONFIRM_MESSAGE_REMOVE_CONFIG = + new LocTextKey("sebserver.exam.confirm.remove-config"); + + private final PageService pageService; + private final ResourceService resourceService; + private final DownloadService downloadService; + private final String downloadFileName; + private final WidgetFactory widgetFactory; + private final RestService restService; + + protected ExamForm( + final PageService pageService, + final ResourceService resourceService, + final DownloadService downloadService, + @Value("${sebserver.gui.seb.exam.config.download.filename}") final String downloadFileName) { + + this.pageService = pageService; + this.resourceService = resourceService; + this.downloadService = downloadService; + this.downloadFileName = downloadFileName; + this.widgetFactory = pageService.getWidgetFactory(); + this.restService = this.resourceService.getRestService(); + + this.consistencyMessageMapping = new HashMap<>(); + this.consistencyMessageMapping.put( + APIMessage.ErrorMessage.EXAM_CONSISTANCY_VALIDATION_SUPPORTER.messageCode, + CONSISTENCY_MESSAGE_MISSING_SUPPORTER); + this.consistencyMessageMapping.put( + APIMessage.ErrorMessage.EXAM_CONSISTANCY_VALIDATION_INDICATOR.messageCode, + CONSISTENCY_MESSAGE_MISSING_INDICATOR); + this.consistencyMessageMapping.put( + APIMessage.ErrorMessage.EXAM_CONSISTANCY_VALIDATION_CONFIG.messageCode, + CONSISTENCY_MESSAGE_MISSING_CONFIG); + this.consistencyMessageMapping.put( + APIMessage.ErrorMessage.EXAM_CONSISTANCY_VALIDATION_SEB_RESTRICTION.messageCode, + CONSISTENCY_MESSAGE_MISSING_SEB_RESTRICTION); + } + + @Override + public void compose(final PageContext pageContext) { + final CurrentUser currentUser = this.resourceService.getCurrentUser(); + + final I18nSupport i18nSupport = this.resourceService.getI18nSupport(); + final EntityKey entityKey = pageContext.getEntityKey(); + final boolean readonly = pageContext.isReadonly(); + final boolean importFromQuizData = BooleanUtils.toBoolean( + pageContext.getAttribute(AttributeKeys.IMPORT_FROM_QUIZ_DATA)); + + // get or create model data + final Exam exam = (importFromQuizData + ? createExamFromQuizData(pageContext) + : getExistingExam(pageContext)) + .onError(error -> pageContext.notifyLoadError(EntityType.EXAM, error)) + .getOrThrow(); + + // new PageContext with actual EntityKey + final PageContext formContext = pageContext.withEntityKey(exam.getEntityKey()); + + // check exam consistency and inform the user if needed + if (readonly) { + this.restService.getBuilder(CheckExamConsistency.class) + .withURIVariable(API.PARAM_MODEL_ID, entityKey.modelId) + .call() + .ifPresent(result -> showConsistencyChecks(result, formContext.getParent())); + } + + // the default page layout with title + final LocTextKey titleKey = importFromQuizData + ? EXAM_FORM_TITLE_IMPORT_KEY + : EXAM_FORM_TITLE_KEY; + final Composite content = this.widgetFactory.defaultPageLayout( + formContext.getParent(), + titleKey); + + final BooleanSupplier isNew = () -> importFromQuizData; + final BooleanSupplier isNotNew = () -> !isNew.getAsBoolean(); + final EntityGrantCheck userGrantCheck = currentUser.entityGrantCheck(exam); + final boolean modifyGrant = userGrantCheck.m(); + final ExamStatus examStatus = exam.getStatus(); + final boolean isExamRunning = examStatus == ExamStatus.RUNNING; + final boolean editable = examStatus == ExamStatus.UP_COMING + || examStatus == ExamStatus.RUNNING + && currentUser.get().hasRole(UserRole.EXAM_ADMIN); + final boolean sebRestrictionAvailable = testSebRestrictionAPI(exam); + + // The Exam form + final FormHandle formHandle = this.pageService.formBuilder( + formContext.copyOf(content), 8) + .withDefaultSpanLabel(1) + .withDefaultSpanInput(4) + .withDefaultSpanEmptyCell(3) + .readonly(readonly) + .putStaticValueIf(isNotNew, + Domain.EXAM.ATTR_ID, + exam.getModelId()) + .putStaticValue( + Domain.EXAM.ATTR_INSTITUTION_ID, + String.valueOf(exam.getInstitutionId())) + .putStaticValueIf(isNotNew, + Domain.EXAM.ATTR_LMS_SETUP_ID, + String.valueOf(exam.lmsSetupId)) + .putStaticValueIf(isNew, + QuizData.QUIZ_ATTR_LMS_SETUP_ID, + String.valueOf(exam.lmsSetupId)) + .putStaticValueIf(isNotNew, + Domain.EXAM.ATTR_EXTERNAL_ID, + exam.externalId) + .putStaticValueIf(isNew, + QuizData.QUIZ_ATTR_ID, + exam.externalId) + + .addField(FormBuilder.text( + QuizData.QUIZ_ATTR_NAME, + FORM_NAME_TEXT_KEY, + exam.name) + .readonly(true) + .withInputSpan(3) + .withEmptyCellSeparation(false)) + + .addField(FormBuilder.singleSelection( + Domain.EXAM.ATTR_LMS_SETUP_ID, + FORM_LMSSETUP_TEXT_KEY, + String.valueOf(exam.lmsSetupId), + this.resourceService::lmsSetupResource) + .readonly(true) + .withInputSpan(3) + .withEmptyCellSeparation(false)) + + .addField(FormBuilder.text( + QuizData.QUIZ_ATTR_START_TIME, + FORM_STARTTIME_TEXT_KEY, + i18nSupport.formatDisplayDateWithTimeZone(exam.startTime)) + .readonly(true) + .withInputSpan(3) + .withEmptyCellSeparation(false)) + .addField(FormBuilder.text( + QuizData.QUIZ_ATTR_END_TIME, + FORM_ENDTIME_TEXT_KEY, + i18nSupport.formatDisplayDateWithTimeZone(exam.endTime)) + .readonly(true) + .withInputSpan(3) + .withEmptyCellSeparation(false)) + + .addField(FormBuilder.text( + Domain.EXAM.ATTR_EXTERNAL_ID, + FORM_QUIZID_TEXT_KEY, + exam.externalId) + .readonly(true) + .withEmptyCellSeparation(false)) + .addField(FormBuilder.text( + QuizData.QUIZ_ATTR_START_URL, + FORM_QUIZ_URL_TEXT_KEY, + exam.startURL) + .readonly(true) + .withInputSpan(7)) + + .addField(FormBuilder.text( + QuizData.QUIZ_ATTR_DESCRIPTION, + FORM_DESCRIPTION_TEXT_KEY, + exam.description) + .asHTML() + .readonly(true) + .withInputSpan(6) + .withEmptyCellSeparation(false)) + + .addField(FormBuilder.text( + Domain.EXAM.ATTR_STATUS + "_display", + FORM_STATUS_TEXT_KEY, + i18nSupport.getText(new LocTextKey("sebserver.exam.status." + examStatus.name()))) + .readonly(true) + .withLabelSpan(2) + .withInputSpan(4) + .withEmptyCellSpan(1)) + .addField(FormBuilder.singleSelection( + Domain.EXAM.ATTR_TYPE, + FORM_TYPE_TEXT_KEY, + String.valueOf(exam.type), + this.resourceService::examTypeResources) + .withLabelSpan(2) + .withInputSpan(4) + .withEmptyCellSpan(2)) + + .addField(FormBuilder.multiComboSelection( + Domain.EXAM.ATTR_SUPPORTER, + FORM_SUPPORTER_TEXT_KEY, + StringUtils.join(exam.supporter, Constants.LIST_SEPARATOR_CHAR), + this.resourceService::examSupporterResources) + .withLabelSpan(2) + .withInputSpan(4) + .withEmptyCellSpan(2)) + + .buildFor(importFromQuizData + ? this.restService.getRestCall(ImportAsExam.class) + : this.restService.getRestCall(SaveExam.class)); + + final PageActionBuilder actionBuilder = this.pageService.pageActionBuilder(formContext + .clearEntityKeys() + .removeAttribute(AttributeKeys.IMPORT_FROM_QUIZ_DATA)); + // propagate content actions to action-pane + actionBuilder + + .newAction(ActionDefinition.EXAM_MODIFY) + .withEntityKey(entityKey) + .publishIf(() -> modifyGrant && readonly && editable) + + .newAction(ActionDefinition.EXAM_SAVE) + .withExec(action -> (importFromQuizData) + ? importExam(action, formHandle, sebRestrictionAvailable && exam.status == ExamStatus.RUNNING) + : formHandle.processFormSave(action)) + .ignoreMoveAwayFromEdit() + .publishIf(() -> !readonly && modifyGrant) + + .newAction(ActionDefinition.EXAM_CANCEL_MODIFY) + .withEntityKey(entityKey) + .withAttribute(AttributeKeys.IMPORT_FROM_QUIZ_DATA, String.valueOf(importFromQuizData)) + .withExec(this.cancelModifyFunction()) + .publishIf(() -> !readonly) + + .newAction(ActionDefinition.EXAM_MODIFY_SEB_RESTRICTION_DETAILS) + .withEntityKey(entityKey) + .withExec(ExamSebRestrictionSettings.settingsFunction(this.pageService)) + .withAttribute( + ExamSebRestrictionSettings.PAGE_CONTEXT_ATTR_LMS_TYPE, + this.restService.getBuilder(GetLmsSetup.class) + .withURIVariable(API.PARAM_MODEL_ID, String.valueOf(exam.lmsSetupId)) + .call() + .getOrThrow().lmsType.name()) + .noEventPropagation() + .publishIf(() -> sebRestrictionAvailable && readonly) + + .newAction(ActionDefinition.EXAM_ENABLE_SEB_RESTRICTION) + .withEntityKey(entityKey) + .withExec(action -> ExamSebRestrictionSettings.setSebRestriction(action, true, this.restService)) + .publishIf(() -> sebRestrictionAvailable && readonly && BooleanUtils.isFalse(exam.lmsSebRestriction)) + + .newAction(ActionDefinition.EXAM_DISABLE_SEB_RESTRICTION) + .withEntityKey(entityKey) + .withExec(action -> ExamSebRestrictionSettings.setSebRestriction(action, false, this.restService)) + .publishIf(() -> sebRestrictionAvailable && readonly && BooleanUtils.isTrue(exam.lmsSebRestriction)); + + // additional data in read-only view + if (readonly && !importFromQuizData) { + + // List of SEB Configuration + this.widgetFactory.label(content, StringUtils.EMPTY); + this.widgetFactory.labelLocalized( + content, + CustomVariant.TEXT_H3, + CONFIG_LIST_TITLE_KEY); + this.widgetFactory.labelSeparator(content); + + final EntityTable configurationTable = + this.pageService.entityTableBuilder(this.restService.getRestCall(GetExamConfigMappingsPage.class)) + .withRestCallAdapter(builder -> builder.withQueryParam( + ExamConfigurationMap.FILTER_ATTR_EXAM_ID, + entityKey.modelId)) + .withEmptyMessage(CONFIG_EMPTY_LIST_MESSAGE) + .withPaging(1) + .hideNavigation() + .withColumn(new ColumnDefinition<>( + Domain.CONFIGURATION_NODE.ATTR_NAME, + CONFIG_NAME_COLUMN_KEY, + ExamConfigurationMap::getConfigName) + .widthProportion(2)) + .withColumn(new ColumnDefinition<>( + Domain.CONFIGURATION_NODE.ATTR_DESCRIPTION, + CONFIG_DESCRIPTION_COLUMN_KEY, + ExamConfigurationMap::getConfigDescription) + .widthProportion(4)) + .withColumn(new ColumnDefinition( + Domain.CONFIGURATION_NODE.ATTR_STATUS, + CONFIG_STATUS_COLUMN_KEY, + this.resourceService::localizedExamConfigStatusName) + .widthProportion(1)) + .withDefaultActionIf( + () -> modifyGrant, + this::viewExamConfigPageAction) + + .compose(pageContext.copyOf(content)); + + final EntityKey configMapKey = (configurationTable.hasAnyContent()) + ? new EntityKey( + configurationTable.getFirstRowData().configurationNodeId, + EntityType.CONFIGURATION_NODE) + : null; + + actionBuilder + + .newAction(ActionDefinition.EXAM_CONFIGURATION_NEW) + .withParentEntityKey(entityKey) + .withExec(ExamToConfigBindingForm.bindFunction(this.pageService)) + .noEventPropagation() + .publishIf(() -> modifyGrant && editable && !configurationTable.hasAnyContent()) + + .newAction(ActionDefinition.EXAM_CONFIGURATION_EXAM_CONFIG_VIEW_PROP) + .withParentEntityKey(entityKey) + .withEntityKey(configMapKey) + .publishIf(() -> modifyGrant && configurationTable.hasAnyContent()) + + .newAction(ActionDefinition.EXAM_CONFIGURATION_DELETE_FROM_LIST) + .withEntityKey(entityKey) + .withSelect( + getConfigMappingSelection(configurationTable), + this::deleteExamConfigMapping, + CONFIG_EMPTY_SELECTION_TEXT_KEY) + .withConfirm(() -> { + if (isExamRunning) { + return CONFIRM_MESSAGE_REMOVE_CONFIG; + } + return null; + }) + .publishIf(() -> modifyGrant && configurationTable.hasAnyContent() && editable) + + .newAction(ActionDefinition.EXAM_CONFIGURATION_EXPORT) + .withParentEntityKey(entityKey) + .withSelect( + getConfigSelection(configurationTable), + this::downloadExamConfigAction, + CONFIG_EMPTY_SELECTION_TEXT_KEY) + .noEventPropagation() + .publishIf(() -> userGrantCheck.r() && configurationTable.hasAnyContent()) + + .newAction(ActionDefinition.EXAM_CONFIGURATION_GET_CONFIG_KEY) + .withSelect( + getConfigSelection(configurationTable), + this::getExamConfigKey, + CONFIG_EMPTY_SELECTION_TEXT_KEY) + .noEventPropagation() + .publishIf(() -> userGrantCheck.r() && configurationTable.hasAnyContent()); + + // List of Indicators + this.widgetFactory.label(content, ""); + this.widgetFactory.labelLocalized( + content, + CustomVariant.TEXT_H3, + INDICATOR_LIST_TITLE_KEY); + this.widgetFactory.labelSeparator(content); + + final EntityTable indicatorTable = + this.pageService.entityTableBuilder(this.restService.getRestCall(GetIndicatorPage.class)) + .withRestCallAdapter(builder -> builder.withQueryParam( + Indicator.FILTER_ATTR_EXAM_ID, + entityKey.modelId)) + .withEmptyMessage(INDICATOR_EMPTY_LIST_MESSAGE) + .withMarkup() + .withPaging(5) + .hideNavigation() + .withColumn(new ColumnDefinition<>( + Domain.INDICATOR.ATTR_NAME, + INDICATOR_NAME_COLUMN_KEY, + Indicator::getName) + .widthProportion(2)) + .withColumn(new ColumnDefinition<>( + Domain.INDICATOR.ATTR_TYPE, + INDICATOR_TYPE_COLUMN_KEY, + this::indicatorTypeName) + .widthProportion(1)) + .withColumn(new ColumnDefinition<>( + Domain.THRESHOLD.REFERENCE_NAME, + INDICATOR_THRESHOLD_COLUMN_KEY, + ExamForm::thresholdsValue) + .asMarkup() + .widthProportion(4)) + .withDefaultActionIf( + () -> modifyGrant, + () -> actionBuilder + .newAction(ActionDefinition.EXAM_INDICATOR_MODIFY_FROM_LIST) + .withParentEntityKey(entityKey) + .create()) + + .compose(pageContext.copyOf(content)); + + actionBuilder + + .newAction(ActionDefinition.EXAM_INDICATOR_NEW) + .withParentEntityKey(entityKey) + .publishIf(() -> modifyGrant) + + .newAction(ActionDefinition.EXAM_INDICATOR_MODIFY_FROM_LIST) + .withParentEntityKey(entityKey) + .withSelect( + indicatorTable::getSelection, + PageAction::applySingleSelectionAsEntityKey, + INDICATOR_EMPTY_SELECTION_TEXT_KEY) + .publishIf(() -> modifyGrant && indicatorTable.hasAnyContent()) + + .newAction(ActionDefinition.EXAM_INDICATOR_DELETE_FROM_LIST) + .withEntityKey(entityKey) + .withSelect( + indicatorTable::getSelection, + this::deleteSelectedIndicator, + INDICATOR_EMPTY_SELECTION_TEXT_KEY) + .publishIf(() -> modifyGrant && indicatorTable.hasAnyContent()); + } + } + + private PageAction importExam( + final PageAction action, + final FormHandle formHandle, + final boolean applySebRestriction) { + + // process normal save first + final PageAction processFormSave = formHandle.processFormSave(action); + + // when okay and the exam sebRestriction is true + if (applySebRestriction) { + ExamSebRestrictionSettings.setSebRestriction( + processFormSave, + true, + this.restService, + t -> log.error("Failed to intially restrict the course for SEB on LMS: {}", t.getMessage())); + } + + return processFormSave; + } + + private boolean testSebRestrictionAPI(final Exam exam) { + return this.restService.getBuilder(GetLmsSetup.class) + .withURIVariable(API.PARAM_MODEL_ID, String.valueOf(exam.lmsSetupId)) + .call() + .onError(t -> log.error("Failed to check SEB restriction API: ", t)) + .map(lmsSetup -> lmsSetup.lmsType.features.contains(Features.SEB_RESTICTION)) + .getOr(false); + } + + private void showConsistencyChecks(final Collection result, final Composite parent) { + if (result == null || result.isEmpty()) { + return; + } + + final Composite warningPanel = this.widgetFactory.createWarningPanel(parent); + this.widgetFactory.labelLocalized( + warningPanel, + CustomVariant.TITLE_LABEL, + CONSISTENCY_MESSAGE_TITLE); + + result + .stream() + .map(message -> this.consistencyMessageMapping.get(message.messageCode)) + .filter(message -> message != null) + .forEach(message -> this.widgetFactory.labelLocalized( + warningPanel, + CustomVariant.MESSAGE, + message)); + } + + private PageAction viewExamConfigPageAction(final EntityTable table) { + + return this.pageService.pageActionBuilder(table.getPageContext() + .clearEntityKeys() + .removeAttribute(AttributeKeys.IMPORT_FROM_QUIZ_DATA)) + .newAction(ActionDefinition.EXAM_CONFIGURATION_EXAM_CONFIG_VIEW_PROP) + .withSelectionSupplier(() -> { + final ExamConfigurationMap selectedROWData = table.getSingleSelectedROWData(); + final HashSet result = new HashSet<>(); + if (selectedROWData != null) { + result.add(new EntityKey( + selectedROWData.configurationNodeId, + EntityType.CONFIGURATION_NODE)); + } + return result; + }) + .create(); + } + + private PageAction downloadExamConfigAction(final PageAction action) { + final UrlLauncher urlLauncher = RWT.getClient().getService(UrlLauncher.class); + final EntityKey selection = action.getSingleSelection(); + if (selection != null) { + final String downloadURL = this.downloadService.createDownloadURL( + selection.modelId, + action.pageContext().getParentEntityKey().modelId, + SebExamConfigDownload.class, + this.downloadFileName); + urlLauncher.openURL(downloadURL); + } + return action; + } + + private Supplier> getConfigMappingSelection( + final EntityTable configurationTable) { + return () -> { + final ExamConfigurationMap firstRowData = configurationTable.getFirstRowData(); + if (firstRowData == null) { + return Collections.emptySet(); + } else { + return new HashSet<>(Arrays.asList(firstRowData.getEntityKey())); + } + }; + } + + private Supplier> getConfigSelection(final EntityTable configurationTable) { + return () -> { + final ExamConfigurationMap firstRowData = configurationTable.getFirstRowData(); + if (firstRowData == null) { + return Collections.emptySet(); + } else { + return new HashSet<>(Arrays.asList(new EntityKey( + firstRowData.configurationNodeId, + EntityType.CONFIGURATION_NODE))); + } + }; + } + + private PageAction deleteSelectedIndicator(final PageAction action) { + final EntityKey indicatorKey = action.getSingleSelection(); + this.resourceService.getRestService() + .getBuilder(DeleteIndicator.class) + .withURIVariable(API.PARAM_MODEL_ID, indicatorKey.modelId) + .call(); + return action; + } + + private PageAction getExamConfigKey(final PageAction action) { + final EntityKey examConfigMappingKey = action.getSingleSelection(); + if (examConfigMappingKey != null) { + action.withEntityKey(examConfigMappingKey); + return SebExamConfigPropForm + .getConfigKeyFunction(this.pageService) + .apply(action); + } + + return action; + } + + private PageAction deleteExamConfigMapping(final PageAction action) { + final EntityKey examConfigMappingKey = action.getSingleSelection(); + this.resourceService.getRestService() + .getBuilder(DeleteExamConfigMapping.class) + .withURIVariable(API.PARAM_MODEL_ID, examConfigMappingKey.modelId) + .call() + .onError(error -> action.pageContext().notifyRemoveError(EntityType.EXAM_CONFIGURATION_MAP, error)); + return action; + } + + private Result getExistingExam(final PageContext pageContext) { + final EntityKey entityKey = pageContext.getEntityKey(); + return this.restService.getBuilder(GetExam.class) + .withURIVariable(API.PARAM_MODEL_ID, entityKey.modelId) + .call(); + } + + private Result createExamFromQuizData(final PageContext pageContext) { + final EntityKey entityKey = pageContext.getEntityKey(); + final EntityKey parentEntityKey = pageContext.getParentEntityKey(); + return this.restService.getBuilder(GetQuizData.class) + .withURIVariable(API.PARAM_MODEL_ID, entityKey.modelId) + .withQueryParam(QuizData.QUIZ_ATTR_LMS_SETUP_ID, parentEntityKey.modelId) + .call() + .map(quizzData -> new Exam(quizzData)) + .onError(error -> pageContext.notifyLoadError(EntityType.EXAM, error)); + } + + private String indicatorTypeName(final Indicator indicator) { + if (indicator.type == null) { + return Constants.EMPTY_NOTE; + } + + return this.resourceService.getI18nSupport() + .getText(ResourceService.EXAM_INDICATOR_TYPE_PREFIX + indicator.type.name()); + } + + private static String thresholdsValue(final Indicator indicator) { + if (indicator.thresholds.isEmpty()) { + return Constants.EMPTY_NOTE; + } + + final StringBuilder builder = indicator.thresholds + .stream() + .reduce( + new StringBuilder(), + (sb, threshold) -> sb + .append("") + .append(Indicator.getDisplayValue(indicator.type, threshold.value)) + .append(" (") + .append(threshold.color) + .append(")") + .append("") + .append(" | "), + (sb1, sb2) -> sb1.append(sb2)); + builder.delete(builder.length() - 3, builder.length() - 1); + return builder.toString(); + } + + private Function cancelModifyFunction() { + final Function backToCurrentFunction = this.pageService.backToCurrentFunction(); + return action -> { + final boolean importFromQuizData = BooleanUtils.toBoolean( + + action.pageContext().getAttribute(AttributeKeys.IMPORT_FROM_QUIZ_DATA)); + if (importFromQuizData) { + final PageActionBuilder actionBuilder = this.pageService.pageActionBuilder(action.pageContext()); + final PageAction activityHomeAction = actionBuilder + .newAction(ActionDefinition.QUIZ_DISCOVERY_VIEW_LIST) + .create(); + this.pageService.firePageEvent(new ActionEvent(activityHomeAction), action.pageContext()); + return activityHomeAction; + } + + return backToCurrentFunction.apply(action); + }; + } + +} diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/content/InstitutionForm.java b/src/main/java/ch/ethz/seb/sebserver/gui/content/InstitutionForm.java index ff4412e7..2fd79940 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/content/InstitutionForm.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/content/InstitutionForm.java @@ -1,172 +1,167 @@ -/* - * 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.content; - -import org.eclipse.swt.widgets.Composite; -import org.springframework.context.annotation.Lazy; -import org.springframework.stereotype.Component; - -import ch.ethz.seb.sebserver.gbl.api.API; -import ch.ethz.seb.sebserver.gbl.api.EntityType; -import ch.ethz.seb.sebserver.gbl.model.Domain; -import ch.ethz.seb.sebserver.gbl.model.EntityKey; -import ch.ethz.seb.sebserver.gbl.model.institution.Institution; -import ch.ethz.seb.sebserver.gbl.profile.GuiProfile; -import ch.ethz.seb.sebserver.gui.content.action.ActionDefinition; -import ch.ethz.seb.sebserver.gui.form.FormBuilder; -import ch.ethz.seb.sebserver.gui.form.FormHandle; -import ch.ethz.seb.sebserver.gui.service.i18n.LocTextKey; -import ch.ethz.seb.sebserver.gui.service.page.PageContext; -import ch.ethz.seb.sebserver.gui.service.page.PageService; -import ch.ethz.seb.sebserver.gui.service.page.TemplateComposer; -import ch.ethz.seb.sebserver.gui.service.page.impl.DefaultPageLayout; -import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestService; -import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.institution.ActivateInstitution; -import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.institution.DeactivateInstitution; -import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.institution.GetInstitution; -import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.institution.NewInstitution; -import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.institution.SaveInstitution; -import ch.ethz.seb.sebserver.gui.service.remote.webservice.auth.CurrentUser; -import ch.ethz.seb.sebserver.gui.service.remote.webservice.auth.CurrentUser.EntityGrantCheck; -import ch.ethz.seb.sebserver.gui.widget.WidgetFactory; - -@Lazy -@Component -@GuiProfile -public class InstitutionForm implements TemplateComposer { - - private static final LocTextKey TITLE_TEXT_KEY = - new LocTextKey("sebserver.institution.form.title"); - private static final LocTextKey NEW_TITLE_TEXT_KEY = - new LocTextKey("sebserver.institution.form.title.new"); - - private static final LocTextKey FORM_LOGO_IMAGE_TEXT_KEY = - new LocTextKey("sebserver.institution.form.logoImage"); - private static final LocTextKey FORM_URL_SUFFIX_TEXT_KEY = - new LocTextKey("sebserver.institution.form.urlSuffix"); - private static final LocTextKey FORM_NAME_TEXT_KEY = - new LocTextKey("sebserver.institution.form.name"); - - private final PageService pageService; - private final RestService restService; - private final CurrentUser currentUser; - - protected InstitutionForm( - final PageService pageService, - final RestService restService, - final CurrentUser currentUser) { - - this.pageService = pageService; - this.restService = restService; - this.currentUser = currentUser; - } - - @Override - public void compose(final PageContext pageContext) { - - final WidgetFactory widgetFactory = this.pageService.getWidgetFactory(); - final EntityKey entityKey = pageContext.getEntityKey(); - final boolean isNew = entityKey == null; - // get data or create new. Handle error if happen - final Institution institution = (isNew) - ? Institution.createNew() - : this.restService - .getBuilder(GetInstitution.class) - .withURIVariable(API.PARAM_MODEL_ID, entityKey.modelId) - .call() - .onError(error -> pageContext.notifyLoadError(EntityType.INSTITUTION, error)) - .getOrThrow(); - - final EntityGrantCheck instGrant = this.currentUser.entityGrantCheck(institution); - final boolean writeGrant = instGrant.w(); - final boolean modifyGrant = instGrant.m(); - final boolean isReadonly = pageContext.isReadonly(); - - // new PageContext with actual EntityKey - final PageContext formContext = pageContext.withEntityKey(institution.getEntityKey()); - - // the default page layout with interactive title - final LocTextKey titleKey = isNew - ? NEW_TITLE_TEXT_KEY - : TITLE_TEXT_KEY; - final Composite content = widgetFactory.defaultPageLayout( - formContext.getParent(), - titleKey); - - // The Institution form - final FormHandle formHandle = this.pageService.formBuilder( - formContext.copyOf(content)) - .readonly(isReadonly) - .putStaticValueIf(() -> !isNew, - Domain.INSTITUTION.ATTR_ID, - institution.getModelId()) - .addField(FormBuilder.text( - Domain.INSTITUTION.ATTR_NAME, - FORM_NAME_TEXT_KEY, - institution.name) - .mandatory(!isReadonly)) - .addField(FormBuilder.text( - Domain.INSTITUTION.ATTR_URL_SUFFIX, - FORM_URL_SUFFIX_TEXT_KEY, - institution.urlSuffix)) - .addField(FormBuilder.imageUpload( - Domain.INSTITUTION.ATTR_LOGO_IMAGE, - FORM_LOGO_IMAGE_TEXT_KEY, - institution.logoImage) - .withMaxWidth(DefaultPageLayout.LOGO_IMAGE_MAX_WIDTH) - .withMaxHeight(DefaultPageLayout.LOGO_IMAGE_MAX_HEIGHT)) - .buildFor((isNew) - ? this.restService.getRestCall(NewInstitution.class) - : this.restService.getRestCall(SaveInstitution.class)); - - // propagate content actions to action-pane - this.pageService.pageActionBuilder(formContext.clearEntityKeys()) - - .newAction(ActionDefinition.INSTITUTION_NEW) - .publishIf(() -> writeGrant && isReadonly) - -// Removed as discussed in SEBSERV-52 -// .newAction(ActionDefinition.USER_ACCOUNT_NEW) -// .withParentEntityKey(entityKey) -// .publishIf(() -> userWriteGrant && isReadonly && institution.isActive()) - - .newAction(ActionDefinition.INSTITUTION_MODIFY) - .withEntityKey(entityKey) - .publishIf(() -> modifyGrant && isReadonly) - - .newAction(ActionDefinition.INSTITUTION_DEACTIVATE) - .withEntityKey(entityKey) - .withSimpleRestCall(this.restService, DeactivateInstitution.class) - .withConfirm(this.pageService.confirmDeactivation(institution)) - .publishIf(() -> writeGrant && isReadonly && institution.isActive()) - - .newAction(ActionDefinition.INSTITUTION_ACTIVATE) - .withEntityKey(entityKey) - .withSimpleRestCall(this.restService, ActivateInstitution.class) - .publishIf(() -> writeGrant && isReadonly && !institution.isActive()) - - .newAction(ActionDefinition.INSTITUTION_SAVE) - .withEntityKey(entityKey) - .withExec(formHandle::processFormSave) - .ignoreMoveAwayFromEdit() - .publishIf(() -> !isReadonly) - - .newAction(ActionDefinition.INSTITUTION_SAVE_AND_ACTIVATE) - .withEntityKey(entityKey) - .withExec(formHandle::saveAndActivate) - .ignoreMoveAwayFromEdit() - .publishIf(() -> !isReadonly && !institution.isActive()) - - .newAction(ActionDefinition.INSTITUTION_CANCEL_MODIFY) - .withEntityKey(entityKey) - .withExec(this.pageService.backToCurrentFunction()) - .publishIf(() -> !isReadonly); - } - -} +/* + * 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.content; + +import org.eclipse.swt.widgets.Composite; +import org.springframework.context.annotation.Lazy; +import org.springframework.stereotype.Component; + +import ch.ethz.seb.sebserver.gbl.api.API; +import ch.ethz.seb.sebserver.gbl.api.EntityType; +import ch.ethz.seb.sebserver.gbl.model.Domain; +import ch.ethz.seb.sebserver.gbl.model.EntityKey; +import ch.ethz.seb.sebserver.gbl.model.institution.Institution; +import ch.ethz.seb.sebserver.gbl.profile.GuiProfile; +import ch.ethz.seb.sebserver.gui.content.action.ActionDefinition; +import ch.ethz.seb.sebserver.gui.form.FormBuilder; +import ch.ethz.seb.sebserver.gui.form.FormHandle; +import ch.ethz.seb.sebserver.gui.service.i18n.LocTextKey; +import ch.ethz.seb.sebserver.gui.service.page.PageContext; +import ch.ethz.seb.sebserver.gui.service.page.PageService; +import ch.ethz.seb.sebserver.gui.service.page.TemplateComposer; +import ch.ethz.seb.sebserver.gui.service.page.impl.DefaultPageLayout; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestService; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.institution.ActivateInstitution; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.institution.DeactivateInstitution; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.institution.GetInstitution; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.institution.NewInstitution; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.institution.SaveInstitution; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.auth.CurrentUser; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.auth.CurrentUser.EntityGrantCheck; +import ch.ethz.seb.sebserver.gui.widget.WidgetFactory; + +@Lazy +@Component +@GuiProfile +public class InstitutionForm implements TemplateComposer { + + private static final LocTextKey TITLE_TEXT_KEY = + new LocTextKey("sebserver.institution.form.title"); + private static final LocTextKey NEW_TITLE_TEXT_KEY = + new LocTextKey("sebserver.institution.form.title.new"); + + private static final LocTextKey FORM_LOGO_IMAGE_TEXT_KEY = + new LocTextKey("sebserver.institution.form.logoImage"); + private static final LocTextKey FORM_URL_SUFFIX_TEXT_KEY = + new LocTextKey("sebserver.institution.form.urlSuffix"); + private static final LocTextKey FORM_NAME_TEXT_KEY = + new LocTextKey("sebserver.institution.form.name"); + + private final PageService pageService; + private final RestService restService; + private final CurrentUser currentUser; + + protected InstitutionForm( + final PageService pageService, + final RestService restService, + final CurrentUser currentUser) { + + this.pageService = pageService; + this.restService = restService; + this.currentUser = currentUser; + } + + @Override + public void compose(final PageContext pageContext) { + + final WidgetFactory widgetFactory = this.pageService.getWidgetFactory(); + final EntityKey entityKey = pageContext.getEntityKey(); + final boolean isNew = entityKey == null; + // get data or create new. Handle error if happen + final Institution institution = (isNew) + ? Institution.createNew() + : this.restService + .getBuilder(GetInstitution.class) + .withURIVariable(API.PARAM_MODEL_ID, entityKey.modelId) + .call() + .onError(error -> pageContext.notifyLoadError(EntityType.INSTITUTION, error)) + .getOrThrow(); + + final EntityGrantCheck instGrant = this.currentUser.entityGrantCheck(institution); + final boolean writeGrant = instGrant.w(); + final boolean modifyGrant = instGrant.m(); + final boolean isReadonly = pageContext.isReadonly(); + + // new PageContext with actual EntityKey + final PageContext formContext = pageContext.withEntityKey(institution.getEntityKey()); + + // the default page layout with interactive title + final LocTextKey titleKey = isNew + ? NEW_TITLE_TEXT_KEY + : TITLE_TEXT_KEY; + final Composite content = widgetFactory.defaultPageLayout( + formContext.getParent(), + titleKey); + + // The Institution form + final FormHandle formHandle = this.pageService.formBuilder( + formContext.copyOf(content)) + .readonly(isReadonly) + .putStaticValueIf(() -> !isNew, + Domain.INSTITUTION.ATTR_ID, + institution.getModelId()) + .addField(FormBuilder.text( + Domain.INSTITUTION.ATTR_NAME, + FORM_NAME_TEXT_KEY, + institution.name) + .mandatory(!isReadonly)) + .addField(FormBuilder.text( + Domain.INSTITUTION.ATTR_URL_SUFFIX, + FORM_URL_SUFFIX_TEXT_KEY, + institution.urlSuffix)) + .addField(FormBuilder.imageUpload( + Domain.INSTITUTION.ATTR_LOGO_IMAGE, + FORM_LOGO_IMAGE_TEXT_KEY, + institution.logoImage) + .withMaxWidth(DefaultPageLayout.LOGO_IMAGE_MAX_WIDTH) + .withMaxHeight(DefaultPageLayout.LOGO_IMAGE_MAX_HEIGHT)) + .buildFor((isNew) + ? this.restService.getRestCall(NewInstitution.class) + : this.restService.getRestCall(SaveInstitution.class)); + + // propagate content actions to action-pane + this.pageService.pageActionBuilder(formContext.clearEntityKeys()) + + .newAction(ActionDefinition.INSTITUTION_NEW) + .publishIf(() -> writeGrant && isReadonly) + + .newAction(ActionDefinition.INSTITUTION_MODIFY) + .withEntityKey(entityKey) + .publishIf(() -> modifyGrant && isReadonly) + + .newAction(ActionDefinition.INSTITUTION_DEACTIVATE) + .withEntityKey(entityKey) + .withSimpleRestCall(this.restService, DeactivateInstitution.class) + .withConfirm(this.pageService.confirmDeactivation(institution)) + .publishIf(() -> writeGrant && isReadonly && institution.isActive()) + + .newAction(ActionDefinition.INSTITUTION_ACTIVATE) + .withEntityKey(entityKey) + .withSimpleRestCall(this.restService, ActivateInstitution.class) + .publishIf(() -> writeGrant && isReadonly && !institution.isActive()) + + .newAction(ActionDefinition.INSTITUTION_SAVE) + .withEntityKey(entityKey) + .withExec(formHandle::processFormSave) + .ignoreMoveAwayFromEdit() + .publishIf(() -> !isReadonly) + + .newAction(ActionDefinition.INSTITUTION_SAVE_AND_ACTIVATE) + .withEntityKey(entityKey) + .withExec(formHandle::saveAndActivate) + .ignoreMoveAwayFromEdit() + .publishIf(() -> !isReadonly && !institution.isActive()) + + .newAction(ActionDefinition.INSTITUTION_CANCEL_MODIFY) + .withEntityKey(entityKey) + .withExec(this.pageService.backToCurrentFunction()) + .publishIf(() -> !isReadonly); + } + +} diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/content/InstitutionList.java b/src/main/java/ch/ethz/seb/sebserver/gui/content/InstitutionList.java index 096ba1cc..3399b1ec 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/content/InstitutionList.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/content/InstitutionList.java @@ -1,158 +1,158 @@ -/* - * 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.content; - -import org.apache.commons.lang3.StringUtils; -import org.eclipse.swt.widgets.Composite; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.context.annotation.Lazy; -import org.springframework.stereotype.Component; - -import ch.ethz.seb.sebserver.gbl.api.EntityType; -import ch.ethz.seb.sebserver.gbl.model.Domain; -import ch.ethz.seb.sebserver.gbl.model.Entity; -import ch.ethz.seb.sebserver.gbl.model.institution.Institution; -import ch.ethz.seb.sebserver.gbl.profile.GuiProfile; -import ch.ethz.seb.sebserver.gui.content.action.ActionDefinition; -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.PageAction; -import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestService; -import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.institution.GetInstitutionPage; -import ch.ethz.seb.sebserver.gui.service.remote.webservice.auth.CurrentUser; -import ch.ethz.seb.sebserver.gui.service.remote.webservice.auth.CurrentUser.GrantCheck; -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; - -@Lazy -@Component -@GuiProfile -public class InstitutionList implements TemplateComposer { - - private static final LocTextKey EMPTY_LIST_TEXT_KEY = - new LocTextKey("sebserver.institution.list.empty"); - private static final LocTextKey TITLE_TEXT_KEY = - new LocTextKey("sebserver.institution.list.title"); - private static final LocTextKey NAME_TEXT_KEY = - new LocTextKey("sebserver.institution.list.column.name"); - private static final LocTextKey URL_TEXT_KEY = - new LocTextKey("sebserver.institution.list.column.urlSuffix"); - private static final LocTextKey ACTIVE_TEXT_KEY = - new LocTextKey("sebserver.institution.list.column.active"); - private static final LocTextKey EMPTY_SELECTION_TEXT_KEY = - new LocTextKey("sebserver.institution.info.pleaseSelect"); - - private final TableFilterAttribute nameFilter = - new TableFilterAttribute(CriteriaType.TEXT, Entity.FILTER_ATTR_NAME); - private final TableFilterAttribute urlSuffixFilter = - new TableFilterAttribute(CriteriaType.TEXT, Institution.FILTER_ATTR_URL_SUFFIX); - private final TableFilterAttribute activityFilter; - - private final PageService pageService; - private final RestService restService; - private final CurrentUser currentUser; - private final int pageSize; - - protected InstitutionList( - final PageService pageService, - final RestService restService, - final CurrentUser currentUser, - @Value("${sebserver.gui.list.page.size:20}") final Integer pageSize) { - - this.pageService = pageService; - this.restService = restService; - this.currentUser = currentUser; - this.pageSize = pageSize; - - this.activityFilter = new TableFilterAttribute( - CriteriaType.SINGLE_SELECTION, - Institution.FILTER_ATTR_ACTIVE, - StringUtils.EMPTY, - this.pageService.getResourceService()::activityResources); - } - - @Override - public void compose(final PageContext pageContext) { - final Composite content = this.pageService.getWidgetFactory().defaultPageLayout( - pageContext.getParent(), - TITLE_TEXT_KEY); - - final PageActionBuilder pageActionBuilder = - this.pageService.pageActionBuilder(pageContext.clearEntityKeys()); - - // table - final EntityTable table = - this.pageService.entityTableBuilder(this.restService.getRestCall(GetInstitutionPage.class)) - .withEmptyMessage(EMPTY_LIST_TEXT_KEY) - .withPaging(this.pageSize) - .withColumn(new ColumnDefinition<>( - Domain.INSTITUTION.ATTR_NAME, - NAME_TEXT_KEY, - Institution::getName) - .sortable() - .withFilter(this.nameFilter)) - .withColumn(new ColumnDefinition<>( - Domain.INSTITUTION.ATTR_URL_SUFFIX, - URL_TEXT_KEY, - Institution::getUrlSuffix) - .sortable() - .withFilter(this.urlSuffixFilter)) - .withColumn(new ColumnDefinition<>( - Domain.INSTITUTION.ATTR_ACTIVE, - ACTIVE_TEXT_KEY, - this.pageService.getResourceService(). localizedActivityFunction()) - .sortable() - .withFilter(this.activityFilter)) - .withDefaultAction(pageActionBuilder - .newAction(ActionDefinition.INSTITUTION_VIEW_FROM_LIST) - .create()) - .withSelectionListener(this.pageService.getSelectionPublisher( - ActionDefinition.INSTITUTION_TOGGLE_ACTIVITY, - ActionDefinition.INSTITUTION_ACTIVATE, - ActionDefinition.INSTITUTION_DEACTIVATE, - pageContext, - ActionDefinition.INSTITUTION_VIEW_FROM_LIST, - ActionDefinition.INSTITUTION_MODIFY_FROM_LIST, - ActionDefinition.INSTITUTION_TOGGLE_ACTIVITY)) - .compose(pageContext.copyOf(content)); - - // propagate content actions to action-pane - final GrantCheck instGrant = this.currentUser.grantCheck(EntityType.INSTITUTION); - - pageActionBuilder - - .newAction(ActionDefinition.INSTITUTION_NEW) - .publishIf(instGrant::w) - - .newAction(ActionDefinition.INSTITUTION_VIEW_FROM_LIST) - .withSelect( - table::getSelection, - PageAction::applySingleSelectionAsEntityKey, - EMPTY_SELECTION_TEXT_KEY) - .publishIf(() -> table.hasAnyContent(), false) - - .newAction(ActionDefinition.INSTITUTION_MODIFY_FROM_LIST) - .withSelect( - table::getSelection, - PageAction::applySingleSelectionAsEntityKey, - EMPTY_SELECTION_TEXT_KEY) - .publishIf(() -> instGrant.m() && table.hasAnyContent(), false) - - .newAction(ActionDefinition.INSTITUTION_TOGGLE_ACTIVITY) - .withExec(this.pageService.activationToggleActionFunction(table, EMPTY_SELECTION_TEXT_KEY)) - .withConfirm(this.pageService.confirmDeactivation(table)) - .publishIf(() -> instGrant.m() && table.hasAnyContent(), false); - } - -} +/* + * 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.content; + +import org.apache.commons.lang3.StringUtils; +import org.eclipse.swt.widgets.Composite; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Lazy; +import org.springframework.stereotype.Component; + +import ch.ethz.seb.sebserver.gbl.api.EntityType; +import ch.ethz.seb.sebserver.gbl.model.Domain; +import ch.ethz.seb.sebserver.gbl.model.Entity; +import ch.ethz.seb.sebserver.gbl.model.institution.Institution; +import ch.ethz.seb.sebserver.gbl.profile.GuiProfile; +import ch.ethz.seb.sebserver.gui.content.action.ActionDefinition; +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.PageAction; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestService; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.institution.GetInstitutionPage; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.auth.CurrentUser; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.auth.CurrentUser.GrantCheck; +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; + +@Lazy +@Component +@GuiProfile +public class InstitutionList implements TemplateComposer { + + private static final LocTextKey EMPTY_LIST_TEXT_KEY = + new LocTextKey("sebserver.institution.list.empty"); + private static final LocTextKey TITLE_TEXT_KEY = + new LocTextKey("sebserver.institution.list.title"); + private static final LocTextKey NAME_TEXT_KEY = + new LocTextKey("sebserver.institution.list.column.name"); + private static final LocTextKey URL_TEXT_KEY = + new LocTextKey("sebserver.institution.list.column.urlSuffix"); + private static final LocTextKey ACTIVE_TEXT_KEY = + new LocTextKey("sebserver.institution.list.column.active"); + private static final LocTextKey EMPTY_SELECTION_TEXT_KEY = + new LocTextKey("sebserver.institution.info.pleaseSelect"); + + private final TableFilterAttribute nameFilter = + new TableFilterAttribute(CriteriaType.TEXT, Entity.FILTER_ATTR_NAME); + private final TableFilterAttribute urlSuffixFilter = + new TableFilterAttribute(CriteriaType.TEXT, Institution.FILTER_ATTR_URL_SUFFIX); + private final TableFilterAttribute activityFilter; + + private final PageService pageService; + private final RestService restService; + private final CurrentUser currentUser; + private final int pageSize; + + protected InstitutionList( + final PageService pageService, + final RestService restService, + final CurrentUser currentUser, + @Value("${sebserver.gui.list.page.size:20}") final Integer pageSize) { + + this.pageService = pageService; + this.restService = restService; + this.currentUser = currentUser; + this.pageSize = pageSize; + + this.activityFilter = new TableFilterAttribute( + CriteriaType.SINGLE_SELECTION, + Institution.FILTER_ATTR_ACTIVE, + StringUtils.EMPTY, + this.pageService.getResourceService()::activityResources); + } + + @Override + public void compose(final PageContext pageContext) { + final Composite content = this.pageService.getWidgetFactory().defaultPageLayout( + pageContext.getParent(), + TITLE_TEXT_KEY); + + final PageActionBuilder pageActionBuilder = + this.pageService.pageActionBuilder(pageContext.clearEntityKeys()); + + // table + final EntityTable table = + this.pageService.entityTableBuilder(this.restService.getRestCall(GetInstitutionPage.class)) + .withEmptyMessage(EMPTY_LIST_TEXT_KEY) + .withPaging(this.pageSize) + .withColumn(new ColumnDefinition<>( + Domain.INSTITUTION.ATTR_NAME, + NAME_TEXT_KEY, + Institution::getName) + .sortable() + .withFilter(this.nameFilter)) + .withColumn(new ColumnDefinition<>( + Domain.INSTITUTION.ATTR_URL_SUFFIX, + URL_TEXT_KEY, + Institution::getUrlSuffix) + .sortable() + .withFilter(this.urlSuffixFilter)) + .withColumn(new ColumnDefinition<>( + Domain.INSTITUTION.ATTR_ACTIVE, + ACTIVE_TEXT_KEY, + this.pageService.getResourceService(). localizedActivityFunction()) + .sortable() + .withFilter(this.activityFilter)) + .withDefaultAction(pageActionBuilder + .newAction(ActionDefinition.INSTITUTION_VIEW_FROM_LIST) + .create()) + .withSelectionListener(this.pageService.getSelectionPublisher( + ActionDefinition.INSTITUTION_TOGGLE_ACTIVITY, + ActionDefinition.INSTITUTION_ACTIVATE, + ActionDefinition.INSTITUTION_DEACTIVATE, + pageContext, + ActionDefinition.INSTITUTION_VIEW_FROM_LIST, + ActionDefinition.INSTITUTION_MODIFY_FROM_LIST, + ActionDefinition.INSTITUTION_TOGGLE_ACTIVITY)) + .compose(pageContext.copyOf(content)); + + // propagate content actions to action-pane + final GrantCheck instGrant = this.currentUser.grantCheck(EntityType.INSTITUTION); + + pageActionBuilder + + .newAction(ActionDefinition.INSTITUTION_NEW) + .publishIf(instGrant::w) + + .newAction(ActionDefinition.INSTITUTION_VIEW_FROM_LIST) + .withSelect( + table::getSelection, + PageAction::applySingleSelectionAsEntityKey, + EMPTY_SELECTION_TEXT_KEY) + .publishIf(table::hasAnyContent, false) + + .newAction(ActionDefinition.INSTITUTION_MODIFY_FROM_LIST) + .withSelect( + table::getSelection, + PageAction::applySingleSelectionAsEntityKey, + EMPTY_SELECTION_TEXT_KEY) + .publishIf(() -> instGrant.m() && table.hasAnyContent(), false) + + .newAction(ActionDefinition.INSTITUTION_TOGGLE_ACTIVITY) + .withExec(this.pageService.activationToggleActionFunction(table, EMPTY_SELECTION_TEXT_KEY)) + .withConfirm(this.pageService.confirmDeactivation(table)) + .publishIf(() -> instGrant.m() && table.hasAnyContent(), false); + } + +} diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/content/SebClientConfigForm.java b/src/main/java/ch/ethz/seb/sebserver/gui/content/SebClientConfigForm.java index e8b6d317..16704734 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/content/SebClientConfigForm.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/content/SebClientConfigForm.java @@ -1,205 +1,211 @@ -/* - * 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.content; - -import org.eclipse.rap.rwt.RWT; -import org.eclipse.rap.rwt.client.service.UrlLauncher; -import org.eclipse.swt.widgets.Composite; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.context.annotation.Lazy; -import org.springframework.stereotype.Component; - -import ch.ethz.seb.sebserver.gbl.api.API; -import ch.ethz.seb.sebserver.gbl.api.EntityType; -import ch.ethz.seb.sebserver.gbl.model.Domain; -import ch.ethz.seb.sebserver.gbl.model.EntityKey; -import ch.ethz.seb.sebserver.gbl.model.sebconfig.SebClientConfig; -import ch.ethz.seb.sebserver.gbl.model.user.UserInfo; -import ch.ethz.seb.sebserver.gbl.profile.GuiProfile; -import ch.ethz.seb.sebserver.gui.content.action.ActionDefinition; -import ch.ethz.seb.sebserver.gui.form.FormBuilder; -import ch.ethz.seb.sebserver.gui.form.FormHandle; -import ch.ethz.seb.sebserver.gui.service.i18n.I18nSupport; -import ch.ethz.seb.sebserver.gui.service.i18n.LocTextKey; -import ch.ethz.seb.sebserver.gui.service.page.PageContext; -import ch.ethz.seb.sebserver.gui.service.page.PageService; -import ch.ethz.seb.sebserver.gui.service.page.TemplateComposer; -import ch.ethz.seb.sebserver.gui.service.remote.download.DownloadService; -import ch.ethz.seb.sebserver.gui.service.remote.download.SebClientConfigDownload; -import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestService; -import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.seb.clientconfig.ActivateClientConfig; -import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.seb.clientconfig.DeactivateClientConfig; -import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.seb.clientconfig.GetClientConfig; -import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.seb.clientconfig.NewClientConfig; -import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.seb.clientconfig.SaveClientConfig; -import ch.ethz.seb.sebserver.gui.service.remote.webservice.auth.CurrentUser; -import ch.ethz.seb.sebserver.gui.service.remote.webservice.auth.CurrentUser.EntityGrantCheck; -import ch.ethz.seb.sebserver.gui.widget.WidgetFactory; - -@Lazy -@Component -@GuiProfile -public class SebClientConfigForm implements TemplateComposer { - - private static final LocTextKey FORM_TITLE_NEW = - new LocTextKey("sebserver.clientconfig.form.title.new"); - private static final LocTextKey FORM_TITLE = - new LocTextKey("sebserver.clientconfig.form.title"); - private static final LocTextKey FORM_NAME_TEXT_KEY = - new LocTextKey("sebserver.clientconfig.form.name"); - private static final LocTextKey FORM_FALLBACK_URL_TEXT_KEY = - new LocTextKey("sebserver.clientconfig.form.fallback-url"); - private static final LocTextKey FORM_DATE_TEXT_KEY = - new LocTextKey("sebserver.clientconfig.form.date"); - private static final LocTextKey FORM_ENCRYPT_SECRET_TEXT_KEY = - new LocTextKey("sebserver.clientconfig.form.encryptSecret"); - private static final LocTextKey FORM_CONFIRM_ENCRYPT_SECRET_TEXT_KEY = - new LocTextKey("sebserver.clientconfig.form.encryptSecret.confirm"); - - private final PageService pageService; - private final RestService restService; - private final CurrentUser currentUser; - private final DownloadService downloadService; - private final String downloadFileName; - - protected SebClientConfigForm( - final PageService pageService, - final RestService restService, - final CurrentUser currentUser, - final DownloadService downloadService, - @Value("${sebserver.gui.seb.client.config.download.filename}") final String downloadFileName) { - - this.pageService = pageService; - this.restService = restService; - this.currentUser = currentUser; - this.downloadService = downloadService; - this.downloadFileName = downloadFileName; - } - - @Override - public void compose(final PageContext pageContext) { - final WidgetFactory widgetFactory = this.pageService.getWidgetFactory(); - final I18nSupport i18nSupport = this.pageService.getI18nSupport(); - - final UserInfo user = this.currentUser.get(); - final EntityKey entityKey = pageContext.getEntityKey(); - final EntityKey parentEntityKey = pageContext.getParentEntityKey(); - - final boolean isNew = entityKey == null; - - // get data or create new. Handle error if happen - final SebClientConfig clientConfig = (isNew) - ? SebClientConfig.createNew((parentEntityKey != null) - ? Long.valueOf(parentEntityKey.modelId) - : user.institutionId) - : this.restService - .getBuilder(GetClientConfig.class) - .withURIVariable(API.PARAM_MODEL_ID, entityKey.modelId) - .call() - .onError(error -> pageContext.notifyLoadError(EntityType.SEB_CLIENT_CONFIGURATION, error)) - .getOrThrow(); - - final EntityGrantCheck entityGrant = this.currentUser.entityGrantCheck(clientConfig); - final boolean writeGrant = entityGrant.w(); - final boolean modifyGrant = entityGrant.m(); - final boolean isReadonly = pageContext.isReadonly(); - - // new PageContext with actual EntityKey - final PageContext formContext = pageContext.withEntityKey(clientConfig.getEntityKey()); - - // the default page layout with interactive title - final LocTextKey titleKey = (isNew) - ? FORM_TITLE_NEW - : FORM_TITLE; - final Composite content = widgetFactory.defaultPageLayout( - formContext.getParent(), - titleKey); - - // The SebClientConfig form - final FormHandle formHandle = this.pageService.formBuilder( - formContext.copyOf(content)) - .readonly(isReadonly) - .putStaticValueIf(() -> !isNew, - Domain.SEB_CLIENT_CONFIGURATION.ATTR_ID, - clientConfig.getModelId()) - .putStaticValue( - Domain.SEB_CLIENT_CONFIGURATION.ATTR_INSTITUTION_ID, - String.valueOf(clientConfig.getInstitutionId())) - .addField(FormBuilder.text( - Domain.SEB_CLIENT_CONFIGURATION.ATTR_NAME, - FORM_NAME_TEXT_KEY, - clientConfig.name)) - .addField(FormBuilder.text( - SebClientConfig.ATTR_FALLBACK_START_URL, - FORM_FALLBACK_URL_TEXT_KEY, - clientConfig.fallbackStartURL)) - .addFieldIf(() -> !isNew, - () -> FormBuilder.text( - Domain.SEB_CLIENT_CONFIGURATION.ATTR_DATE, - FORM_DATE_TEXT_KEY, - i18nSupport.formatDisplayDateWithTimeZone(clientConfig.date)) - .readonly(true)) - .addField(FormBuilder.text( - Domain.SEB_CLIENT_CONFIGURATION.ATTR_ENCRYPT_SECRET, - FORM_ENCRYPT_SECRET_TEXT_KEY) - .asPasswordField()) - .addField(FormBuilder.text( - SebClientConfig.ATTR_CONFIRM_ENCRYPT_SECRET, - FORM_CONFIRM_ENCRYPT_SECRET_TEXT_KEY) - .asPasswordField()) - .buildFor((isNew) - ? this.restService.getRestCall(NewClientConfig.class) - : this.restService.getRestCall(SaveClientConfig.class)); - - final UrlLauncher urlLauncher = RWT.getClient().getService(UrlLauncher.class); - this.pageService.pageActionBuilder(formContext.clearEntityKeys()) - - .newAction(ActionDefinition.SEB_CLIENT_CONFIG_NEW) - .publishIf(() -> writeGrant && isReadonly) - - .newAction(ActionDefinition.SEB_CLIENT_CONFIG_MODIFY) - .withEntityKey(entityKey) - .publishIf(() -> modifyGrant && isReadonly) - - .newAction(ActionDefinition.SEB_CLIENT_CONFIG_EXPORT) - .withEntityKey(entityKey) - .withExec(action -> { - final String downloadURL = this.downloadService.createDownloadURL( - entityKey.modelId, - SebClientConfigDownload.class, - this.downloadFileName); - urlLauncher.openURL(downloadURL); - return action; - }) - .publishIf(() -> writeGrant && isReadonly && clientConfig.isActive()) - - .newAction(ActionDefinition.SEB_CLIENT_CONFIG_DEACTIVATE) - .withEntityKey(entityKey) - .withSimpleRestCall(this.restService, DeactivateClientConfig.class) - .withConfirm(this.pageService.confirmDeactivation(clientConfig)) - .publishIf(() -> writeGrant && isReadonly && clientConfig.isActive()) - - .newAction(ActionDefinition.SEB_CLIENT_CONFIG_ACTIVATE) - .withEntityKey(entityKey) - .withSimpleRestCall(this.restService, ActivateClientConfig.class) - .publishIf(() -> writeGrant && isReadonly && !clientConfig.isActive()) - - .newAction(ActionDefinition.SEB_CLIENT_CONFIG_SAVE) - .withEntityKey(entityKey) - .withExec(formHandle::processFormSave) - .ignoreMoveAwayFromEdit() - .publishIf(() -> !isReadonly) - - .newAction(ActionDefinition.SEB_CLIENT_CONFIG_CANCEL_MODIFY) - .withEntityKey(entityKey) - .withExec(this.pageService.backToCurrentFunction()) - .publishIf(() -> !isReadonly); - } - -} +/* + * 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.content; + +import org.eclipse.rap.rwt.RWT; +import org.eclipse.rap.rwt.client.service.UrlLauncher; +import org.eclipse.swt.widgets.Composite; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Lazy; +import org.springframework.stereotype.Component; + +import ch.ethz.seb.sebserver.gbl.api.API; +import ch.ethz.seb.sebserver.gbl.api.EntityType; +import ch.ethz.seb.sebserver.gbl.model.Domain; +import ch.ethz.seb.sebserver.gbl.model.EntityKey; +import ch.ethz.seb.sebserver.gbl.model.sebconfig.SebClientConfig; +import ch.ethz.seb.sebserver.gbl.model.user.UserInfo; +import ch.ethz.seb.sebserver.gbl.profile.GuiProfile; +import ch.ethz.seb.sebserver.gui.content.action.ActionDefinition; +import ch.ethz.seb.sebserver.gui.form.FormBuilder; +import ch.ethz.seb.sebserver.gui.form.FormHandle; +import ch.ethz.seb.sebserver.gui.service.i18n.I18nSupport; +import ch.ethz.seb.sebserver.gui.service.i18n.LocTextKey; +import ch.ethz.seb.sebserver.gui.service.page.PageContext; +import ch.ethz.seb.sebserver.gui.service.page.PageService; +import ch.ethz.seb.sebserver.gui.service.page.TemplateComposer; +import ch.ethz.seb.sebserver.gui.service.remote.download.DownloadService; +import ch.ethz.seb.sebserver.gui.service.remote.download.SebClientConfigDownload; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestService; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.seb.clientconfig.ActivateClientConfig; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.seb.clientconfig.DeactivateClientConfig; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.seb.clientconfig.GetClientConfig; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.seb.clientconfig.NewClientConfig; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.seb.clientconfig.SaveClientConfig; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.auth.CurrentUser; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.auth.CurrentUser.EntityGrantCheck; +import ch.ethz.seb.sebserver.gui.widget.WidgetFactory; + +@Lazy +@Component +@GuiProfile +public class SebClientConfigForm implements TemplateComposer { + + private static final LocTextKey FORM_TITLE_NEW = + new LocTextKey("sebserver.clientconfig.form.title.new"); + private static final LocTextKey FORM_TITLE = + new LocTextKey("sebserver.clientconfig.form.title"); + private static final LocTextKey FORM_NAME_TEXT_KEY = + new LocTextKey("sebserver.clientconfig.form.name"); + private static final LocTextKey FORM_FALLBACK_URL_TEXT_KEY = + new LocTextKey("sebserver.clientconfig.form.fallback-url"); + private static final LocTextKey FORM_DATE_TEXT_KEY = + new LocTextKey("sebserver.clientconfig.form.date"); + private static final LocTextKey FORM_ENCRYPT_SECRET_TEXT_KEY = + new LocTextKey("sebserver.clientconfig.form.encryptSecret"); + private static final LocTextKey FORM_CONFIRM_ENCRYPT_SECRET_TEXT_KEY = + new LocTextKey("sebserver.clientconfig.form.encryptSecret.confirm"); + + private final PageService pageService; + private final RestService restService; + private final CurrentUser currentUser; + private final DownloadService downloadService; + private final String downloadFileName; + + protected SebClientConfigForm( + final PageService pageService, + final RestService restService, + final CurrentUser currentUser, + final DownloadService downloadService, + @Value("${sebserver.gui.seb.client.config.download.filename}") final String downloadFileName) { + + this.pageService = pageService; + this.restService = restService; + this.currentUser = currentUser; + this.downloadService = downloadService; + this.downloadFileName = downloadFileName; + } + + @Override + public void compose(final PageContext pageContext) { + final WidgetFactory widgetFactory = this.pageService.getWidgetFactory(); + final I18nSupport i18nSupport = this.pageService.getI18nSupport(); + + final UserInfo user = this.currentUser.get(); + final EntityKey entityKey = pageContext.getEntityKey(); + final EntityKey parentEntityKey = pageContext.getParentEntityKey(); + + final boolean isNew = entityKey == null; + + // get data or create new. Handle error if happen + final SebClientConfig clientConfig = (isNew) + ? SebClientConfig.createNew((parentEntityKey != null) + ? Long.valueOf(parentEntityKey.modelId) + : user.institutionId) + : this.restService + .getBuilder(GetClientConfig.class) + .withURIVariable(API.PARAM_MODEL_ID, entityKey.modelId) + .call() + .onError(error -> pageContext.notifyLoadError(EntityType.SEB_CLIENT_CONFIGURATION, error)) + .getOrThrow(); + + final EntityGrantCheck entityGrant = this.currentUser.entityGrantCheck(clientConfig); + final boolean writeGrant = entityGrant.w(); + final boolean modifyGrant = entityGrant.m(); + final boolean isReadonly = pageContext.isReadonly(); + + // new PageContext with actual EntityKey + final PageContext formContext = pageContext.withEntityKey(clientConfig.getEntityKey()); + + // the default page layout with interactive title + final LocTextKey titleKey = (isNew) + ? FORM_TITLE_NEW + : FORM_TITLE; + final Composite content = widgetFactory.defaultPageLayout( + formContext.getParent(), + titleKey); + + // The SebClientConfig form + final FormHandle formHandle = this.pageService.formBuilder( + formContext.copyOf(content)) + .readonly(isReadonly) + .putStaticValueIf(() -> !isNew, + Domain.SEB_CLIENT_CONFIGURATION.ATTR_ID, + clientConfig.getModelId()) + .putStaticValue( + Domain.SEB_CLIENT_CONFIGURATION.ATTR_INSTITUTION_ID, + String.valueOf(clientConfig.getInstitutionId())) + .addField(FormBuilder.text( + Domain.SEB_CLIENT_CONFIGURATION.ATTR_NAME, + FORM_NAME_TEXT_KEY, + clientConfig.name)) + .addField(FormBuilder.text( + SebClientConfig.ATTR_FALLBACK_START_URL, + FORM_FALLBACK_URL_TEXT_KEY, + clientConfig.fallbackStartURL)) + .addFieldIf(() -> !isNew, + () -> FormBuilder.text( + Domain.SEB_CLIENT_CONFIGURATION.ATTR_DATE, + FORM_DATE_TEXT_KEY, + i18nSupport.formatDisplayDateWithTimeZone(clientConfig.date)) + .readonly(true)) + .addField(FormBuilder.text( + Domain.SEB_CLIENT_CONFIGURATION.ATTR_ENCRYPT_SECRET, + FORM_ENCRYPT_SECRET_TEXT_KEY) + .asPasswordField()) + .addField(FormBuilder.text( + SebClientConfig.ATTR_CONFIRM_ENCRYPT_SECRET, + FORM_CONFIRM_ENCRYPT_SECRET_TEXT_KEY) + .asPasswordField()) + .buildFor((isNew) + ? this.restService.getRestCall(NewClientConfig.class) + : this.restService.getRestCall(SaveClientConfig.class)); + + final UrlLauncher urlLauncher = RWT.getClient().getService(UrlLauncher.class); + this.pageService.pageActionBuilder(formContext.clearEntityKeys()) + + .newAction(ActionDefinition.SEB_CLIENT_CONFIG_NEW) + .publishIf(() -> writeGrant && isReadonly) + + .newAction(ActionDefinition.SEB_CLIENT_CONFIG_MODIFY) + .withEntityKey(entityKey) + .publishIf(() -> modifyGrant && isReadonly) + + .newAction(ActionDefinition.SEB_CLIENT_CONFIG_EXPORT) + .withEntityKey(entityKey) + .withExec(action -> { + final String downloadURL = this.downloadService.createDownloadURL( + entityKey.modelId, + SebClientConfigDownload.class, + this.downloadFileName); + urlLauncher.openURL(downloadURL); + return action; + }) + .publishIf(() -> writeGrant && isReadonly && clientConfig.isActive()) + + .newAction(ActionDefinition.SEB_CLIENT_CONFIG_DEACTIVATE) + .withEntityKey(entityKey) + .withSimpleRestCall(this.restService, DeactivateClientConfig.class) + .withConfirm(this.pageService.confirmDeactivation(clientConfig)) + .publishIf(() -> writeGrant && isReadonly && clientConfig.isActive()) + + .newAction(ActionDefinition.SEB_CLIENT_CONFIG_ACTIVATE) + .withEntityKey(entityKey) + .withSimpleRestCall(this.restService, ActivateClientConfig.class) + .publishIf(() -> writeGrant && isReadonly && !clientConfig.isActive()) + + .newAction(ActionDefinition.SEB_CLIENT_CONFIG_SAVE) + .withEntityKey(entityKey) + .withExec(formHandle::processFormSave) + .ignoreMoveAwayFromEdit() + .publishIf(() -> !isReadonly) + + .newAction(ActionDefinition.SEB_CLIENT_CONFIG_SAVE_AND_ACTIVATE) + .withEntityKey(entityKey) + .withExec(formHandle::saveAndActivate) + .ignoreMoveAwayFromEdit() + .publishIf(() -> !isReadonly && !clientConfig.isActive()) + + .newAction(ActionDefinition.SEB_CLIENT_CONFIG_CANCEL_MODIFY) + .withEntityKey(entityKey) + .withExec(this.pageService.backToCurrentFunction()) + .publishIf(() -> !isReadonly); + } + +} diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/content/SebClientConfigList.java b/src/main/java/ch/ethz/seb/sebserver/gui/content/SebClientConfigList.java index 5de41fcf..c1d55c51 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/content/SebClientConfigList.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/content/SebClientConfigList.java @@ -1,182 +1,199 @@ -/* - * 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.content; - -import java.util.function.Function; - -import org.eclipse.swt.widgets.Composite; -import org.joda.time.DateTime; -import org.joda.time.DateTimeZone; -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.EntityType; -import ch.ethz.seb.sebserver.gbl.model.Domain; -import ch.ethz.seb.sebserver.gbl.model.Entity; -import ch.ethz.seb.sebserver.gbl.model.sebconfig.SebClientConfig; -import ch.ethz.seb.sebserver.gbl.model.user.UserInfo; -import ch.ethz.seb.sebserver.gbl.model.user.UserRole; -import ch.ethz.seb.sebserver.gbl.profile.GuiProfile; -import ch.ethz.seb.sebserver.gui.content.action.ActionDefinition; -import ch.ethz.seb.sebserver.gui.service.ResourceService; -import ch.ethz.seb.sebserver.gui.service.i18n.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.PageAction; -import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestService; -import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.seb.clientconfig.GetClientConfigPage; -import ch.ethz.seb.sebserver.gui.service.remote.webservice.auth.CurrentUser; -import ch.ethz.seb.sebserver.gui.service.remote.webservice.auth.CurrentUser.GrantCheck; -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; - -@Lazy -@Component -@GuiProfile -public class SebClientConfigList implements TemplateComposer { - - private static final LocTextKey NO_MODIFY_PRIVILEGE_ON_OTHER_INSTITUION = - new LocTextKey("sebserver.clientconfig.list.action.no.modify.privilege"); - private static final LocTextKey EMPTY_LIST_TEXT_KEY = - new LocTextKey("sebserver.clientconfig.list.empty"); - private static final LocTextKey TITLE_TEXT_KEY = - new LocTextKey("sebserver.clientconfig.list.title"); - private static final LocTextKey INSTITUTION_TEXT_KEY = - new LocTextKey("sebserver.clientconfig.list.column.institution"); - private static final LocTextKey NAME_TEXT_KEY = - new LocTextKey("sebserver.clientconfig.list.column.name"); - private static final LocTextKey ACTIVE_TEXT_KEY = - new LocTextKey("sebserver.clientconfig.list.column.active"); - private static final LocTextKey EMPTY_SELECTION_TEXT_KEY = - new LocTextKey("sebserver.clientconfig.info.pleaseSelect"); - - private final TableFilterAttribute institutionFilter; - private final TableFilterAttribute nameFilter = - new TableFilterAttribute(CriteriaType.TEXT, Entity.FILTER_ATTR_NAME); - private final TableFilterAttribute dateFilter = - new TableFilterAttribute( - CriteriaType.DATE, - SebClientConfig.FILTER_ATTR_CREATION_DATE, - DateTime.now(DateTimeZone.UTC) - .minusYears(1) - .toString(Constants.DEFAULT_DATE_TIME_FORMAT)); - private final TableFilterAttribute activityFilter; - - private final PageService pageService; - private final RestService restService; - private final CurrentUser currentUser; - private final ResourceService resourceService; - private final int pageSize; - - protected SebClientConfigList( - final PageService pageService, - final RestService restService, - final CurrentUser currentUser, - @Value("${sebserver.gui.list.page.size:20}") final Integer pageSize) { - - this.pageService = pageService; - this.restService = restService; - this.currentUser = currentUser; - this.resourceService = pageService.getResourceService(); - this.pageSize = pageSize; - - this.institutionFilter = new TableFilterAttribute( - CriteriaType.SINGLE_SELECTION, - Entity.FILTER_ATTR_INSTITUTION, - this.resourceService::institutionResource); - - this.activityFilter = new TableFilterAttribute( - CriteriaType.SINGLE_SELECTION, - UserInfo.FILTER_ATTR_ACTIVE, - this.resourceService::activityResources); - } - - @Override - public void compose(final PageContext pageContext) { - - final I18nSupport i18nSupport = this.pageService.getI18nSupport(); - final Composite content = this.pageService.getWidgetFactory().defaultPageLayout( - pageContext.getParent(), - TITLE_TEXT_KEY); - - final boolean isSEBAdmin = this.currentUser.get().hasRole(UserRole.SEB_SERVER_ADMIN); - final PageActionBuilder pageActionBuilder = - this.pageService.pageActionBuilder(pageContext.clearEntityKeys()); - - // table - final EntityTable table = - this.pageService.entityTableBuilder(this.restService.getRestCall(GetClientConfigPage.class)) - .withEmptyMessage(EMPTY_LIST_TEXT_KEY) - .withPaging(this.pageSize) - .withColumnIf( - () -> isSEBAdmin, - () -> new ColumnDefinition<>( - Domain.LMS_SETUP.ATTR_INSTITUTION_ID, - INSTITUTION_TEXT_KEY, - clientConfigInstitutionNameFunction(this.resourceService)) - .withFilter(this.institutionFilter)) - .withColumn(new ColumnDefinition<>( - Domain.SEB_CLIENT_CONFIGURATION.ATTR_NAME, - NAME_TEXT_KEY, - SebClientConfig::getName) - .withFilter(this.nameFilter) - .sortable()) - .withColumn(new ColumnDefinition<>( - Domain.SEB_CLIENT_CONFIGURATION.ATTR_DATE, - new LocTextKey( - "sebserver.clientconfig.list.column.date", - i18nSupport.getUsersTimeZoneTitleSuffix()), - SebClientConfig::getDate) - .withFilter(this.dateFilter) - .sortable()) - .withColumn(new ColumnDefinition<>( - Domain.SEB_CLIENT_CONFIGURATION.ATTR_ACTIVE, - ACTIVE_TEXT_KEY, - this.pageService.getResourceService(). localizedActivityFunction()) - .withFilter(this.activityFilter) - .sortable()) - .withDefaultAction(pageActionBuilder - .newAction(ActionDefinition.SEB_CLIENT_CONFIG_VIEW_FROM_LIST) - .create()) - .compose(pageContext.copyOf(content)); - - final GrantCheck clientConfigGrant = this.currentUser.grantCheck(EntityType.SEB_CLIENT_CONFIGURATION); - - pageActionBuilder - - .newAction(ActionDefinition.SEB_CLIENT_CONFIG_NEW) - .publishIf(clientConfigGrant::iw) - - .newAction(ActionDefinition.SEB_CLIENT_CONFIG_VIEW_FROM_LIST) - .withSelect(table::getSelection, PageAction::applySingleSelectionAsEntityKey, EMPTY_SELECTION_TEXT_KEY) - .publishIf(() -> table.hasAnyContent()) - - .newAction(ActionDefinition.SEB_CLIENT_CONFIG_MODIFY_FROM_LIST) - .withSelect( - table.getGrantedSelection(this.currentUser, NO_MODIFY_PRIVILEGE_ON_OTHER_INSTITUION), - PageAction::applySingleSelectionAsEntityKey, EMPTY_SELECTION_TEXT_KEY) - .publishIf(() -> clientConfigGrant.im() && table.hasAnyContent()); - - } - - private static Function clientConfigInstitutionNameFunction( - final ResourceService resourceService) { - - return config -> resourceService.getInstitutionNameFunction() - .apply(String.valueOf(config.institutionId)); - } - -} +/* + * 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.content; + +import java.util.function.Function; + +import org.eclipse.swt.widgets.Composite; +import org.joda.time.DateTime; +import org.joda.time.DateTimeZone; +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.EntityType; +import ch.ethz.seb.sebserver.gbl.model.Domain; +import ch.ethz.seb.sebserver.gbl.model.Entity; +import ch.ethz.seb.sebserver.gbl.model.sebconfig.SebClientConfig; +import ch.ethz.seb.sebserver.gbl.model.user.UserInfo; +import ch.ethz.seb.sebserver.gbl.model.user.UserRole; +import ch.ethz.seb.sebserver.gbl.profile.GuiProfile; +import ch.ethz.seb.sebserver.gui.content.action.ActionDefinition; +import ch.ethz.seb.sebserver.gui.service.ResourceService; +import ch.ethz.seb.sebserver.gui.service.i18n.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.PageAction; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestService; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.seb.clientconfig.GetClientConfigPage; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.auth.CurrentUser; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.auth.CurrentUser.GrantCheck; +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; + +@Lazy +@Component +@GuiProfile +public class SebClientConfigList implements TemplateComposer { + + private static final LocTextKey NO_MODIFY_PRIVILEGE_ON_OTHER_INSTITUION = + new LocTextKey("sebserver.clientconfig.list.action.no.modify.privilege"); + private static final LocTextKey EMPTY_LIST_TEXT_KEY = + new LocTextKey("sebserver.clientconfig.list.empty"); + private static final LocTextKey TITLE_TEXT_KEY = + new LocTextKey("sebserver.clientconfig.list.title"); + private static final LocTextKey INSTITUTION_TEXT_KEY = + new LocTextKey("sebserver.clientconfig.list.column.institution"); + private static final LocTextKey NAME_TEXT_KEY = + new LocTextKey("sebserver.clientconfig.list.column.name"); + private static final LocTextKey ACTIVE_TEXT_KEY = + new LocTextKey("sebserver.clientconfig.list.column.active"); + private static final LocTextKey EMPTY_SELECTION_TEXT_KEY = + new LocTextKey("sebserver.clientconfig.info.pleaseSelect"); + + private final TableFilterAttribute institutionFilter; + private final TableFilterAttribute nameFilter = + new TableFilterAttribute(CriteriaType.TEXT, Entity.FILTER_ATTR_NAME); + private final TableFilterAttribute dateFilter = + new TableFilterAttribute( + CriteriaType.DATE, + SebClientConfig.FILTER_ATTR_CREATION_DATE, + DateTime.now(DateTimeZone.UTC) + .minusYears(1) + .toString(Constants.DEFAULT_DATE_TIME_FORMAT)); + private final TableFilterAttribute activityFilter; + + private final PageService pageService; + private final RestService restService; + private final CurrentUser currentUser; + private final ResourceService resourceService; + private final int pageSize; + + protected SebClientConfigList( + final PageService pageService, + final RestService restService, + final CurrentUser currentUser, + @Value("${sebserver.gui.list.page.size:20}") final Integer pageSize) { + + this.pageService = pageService; + this.restService = restService; + this.currentUser = currentUser; + this.resourceService = pageService.getResourceService(); + this.pageSize = pageSize; + + this.institutionFilter = new TableFilterAttribute( + CriteriaType.SINGLE_SELECTION, + Entity.FILTER_ATTR_INSTITUTION, + this.resourceService::institutionResource); + + this.activityFilter = new TableFilterAttribute( + CriteriaType.SINGLE_SELECTION, + UserInfo.FILTER_ATTR_ACTIVE, + this.resourceService::activityResources); + } + + @Override + public void compose(final PageContext pageContext) { + + final I18nSupport i18nSupport = this.pageService.getI18nSupport(); + final Composite content = this.pageService.getWidgetFactory().defaultPageLayout( + pageContext.getParent(), + TITLE_TEXT_KEY); + + final boolean isSEBAdmin = this.currentUser.get().hasRole(UserRole.SEB_SERVER_ADMIN); + final PageActionBuilder pageActionBuilder = + this.pageService.pageActionBuilder(pageContext.clearEntityKeys()); + + // table + final EntityTable table = + this.pageService.entityTableBuilder(this.restService.getRestCall(GetClientConfigPage.class)) + .withEmptyMessage(EMPTY_LIST_TEXT_KEY) + .withPaging(this.pageSize) + .withColumnIf( + () -> isSEBAdmin, + () -> new ColumnDefinition<>( + Domain.LMS_SETUP.ATTR_INSTITUTION_ID, + INSTITUTION_TEXT_KEY, + clientConfigInstitutionNameFunction(this.resourceService)) + .withFilter(this.institutionFilter)) + .withColumn(new ColumnDefinition<>( + Domain.SEB_CLIENT_CONFIGURATION.ATTR_NAME, + NAME_TEXT_KEY, + SebClientConfig::getName) + .withFilter(this.nameFilter) + .sortable()) + .withColumn(new ColumnDefinition<>( + Domain.SEB_CLIENT_CONFIGURATION.ATTR_DATE, + new LocTextKey( + "sebserver.clientconfig.list.column.date", + i18nSupport.getUsersTimeZoneTitleSuffix()), + SebClientConfig::getDate) + .withFilter(this.dateFilter) + .sortable()) + .withColumn(new ColumnDefinition<>( + Domain.SEB_CLIENT_CONFIGURATION.ATTR_ACTIVE, + ACTIVE_TEXT_KEY, + this.pageService.getResourceService(). localizedActivityFunction()) + .withFilter(this.activityFilter) + .sortable()) + .withDefaultAction(pageActionBuilder + .newAction(ActionDefinition.SEB_CLIENT_CONFIG_VIEW_FROM_LIST) + .create()) + + .withSelectionListener(this.pageService.getSelectionPublisher( + ActionDefinition.SEB_CLIENT_CONFIG_TOGGLE_ACTIVITY, + ActionDefinition.SEB_CLIENT_CONFIG_ACTIVATE, + ActionDefinition.SEB_CLIENT_CONFIG_DEACTIVATE, + pageContext, + ActionDefinition.SEB_CLIENT_CONFIG_VIEW_FROM_LIST, + ActionDefinition.SEB_CLIENT_CONFIG_MODIFY_FROM_LIST, + ActionDefinition.SEB_CLIENT_CONFIG_TOGGLE_ACTIVITY)) + + .compose(pageContext.copyOf(content)); + + final GrantCheck clientConfigGrant = this.currentUser.grantCheck(EntityType.SEB_CLIENT_CONFIGURATION); + + pageActionBuilder + + .newAction(ActionDefinition.SEB_CLIENT_CONFIG_NEW) + .publishIf(clientConfigGrant::iw) + + .newAction(ActionDefinition.SEB_CLIENT_CONFIG_VIEW_FROM_LIST) + .withSelect(table::getSelection, PageAction::applySingleSelectionAsEntityKey, EMPTY_SELECTION_TEXT_KEY) + .publishIf(table::hasAnyContent, false) + + .newAction(ActionDefinition.SEB_CLIENT_CONFIG_MODIFY_FROM_LIST) + .withSelect( + table.getGrantedSelection(this.currentUser, NO_MODIFY_PRIVILEGE_ON_OTHER_INSTITUION), + PageAction::applySingleSelectionAsEntityKey, EMPTY_SELECTION_TEXT_KEY) + .publishIf(() -> clientConfigGrant.im() && table.hasAnyContent(), false) + + .newAction(ActionDefinition.SEB_CLIENT_CONFIG_TOGGLE_ACTIVITY) + .withExec(this.pageService.activationToggleActionFunction(table, EMPTY_SELECTION_TEXT_KEY)) + .withConfirm(this.pageService.confirmDeactivation(table)) + .publishIf(() -> clientConfigGrant.im() && table.hasAnyContent(), false); + + + + } + + private static Function clientConfigInstitutionNameFunction( + final ResourceService resourceService) { + + return config -> resourceService.getInstitutionNameFunction() + .apply(String.valueOf(config.institutionId)); + } + +} diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/content/SebExamConfigList.java b/src/main/java/ch/ethz/seb/sebserver/gui/content/SebExamConfigList.java index ec2783b1..6d923b73 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/content/SebExamConfigList.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/content/SebExamConfigList.java @@ -1,174 +1,178 @@ -/* - * 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.content; - -import org.eclipse.swt.widgets.Composite; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.context.annotation.Lazy; -import org.springframework.stereotype.Component; - -import ch.ethz.seb.sebserver.gbl.api.EntityType; -import ch.ethz.seb.sebserver.gbl.model.Domain; -import ch.ethz.seb.sebserver.gbl.model.Entity; -import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationNode; -import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationNode.ConfigurationType; -import ch.ethz.seb.sebserver.gbl.model.user.UserRole; -import ch.ethz.seb.sebserver.gbl.profile.GuiProfile; -import ch.ethz.seb.sebserver.gui.content.action.ActionDefinition; -import ch.ethz.seb.sebserver.gui.service.ResourceService; -import ch.ethz.seb.sebserver.gui.service.i18n.LocTextKey; -import ch.ethz.seb.sebserver.gui.service.page.PageContext; -import ch.ethz.seb.sebserver.gui.service.page.PageService; -import ch.ethz.seb.sebserver.gui.service.page.PageService.PageActionBuilder; -import ch.ethz.seb.sebserver.gui.service.page.TemplateComposer; -import ch.ethz.seb.sebserver.gui.service.page.impl.PageAction; -import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestService; -import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.seb.examconfig.GetExamConfigNodePage; -import ch.ethz.seb.sebserver.gui.service.remote.webservice.auth.CurrentUser; -import ch.ethz.seb.sebserver.gui.service.remote.webservice.auth.CurrentUser.GrantCheck; -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; - -@Lazy -@Component -@GuiProfile -public class SebExamConfigList implements TemplateComposer { - - private static final LocTextKey NO_MODIFY_PRIVILEGE_ON_OTHER_INSTITUION = - new LocTextKey("sebserver.examconfig.list.action.no.modify.privilege"); - private static final LocTextKey EMPTY_CONFIG_LIST_TEXT_KEY = - new LocTextKey("sebserver.examconfig.list.empty"); - private static final LocTextKey TITLE_CONFIGURATION_TEXT_KEY = - new LocTextKey("sebserver.examconfig.list.title"); - private static final LocTextKey INSTITUTION_TEXT_KEY = - new LocTextKey("sebserver.examconfig.list.column.institution"); - private static final LocTextKey NAME_TEXT_KEY = - new LocTextKey("sebserver.examconfig.list.column.name"); - private static final LocTextKey DESCRIPTION_TEXT_KEY = - new LocTextKey("sebserver.examconfig.list.column.description"); - private static final LocTextKey STATUS_TEXT_KEY = - new LocTextKey("sebserver.examconfig.list.column.status"); - private static final LocTextKey EMPTY_SELECTION_TEXT_KEY = - new LocTextKey("sebserver.examconfig.info.pleaseSelect"); - - private final TableFilterAttribute institutionFilter; - private final TableFilterAttribute nameFilter = - new TableFilterAttribute(CriteriaType.TEXT, Entity.FILTER_ATTR_NAME); - private final TableFilterAttribute descFilter = - new TableFilterAttribute(CriteriaType.TEXT, ConfigurationNode.FILTER_ATTR_DESCRIPTION); - private final TableFilterAttribute statusFilter; - - private final PageService pageService; - private final RestService restService; - private final CurrentUser currentUser; - private final ResourceService resourceService; - private final int pageSize; - - protected SebExamConfigList( - final PageService pageService, - final RestService restService, - final CurrentUser currentUser, - @Value("${sebserver.gui.list.page.size:20}") final Integer pageSize) { - - this.pageService = pageService; - this.restService = restService; - this.currentUser = currentUser; - this.resourceService = pageService.getResourceService(); - this.pageSize = pageSize; - - this.institutionFilter = new TableFilterAttribute( - CriteriaType.SINGLE_SELECTION, - Entity.FILTER_ATTR_INSTITUTION, - this.resourceService::institutionResource); - - this.statusFilter = new TableFilterAttribute( - CriteriaType.SINGLE_SELECTION, - ConfigurationNode.FILTER_ATTR_STATUS, - this.resourceService::examConfigStatusResources); - } - - @Override - public void compose(final PageContext pageContext) { - final WidgetFactory widgetFactory = this.pageService.getWidgetFactory(); - final Composite content = widgetFactory.defaultPageLayout( - pageContext.getParent(), - TITLE_CONFIGURATION_TEXT_KEY); - - final boolean isSEBAdmin = this.currentUser.get().hasRole(UserRole.SEB_SERVER_ADMIN); - final PageActionBuilder pageActionBuilder = - this.pageService.pageActionBuilder(pageContext.clearEntityKeys()); - - // exam configuration table - final EntityTable configTable = - this.pageService.entityTableBuilder(this.restService.getRestCall(GetExamConfigNodePage.class)) - .withStaticFilter( - Domain.CONFIGURATION_NODE.ATTR_TYPE, - ConfigurationType.EXAM_CONFIG.name()) - .withEmptyMessage(EMPTY_CONFIG_LIST_TEXT_KEY) - .withPaging(this.pageSize) - .withColumnIf( - () -> isSEBAdmin, - () -> new ColumnDefinition<>( - Domain.LMS_SETUP.ATTR_INSTITUTION_ID, - INSTITUTION_TEXT_KEY, - this.resourceService::localizedExamConfigInstitutionName) - .withFilter(this.institutionFilter) - .sortable()) - .withColumn(new ColumnDefinition<>( - Domain.CONFIGURATION_NODE.ATTR_NAME, - NAME_TEXT_KEY, - ConfigurationNode::getName) - .withFilter(this.nameFilter) - .sortable()) - .withColumn(new ColumnDefinition<>( - Domain.CONFIGURATION_NODE.ATTR_DESCRIPTION, - DESCRIPTION_TEXT_KEY, - ConfigurationNode::getDescription) - .withFilter(this.descFilter) - .sortable()) - .withColumn(new ColumnDefinition( - Domain.CONFIGURATION_NODE.ATTR_STATUS, - STATUS_TEXT_KEY, - this.resourceService::localizedExamConfigStatusName) - .withFilter(this.statusFilter) - .sortable()) - .withDefaultAction(pageActionBuilder - .newAction(ActionDefinition.SEB_EXAM_CONFIG_VIEW_PROP_FROM_LIST) - .create()) - .compose(pageContext.copyOf(content)); - - final GrantCheck examConfigGrant = this.currentUser.grantCheck(EntityType.CONFIGURATION_NODE); - pageActionBuilder - // Exam Configuration actions... - .newAction(ActionDefinition.SEB_EXAM_CONFIG_NEW) - .publishIf(examConfigGrant::iw) - - .newAction(ActionDefinition.SEB_EXAM_CONFIG_VIEW_PROP_FROM_LIST) - .withSelect(configTable::getSelection, PageAction::applySingleSelectionAsEntityKey, - EMPTY_SELECTION_TEXT_KEY) - .publishIf(() -> configTable.hasAnyContent()) - - .newAction(ActionDefinition.SEB_EXAM_CONFIG_MODIFY_PROP_FROM_LIST) - .withSelect( - configTable.getGrantedSelection(this.currentUser, NO_MODIFY_PRIVILEGE_ON_OTHER_INSTITUION), - PageAction::applySingleSelectionAsEntityKey, EMPTY_SELECTION_TEXT_KEY) - .publishIf(() -> examConfigGrant.im() && configTable.hasAnyContent()) - - .newAction(ActionDefinition.SEB_EXAM_CONFIG_IMPORT_TO_NEW_CONFIG) - .withExec(SebExamConfigImportPopup.importFunction(this.pageService, true)) - .noEventPropagation() - .publishIf(() -> examConfigGrant.im()) - - ; - } - -} +/* + * 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.content; + +import org.eclipse.swt.widgets.Composite; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Lazy; +import org.springframework.stereotype.Component; + +import ch.ethz.seb.sebserver.gbl.api.EntityType; +import ch.ethz.seb.sebserver.gbl.model.Domain; +import ch.ethz.seb.sebserver.gbl.model.Entity; +import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationNode; +import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationNode.ConfigurationType; +import ch.ethz.seb.sebserver.gbl.model.user.UserRole; +import ch.ethz.seb.sebserver.gbl.profile.GuiProfile; +import ch.ethz.seb.sebserver.gui.content.action.ActionDefinition; +import ch.ethz.seb.sebserver.gui.service.ResourceService; +import ch.ethz.seb.sebserver.gui.service.i18n.LocTextKey; +import ch.ethz.seb.sebserver.gui.service.page.PageContext; +import ch.ethz.seb.sebserver.gui.service.page.PageService; +import ch.ethz.seb.sebserver.gui.service.page.PageService.PageActionBuilder; +import ch.ethz.seb.sebserver.gui.service.page.TemplateComposer; +import ch.ethz.seb.sebserver.gui.service.page.impl.PageAction; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestService; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.seb.examconfig.GetExamConfigNodePage; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.auth.CurrentUser; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.auth.CurrentUser.GrantCheck; +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; + +@Lazy +@Component +@GuiProfile +public class SebExamConfigList implements TemplateComposer { + + private static final LocTextKey NO_MODIFY_PRIVILEGE_ON_OTHER_INSTITUION = + new LocTextKey("sebserver.examconfig.list.action.no.modify.privilege"); + private static final LocTextKey EMPTY_CONFIG_LIST_TEXT_KEY = + new LocTextKey("sebserver.examconfig.list.empty"); + private static final LocTextKey TITLE_CONFIGURATION_TEXT_KEY = + new LocTextKey("sebserver.examconfig.list.title"); + private static final LocTextKey INSTITUTION_TEXT_KEY = + new LocTextKey("sebserver.examconfig.list.column.institution"); + private static final LocTextKey NAME_TEXT_KEY = + new LocTextKey("sebserver.examconfig.list.column.name"); + private static final LocTextKey DESCRIPTION_TEXT_KEY = + new LocTextKey("sebserver.examconfig.list.column.description"); + private static final LocTextKey STATUS_TEXT_KEY = + new LocTextKey("sebserver.examconfig.list.column.status"); + private static final LocTextKey EMPTY_SELECTION_TEXT_KEY = + new LocTextKey("sebserver.examconfig.info.pleaseSelect"); + + private final TableFilterAttribute institutionFilter; + private final TableFilterAttribute nameFilter = + new TableFilterAttribute(CriteriaType.TEXT, Entity.FILTER_ATTR_NAME); + private final TableFilterAttribute descFilter = + new TableFilterAttribute(CriteriaType.TEXT, ConfigurationNode.FILTER_ATTR_DESCRIPTION); + private final TableFilterAttribute statusFilter; + + private final PageService pageService; + private final RestService restService; + private final CurrentUser currentUser; + private final ResourceService resourceService; + private final int pageSize; + + protected SebExamConfigList( + final PageService pageService, + final RestService restService, + final CurrentUser currentUser, + @Value("${sebserver.gui.list.page.size:20}") final Integer pageSize) { + + this.pageService = pageService; + this.restService = restService; + this.currentUser = currentUser; + this.resourceService = pageService.getResourceService(); + this.pageSize = pageSize; + + this.institutionFilter = new TableFilterAttribute( + CriteriaType.SINGLE_SELECTION, + Entity.FILTER_ATTR_INSTITUTION, + this.resourceService::institutionResource); + + this.statusFilter = new TableFilterAttribute( + CriteriaType.SINGLE_SELECTION, + ConfigurationNode.FILTER_ATTR_STATUS, + this.resourceService::examConfigStatusResources); + } + + @Override + public void compose(final PageContext pageContext) { + final WidgetFactory widgetFactory = this.pageService.getWidgetFactory(); + final Composite content = widgetFactory.defaultPageLayout( + pageContext.getParent(), + TITLE_CONFIGURATION_TEXT_KEY); + + final boolean isSEBAdmin = this.currentUser.get().hasRole(UserRole.SEB_SERVER_ADMIN); + final PageActionBuilder pageActionBuilder = + this.pageService.pageActionBuilder(pageContext.clearEntityKeys()); + + // exam configuration table + final EntityTable configTable = + this.pageService.entityTableBuilder(this.restService.getRestCall(GetExamConfigNodePage.class)) + .withStaticFilter( + Domain.CONFIGURATION_NODE.ATTR_TYPE, + ConfigurationType.EXAM_CONFIG.name()) + .withEmptyMessage(EMPTY_CONFIG_LIST_TEXT_KEY) + .withPaging(this.pageSize) + .withColumnIf( + () -> isSEBAdmin, + () -> new ColumnDefinition<>( + Domain.LMS_SETUP.ATTR_INSTITUTION_ID, + INSTITUTION_TEXT_KEY, + this.resourceService::localizedExamConfigInstitutionName) + .withFilter(this.institutionFilter) + .sortable()) + .withColumn(new ColumnDefinition<>( + Domain.CONFIGURATION_NODE.ATTR_NAME, + NAME_TEXT_KEY, + ConfigurationNode::getName) + .withFilter(this.nameFilter) + .sortable()) + .withColumn(new ColumnDefinition<>( + Domain.CONFIGURATION_NODE.ATTR_DESCRIPTION, + DESCRIPTION_TEXT_KEY, + ConfigurationNode::getDescription) + .withFilter(this.descFilter) + .sortable()) + .withColumn(new ColumnDefinition( + Domain.CONFIGURATION_NODE.ATTR_STATUS, + STATUS_TEXT_KEY, + this.resourceService::localizedExamConfigStatusName) + .withFilter(this.statusFilter) + .sortable()) + .withDefaultAction(pageActionBuilder + .newAction(ActionDefinition.SEB_EXAM_CONFIG_VIEW_PROP_FROM_LIST) + .create()) + + .withSelectionListener(this.pageService.getSelectionPublisher( + pageContext, + ActionDefinition.SEB_EXAM_CONFIG_VIEW_PROP_FROM_LIST, + ActionDefinition.SEB_EXAM_CONFIG_MODIFY_PROP_FROM_LIST)) + + .compose(pageContext.copyOf(content)); + + final GrantCheck examConfigGrant = this.currentUser.grantCheck(EntityType.CONFIGURATION_NODE); + pageActionBuilder + // Exam Configuration actions... + .newAction(ActionDefinition.SEB_EXAM_CONFIG_NEW) + .publishIf(examConfigGrant::iw) + + .newAction(ActionDefinition.SEB_EXAM_CONFIG_VIEW_PROP_FROM_LIST) + .withSelect(configTable::getSelection, PageAction::applySingleSelectionAsEntityKey, + EMPTY_SELECTION_TEXT_KEY) + .publishIf(configTable::hasAnyContent, false) + + .newAction(ActionDefinition.SEB_EXAM_CONFIG_MODIFY_PROP_FROM_LIST) + .withSelect( + configTable.getGrantedSelection(this.currentUser, NO_MODIFY_PRIVILEGE_ON_OTHER_INSTITUION), + PageAction::applySingleSelectionAsEntityKey, EMPTY_SELECTION_TEXT_KEY) + .publishIf(() -> examConfigGrant.im() && configTable.hasAnyContent(), false) + + .newAction(ActionDefinition.SEB_EXAM_CONFIG_IMPORT_TO_NEW_CONFIG) + .withExec(SebExamConfigImportPopup.importFunction(this.pageService, true)) + .noEventPropagation() + .publishIf(examConfigGrant::im); + } + +} diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/content/SebExamConfigPropForm.java b/src/main/java/ch/ethz/seb/sebserver/gui/content/SebExamConfigPropForm.java index f90c31a0..abc15f1a 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/content/SebExamConfigPropForm.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/content/SebExamConfigPropForm.java @@ -1,413 +1,423 @@ -/* - * 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.content; - -import java.util.Arrays; -import java.util.HashSet; -import java.util.List; -import java.util.function.Function; - -import org.eclipse.rap.rwt.RWT; -import org.eclipse.rap.rwt.client.service.UrlLauncher; -import org.eclipse.swt.SWT; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Text; -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.api.EntityType; -import ch.ethz.seb.sebserver.gbl.model.Domain; -import ch.ethz.seb.sebserver.gbl.model.EntityKey; -import ch.ethz.seb.sebserver.gbl.model.exam.ExamConfigurationMap; -import ch.ethz.seb.sebserver.gbl.model.exam.QuizData; -import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigKey; -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.user.UserInfo; -import ch.ethz.seb.sebserver.gbl.profile.GuiProfile; -import ch.ethz.seb.sebserver.gbl.util.Tuple; -import ch.ethz.seb.sebserver.gui.content.action.ActionDefinition; -import ch.ethz.seb.sebserver.gui.form.FormBuilder; -import ch.ethz.seb.sebserver.gui.form.FormHandle; -import ch.ethz.seb.sebserver.gui.service.ResourceService; -import ch.ethz.seb.sebserver.gui.service.i18n.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.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.download.DownloadService; -import ch.ethz.seb.sebserver.gui.service.remote.download.SebExamConfigPlaintextDownload; -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.GetExamConfigMappingsPage; -import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.seb.examconfig.ExportConfigKey; -import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.seb.examconfig.GetExamConfigNode; -import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.seb.examconfig.NewExamConfig; -import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.seb.examconfig.SaveExamConfig; -import ch.ethz.seb.sebserver.gui.service.remote.webservice.auth.CurrentUser; -import ch.ethz.seb.sebserver.gui.service.remote.webservice.auth.CurrentUser.EntityGrantCheck; -import ch.ethz.seb.sebserver.gui.table.ColumnDefinition; -import ch.ethz.seb.sebserver.gui.table.EntityTable; -import ch.ethz.seb.sebserver.gui.widget.WidgetFactory; -import ch.ethz.seb.sebserver.gui.widget.WidgetFactory.CustomVariant; - -@Lazy -@Component -@GuiProfile -public class SebExamConfigPropForm implements TemplateComposer { - - static final LocTextKey FORM_TITLE_NEW = - new LocTextKey("sebserver.examconfig.form.title.new"); - static final LocTextKey FORM_TITLE = - new LocTextKey("sebserver.examconfig.form.title"); - static final LocTextKey FORM_NAME_TEXT_KEY = - new LocTextKey("sebserver.examconfig.form.name"); - static final LocTextKey FORM_DESCRIPTION_TEXT_KEY = - new LocTextKey("sebserver.examconfig.form.description"); - static final LocTextKey FORM_HISTORY_TEXT_KEY = - new LocTextKey("sebserver.examconfig.form.with-history"); - static final LocTextKey FORM_TEMPLATE_TEXT_KEY = - new LocTextKey("sebserver.examconfig.form.template"); - static final LocTextKey FORM_STATUS_TEXT_KEY = - new LocTextKey("sebserver.examconfig.form.status"); - static final LocTextKey FORM_IMPORT_TEXT_KEY = - new LocTextKey("sebserver.examconfig.action.import-config"); - static final LocTextKey FORM_IMPORT_SELECT_TEXT_KEY = - new LocTextKey("sebserver.examconfig.action.import-file-select"); - static final LocTextKey FORM_IMPORT_PASSWORD_TEXT_KEY = - new LocTextKey("sebserver.examconfig.action.import-file-password"); - static final LocTextKey CONFIG_KEY_TITLE_TEXT_KEY = - new LocTextKey("sebserver.examconfig.form.config-key.title"); - static final LocTextKey FORM_IMPORT_CONFIRM_TEXT_KEY = - new LocTextKey("sebserver.examconfig.action.import-config.confirm"); - static final LocTextKey FORM_ATTACHED_EXAMS_TITLE_TEXT_KEY = - new LocTextKey("sebserver.examconfig.form.attched-to"); - - static final LocTextKey SAVE_CONFIRM_STATE_CHANGE_WHILE_ATTACHED = - new LocTextKey("sebserver.examconfig.action.state-change.confirm"); - - private final PageService pageService; - private final RestService restService; - private final CurrentUser currentUser; - private final DownloadService downloadService; - private final String downloadFileName; - - protected SebExamConfigPropForm( - final PageService pageService, - final CurrentUser currentUser, - final DownloadService downloadService, - @Value("${sebserver.gui.seb.exam.config.download.filename}") final String downloadFileName) { - - this.pageService = pageService; - this.restService = pageService.getRestService(); - this.currentUser = currentUser; - this.downloadService = downloadService; - this.downloadFileName = downloadFileName; - } - - @Override - public void compose(final PageContext pageContext) { - final WidgetFactory widgetFactory = this.pageService.getWidgetFactory(); - final ResourceService resourceService = this.pageService.getResourceService(); - - final UserInfo user = this.currentUser.get(); - final EntityKey entityKey = pageContext.getEntityKey(); - final EntityKey parentEntityKey = pageContext.getParentEntityKey(); - final boolean isNew = entityKey == null; - - // get data or create new. Handle error if happen - final ConfigurationNode examConfig = (isNew) - ? ConfigurationNode.createNewExamConfig(user.institutionId) - : this.restService - .getBuilder(GetExamConfigNode.class) - .withURIVariable(API.PARAM_MODEL_ID, entityKey.modelId) - .call() - .onError(error -> pageContext.notifyLoadError(EntityType.CONFIGURATION_NODE, error)) - .getOrThrow(); - - final EntityGrantCheck entityGrant = this.currentUser.entityGrantCheck(examConfig); - final boolean writeGrant = entityGrant.w(); - final boolean modifyGrant = entityGrant.m(); - final boolean isReadonly = pageContext.isReadonly(); - final boolean isAttachedToExam = !isNew && this.restService - .getBuilder(GetExamConfigMappingNames.class) - .withQueryParam(ExamConfigurationMap.FILTER_ATTR_CONFIG_ID, examConfig.getModelId()) - .call() - .map(names -> names != null && !names.isEmpty()) - .getOr(Boolean.FALSE); - - // new PageContext with actual EntityKey - final PageContext formContext = pageContext.withEntityKey(examConfig.getEntityKey()); - - // the default page layout with interactive title - final LocTextKey titleKey = (isNew) - ? FORM_TITLE_NEW - : FORM_TITLE; - final Composite content = widgetFactory.defaultPageLayout( - formContext.getParent(), - titleKey); - - final List> examConfigTemplateResources = resourceService.getExamConfigTemplateResources(); - final FormHandle formHandle = this.pageService.formBuilder( - formContext.copyOf(content)) - .readonly(isReadonly) - .putStaticValueIf(() -> !isNew, - Domain.CONFIGURATION_NODE.ATTR_ID, - examConfig.getModelId()) - .putStaticValue( - Domain.CONFIGURATION_NODE.ATTR_INSTITUTION_ID, - String.valueOf(examConfig.getInstitutionId())) - .putStaticValue( - Domain.CONFIGURATION_NODE.ATTR_TYPE, - ConfigurationType.EXAM_CONFIG.name()) - .addFieldIf( - () -> !examConfigTemplateResources.isEmpty(), - () -> FormBuilder.singleSelection( - Domain.CONFIGURATION_NODE.ATTR_TEMPLATE_ID, - FORM_TEMPLATE_TEXT_KEY, - (parentEntityKey != null) - ? parentEntityKey.modelId - : String.valueOf(examConfig.templateId), - resourceService::getExamConfigTemplateResources) - .readonly(!isNew)) - .addField(FormBuilder.text( - Domain.CONFIGURATION_NODE.ATTR_NAME, - FORM_NAME_TEXT_KEY, - examConfig.name)) - .addField(FormBuilder.text( - Domain.CONFIGURATION_NODE.ATTR_DESCRIPTION, - FORM_DESCRIPTION_TEXT_KEY, - examConfig.description) - .asArea()) - - .addField(FormBuilder.singleSelection( - Domain.CONFIGURATION_NODE.ATTR_STATUS, - FORM_STATUS_TEXT_KEY, - examConfig.status.name(), - () -> resourceService.examConfigStatusResources(isAttachedToExam)) - .withEmptyCellSeparation((isReadonly) ? false : true)) - .buildFor((isNew) - ? this.restService.getRestCall(NewExamConfig.class) - : this.restService.getRestCall(SaveExamConfig.class)); - - final boolean settingsReadonly = examConfig.status == ConfigurationStatus.IN_USE; - final UrlLauncher urlLauncher = RWT.getClient().getService(UrlLauncher.class); - final PageContext actionContext = formContext.clearEntityKeys(); - final PageActionBuilder actionBuilder = this.pageService.pageActionBuilder(actionContext); - actionBuilder - - .newAction(ActionDefinition.SEB_EXAM_CONFIG_NEW) - .publishIf(() -> writeGrant && isReadonly) - - .newAction(ActionDefinition.SEB_EXAM_CONFIG_PROP_MODIFY) - .withEntityKey(entityKey) - .publishIf(() -> modifyGrant && isReadonly) - - .newAction(ActionDefinition.SEB_EXAM_CONFIG_VIEW) - .withEntityKey(entityKey) - .publishIf(() -> isReadonly) - - .newAction(ActionDefinition.SEB_EXAM_CONFIG_MODIFY) - .withEntityKey(entityKey) - .publishIf(() -> modifyGrant && isReadonly && !settingsReadonly) - - .newAction(ActionDefinition.SEB_EXAM_CONFIG_EXPORT_PLAIN_XML) - .withEntityKey(entityKey) - .withExec(action -> { - final String downloadURL = this.downloadService.createDownloadURL( - entityKey.modelId, - SebExamConfigPlaintextDownload.class, - this.downloadFileName); - urlLauncher.openURL(downloadURL); - return action; - }) - .noEventPropagation() - .publishIf(() -> modifyGrant && isReadonly) - - .newAction(ActionDefinition.SEB_EXAM_CONFIG_GET_CONFIG_KEY) - .withEntityKey(entityKey) - .withExec(SebExamConfigPropForm.getConfigKeyFunction(this.pageService)) - .noEventPropagation() - .publishIf(() -> modifyGrant && isReadonly) - - .newAction(ActionDefinition.SEB_EXAM_CONFIG_IMPORT_TO_EXISTING_CONFIG) - .withEntityKey(entityKey) - .withExec(SebExamConfigImportPopup.importFunction(this.pageService, false)) - .noEventPropagation() - .publishIf(() -> modifyGrant && isReadonly && !isAttachedToExam) - - .newAction(ActionDefinition.SEB_EXAM_CONFIG_COPY_CONFIG) - .withEntityKey(entityKey) - .withExec(SebExamConfigCreationPopup.configCreationFunction( - this.pageService, - actionContext - .withEntityKey(entityKey) - .withAttribute( - PageContext.AttributeKeys.COPY_AS_TEMPLATE, - Constants.FALSE_STRING) - .withAttribute( - PageContext.AttributeKeys.CREATE_FROM_TEMPLATE, - Constants.FALSE_STRING))) - .noEventPropagation() - .publishIf(() -> modifyGrant && isReadonly) - - .newAction(ActionDefinition.SEB_EXAM_CONFIG_COPY_CONFIG_AS_TEMPALTE) - .withEntityKey(entityKey) - .withExec(SebExamConfigCreationPopup.configCreationFunction( - this.pageService, - pageContext.withAttribute( - PageContext.AttributeKeys.COPY_AS_TEMPLATE, - Constants.TRUE_STRING))) - .noEventPropagation() - .publishIf(() -> modifyGrant && isReadonly) - - .newAction(ActionDefinition.SEB_EXAM_CONFIG_PROP_SAVE) - .withEntityKey(entityKey) - .withExec(formHandle::processFormSave) - .ignoreMoveAwayFromEdit() - .withConfirm(() -> stateChangeConfirm(isAttachedToExam, formHandle)) - .publishIf(() -> !isReadonly) - - .newAction(ActionDefinition.SEB_EXAM_CONFIG_PROP_CANCEL_MODIFY) - .withEntityKey(entityKey) - .withExec(this.pageService.backToCurrentFunction()) - .publishIf(() -> !isReadonly); - - if (isAttachedToExam && isReadonly) { - - widgetFactory.labelSeparator(content); - widgetFactory.labelLocalized( - content, - CustomVariant.TEXT_H3, - FORM_ATTACHED_EXAMS_TITLE_TEXT_KEY); - - final EntityTable table = - this.pageService.entityTableBuilder(this.restService.getRestCall(GetExamConfigMappingsPage.class)) - .withRestCallAdapter(restCall -> restCall.withQueryParam( - ExamConfigurationMap.FILTER_ATTR_CONFIG_ID, examConfig.getModelId())) - .withPaging(1) - .hideNavigation() - .withRowDecorator(ExamList.decorateOnExamMapConsistency(this.pageService)) - - .withColumn(new ColumnDefinition<>( - QuizData.QUIZ_ATTR_NAME, - ExamList.COLUMN_TITLE_NAME_KEY, - ExamConfigurationMap::getExamName)) - - .withColumn(new ColumnDefinition<>( - QuizData.QUIZ_ATTR_START_TIME, - new LocTextKey( - ExamList.EXAM_LIST_COLUMN_STARTTIME, - this.pageService.getI18nSupport().getUsersTimeZoneTitleSuffix()), - ExamConfigurationMap::getExamStartTime)) - - .withColumn(new ColumnDefinition( - Domain.EXAM.ATTR_TYPE, - ExamList.COLUMN_TITLE_TYPE_KEY, - resourceService::localizedExamTypeName)) - - .withDefaultAction(this::showExamAction) - - .compose(pageContext.copyOf(content)); - - actionBuilder - - .newAction(ActionDefinition.EXAM_VIEW_FROM_LIST) - .withExec(pageAction -> { - final ExamConfigurationMap selectedExamMapping = getSelectedExamMapping(table); - return pageAction.withEntityKey( - new EntityKey(selectedExamMapping.examId, EntityType.EXAM)); - }) - .publishIf(table::hasAnyContent); - } - } - - private PageAction showExamAction(final EntityTable table) { - return this.pageService.pageActionBuilder(table.getPageContext()) - .newAction(ActionDefinition.EXAM_VIEW_FROM_LIST) - .withSelectionSupplier(() -> { - final ExamConfigurationMap selectedROWData = getSelectedExamMapping(table); - return new HashSet<>(Arrays.asList(new EntityKey(selectedROWData.examId, EntityType.EXAM))); - }) - .withExec(PageAction::applySingleSelectionAsEntityKey) - .create(); - } - - private ExamConfigurationMap getSelectedExamMapping(final EntityTable table) { - final ExamConfigurationMap selectedROWData = table.getSingleSelectedROWData(); - - if (selectedROWData == null) { - throw new PageMessageException(ExamList.EMPTY_SELECTION_TEXT_KEY); - } - return selectedROWData; - } - - private LocTextKey stateChangeConfirm( - final boolean isAttachedToExam, - final FormHandle formHandle) { - - if (isAttachedToExam) { - final String fieldValue = formHandle - .getForm() - .getFieldValue(Domain.CONFIGURATION_NODE.ATTR_STATUS); - - if (fieldValue != null) { - final ConfigurationStatus state = ConfigurationStatus.valueOf(fieldValue); - if (state != ConfigurationStatus.IN_USE) { - return SAVE_CONFIRM_STATE_CHANGE_WHILE_ATTACHED; - } - } - } - - return null; - } - - public static Function getConfigKeyFunction(final PageService pageService) { - final RestService restService = pageService.getResourceService().getRestService(); - return action -> { - final ConfigKey configKey = restService.getBuilder(ExportConfigKey.class) - .withURIVariable(API.PARAM_MODEL_ID, action.getEntityKey().modelId) - .call() - .getOrThrow(); - - final WidgetFactory widgetFactory = pageService.getWidgetFactory(); - final ModalInputDialog dialog = new ModalInputDialog<>( - action.pageContext().getParent().getShell(), - widgetFactory); - - dialog.setDialogWidth(500); - - dialog.open( - CONFIG_KEY_TITLE_TEXT_KEY, - action.pageContext(), - pc -> { - final Composite content = widgetFactory.defaultPageLayout( - pc.getParent()); - - widgetFactory.labelLocalized( - content, - CustomVariant.TEXT_H3, - CONFIG_KEY_TITLE_TEXT_KEY); - - final Text text = new Text(content, SWT.NONE); - text.setEditable(false); - text.setText(configKey.key); - }); - return action; - }; - } - -} +/* + * 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.content; + +import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.function.Function; + +import org.apache.commons.lang3.StringUtils; +import org.eclipse.rap.rwt.RWT; +import org.eclipse.rap.rwt.client.service.UrlLauncher; +import org.eclipse.swt.SWT; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.Text; +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.api.EntityType; +import ch.ethz.seb.sebserver.gbl.model.Domain; +import ch.ethz.seb.sebserver.gbl.model.EntityKey; +import ch.ethz.seb.sebserver.gbl.model.exam.ExamConfigurationMap; +import ch.ethz.seb.sebserver.gbl.model.exam.QuizData; +import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigKey; +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.user.UserInfo; +import ch.ethz.seb.sebserver.gbl.profile.GuiProfile; +import ch.ethz.seb.sebserver.gbl.util.Tuple; +import ch.ethz.seb.sebserver.gui.content.action.ActionDefinition; +import ch.ethz.seb.sebserver.gui.form.FormBuilder; +import ch.ethz.seb.sebserver.gui.form.FormHandle; +import ch.ethz.seb.sebserver.gui.service.ResourceService; +import ch.ethz.seb.sebserver.gui.service.i18n.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.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.download.DownloadService; +import ch.ethz.seb.sebserver.gui.service.remote.download.SebExamConfigPlaintextDownload; +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.GetExamConfigMappingsPage; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.seb.examconfig.ExportConfigKey; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.seb.examconfig.GetExamConfigNode; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.seb.examconfig.NewExamConfig; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.seb.examconfig.SaveExamConfig; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.auth.CurrentUser; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.auth.CurrentUser.EntityGrantCheck; +import ch.ethz.seb.sebserver.gui.table.ColumnDefinition; +import ch.ethz.seb.sebserver.gui.table.EntityTable; +import ch.ethz.seb.sebserver.gui.widget.WidgetFactory; +import ch.ethz.seb.sebserver.gui.widget.WidgetFactory.CustomVariant; + +@Lazy +@Component +@GuiProfile +public class SebExamConfigPropForm implements TemplateComposer { + + static final LocTextKey FORM_TITLE_NEW = + new LocTextKey("sebserver.examconfig.form.title.new"); + static final LocTextKey FORM_TITLE = + new LocTextKey("sebserver.examconfig.form.title"); + static final LocTextKey FORM_NAME_TEXT_KEY = + new LocTextKey("sebserver.examconfig.form.name"); + static final LocTextKey FORM_DESCRIPTION_TEXT_KEY = + new LocTextKey("sebserver.examconfig.form.description"); + static final LocTextKey FORM_HISTORY_TEXT_KEY = + new LocTextKey("sebserver.examconfig.form.with-history"); + static final LocTextKey FORM_TEMPLATE_TEXT_KEY = + new LocTextKey("sebserver.examconfig.form.template"); + static final LocTextKey FORM_STATUS_TEXT_KEY = + new LocTextKey("sebserver.examconfig.form.status"); + static final LocTextKey FORM_IMPORT_TEXT_KEY = + new LocTextKey("sebserver.examconfig.action.import-config"); + static final LocTextKey FORM_IMPORT_SELECT_TEXT_KEY = + new LocTextKey("sebserver.examconfig.action.import-file-select"); + static final LocTextKey FORM_IMPORT_PASSWORD_TEXT_KEY = + new LocTextKey("sebserver.examconfig.action.import-file-password"); + static final LocTextKey CONFIG_KEY_TITLE_TEXT_KEY = + new LocTextKey("sebserver.examconfig.form.config-key.title"); + static final LocTextKey FORM_IMPORT_CONFIRM_TEXT_KEY = + new LocTextKey("sebserver.examconfig.action.import-config.confirm"); + static final LocTextKey FORM_ATTACHED_EXAMS_TITLE_TEXT_KEY = + new LocTextKey("sebserver.examconfig.form.attached-to"); + static final LocTextKey FORM_ATTACHED_EXAMS_TITLE_TOOLTIP_TEXT_KEY = + new LocTextKey("sebserver.examconfig.form.attached-to" + Constants.TOOLTIP_TEXT_KEY_SUFFIX); + + static final LocTextKey SAVE_CONFIRM_STATE_CHANGE_WHILE_ATTACHED = + new LocTextKey("sebserver.examconfig.action.state-change.confirm"); + + private final PageService pageService; + private final RestService restService; + private final CurrentUser currentUser; + private final DownloadService downloadService; + private final String downloadFileName; + + protected SebExamConfigPropForm( + final PageService pageService, + final CurrentUser currentUser, + final DownloadService downloadService, + @Value("${sebserver.gui.seb.exam.config.download.filename}") final String downloadFileName) { + + this.pageService = pageService; + this.restService = pageService.getRestService(); + this.currentUser = currentUser; + this.downloadService = downloadService; + this.downloadFileName = downloadFileName; + } + + @Override + public void compose(final PageContext pageContext) { + final WidgetFactory widgetFactory = this.pageService.getWidgetFactory(); + final ResourceService resourceService = this.pageService.getResourceService(); + + final UserInfo user = this.currentUser.get(); + final EntityKey entityKey = pageContext.getEntityKey(); + final EntityKey parentEntityKey = pageContext.getParentEntityKey(); + final boolean isNew = entityKey == null; + + // get data or create new. Handle error if happen + final ConfigurationNode examConfig = (isNew) + ? ConfigurationNode.createNewExamConfig(user.institutionId) + : this.restService + .getBuilder(GetExamConfigNode.class) + .withURIVariable(API.PARAM_MODEL_ID, entityKey.modelId) + .call() + .onError(error -> pageContext.notifyLoadError(EntityType.CONFIGURATION_NODE, error)) + .getOrThrow(); + + final EntityGrantCheck entityGrant = this.currentUser.entityGrantCheck(examConfig); + final boolean writeGrant = entityGrant.w(); + final boolean modifyGrant = entityGrant.m(); + final boolean isReadonly = pageContext.isReadonly(); + final boolean isAttachedToExam = !isNew && this.restService + .getBuilder(GetExamConfigMappingNames.class) + .withQueryParam(ExamConfigurationMap.FILTER_ATTR_CONFIG_ID, examConfig.getModelId()) + .call() + .map(names -> names != null && !names.isEmpty()) + .getOr(Boolean.FALSE); + + // new PageContext with actual EntityKey + final PageContext formContext = pageContext.withEntityKey(examConfig.getEntityKey()); + + // the default page layout with interactive title + final LocTextKey titleKey = (isNew) + ? FORM_TITLE_NEW + : FORM_TITLE; + final Composite content = widgetFactory.defaultPageLayout( + formContext.getParent(), + titleKey); + + final List> examConfigTemplateResources = resourceService.getExamConfigTemplateResources(); + final FormHandle formHandle = this.pageService.formBuilder( + formContext.copyOf(content)) + .readonly(isReadonly) + .putStaticValueIf(() -> !isNew, + Domain.CONFIGURATION_NODE.ATTR_ID, + examConfig.getModelId()) + .putStaticValue( + Domain.CONFIGURATION_NODE.ATTR_INSTITUTION_ID, + String.valueOf(examConfig.getInstitutionId())) + .putStaticValue( + Domain.CONFIGURATION_NODE.ATTR_TYPE, + ConfigurationType.EXAM_CONFIG.name()) + .addFieldIf( + () -> !examConfigTemplateResources.isEmpty(), + () -> FormBuilder.singleSelection( + Domain.CONFIGURATION_NODE.ATTR_TEMPLATE_ID, + FORM_TEMPLATE_TEXT_KEY, + (parentEntityKey != null) + ? parentEntityKey.modelId + : String.valueOf(examConfig.templateId), + resourceService::getExamConfigTemplateResources) + .readonly(!isNew)) + .addField(FormBuilder.text( + Domain.CONFIGURATION_NODE.ATTR_NAME, + FORM_NAME_TEXT_KEY, + examConfig.name) + .mandatory(!isReadonly)) + .addField(FormBuilder.text( + Domain.CONFIGURATION_NODE.ATTR_DESCRIPTION, + FORM_DESCRIPTION_TEXT_KEY, + examConfig.description) + .asArea()) + + .addField(FormBuilder.singleSelection( + Domain.CONFIGURATION_NODE.ATTR_STATUS, + FORM_STATUS_TEXT_KEY, + examConfig.status.name(), + () -> resourceService.examConfigStatusResources(isAttachedToExam)) + .withEmptyCellSeparation(!isReadonly)) + .buildFor((isNew) + ? this.restService.getRestCall(NewExamConfig.class) + : this.restService.getRestCall(SaveExamConfig.class)); + + final boolean settingsReadonly = examConfig.status == ConfigurationStatus.IN_USE; + final UrlLauncher urlLauncher = RWT.getClient().getService(UrlLauncher.class); + final PageContext actionContext = formContext.clearEntityKeys(); + final PageActionBuilder actionBuilder = this.pageService.pageActionBuilder(actionContext); + actionBuilder + + .newAction(ActionDefinition.SEB_EXAM_CONFIG_NEW) + .publishIf(() -> writeGrant && isReadonly) + + .newAction(ActionDefinition.SEB_EXAM_CONFIG_PROP_MODIFY) + .withEntityKey(entityKey) + + .publishIf(() -> modifyGrant && isReadonly) + + .newAction(ActionDefinition.SEB_EXAM_CONFIG_MODIFY) + .withEntityKey(entityKey) + .withAttribute(PageContext.AttributeKeys.READ_ONLY, String.valueOf(!modifyGrant)) + .publishIf(() -> modifyGrant && isReadonly) + + .newAction(ActionDefinition.SEB_EXAM_CONFIG_EXPORT_PLAIN_XML) + .withEntityKey(entityKey) + .withExec(action -> { + final String downloadURL = this.downloadService.createDownloadURL( + entityKey.modelId, + SebExamConfigPlaintextDownload.class, + this.downloadFileName); + urlLauncher.openURL(downloadURL); + return action; + }) + .noEventPropagation() + .publishIf(() -> modifyGrant && isReadonly) + + .newAction(ActionDefinition.SEB_EXAM_CONFIG_GET_CONFIG_KEY) + .withEntityKey(entityKey) + .withExec(SebExamConfigPropForm.getConfigKeyFunction(this.pageService)) + .noEventPropagation() + .publishIf(() -> modifyGrant && isReadonly) + + .newAction(ActionDefinition.SEB_EXAM_CONFIG_IMPORT_TO_EXISTING_CONFIG) + .withEntityKey(entityKey) + .withExec(SebExamConfigImportPopup.importFunction(this.pageService, false)) + .noEventPropagation() + .publishIf(() -> modifyGrant && isReadonly && !isAttachedToExam) + + .newAction(ActionDefinition.SEB_EXAM_CONFIG_COPY_CONFIG) + .withEntityKey(entityKey) + .withExec(SebExamConfigCreationPopup.configCreationFunction( + this.pageService, + actionContext + .withEntityKey(entityKey) + .withAttribute( + PageContext.AttributeKeys.COPY_AS_TEMPLATE, + Constants.FALSE_STRING) + .withAttribute( + PageContext.AttributeKeys.CREATE_FROM_TEMPLATE, + Constants.FALSE_STRING))) + .noEventPropagation() + .publishIf(() -> modifyGrant && isReadonly) + + .newAction(ActionDefinition.SEB_EXAM_CONFIG_COPY_CONFIG_AS_TEMPALTE) + .withEntityKey(entityKey) + .withExec(SebExamConfigCreationPopup.configCreationFunction( + this.pageService, + pageContext.withAttribute( + PageContext.AttributeKeys.COPY_AS_TEMPLATE, + Constants.TRUE_STRING))) + .noEventPropagation() + .publishIf(() -> modifyGrant && isReadonly) + + .newAction(ActionDefinition.SEB_EXAM_CONFIG_PROP_SAVE) + .withEntityKey(entityKey) + .withExec(formHandle::processFormSave) + .ignoreMoveAwayFromEdit() + .withConfirm(() -> stateChangeConfirm(isAttachedToExam, formHandle)) + .publishIf(() -> !isReadonly) + + .newAction(ActionDefinition.SEB_EXAM_CONFIG_PROP_CANCEL_MODIFY) + .withEntityKey(entityKey) + .withExec(this.pageService.backToCurrentFunction()) + .publishIf(() -> !isReadonly); + + if (isAttachedToExam && isReadonly) { + + widgetFactory.label(content, StringUtils.EMPTY); + widgetFactory.labelLocalized( + content, + CustomVariant.TEXT_H3, + FORM_ATTACHED_EXAMS_TITLE_TEXT_KEY, + FORM_ATTACHED_EXAMS_TITLE_TOOLTIP_TEXT_KEY); + widgetFactory.labelSeparator(content); + + final EntityTable table = + this.pageService.entityTableBuilder(this.restService.getRestCall(GetExamConfigMappingsPage.class)) + .withRestCallAdapter(restCall -> restCall.withQueryParam( + ExamConfigurationMap.FILTER_ATTR_CONFIG_ID, examConfig.getModelId())) + .withPaging(1) + .hideNavigation() + .withRowDecorator(ExamList.decorateOnExamMapConsistency(this.pageService)) + + .withColumn(new ColumnDefinition<>( + QuizData.QUIZ_ATTR_NAME, + ExamList.COLUMN_TITLE_NAME_KEY, + ExamConfigurationMap::getExamName)) + + .withColumn(new ColumnDefinition<>( + QuizData.QUIZ_ATTR_START_TIME, + new LocTextKey( + ExamList.EXAM_LIST_COLUMN_STARTTIME, + this.pageService.getI18nSupport().getUsersTimeZoneTitleSuffix()), + ExamConfigurationMap::getExamStartTime)) + + .withColumn(new ColumnDefinition<>( + Domain.EXAM.ATTR_TYPE, + ExamList.COLUMN_TITLE_TYPE_KEY, + resourceService::localizedExamTypeName)) + + .withDefaultAction(this::showExamAction) + + .withSelectionListener(this.pageService.getSelectionPublisher( + pageContext, + ActionDefinition.EXAM_VIEW_FROM_LIST)) + + .compose(pageContext.copyOf(content)); + + actionBuilder + + .newAction(ActionDefinition.EXAM_VIEW_FROM_LIST) + .withExec(pageAction -> { + final ExamConfigurationMap selectedExamMapping = getSelectedExamMapping(table); + return pageAction.withEntityKey( + new EntityKey(selectedExamMapping.examId, EntityType.EXAM)); + }) + .publishIf(table::hasAnyContent, false); + } + } + + private PageAction showExamAction(final EntityTable table) { + return this.pageService.pageActionBuilder(table.getPageContext()) + .newAction(ActionDefinition.EXAM_VIEW_FROM_LIST) + .withSelectionSupplier(() -> { + final ExamConfigurationMap selectedROWData = getSelectedExamMapping(table); + return new HashSet<>(Collections.singletonList(new EntityKey(selectedROWData.examId, EntityType.EXAM))); + }) + .withExec(PageAction::applySingleSelectionAsEntityKey) + .create(); + } + + private ExamConfigurationMap getSelectedExamMapping(final EntityTable table) { + final ExamConfigurationMap selectedROWData = table.getSingleSelectedROWData(); + + if (selectedROWData == null) { + throw new PageMessageException(ExamList.EMPTY_SELECTION_TEXT_KEY); + } + return selectedROWData; + } + + private LocTextKey stateChangeConfirm( + final boolean isAttachedToExam, + final FormHandle formHandle) { + + if (isAttachedToExam) { + final String fieldValue = formHandle + .getForm() + .getFieldValue(Domain.CONFIGURATION_NODE.ATTR_STATUS); + + if (fieldValue != null) { + final ConfigurationStatus state = ConfigurationStatus.valueOf(fieldValue); + if (state != ConfigurationStatus.IN_USE) { + return SAVE_CONFIRM_STATE_CHANGE_WHILE_ATTACHED; + } + } + } + + return null; + } + + public static Function getConfigKeyFunction(final PageService pageService) { + final RestService restService = pageService.getResourceService().getRestService(); + return action -> { + final ConfigKey configKey = restService.getBuilder(ExportConfigKey.class) + .withURIVariable(API.PARAM_MODEL_ID, action.getEntityKey().modelId) + .call() + .getOrThrow(); + + final WidgetFactory widgetFactory = pageService.getWidgetFactory(); + final ModalInputDialog dialog = new ModalInputDialog<>( + action.pageContext().getParent().getShell(), + widgetFactory); + + dialog.setDialogWidth(500); + + dialog.open( + CONFIG_KEY_TITLE_TEXT_KEY, + action.pageContext(), + pc -> { + final Composite content = widgetFactory.defaultPageLayout( + pc.getParent()); + + widgetFactory.labelLocalized( + content, + CustomVariant.TEXT_H3, + CONFIG_KEY_TITLE_TEXT_KEY); + + final Text text = new Text(content, SWT.NONE); + text.setEditable(false); + text.setText(configKey.key); + }); + return action; + }; + } + +} diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/content/SebExamConfigSettingsForm.java b/src/main/java/ch/ethz/seb/sebserver/gui/content/SebExamConfigSettingsForm.java index ec77a9af..861c6a0f 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/content/SebExamConfigSettingsForm.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/content/SebExamConfigSettingsForm.java @@ -1,239 +1,238 @@ -/* - * 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.content; - -import java.util.ArrayList; -import java.util.List; - -import org.apache.commons.lang3.StringUtils; -import org.eclipse.swt.SWT; -import org.eclipse.swt.layout.GridData; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.TabFolder; -import org.eclipse.swt.widgets.TabItem; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.context.annotation.Lazy; -import org.springframework.stereotype.Component; - -import ch.ethz.seb.sebserver.gbl.Constants; -import ch.ethz.seb.sebserver.gbl.api.API; -import ch.ethz.seb.sebserver.gbl.api.APIMessage; -import ch.ethz.seb.sebserver.gbl.api.APIMessageError; -import ch.ethz.seb.sebserver.gbl.api.EntityType; -import ch.ethz.seb.sebserver.gbl.model.EntityKey; -import ch.ethz.seb.sebserver.gbl.model.sebconfig.Configuration; -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.View; -import ch.ethz.seb.sebserver.gbl.profile.GuiProfile; -import ch.ethz.seb.sebserver.gbl.util.Utils; -import ch.ethz.seb.sebserver.gui.content.action.ActionDefinition; -import ch.ethz.seb.sebserver.gui.service.examconfig.ExamConfigurationService; -import ch.ethz.seb.sebserver.gui.service.examconfig.impl.AttributeMapping; -import ch.ethz.seb.sebserver.gui.service.examconfig.impl.ViewContext; -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.TemplateComposer; -import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestService; -import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.seb.examconfig.GetConfigurations; -import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.seb.examconfig.GetExamConfigNode; -import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.seb.examconfig.SaveExamConfigHistory; -import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.seb.examconfig.SebExamConfigUndo; -import ch.ethz.seb.sebserver.gui.service.remote.webservice.auth.CurrentUser; -import ch.ethz.seb.sebserver.gui.service.remote.webservice.auth.CurrentUser.GrantCheck; -import ch.ethz.seb.sebserver.gui.widget.WidgetFactory; - -@Lazy -@Component -@GuiProfile -public class SebExamConfigSettingsForm implements TemplateComposer { - - private static final Logger log = LoggerFactory.getLogger(SebExamConfigSettingsForm.class); - - private static final String VIEW_TEXT_KEY_PREFIX = - "sebserver.examconfig.props.form.views."; - private static final String KEY_SAVE_TO_HISTORY_SUCCESS = - "sebserver.examconfig.action.saveToHistory.success"; - private static final String KEY_UNDO_SUCCESS = - "sebserver.examconfig.action.undo.success"; - private static final LocTextKey TITLE_TEXT_KEY = - new LocTextKey("sebserver.examconfig.props.from.title"); - - private static final LocTextKey MESSAGE_SAVE_INTEGRITY_VIOLATION = - new LocTextKey("sebserver.examconfig.action.saveToHistory.integrity-violation"); - - private final PageService pageService; - private final RestService restService; - private final CurrentUser currentUser; - private final ExamConfigurationService examConfigurationService; - - protected SebExamConfigSettingsForm( - final PageService pageService, - final RestService restService, - final CurrentUser currentUser, - final ExamConfigurationService examConfigurationService) { - - this.pageService = pageService; - this.restService = restService; - this.currentUser = currentUser; - this.examConfigurationService = examConfigurationService; - } - - @Override - public void compose(final PageContext pageContext) { - final WidgetFactory widgetFactory = this.pageService.getWidgetFactory(); - - final EntityKey entityKey = pageContext.getEntityKey(); - - final Composite content = widgetFactory.defaultPageLayout( - pageContext.getParent(), - TITLE_TEXT_KEY); - - try { - - final ConfigurationNode configNode = this.restService.getBuilder(GetExamConfigNode.class) - .withURIVariable(API.PARAM_MODEL_ID, entityKey.modelId) - .call() - .onError(error -> pageContext.notifyLoadError(EntityType.CONFIGURATION_NODE, error)) - .getOrThrow(); - - final Configuration configuration = this.restService.getBuilder(GetConfigurations.class) - .withQueryParam(Configuration.FILTER_ATTR_CONFIGURATION_NODE_ID, configNode.getModelId()) - .withQueryParam(Configuration.FILTER_ATTR_FOLLOWUP, Constants.TRUE_STRING) - .call() - .map(Utils::toSingleton) - .onError(error -> pageContext.notifyLoadError(EntityType.CONFIGURATION, error)) - .getOrThrow(); - - final AttributeMapping attributes = this.examConfigurationService - .getAttributes(configNode.templateId) - .onError(error -> pageContext.notifyLoadError(EntityType.CONFIGURATION_ATTRIBUTE, error)) - .getOrThrow(); - - final boolean readonly = pageContext.isReadonly() || configNode.status == ConfigurationStatus.IN_USE; - final List views = this.examConfigurationService.getViews(attributes); - final TabFolder tabFolder = widgetFactory.tabFolderLocalized(content); - tabFolder.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false)); - - final List viewContexts = new ArrayList<>(); - for (final View view : views) { - final ViewContext viewContext = this.examConfigurationService.createViewContext( - pageContext, - configuration, - view, - attributes, - 20, - readonly); - viewContexts.add(viewContext); - - final Composite viewGrid = this.examConfigurationService.createViewGrid( - tabFolder, - viewContext); - - final TabItem tabItem = widgetFactory.tabItemLocalized( - tabFolder, - new LocTextKey(VIEW_TEXT_KEY_PREFIX + view.name)); - tabItem.setControl(viewGrid); - } - - // set selection if available - final String viewIndex = pageContext.getAttribute("VIEW_INDEX"); - if (StringUtils.isNotBlank(viewIndex)) { - try { - tabFolder.setSelection(Integer.parseInt(viewIndex)); - } catch (final NumberFormatException e) { - log.warn("Failed to initialize view selection"); - } - } - - this.examConfigurationService.initInputFieldValues(configuration.id, viewContexts); - - final GrantCheck examConfigGrant = this.currentUser.grantCheck(EntityType.CONFIGURATION_NODE); - this.pageService.pageActionBuilder(pageContext.clearEntityKeys()) - - .newAction(ActionDefinition.SEB_EXAM_CONFIG_SAVE_TO_HISTORY) - .withEntityKey(entityKey) - .withExec(action -> { - this.restService.getBuilder(SaveExamConfigHistory.class) - .withURIVariable(API.PARAM_MODEL_ID, configuration.getModelId()) - .call() - .onError(t -> notifyErrorOnSave(t, pageContext)); - return action.withAttribute( - "VIEW_INDEX", - String.valueOf(tabFolder.getSelectionIndex())); - }) - .withSuccess(KEY_SAVE_TO_HISTORY_SUCCESS) - .ignoreMoveAwayFromEdit() - .publishIf(() -> examConfigGrant.iw() && !readonly) - - .newAction(ActionDefinition.SEB_EXAM_CONFIG_UNDO) - .withEntityKey(entityKey) - .withExec(action -> { - this.restService.getBuilder(SebExamConfigUndo.class) - .withURIVariable(API.PARAM_MODEL_ID, configuration.getModelId()) - .call() - .getOrThrow(); - return action.withAttribute( - "VIEW_INDEX", - String.valueOf(tabFolder.getSelectionIndex())); - }) - .withSuccess(KEY_UNDO_SUCCESS) - .ignoreMoveAwayFromEdit() - .publishIf(() -> examConfigGrant.iw() && !readonly) - - .newAction(ActionDefinition.SEB_EXAM_CONFIG_COPY_CONFIG_AS_TEMPALTE) - .withEntityKey(entityKey) - .withExec(SebExamConfigCreationPopup.configCreationFunction( - this.pageService, - pageContext - .withAttribute( - PageContext.AttributeKeys.COPY_AS_TEMPLATE, - Constants.TRUE_STRING) - .withAttribute( - PageContext.AttributeKeys.CREATE_FROM_TEMPLATE, - Constants.FALSE_STRING))) - .noEventPropagation() - .publishIf(() -> examConfigGrant.iw()) - - .newAction(ActionDefinition.SEB_EXAM_CONFIG_VIEW_PROP) - .withEntityKey(entityKey) - .ignoreMoveAwayFromEdit() - .publish(); - - } catch (final RuntimeException e) { - pageContext.notifyUnexpectedError(e); - throw e; - } catch (final Exception e) { - log.error("Unexpected error while trying to fetch exam configuration data and create views", e); - pageContext.notifyError(SebExamConfigPropForm.FORM_TITLE, e); - } - } - - private void notifyErrorOnSave(final Exception error, final PageContext context) { - if (error instanceof APIMessageError) { - try { - final List errorMessages = ((APIMessageError) error).getErrorMessages(); - final APIMessage apiMessage = errorMessages.get(0); - if (APIMessage.ErrorMessage.INTEGRITY_VALIDATION.isOf(apiMessage)) { - throw new PageMessageException(MESSAGE_SAVE_INTEGRITY_VIOLATION); - } else { - throw error; - } - } catch (final PageMessageException e) { - throw e; - } catch (final Exception e) { - throw new RuntimeException(error); - } - } - } - -} +/* + * 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.content; + +import java.util.ArrayList; +import java.util.List; + +import org.apache.commons.lang3.StringUtils; +import org.eclipse.swt.SWT; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.TabFolder; +import org.eclipse.swt.widgets.TabItem; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.context.annotation.Lazy; +import org.springframework.stereotype.Component; + +import ch.ethz.seb.sebserver.gbl.Constants; +import ch.ethz.seb.sebserver.gbl.api.API; +import ch.ethz.seb.sebserver.gbl.api.APIMessage; +import ch.ethz.seb.sebserver.gbl.api.APIMessageError; +import ch.ethz.seb.sebserver.gbl.api.EntityType; +import ch.ethz.seb.sebserver.gbl.model.EntityKey; +import ch.ethz.seb.sebserver.gbl.model.sebconfig.Configuration; +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.View; +import ch.ethz.seb.sebserver.gbl.profile.GuiProfile; +import ch.ethz.seb.sebserver.gbl.util.Utils; +import ch.ethz.seb.sebserver.gui.content.action.ActionDefinition; +import ch.ethz.seb.sebserver.gui.service.examconfig.ExamConfigurationService; +import ch.ethz.seb.sebserver.gui.service.examconfig.impl.AttributeMapping; +import ch.ethz.seb.sebserver.gui.service.examconfig.impl.ViewContext; +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.TemplateComposer; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestService; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.seb.examconfig.GetConfigurations; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.seb.examconfig.GetExamConfigNode; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.seb.examconfig.SaveExamConfigHistory; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.seb.examconfig.SebExamConfigUndo; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.auth.CurrentUser; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.auth.CurrentUser.GrantCheck; +import ch.ethz.seb.sebserver.gui.widget.WidgetFactory; + +@Lazy +@Component +@GuiProfile +public class SebExamConfigSettingsForm implements TemplateComposer { + + private static final Logger log = LoggerFactory.getLogger(SebExamConfigSettingsForm.class); + + private static final String VIEW_TEXT_KEY_PREFIX = + "sebserver.examconfig.props.form.views."; + private static final String KEY_SAVE_TO_HISTORY_SUCCESS = + "sebserver.examconfig.action.saveToHistory.success"; + private static final String KEY_UNDO_SUCCESS = + "sebserver.examconfig.action.undo.success"; + private static final LocTextKey TITLE_TEXT_KEY = + new LocTextKey("sebserver.examconfig.props.from.title"); + + private static final LocTextKey MESSAGE_SAVE_INTEGRITY_VIOLATION = + new LocTextKey("sebserver.examconfig.action.saveToHistory.integrity-violation"); + + private final PageService pageService; + private final RestService restService; + private final CurrentUser currentUser; + private final ExamConfigurationService examConfigurationService; + + protected SebExamConfigSettingsForm( + final PageService pageService, + final RestService restService, + final CurrentUser currentUser, + final ExamConfigurationService examConfigurationService) { + + this.pageService = pageService; + this.restService = restService; + this.currentUser = currentUser; + this.examConfigurationService = examConfigurationService; + } + + @Override + public void compose(final PageContext pageContext) { + final WidgetFactory widgetFactory = this.pageService.getWidgetFactory(); + + final EntityKey entityKey = pageContext.getEntityKey(); + + final Composite content = widgetFactory.defaultPageLayout( + pageContext.getParent(), + TITLE_TEXT_KEY); + + try { + + final ConfigurationNode configNode = this.restService.getBuilder(GetExamConfigNode.class) + .withURIVariable(API.PARAM_MODEL_ID, entityKey.modelId) + .call() + .onError(error -> pageContext.notifyLoadError(EntityType.CONFIGURATION_NODE, error)) + .getOrThrow(); + + final Configuration configuration = this.restService.getBuilder(GetConfigurations.class) + .withQueryParam(Configuration.FILTER_ATTR_CONFIGURATION_NODE_ID, configNode.getModelId()) + .withQueryParam(Configuration.FILTER_ATTR_FOLLOWUP, Constants.TRUE_STRING) + .call() + .map(Utils::toSingleton) + .onError(error -> pageContext.notifyLoadError(EntityType.CONFIGURATION, error)) + .getOrThrow(); + + final AttributeMapping attributes = this.examConfigurationService + .getAttributes(configNode.templateId) + .onError(error -> pageContext.notifyLoadError(EntityType.CONFIGURATION_ATTRIBUTE, error)) + .getOrThrow(); + + final boolean readonly = pageContext.isReadonly() || configNode.status == ConfigurationStatus.IN_USE; + final List views = this.examConfigurationService.getViews(attributes); + final TabFolder tabFolder = widgetFactory.tabFolderLocalized(content); + tabFolder.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false)); + + final List viewContexts = new ArrayList<>(); + for (final View view : views) { + final ViewContext viewContext = this.examConfigurationService.createViewContext( + pageContext, + configuration, + view, + attributes, + 20, + readonly); + viewContexts.add(viewContext); + + final Composite viewGrid = this.examConfigurationService.createViewGrid( + tabFolder, + viewContext); + + final TabItem tabItem = widgetFactory.tabItemLocalized( + tabFolder, + new LocTextKey(VIEW_TEXT_KEY_PREFIX + view.name)); + tabItem.setControl(viewGrid); + } + + // set selection if available + final String viewIndex = pageContext.getAttribute("VIEW_INDEX"); + if (StringUtils.isNotBlank(viewIndex)) { + try { + tabFolder.setSelection(Integer.parseInt(viewIndex)); + } catch (final NumberFormatException e) { + log.warn("Failed to initialize view selection"); + } + } + + this.examConfigurationService.initInputFieldValues(configuration.id, viewContexts); + + final GrantCheck examConfigGrant = this.currentUser.grantCheck(EntityType.CONFIGURATION_NODE); + this.pageService.pageActionBuilder(pageContext.clearEntityKeys()) + + .newAction(ActionDefinition.SEB_EXAM_CONFIG_SAVE_TO_HISTORY) + .withEntityKey(entityKey) + .withExec(action -> { + this.restService.getBuilder(SaveExamConfigHistory.class) + .withURIVariable(API.PARAM_MODEL_ID, configuration.getModelId()) + .call() + .onError(t -> notifyErrorOnSave(t, pageContext)); + return action.withAttribute( + "VIEW_INDEX", + String.valueOf(tabFolder.getSelectionIndex())); + }) + .withSuccess(KEY_SAVE_TO_HISTORY_SUCCESS) + .ignoreMoveAwayFromEdit() + .publishIf(() -> examConfigGrant.iw() && !readonly) + + .newAction(ActionDefinition.SEB_EXAM_CONFIG_UNDO) + .withEntityKey(entityKey) + .withExec(action -> { + this.restService.getBuilder(SebExamConfigUndo.class) + .withURIVariable(API.PARAM_MODEL_ID, configuration.getModelId()) + .call() + .getOrThrow(); + return action.withAttribute( + "VIEW_INDEX", + String.valueOf(tabFolder.getSelectionIndex())); + }) + .withSuccess(KEY_UNDO_SUCCESS) + .ignoreMoveAwayFromEdit() + .publishIf(() -> examConfigGrant.iw() && !readonly) + + .newAction(ActionDefinition.SEB_EXAM_CONFIG_COPY_CONFIG_AS_TEMPALTE) + .withEntityKey(entityKey) + .withExec(SebExamConfigCreationPopup.configCreationFunction( + this.pageService, + pageContext + .withAttribute( + PageContext.AttributeKeys.COPY_AS_TEMPLATE, + Constants.TRUE_STRING) + .withAttribute( + PageContext.AttributeKeys.CREATE_FROM_TEMPLATE, + Constants.FALSE_STRING))) + .noEventPropagation() + .publishIf(examConfigGrant::iw) + + .newAction(ActionDefinition.SEB_EXAM_CONFIG_VIEW_PROP) + .withEntityKey(entityKey) + .ignoreMoveAwayFromEdit() + .publish(); + + } catch (final RuntimeException e) { + pageContext.notifyUnexpectedError(e); + throw e; + } catch (final Exception e) { + log.error("Unexpected error while trying to fetch exam configuration data and create views", e); + pageContext.notifyError(SebExamConfigPropForm.FORM_TITLE, e); + } + } + + private void notifyErrorOnSave(final Exception error, final PageContext context) { + if (error instanceof APIMessageError) { + try { + final List errorMessages = ((APIMessageError) error).getErrorMessages(); + final APIMessage apiMessage = errorMessages.get(0); + if (APIMessage.ErrorMessage.INTEGRITY_VALIDATION.isOf(apiMessage)) { + throw new PageMessageException(MESSAGE_SAVE_INTEGRITY_VIOLATION); + } else { + throw error; + } + } catch (final PageMessageException e) { + throw e; + } catch (final Exception e) { + throw new RuntimeException(error); + } + } + } +} diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/content/UserAccountChangePasswordForm.java b/src/main/java/ch/ethz/seb/sebserver/gui/content/UserAccountChangePasswordForm.java index 2b0598ea..8b939e7e 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/content/UserAccountChangePasswordForm.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/content/UserAccountChangePasswordForm.java @@ -1,143 +1,147 @@ -/* - * 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.content; - -import org.eclipse.swt.SWT; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.MessageBox; -import org.springframework.context.annotation.Lazy; -import org.springframework.stereotype.Component; - -import ch.ethz.seb.sebserver.gbl.api.API; -import ch.ethz.seb.sebserver.gbl.api.EntityType; -import ch.ethz.seb.sebserver.gbl.model.Domain; -import ch.ethz.seb.sebserver.gbl.model.EntityKey; -import ch.ethz.seb.sebserver.gbl.model.user.PasswordChange; -import ch.ethz.seb.sebserver.gbl.model.user.UserInfo; -import ch.ethz.seb.sebserver.gbl.profile.GuiProfile; -import ch.ethz.seb.sebserver.gui.content.action.ActionDefinition; -import ch.ethz.seb.sebserver.gui.form.FormBuilder; -import ch.ethz.seb.sebserver.gui.form.FormHandle; -import ch.ethz.seb.sebserver.gui.service.i18n.I18nSupport; -import ch.ethz.seb.sebserver.gui.service.i18n.LocTextKey; -import ch.ethz.seb.sebserver.gui.service.page.PageContext; -import ch.ethz.seb.sebserver.gui.service.page.PageService; -import ch.ethz.seb.sebserver.gui.service.page.TemplateComposer; -import ch.ethz.seb.sebserver.gui.service.page.impl.PageAction; -import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestService; -import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.useraccount.ChangePassword; -import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.useraccount.GetUserAccount; -import ch.ethz.seb.sebserver.gui.service.remote.webservice.auth.CurrentUser; -import ch.ethz.seb.sebserver.gui.widget.Message; -import ch.ethz.seb.sebserver.gui.widget.WidgetFactory; - -@Lazy -@Component -@GuiProfile -/** The form to change an User-Account password. - * If the current user is the owner of the User-Account the password is required and must - * match the users current password. - * If the current user is an administrator that has to reset another users password the - * password that is also required must match the administrators current password. */ -public class UserAccountChangePasswordForm implements TemplateComposer { - - private static final String FORM_TITLE_KEY = "sebserver.useraccount.form.pwchange.title"; - private static final LocTextKey FORM_PASSWORD_NEW_TEXT_KEY = - new LocTextKey("sebserver.useraccount.form.password.new"); - private static final LocTextKey FORM_PASSWORD_NEW_CONFIRM_TEXT_KEY = - new LocTextKey("sebserver.useraccount.form.password.new.confirm"); - private static final LocTextKey FORM_PASSWORD_TEXT_KEY = - new LocTextKey("sebserver.useraccount.form.password"); - - private final PageService pageService; - private final RestService restService; - private final CurrentUser currentUser; - private final I18nSupport i18nSupport; - - protected UserAccountChangePasswordForm( - final PageService pageService, - final RestService restService, - final CurrentUser currentUser) { - - this.pageService = pageService; - this.restService = restService; - this.currentUser = currentUser; - this.i18nSupport = pageService.getI18nSupport(); - } - - @Override - public void compose(final PageContext pageContext) { - - final WidgetFactory widgetFactory = this.pageService.getWidgetFactory(); - final EntityKey entityKey = pageContext.getEntityKey(); - - final UserInfo userInfo = this.restService - .getBuilder(GetUserAccount.class) - .withURIVariable(API.PARAM_MODEL_ID, entityKey.modelId) - .call() - .onError(error -> pageContext.notifyLoadError(EntityType.USER, error)) - .getOrThrow(); - - final Composite content = widgetFactory.defaultPageLayout( - pageContext.getParent(), - new LocTextKey(FORM_TITLE_KEY, userInfo.username)); - - final boolean ownAccount = this.currentUser.get().uuid.equals(entityKey.getModelId()); - - // The Password Change form - final FormHandle formHandle = this.pageService.formBuilder(pageContext.copyOf(content)) - .readonly(false) - .putStaticValueIf(() -> entityKey != null, - Domain.USER.ATTR_UUID, - entityKey.getModelId()) - .addField(FormBuilder.text( - PasswordChange.ATTR_NAME_PASSWORD, - FORM_PASSWORD_TEXT_KEY) - .asPasswordField()) - .addField(FormBuilder.text( - PasswordChange.ATTR_NAME_NEW_PASSWORD, - FORM_PASSWORD_NEW_TEXT_KEY) - .asPasswordField()) - .addFieldIf( - () -> entityKey != null, - () -> FormBuilder.text( - PasswordChange.ATTR_NAME_CONFIRM_NEW_PASSWORD, - FORM_PASSWORD_NEW_CONFIRM_TEXT_KEY) - .asPasswordField()) - .buildFor(this.restService.getRestCall(ChangePassword.class)); - - this.pageService.pageActionBuilder(pageContext) - - .newAction(ActionDefinition.USER_ACCOUNT_CHANGE_PASSOWRD_SAVE) - .withExec(action -> { - final PageAction saveAction = formHandle.processFormSave(action); - if (ownAccount) { - // NOTE: in this case the user changed the password of the own account - // this should cause an logout with specified message that password change - // was successful and the pointing the need of re login with the new password - this.pageService.logout(pageContext); - final MessageBox error = new Message( - pageContext.getShell(), - this.i18nSupport.getText("sebserver.login.password.change"), - this.i18nSupport.getText("sebserver.login.password.change.success"), - SWT.ICON_INFORMATION, - this.i18nSupport); - error.open(null); - } - return saveAction; - }) - .ignoreMoveAwayFromEdit() - .publish() - - .newAction(ActionDefinition.USER_ACCOUNT_CANCEL_MODIFY) - .withExec(this.pageService.backToCurrentFunction()) - .publish(); - } - -} +/* + * 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.content; + +import org.eclipse.swt.SWT; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.MessageBox; +import org.springframework.context.annotation.Lazy; +import org.springframework.stereotype.Component; + +import ch.ethz.seb.sebserver.gbl.api.API; +import ch.ethz.seb.sebserver.gbl.api.EntityType; +import ch.ethz.seb.sebserver.gbl.model.Domain; +import ch.ethz.seb.sebserver.gbl.model.EntityKey; +import ch.ethz.seb.sebserver.gbl.model.user.PasswordChange; +import ch.ethz.seb.sebserver.gbl.model.user.UserInfo; +import ch.ethz.seb.sebserver.gbl.profile.GuiProfile; +import ch.ethz.seb.sebserver.gui.content.action.ActionDefinition; +import ch.ethz.seb.sebserver.gui.form.FormBuilder; +import ch.ethz.seb.sebserver.gui.form.FormHandle; +import ch.ethz.seb.sebserver.gui.service.i18n.I18nSupport; +import ch.ethz.seb.sebserver.gui.service.i18n.LocTextKey; +import ch.ethz.seb.sebserver.gui.service.page.PageContext; +import ch.ethz.seb.sebserver.gui.service.page.PageService; +import ch.ethz.seb.sebserver.gui.service.page.TemplateComposer; +import ch.ethz.seb.sebserver.gui.service.page.impl.PageAction; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestService; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.useraccount.ChangePassword; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.useraccount.GetUserAccount; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.auth.CurrentUser; +import ch.ethz.seb.sebserver.gui.widget.Message; +import ch.ethz.seb.sebserver.gui.widget.WidgetFactory; + +@Lazy +@Component +@GuiProfile +/** The form to change an User-Account password. + * If the current user is the owner of the User-Account the password is required and must + * match the users current password. + * If the current user is an administrator that has to reset another users password the + * password that is also required must match the administrators current password. + **/ +public class UserAccountChangePasswordForm implements TemplateComposer { + + private static final String FORM_TITLE_KEY = "sebserver.useraccount.form.pwchange.title"; + private static final LocTextKey FORM_PASSWORD_NEW_TEXT_KEY = + new LocTextKey("sebserver.useraccount.form.password.new"); + private static final LocTextKey FORM_PASSWORD_NEW_CONFIRM_TEXT_KEY = + new LocTextKey("sebserver.useraccount.form.password.new.confirm"); + private static final LocTextKey FORM_PASSWORD_TEXT_KEY = + new LocTextKey("sebserver.useraccount.form.password"); + + private final PageService pageService; + private final RestService restService; + private final CurrentUser currentUser; + private final I18nSupport i18nSupport; + + protected UserAccountChangePasswordForm( + final PageService pageService, + final RestService restService, + final CurrentUser currentUser) { + + this.pageService = pageService; + this.restService = restService; + this.currentUser = currentUser; + this.i18nSupport = pageService.getI18nSupport(); + } + + @Override + public void compose(final PageContext pageContext) { + + final WidgetFactory widgetFactory = this.pageService.getWidgetFactory(); + final EntityKey entityKey = pageContext.getEntityKey(); + + final UserInfo userInfo = this.restService + .getBuilder(GetUserAccount.class) + .withURIVariable(API.PARAM_MODEL_ID, entityKey.modelId) + .call() + .onError(error -> pageContext.notifyLoadError(EntityType.USER, error)) + .getOrThrow(); + + final Composite content = widgetFactory.defaultPageLayout( + pageContext.getParent(), + new LocTextKey(FORM_TITLE_KEY, userInfo.username)); + + final boolean ownAccount = this.currentUser.get().uuid.equals(entityKey.getModelId()); + + // The Password Change form + final FormHandle formHandle = this.pageService.formBuilder(pageContext.copyOf(content)) + .readonly(false) + .putStaticValueIf(() -> entityKey != null, + Domain.USER.ATTR_UUID, + entityKey.getModelId()) + .addField(FormBuilder.text( + PasswordChange.ATTR_NAME_PASSWORD, + FORM_PASSWORD_TEXT_KEY) + .asPasswordField() + .mandatory()) + .addField(FormBuilder.text( + PasswordChange.ATTR_NAME_NEW_PASSWORD, + FORM_PASSWORD_NEW_TEXT_KEY) + .asPasswordField() + .mandatory()) + .addFieldIf( + () -> entityKey != null, + () -> FormBuilder.text( + PasswordChange.ATTR_NAME_CONFIRM_NEW_PASSWORD, + FORM_PASSWORD_NEW_CONFIRM_TEXT_KEY) + .asPasswordField() + .mandatory()) + .buildFor(this.restService.getRestCall(ChangePassword.class)); + + this.pageService.pageActionBuilder(pageContext) + + .newAction(ActionDefinition.USER_ACCOUNT_CHANGE_PASSWORD_SAVE) + .withExec(action -> { + final PageAction saveAction = formHandle.processFormSave(action); + if (ownAccount) { + // NOTE: in this case the user changed the password of the own account + // this should cause an logout with specified message that password change + // was successful and the pointing the need of re login with the new password + this.pageService.logout(pageContext); + final MessageBox error = new Message( + pageContext.getShell(), + this.i18nSupport.getText("sebserver.login.password.change"), + this.i18nSupport.getText("sebserver.login.password.change.success"), + SWT.ICON_INFORMATION, + this.i18nSupport); + error.open(null); + } + return saveAction; + }) + .ignoreMoveAwayFromEdit() + .publish() + + .newAction(ActionDefinition.USER_ACCOUNT_CANCEL_MODIFY) + .withExec(this.pageService.backToCurrentFunction()) + .publish(); + } + +} diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/content/UserAccountForm.java b/src/main/java/ch/ethz/seb/sebserver/gui/content/UserAccountForm.java index caa81a82..708267bf 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/content/UserAccountForm.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/content/UserAccountForm.java @@ -1,279 +1,291 @@ -/* - * 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.content; - -import java.util.function.BooleanSupplier; - -import org.apache.commons.lang3.BooleanUtils; -import org.apache.tomcat.util.buf.StringUtils; -import org.eclipse.swt.widgets.Composite; -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.api.EntityType; -import ch.ethz.seb.sebserver.gbl.api.authorization.Privilege; -import ch.ethz.seb.sebserver.gbl.api.authorization.PrivilegeType; -import ch.ethz.seb.sebserver.gbl.model.Domain; -import ch.ethz.seb.sebserver.gbl.model.Domain.USER_ROLE; -import ch.ethz.seb.sebserver.gbl.model.EntityKey; -import ch.ethz.seb.sebserver.gbl.model.user.PasswordChange; -import ch.ethz.seb.sebserver.gbl.model.user.UserAccount; -import ch.ethz.seb.sebserver.gbl.model.user.UserInfo; -import ch.ethz.seb.sebserver.gbl.model.user.UserMod; -import ch.ethz.seb.sebserver.gbl.model.user.UserRole; -import ch.ethz.seb.sebserver.gbl.profile.GuiProfile; -import ch.ethz.seb.sebserver.gui.content.action.ActionDefinition; -import ch.ethz.seb.sebserver.gui.form.FormBuilder; -import ch.ethz.seb.sebserver.gui.form.FormHandle; -import ch.ethz.seb.sebserver.gui.service.ResourceService; -import ch.ethz.seb.sebserver.gui.service.i18n.LocTextKey; -import ch.ethz.seb.sebserver.gui.service.page.PageContext; -import ch.ethz.seb.sebserver.gui.service.page.PageService; -import ch.ethz.seb.sebserver.gui.service.page.TemplateComposer; -import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestService; -import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.institution.GetInstitution; -import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.useraccount.ActivateUserAccount; -import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.useraccount.DeactivateUserAccount; -import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.useraccount.GetUserAccount; -import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.useraccount.NewUserAccount; -import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.useraccount.SaveUserAccount; -import ch.ethz.seb.sebserver.gui.service.remote.webservice.auth.CurrentUser; -import ch.ethz.seb.sebserver.gui.service.remote.webservice.auth.CurrentUser.EntityGrantCheck; -import ch.ethz.seb.sebserver.gui.widget.WidgetFactory; - -@Lazy -@Component -@GuiProfile -public class UserAccountForm implements TemplateComposer { - - static final LocTextKey TITLE_TEXT_KEY = - new LocTextKey("sebserver.useraccount.form.title"); - static final LocTextKey NEW_TITLE_TEXT_KEY = - new LocTextKey("sebserver.useraccount.form.title.new"); - - static final LocTextKey FORM_PASSWORD_CONFIRM_TEXT_KEY = - new LocTextKey("sebserver.useraccount.form.password.confirm"); - static final LocTextKey FORM_PASSWORD_TEXT_KEY = - new LocTextKey("sebserver.useraccount.form.password"); - static final LocTextKey FORM_ROLES_TEXT_KEY = - new LocTextKey("sebserver.useraccount.form.roles"); - static final LocTextKey FORM_TIMEZONE_TEXT_KEY = - new LocTextKey("sebserver.useraccount.form.timezone"); - static final LocTextKey FORM_MAIL_TEXT_KEY = - new LocTextKey("sebserver.useraccount.form.mail"); - static final LocTextKey FORM_USERNAME_TEXT_KEY = - new LocTextKey("sebserver.useraccount.form.username"); - static final LocTextKey FORM_NAME_TEXT_KEY = - new LocTextKey("sebserver.useraccount.form.name"); - static final LocTextKey FORM_SURNAME_TEXT_KEY = - new LocTextKey("sebserver.useraccount.form.surname"); - static final LocTextKey FORM_INSTITUTION_TEXT_KEY = - new LocTextKey("sebserver.useraccount.form.institution"); - static final LocTextKey FORM_CREATION_DATE_TEXT_KEY = - new LocTextKey("sebserver.useraccount.form.creationdate"); - static final LocTextKey FORM_LANG_TEXT_KEY = - new LocTextKey("sebserver.useraccount.form.language"); - - private final PageService pageService; - private final ResourceService resourceService; - private final boolean multilingual; - - protected UserAccountForm( - final PageService pageService, - final ResourceService resourceService, - @Value("${sebserver.gui.multilingual:false}") final Boolean multilingual) { - - this.pageService = pageService; - this.resourceService = resourceService; - this.multilingual = BooleanUtils.toBoolean(multilingual); - } - - @Override - public void compose(final PageContext pageContext) { - final CurrentUser currentUser = this.resourceService.getCurrentUser(); - final RestService restService = this.resourceService.getRestService(); - final WidgetFactory widgetFactory = this.pageService.getWidgetFactory(); - - final UserInfo user = currentUser.get(); - final EntityKey entityKey = pageContext.getEntityKey(); - final EntityKey parentEntityKey = pageContext.getParentEntityKey(); - final boolean readonly = pageContext.isReadonly(); - - final BooleanSupplier isNew = () -> entityKey == null; - final BooleanSupplier isNotNew = () -> !isNew.getAsBoolean(); - final BooleanSupplier isSEBAdmin = () -> user.hasRole(UserRole.SEB_SERVER_ADMIN); - - // get data or create new. handle error if happen - final UserAccount userAccount = isNew.getAsBoolean() - ? UserMod.createNew((parentEntityKey != null) - ? Long.valueOf(parentEntityKey.modelId) - : user.institutionId) - : restService - .getBuilder(GetUserAccount.class) - .withURIVariable(API.PARAM_MODEL_ID, entityKey.modelId) - .call() - .onError(error -> pageContext.notifyLoadError(EntityType.USER, error)) - .getOrThrow(); - - final boolean roleBasedEditGrant = Privilege.hasRoleBasedUserAccountEditGrant(userAccount, currentUser.get()); - // new PageContext with actual EntityKey - final PageContext formContext = pageContext.withEntityKey(userAccount.getEntityKey()); - final boolean ownAccount = user.uuid.equals(userAccount.getModelId()); - final EntityGrantCheck userGrantCheck = currentUser.entityGrantCheck(userAccount); - - final boolean writeGrant = roleBasedEditGrant && userGrantCheck.w(); - final boolean modifyGrant = roleBasedEditGrant && userGrantCheck.m(); - final boolean institutionalWriteGrant = currentUser.hasInstitutionalPrivilege( - PrivilegeType.WRITE, - EntityType.USER); - - final boolean institutionActive = restService.getBuilder(GetInstitution.class) - .withURIVariable(API.PARAM_MODEL_ID, String.valueOf(userAccount.getInstitutionId())) - .call() - .map(inst -> inst.active) - .getOr(false); - - // the default page layout with title - final LocTextKey titleKey = isNotNew.getAsBoolean() - ? TITLE_TEXT_KEY - : NEW_TITLE_TEXT_KEY; - final Composite content = widgetFactory.defaultPageLayout( - formContext.getParent(), - titleKey); - - // The UserAccount form - final FormHandle formHandle = this.pageService.formBuilder( - formContext.copyOf(content)) - .readonly(readonly) - .putStaticValueIf(isNotNew, - Domain.USER.ATTR_UUID, - userAccount.getModelId()) - .putStaticValueIf(isNotNew, - Domain.USER.ATTR_INSTITUTION_ID, - String.valueOf(userAccount.getInstitutionId())) - .putStaticValueIf( - () -> !this.multilingual, - Domain.USER.ATTR_LANGUAGE, - "en") - .addFieldIf( - isSEBAdmin, - () -> FormBuilder.singleSelection( - Domain.USER.ATTR_INSTITUTION_ID, - FORM_INSTITUTION_TEXT_KEY, - String.valueOf(userAccount.getInstitutionId()), - () -> this.resourceService.institutionResource()) - .readonlyIf(isNotNew)) - .addFieldIf( - () -> readonly, - () -> FormBuilder.text( - Domain.USER.ATTR_CREATION_DATE, - FORM_CREATION_DATE_TEXT_KEY, - this.pageService.getI18nSupport().formatDisplayDate(userAccount.getCreationDate())) - .readonly(true)) - .addField(FormBuilder.text( - Domain.USER.ATTR_NAME, - FORM_NAME_TEXT_KEY, - userAccount.getName())) - .addField(FormBuilder.text( - Domain.USER.ATTR_SURNAME, - FORM_SURNAME_TEXT_KEY, - userAccount.getSurname())) - .addField(FormBuilder.text( - Domain.USER.ATTR_USERNAME, - FORM_USERNAME_TEXT_KEY, - userAccount.getUsername())) - .addField(FormBuilder.text( - Domain.USER.ATTR_EMAIL, - FORM_MAIL_TEXT_KEY, - userAccount.getEmail())) - .addFieldIf( - () -> this.multilingual, - () -> FormBuilder.singleSelection( - Domain.USER.ATTR_LANGUAGE, - FORM_LANG_TEXT_KEY, - userAccount.getLanguage().getLanguage(), - this.resourceService::languageResources)) - .addField(FormBuilder.singleSelection( - Domain.USER.ATTR_TIMEZONE, - FORM_TIMEZONE_TEXT_KEY, - userAccount.getTimeZone().getID(), - this.resourceService::timeZoneResources)) - .addFieldIf( - () -> modifyGrant, - () -> FormBuilder.multiCheckboxSelection( - USER_ROLE.REFERENCE_NAME, - FORM_ROLES_TEXT_KEY, - StringUtils.join(userAccount.getRoles(), Constants.LIST_SEPARATOR_CHAR), - this.resourceService::userRoleResources) - .visibleIf(writeGrant)) - .addFieldIf( - isNew, - () -> FormBuilder.text( - PasswordChange.ATTR_NAME_NEW_PASSWORD, - FORM_PASSWORD_TEXT_KEY) - .asPasswordField()) - .addFieldIf( - isNew, - () -> FormBuilder.text( - PasswordChange.ATTR_NAME_CONFIRM_NEW_PASSWORD, - FORM_PASSWORD_CONFIRM_TEXT_KEY) - .asPasswordField()) - .buildFor((entityKey == null) - ? restService.getRestCall(NewUserAccount.class) - : restService.getRestCall(SaveUserAccount.class)); - - // propagate content actions to action-pane - this.pageService.pageActionBuilder(formContext.clearEntityKeys()) - - .newAction(ActionDefinition.USER_ACCOUNT_NEW) - .publishIf(() -> institutionalWriteGrant && readonly && institutionActive) - - .newAction(ActionDefinition.USER_ACCOUNT_MODIFY) - .withEntityKey(entityKey) - .publishIf(() -> modifyGrant && readonly && institutionActive) - - .newAction(ActionDefinition.USER_ACCOUNT_CHANGE_PASSOWRD) - .withEntityKey(entityKey) - .publishIf(() -> modifyGrant && readonly && institutionActive && userAccount.isActive()) - - .newAction(ActionDefinition.USER_ACCOUNT_DEACTIVATE) - .withEntityKey(entityKey) - .withSimpleRestCall(restService, DeactivateUserAccount.class) - .withConfirm(this.pageService.confirmDeactivation(userAccount)) - .publishIf(() -> writeGrant && readonly && institutionActive && userAccount.isActive()) - - .newAction(ActionDefinition.USER_ACCOUNT_ACTIVATE) - .withEntityKey(entityKey) - .withSimpleRestCall(restService, ActivateUserAccount.class) - .publishIf(() -> writeGrant && readonly && institutionActive && !userAccount.isActive()) - - .newAction(ActionDefinition.USER_ACCOUNT_SAVE) - .withEntityKey(entityKey) - .withExec(action -> { - return formHandle.handleFormPost(formHandle.doAPIPost() - .map(userInfo -> { - if (ownAccount) { - currentUser.refresh(userInfo); - } - return userInfo; - }), - action); - }) - .ignoreMoveAwayFromEdit() - .publishIf(() -> !readonly) - - .newAction(ActionDefinition.USER_ACCOUNT_CANCEL_MODIFY) - .withExec(this.pageService.backToCurrentFunction()) - .publishIf(() -> !readonly); - } - -} +/* + * 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.content; + +import java.util.function.BooleanSupplier; + +import org.apache.commons.lang3.BooleanUtils; +import org.apache.tomcat.util.buf.StringUtils; +import org.eclipse.swt.widgets.Composite; +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.api.EntityType; +import ch.ethz.seb.sebserver.gbl.api.authorization.Privilege; +import ch.ethz.seb.sebserver.gbl.api.authorization.PrivilegeType; +import ch.ethz.seb.sebserver.gbl.model.Domain; +import ch.ethz.seb.sebserver.gbl.model.Domain.USER_ROLE; +import ch.ethz.seb.sebserver.gbl.model.EntityKey; +import ch.ethz.seb.sebserver.gbl.model.user.PasswordChange; +import ch.ethz.seb.sebserver.gbl.model.user.UserAccount; +import ch.ethz.seb.sebserver.gbl.model.user.UserInfo; +import ch.ethz.seb.sebserver.gbl.model.user.UserMod; +import ch.ethz.seb.sebserver.gbl.model.user.UserRole; +import ch.ethz.seb.sebserver.gbl.profile.GuiProfile; +import ch.ethz.seb.sebserver.gui.content.action.ActionDefinition; +import ch.ethz.seb.sebserver.gui.form.FormBuilder; +import ch.ethz.seb.sebserver.gui.form.FormHandle; +import ch.ethz.seb.sebserver.gui.service.ResourceService; +import ch.ethz.seb.sebserver.gui.service.i18n.LocTextKey; +import ch.ethz.seb.sebserver.gui.service.page.PageContext; +import ch.ethz.seb.sebserver.gui.service.page.PageService; +import ch.ethz.seb.sebserver.gui.service.page.TemplateComposer; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestService; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.institution.GetInstitution; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.useraccount.ActivateUserAccount; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.useraccount.DeactivateUserAccount; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.useraccount.GetUserAccount; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.useraccount.NewUserAccount; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.useraccount.SaveUserAccount; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.auth.CurrentUser; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.auth.CurrentUser.EntityGrantCheck; +import ch.ethz.seb.sebserver.gui.widget.WidgetFactory; + +@Lazy +@Component +@GuiProfile +public class UserAccountForm implements TemplateComposer { + + static final LocTextKey TITLE_TEXT_KEY = + new LocTextKey("sebserver.useraccount.form.title"); + static final LocTextKey NEW_TITLE_TEXT_KEY = + new LocTextKey("sebserver.useraccount.form.title.new"); + + static final LocTextKey FORM_PASSWORD_CONFIRM_TEXT_KEY = + new LocTextKey("sebserver.useraccount.form.password.confirm"); + static final LocTextKey FORM_PASSWORD_TEXT_KEY = + new LocTextKey("sebserver.useraccount.form.password"); + static final LocTextKey FORM_ROLES_TEXT_KEY = + new LocTextKey("sebserver.useraccount.form.roles"); + static final LocTextKey FORM_TIMEZONE_TEXT_KEY = + new LocTextKey("sebserver.useraccount.form.timezone"); + static final LocTextKey FORM_MAIL_TEXT_KEY = + new LocTextKey("sebserver.useraccount.form.mail"); + static final LocTextKey FORM_USERNAME_TEXT_KEY = + new LocTextKey("sebserver.useraccount.form.username"); + static final LocTextKey FORM_NAME_TEXT_KEY = + new LocTextKey("sebserver.useraccount.form.name"); + static final LocTextKey FORM_SURNAME_TEXT_KEY = + new LocTextKey("sebserver.useraccount.form.surname"); + static final LocTextKey FORM_INSTITUTION_TEXT_KEY = + new LocTextKey("sebserver.useraccount.form.institution"); + static final LocTextKey FORM_CREATION_DATE_TEXT_KEY = + new LocTextKey("sebserver.useraccount.form.creationdate"); + static final LocTextKey FORM_LANG_TEXT_KEY = + new LocTextKey("sebserver.useraccount.form.language"); + + private final PageService pageService; + private final ResourceService resourceService; + private final boolean multilingual; + + protected UserAccountForm( + final PageService pageService, + final ResourceService resourceService, + @Value("${sebserver.gui.multilingual:false}") final Boolean multilingual) { + + this.pageService = pageService; + this.resourceService = resourceService; + this.multilingual = BooleanUtils.toBoolean(multilingual); + } + + @Override + public void compose(final PageContext pageContext) { + final CurrentUser currentUser = this.resourceService.getCurrentUser(); + final RestService restService = this.resourceService.getRestService(); + final WidgetFactory widgetFactory = this.pageService.getWidgetFactory(); + + final UserInfo user = currentUser.get(); + final EntityKey entityKey = pageContext.getEntityKey(); + final EntityKey parentEntityKey = pageContext.getParentEntityKey(); + final boolean readonly = pageContext.isReadonly(); + + final BooleanSupplier isNew = () -> entityKey == null; + final BooleanSupplier isNotNew = () -> !isNew.getAsBoolean(); + final BooleanSupplier isSEBAdmin = () -> user.hasRole(UserRole.SEB_SERVER_ADMIN); + + // get data or create new. handle error if happen + final UserAccount userAccount = isNew.getAsBoolean() + ? UserMod.createNew((parentEntityKey != null) + ? Long.valueOf(parentEntityKey.modelId) + : currentUser.get().institutionId) + : restService + .getBuilder(GetUserAccount.class) + .withURIVariable(API.PARAM_MODEL_ID, entityKey.modelId) + .call() + .onError(error -> pageContext.notifyLoadError(EntityType.USER, error)) + .getOrThrow(); + + final boolean roleBasedEditGrant = Privilege.hasRoleBasedUserAccountEditGrant(userAccount, currentUser.get()); + // new PageContext with actual EntityKey + final PageContext formContext = pageContext.withEntityKey(userAccount.getEntityKey()); + final boolean ownAccount = user.uuid.equals(userAccount.getModelId()); + final EntityGrantCheck userGrantCheck = currentUser.entityGrantCheck(userAccount); + + final boolean writeGrant = roleBasedEditGrant && userGrantCheck.w(); + final boolean modifyGrant = roleBasedEditGrant && userGrantCheck.m(); + final boolean institutionalWriteGrant = currentUser.hasInstitutionalPrivilege( + PrivilegeType.WRITE, + EntityType.USER); + + final boolean institutionActive = userAccount.getInstitutionId() != null && + restService.getBuilder(GetInstitution.class) + .withURIVariable(API.PARAM_MODEL_ID, String.valueOf(userAccount.getInstitutionId())) + .call() + .map(inst -> inst.active) + .getOr(false); + + // the default page layout with title + final LocTextKey titleKey = isNotNew.getAsBoolean() + ? TITLE_TEXT_KEY + : NEW_TITLE_TEXT_KEY; + final Composite content = widgetFactory.defaultPageLayout( + formContext.getParent(), + titleKey); + + // The UserAccount form + final FormHandle formHandle = this.pageService.formBuilder( + formContext.copyOf(content)) + .readonly(readonly) + .putStaticValueIf(isNotNew, + Domain.USER.ATTR_UUID, + userAccount.getModelId()) + .putStaticValueIf(isNotNew, + Domain.USER.ATTR_INSTITUTION_ID, + String.valueOf(userAccount.getInstitutionId())) + .putStaticValueIf( + () -> !this.multilingual, + Domain.USER.ATTR_LANGUAGE, + "en") + .addFieldIf( + isSEBAdmin, + () -> FormBuilder.singleSelection( + Domain.USER.ATTR_INSTITUTION_ID, + FORM_INSTITUTION_TEXT_KEY, + String.valueOf(userAccount.getInstitutionId()), + this.resourceService::institutionResource) + .readonlyIf(isNotNew) + .mandatory(!readonly)) + .addFieldIf( + () -> readonly, + () -> FormBuilder.text( + Domain.USER.ATTR_CREATION_DATE, + FORM_CREATION_DATE_TEXT_KEY, + this.pageService.getI18nSupport().formatDisplayDate(userAccount.getCreationDate())) + .readonly(true)) + .addField(FormBuilder.text( + Domain.USER.ATTR_NAME, + FORM_NAME_TEXT_KEY, + userAccount.getName()) + .mandatory(!readonly)) + .addField(FormBuilder.text( + Domain.USER.ATTR_SURNAME, + FORM_SURNAME_TEXT_KEY, + userAccount.getSurname()) + .mandatory(!readonly)) + .addField(FormBuilder.text( + Domain.USER.ATTR_USERNAME, + FORM_USERNAME_TEXT_KEY, + userAccount.getUsername()) + .mandatory(!readonly)) + .addField(FormBuilder.text( + Domain.USER.ATTR_EMAIL, + FORM_MAIL_TEXT_KEY, + userAccount.getEmail())) + .addFieldIf( + () -> this.multilingual, + () -> FormBuilder.singleSelection( + Domain.USER.ATTR_LANGUAGE, + FORM_LANG_TEXT_KEY, + userAccount.getLanguage().getLanguage(), + this.resourceService::languageResources)) + .addField(FormBuilder.singleSelection( + Domain.USER.ATTR_TIMEZONE, + FORM_TIMEZONE_TEXT_KEY, + userAccount.getTimeZone().getID(), + this.resourceService::timeZoneResources) + .mandatory(!readonly)) + .addFieldIf( + () -> modifyGrant, + () -> FormBuilder.multiCheckboxSelection( + USER_ROLE.REFERENCE_NAME, + FORM_ROLES_TEXT_KEY, + StringUtils.join(userAccount.getRoles(), Constants.LIST_SEPARATOR_CHAR), + this.resourceService::userRoleResources) + .visibleIf(writeGrant) + .mandatory(!readonly)) + .addFieldIf( + isNew, + () -> FormBuilder.text( + PasswordChange.ATTR_NAME_NEW_PASSWORD, + FORM_PASSWORD_TEXT_KEY) + .asPasswordField() + .mandatory(!readonly)) + .addFieldIf( + isNew, + () -> FormBuilder.text( + PasswordChange.ATTR_NAME_CONFIRM_NEW_PASSWORD, + FORM_PASSWORD_CONFIRM_TEXT_KEY) + .asPasswordField() + .mandatory(!readonly)) + .buildFor((entityKey == null) + ? restService.getRestCall(NewUserAccount.class) + : restService.getRestCall(SaveUserAccount.class)); + + // propagate content actions to action-pane + this.pageService.pageActionBuilder(formContext.clearEntityKeys()) + + .newAction(ActionDefinition.USER_ACCOUNT_NEW) + .publishIf(() -> institutionalWriteGrant && readonly && institutionActive) + + .newAction(ActionDefinition.USER_ACCOUNT_MODIFY) + .withEntityKey(entityKey) + .publishIf(() -> modifyGrant && readonly && institutionActive) + + .newAction(ActionDefinition.USER_ACCOUNT_CHANGE_PASSWORD) + .withEntityKey(entityKey) + .publishIf(() -> modifyGrant && readonly && institutionActive && userAccount.isActive()) + + .newAction(ActionDefinition.USER_ACCOUNT_DEACTIVATE) + .withEntityKey(entityKey) + .withSimpleRestCall(restService, DeactivateUserAccount.class) + .withConfirm(this.pageService.confirmDeactivation(userAccount)) + .publishIf(() -> writeGrant && readonly && institutionActive && userAccount.isActive()) + + .newAction(ActionDefinition.USER_ACCOUNT_ACTIVATE) + .withEntityKey(entityKey) + .withSimpleRestCall(restService, ActivateUserAccount.class) + .publishIf(() -> writeGrant && readonly && institutionActive && !userAccount.isActive()) + + .newAction(ActionDefinition.USER_ACCOUNT_SAVE) + .withEntityKey(entityKey) + .withExec(action -> formHandle.handleFormPost(formHandle.doAPIPost() + .map(userInfo -> { + if (ownAccount) { + currentUser.refresh(userInfo); + } + return userInfo; + }), + action)) + .ignoreMoveAwayFromEdit() + .publishIf(() -> !readonly) + + .newAction(ActionDefinition.USER_ACCOUNT_SAVE_AND_ACTIVATE) + .withEntityKey(entityKey) + .withExec(formHandle::saveAndActivate) + .ignoreMoveAwayFromEdit() + .publishIf(() -> !readonly && !ownAccount && !userAccount.isActive()) + + .newAction(ActionDefinition.USER_ACCOUNT_CANCEL_MODIFY) + .withExec(this.pageService.backToCurrentFunction()) + .publishIf(() -> !readonly); + } +} diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/content/UserAccountList.java b/src/main/java/ch/ethz/seb/sebserver/gui/content/UserAccountList.java index a8a68cbc..5e4d60e8 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/content/UserAccountList.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/content/UserAccountList.java @@ -1,236 +1,250 @@ -/* - * 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.content; - -import java.util.function.BooleanSupplier; -import java.util.function.Function; - -import org.apache.commons.lang3.BooleanUtils; -import org.eclipse.swt.widgets.Composite; -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.api.EntityType; -import ch.ethz.seb.sebserver.gbl.api.authorization.Privilege; -import ch.ethz.seb.sebserver.gbl.model.Domain; -import ch.ethz.seb.sebserver.gbl.model.Entity; -import ch.ethz.seb.sebserver.gbl.model.user.UserInfo; -import ch.ethz.seb.sebserver.gbl.model.user.UserRole; -import ch.ethz.seb.sebserver.gbl.profile.GuiProfile; -import ch.ethz.seb.sebserver.gui.content.action.ActionDefinition; -import ch.ethz.seb.sebserver.gui.service.ResourceService; -import ch.ethz.seb.sebserver.gui.service.i18n.LocTextKey; -import ch.ethz.seb.sebserver.gui.service.page.PageContext; -import ch.ethz.seb.sebserver.gui.service.page.PageMessageException; -import ch.ethz.seb.sebserver.gui.service.page.PageService; -import ch.ethz.seb.sebserver.gui.service.page.PageService.PageActionBuilder; -import ch.ethz.seb.sebserver.gui.service.page.TemplateComposer; -import ch.ethz.seb.sebserver.gui.service.page.impl.PageAction; -import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestService; -import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.useraccount.GetUserAccount; -import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.useraccount.GetUserAccountPage; -import ch.ethz.seb.sebserver.gui.service.remote.webservice.auth.CurrentUser; -import ch.ethz.seb.sebserver.gui.service.remote.webservice.auth.CurrentUser.GrantCheck; -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; - -@Lazy -@Component -@GuiProfile -public class UserAccountList implements TemplateComposer { - - // localized text keys - private static final LocTextKey EMPTY_TEXT_KEY = - new LocTextKey("sebserver.useraccount.list.empty"); - private static final LocTextKey INSTITUTION_TEXT_KEY = - new LocTextKey("sebserver.useraccount.list.column.institution"); - private static final LocTextKey EMPTY_SELECTION_TEXT_KEY = - new LocTextKey("sebserver.useraccount.info.pleaseSelect"); - private static final LocTextKey ACTIVE_TEXT_KEY = - new LocTextKey("sebserver.useraccount.list.column.active"); - private static final LocTextKey LANG_TEXT_KEY = - new LocTextKey("sebserver.useraccount.list.column.language"); - private static final LocTextKey MAIL_TEXT_KEY = - new LocTextKey("sebserver.useraccount.list.column.email"); - private static final LocTextKey USER_NAME_TEXT_KEY = - new LocTextKey("sebserver.useraccount.list.column.username"); - private static final LocTextKey NAME_TEXT_KEY = - new LocTextKey("sebserver.useraccount.list.column.name"); - private static final LocTextKey TITLE_TEXT_KEY = - new LocTextKey("sebserver.useraccount.list.title"); - private static final LocTextKey NO_EDIT_RIGHT_MESSAGE = - new LocTextKey("sebserver.useraccount.info.notEditable"); - - // filter attribute models - private final TableFilterAttribute institutionFilter; - private final TableFilterAttribute nameFilter = - new TableFilterAttribute(CriteriaType.TEXT, Entity.FILTER_ATTR_NAME); - private final TableFilterAttribute usernameFilter = - new TableFilterAttribute(CriteriaType.TEXT, UserInfo.FILTER_ATTR_USER_NAME); - private final TableFilterAttribute mailFilter = - new TableFilterAttribute(CriteriaType.TEXT, UserInfo.FILTER_ATTR_EMAIL); - private final TableFilterAttribute languageFilter; - private final TableFilterAttribute activityFilter; - - // dependencies - private final PageService pageService; - private final ResourceService resourceService; - private final int pageSize; - private final boolean multilingual; - - protected UserAccountList( - final PageService pageService, - final ResourceService resourceService, - @Value("${sebserver.gui.list.page.size:20}") final Integer pageSize, - @Value("${sebserver.gui.multilingual:false}") final Boolean ml) { - - this.pageService = pageService; - this.resourceService = resourceService; - this.pageSize = pageSize; - this.multilingual = BooleanUtils.isTrue(ml); - - this.institutionFilter = new TableFilterAttribute( - CriteriaType.SINGLE_SELECTION, - Entity.FILTER_ATTR_INSTITUTION, - this.resourceService::institutionResource); - - this.languageFilter = new TableFilterAttribute( - CriteriaType.SINGLE_SELECTION, - UserInfo.FILTER_ATTR_LANGUAGE, - this.resourceService::languageResources); - - this.activityFilter = new TableFilterAttribute( - CriteriaType.SINGLE_SELECTION, - UserInfo.FILTER_ATTR_ACTIVE, - this.resourceService::activityResources); - } - - @Override - public void compose(final PageContext pageContext) { - final WidgetFactory widgetFactory = this.pageService.getWidgetFactory(); - final CurrentUser currentUser = this.resourceService.getCurrentUser(); - final RestService restService = this.resourceService.getRestService(); - // content page layout with title - final Composite content = widgetFactory.defaultPageLayout( - pageContext.getParent(), - TITLE_TEXT_KEY); - - final BooleanSupplier isSEBAdmin = () -> currentUser.get().hasRole(UserRole.SEB_SERVER_ADMIN); - final PageActionBuilder actionBuilder = this.pageService.pageActionBuilder(pageContext.clearEntityKeys()); - - // table - final EntityTable table = this.pageService.entityTableBuilder( - restService.getRestCall(GetUserAccountPage.class)) - .withEmptyMessage(EMPTY_TEXT_KEY) - .withPaging(this.pageSize) - - .withColumnIf( - isSEBAdmin, - () -> new ColumnDefinition<>( - Domain.USER.ATTR_INSTITUTION_ID, - INSTITUTION_TEXT_KEY, - userInstitutionNameFunction(this.resourceService)) - .withFilter(this.institutionFilter) - .widthProportion(2)) - - .withColumn(new ColumnDefinition<>( - Domain.USER.ATTR_NAME, - NAME_TEXT_KEY, - UserInfo::getName) - .withFilter(this.nameFilter) - .sortable() - .widthProportion(2)) - - .withColumn(new ColumnDefinition<>( - Domain.USER.ATTR_USERNAME, - USER_NAME_TEXT_KEY, - UserInfo::getUsername) - .withFilter(this.usernameFilter) - .sortable() - .widthProportion(2)) - - .withColumn(new ColumnDefinition<>( - Domain.USER.ATTR_EMAIL, - MAIL_TEXT_KEY, - UserInfo::getEmail) - .withFilter(this.mailFilter) - .sortable() - .widthProportion(3)) - - .withColumnIf(() -> this.multilingual, - () -> new ColumnDefinition<>( - Domain.USER.ATTR_LANGUAGE, - LANG_TEXT_KEY, - this::getLocaleDisplayText) - .withFilter(this.languageFilter) - .localized() - .sortable() - .widthProportion(1)) - - .withColumn(new ColumnDefinition<>( - Domain.USER.ATTR_ACTIVE, - ACTIVE_TEXT_KEY, - this.pageService.getResourceService(). localizedActivityFunction()) - .sortable() - .withFilter(this.activityFilter) - .widthProportion(1)) - - .withDefaultAction(actionBuilder - .newAction(ActionDefinition.USER_ACCOUNT_VIEW_FROM_LIST) - .create()) - .compose(pageContext.copyOf(content)); - - // propagate content actions to action-pane - final GrantCheck userGrant = currentUser.grantCheck(EntityType.USER); - actionBuilder - - .newAction(ActionDefinition.USER_ACCOUNT_NEW) - .publishIf(userGrant::iw) - - .newAction(ActionDefinition.USER_ACCOUNT_VIEW_FROM_LIST) - .withSelect(table::getSelection, PageAction::applySingleSelectionAsEntityKey, EMPTY_SELECTION_TEXT_KEY) - .publishIf(() -> table.hasAnyContent()) - - .newAction(ActionDefinition.USER_ACCOUNT_MODIFY_FROM_LIST) - .withSelect(table::getSelection, this::editAction, EMPTY_SELECTION_TEXT_KEY) - .publishIf(() -> userGrant.im() && table.hasAnyContent()); - } - - private PageAction editAction(final PageAction pageAction) { - if (!this.resourceService.getRestService() - .getBuilder(GetUserAccount.class) - .withURIVariable(API.PARAM_MODEL_ID, pageAction.getSingleSelection().modelId) - .call() - .map(user -> Privilege.hasRoleBasedUserAccountEditGrant(user, - this.resourceService.getCurrentUser().get())) - .getOr(false)) { - throw new PageMessageException(NO_EDIT_RIGHT_MESSAGE); - } - - return PageAction.applySingleSelectionAsEntityKey(pageAction); - } - - private String getLocaleDisplayText(final UserInfo userInfo) { - return (userInfo.language != null) - ? userInfo.language.getDisplayLanguage(this.pageService.getI18nSupport().getUsersLanguageLocale()) - : Constants.EMPTY_NOTE; - } - - private static Function userInstitutionNameFunction(final ResourceService resourceService) { - final Function institutionNameFunction = resourceService.getInstitutionNameFunction(); - return userInfo -> institutionNameFunction.apply(String.valueOf(userInfo.institutionId)); - } - -} +/* + * 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.content; + +import ch.ethz.seb.sebserver.gbl.Constants; +import ch.ethz.seb.sebserver.gbl.api.API; +import ch.ethz.seb.sebserver.gbl.api.EntityType; +import ch.ethz.seb.sebserver.gbl.api.authorization.Privilege; +import ch.ethz.seb.sebserver.gbl.model.Domain; +import ch.ethz.seb.sebserver.gbl.model.Entity; +import ch.ethz.seb.sebserver.gbl.model.user.UserInfo; +import ch.ethz.seb.sebserver.gbl.model.user.UserRole; +import ch.ethz.seb.sebserver.gbl.profile.GuiProfile; +import ch.ethz.seb.sebserver.gui.content.action.ActionDefinition; +import ch.ethz.seb.sebserver.gui.service.ResourceService; +import ch.ethz.seb.sebserver.gui.service.i18n.LocTextKey; +import ch.ethz.seb.sebserver.gui.service.page.PageContext; +import ch.ethz.seb.sebserver.gui.service.page.PageMessageException; +import ch.ethz.seb.sebserver.gui.service.page.PageService; +import ch.ethz.seb.sebserver.gui.service.page.PageService.PageActionBuilder; +import ch.ethz.seb.sebserver.gui.service.page.TemplateComposer; +import ch.ethz.seb.sebserver.gui.service.page.impl.PageAction; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestService; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.useraccount.GetUserAccount; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.useraccount.GetUserAccountPage; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.auth.CurrentUser; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.auth.CurrentUser.GrantCheck; +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 org.apache.commons.lang3.BooleanUtils; +import org.eclipse.swt.widgets.Composite; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Lazy; +import org.springframework.stereotype.Component; + +import java.util.function.BooleanSupplier; +import java.util.function.Function; + +@Lazy +@Component +@GuiProfile +public class UserAccountList implements TemplateComposer { + + // localized text keys + private static final LocTextKey EMPTY_TEXT_KEY = + new LocTextKey("sebserver.useraccount.list.empty"); + private static final LocTextKey INSTITUTION_TEXT_KEY = + new LocTextKey("sebserver.useraccount.list.column.institution"); + private static final LocTextKey EMPTY_SELECTION_TEXT_KEY = + new LocTextKey("sebserver.useraccount.info.pleaseSelect"); + private static final LocTextKey ACTIVE_TEXT_KEY = + new LocTextKey("sebserver.useraccount.list.column.active"); + private static final LocTextKey LANG_TEXT_KEY = + new LocTextKey("sebserver.useraccount.list.column.language"); + private static final LocTextKey MAIL_TEXT_KEY = + new LocTextKey("sebserver.useraccount.list.column.email"); + private static final LocTextKey USER_NAME_TEXT_KEY = + new LocTextKey("sebserver.useraccount.list.column.username"); + private static final LocTextKey NAME_TEXT_KEY = + new LocTextKey("sebserver.useraccount.list.column.name"); + private static final LocTextKey TITLE_TEXT_KEY = + new LocTextKey("sebserver.useraccount.list.title"); + private static final LocTextKey NO_EDIT_RIGHT_MESSAGE = + new LocTextKey("sebserver.useraccount.info.notEditable"); + + // filter attribute models + private final TableFilterAttribute institutionFilter; + private final TableFilterAttribute nameFilter = + new TableFilterAttribute(CriteriaType.TEXT, Entity.FILTER_ATTR_NAME); + private final TableFilterAttribute usernameFilter = + new TableFilterAttribute(CriteriaType.TEXT, UserInfo.FILTER_ATTR_USER_NAME); + private final TableFilterAttribute mailFilter = + new TableFilterAttribute(CriteriaType.TEXT, UserInfo.FILTER_ATTR_EMAIL); + private final TableFilterAttribute languageFilter; + private final TableFilterAttribute activityFilter; + + // dependencies + private final PageService pageService; + private final ResourceService resourceService; + private final int pageSize; + private final boolean multilingual; + + protected UserAccountList( + final PageService pageService, + final ResourceService resourceService, + @Value("${sebserver.gui.list.page.size:20}") final Integer pageSize, + @Value("${sebserver.gui.multilingual:false}") final Boolean ml) { + + this.pageService = pageService; + this.resourceService = resourceService; + this.pageSize = pageSize; + this.multilingual = BooleanUtils.isTrue(ml); + + this.institutionFilter = new TableFilterAttribute( + CriteriaType.SINGLE_SELECTION, + Entity.FILTER_ATTR_INSTITUTION, + this.resourceService::institutionResource); + + this.languageFilter = new TableFilterAttribute( + CriteriaType.SINGLE_SELECTION, + UserInfo.FILTER_ATTR_LANGUAGE, + this.resourceService::languageResources); + + this.activityFilter = new TableFilterAttribute( + CriteriaType.SINGLE_SELECTION, + UserInfo.FILTER_ATTR_ACTIVE, + this.resourceService::activityResources); + } + + @Override + public void compose(final PageContext pageContext) { + final WidgetFactory widgetFactory = this.pageService.getWidgetFactory(); + final CurrentUser currentUser = this.resourceService.getCurrentUser(); + final RestService restService = this.resourceService.getRestService(); + // content page layout with title + final Composite content = widgetFactory.defaultPageLayout( + pageContext.getParent(), + TITLE_TEXT_KEY); + + final BooleanSupplier isSEBAdmin = () -> currentUser.get().hasRole(UserRole.SEB_SERVER_ADMIN); + final PageActionBuilder actionBuilder = this.pageService.pageActionBuilder(pageContext.clearEntityKeys()); + + // table + final EntityTable table = this.pageService.entityTableBuilder( + restService.getRestCall(GetUserAccountPage.class)) + .withEmptyMessage(EMPTY_TEXT_KEY) + .withPaging(this.pageSize) + + .withColumnIf( + isSEBAdmin, + () -> new ColumnDefinition<>( + Domain.USER.ATTR_INSTITUTION_ID, + INSTITUTION_TEXT_KEY, + userInstitutionNameFunction(this.resourceService)) + .withFilter(this.institutionFilter) + .widthProportion(2)) + + .withColumn(new ColumnDefinition<>( + Domain.USER.ATTR_NAME, + NAME_TEXT_KEY, + UserInfo::getName) + .withFilter(this.nameFilter) + .sortable() + .widthProportion(2)) + + .withColumn(new ColumnDefinition<>( + Domain.USER.ATTR_USERNAME, + USER_NAME_TEXT_KEY, + UserInfo::getUsername) + .withFilter(this.usernameFilter) + .sortable() + .widthProportion(2)) + + .withColumn(new ColumnDefinition<>( + Domain.USER.ATTR_EMAIL, + MAIL_TEXT_KEY, + UserInfo::getEmail) + .withFilter(this.mailFilter) + .sortable() + .widthProportion(3)) + + .withColumnIf(() -> this.multilingual, + () -> new ColumnDefinition<>( + Domain.USER.ATTR_LANGUAGE, + LANG_TEXT_KEY, + this::getLocaleDisplayText) + .withFilter(this.languageFilter) + .localized() + .sortable() + .widthProportion(1)) + + .withColumn(new ColumnDefinition<>( + Domain.USER.ATTR_ACTIVE, + ACTIVE_TEXT_KEY, + this.pageService.getResourceService(). localizedActivityFunction()) + .sortable() + .withFilter(this.activityFilter) + .widthProportion(1)) + + .withDefaultAction(actionBuilder + .newAction(ActionDefinition.USER_ACCOUNT_VIEW_FROM_LIST) + .create()) + + .withSelectionListener(this.pageService.getSelectionPublisher( + ActionDefinition.USER_ACCOUNT_TOGGLE_ACTIVITY, + ActionDefinition.USER_ACCOUNT_ACTIVATE, + ActionDefinition.USER_ACCOUNT_DEACTIVATE, + pageContext, + ActionDefinition.USER_ACCOUNT_VIEW_FROM_LIST, + ActionDefinition.USER_ACCOUNT_MODIFY_FROM_LIST, + ActionDefinition.USER_ACCOUNT_TOGGLE_ACTIVITY)) + + .compose(pageContext.copyOf(content)); + + // propagate content actions to action-pane + final GrantCheck userGrant = currentUser.grantCheck(EntityType.USER); + actionBuilder + + .newAction(ActionDefinition.USER_ACCOUNT_NEW) + .publishIf(userGrant::iw) + + .newAction(ActionDefinition.USER_ACCOUNT_VIEW_FROM_LIST) + .withSelect(table::getSelection, PageAction::applySingleSelectionAsEntityKey, EMPTY_SELECTION_TEXT_KEY) + .publishIf(table::hasAnyContent, false) + + .newAction(ActionDefinition.USER_ACCOUNT_MODIFY_FROM_LIST) + .withSelect(table::getSelection, this::editAction, EMPTY_SELECTION_TEXT_KEY) + .publishIf(() -> userGrant.im() && table.hasAnyContent(), false) + + .newAction(ActionDefinition.USER_ACCOUNT_TOGGLE_ACTIVITY) + .withExec(this.pageService.activationToggleActionFunction(table, EMPTY_SELECTION_TEXT_KEY)) + .withConfirm(this.pageService.confirmDeactivation(table)) + .publishIf(() -> userGrant.m() && table.hasAnyContent(), false); + } + + private PageAction editAction(final PageAction pageAction) { + if (!this.resourceService.getRestService() + .getBuilder(GetUserAccount.class) + .withURIVariable(API.PARAM_MODEL_ID, pageAction.getSingleSelection().modelId) + .call() + .map(user -> Privilege.hasRoleBasedUserAccountEditGrant(user, + this.resourceService.getCurrentUser().get())) + .getOr(false)) { + throw new PageMessageException(NO_EDIT_RIGHT_MESSAGE); + } + + return PageAction.applySingleSelectionAsEntityKey(pageAction); + } + + private String getLocaleDisplayText(final UserInfo userInfo) { + return (userInfo.language != null) + ? userInfo.language.getDisplayLanguage(this.pageService.getI18nSupport().getUsersLanguageLocale()) + : Constants.EMPTY_NOTE; + } + + private static Function userInstitutionNameFunction(final ResourceService resourceService) { + final Function institutionNameFunction = resourceService.getInstitutionNameFunction(); + return userInfo -> institutionNameFunction.apply(String.valueOf(userInfo.institutionId)); + } + +} diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/content/UserActivityLogs.java b/src/main/java/ch/ethz/seb/sebserver/gui/content/UserActivityLogs.java index 5529e9c7..26d6db09 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/content/UserActivityLogs.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/content/UserActivityLogs.java @@ -1,276 +1,279 @@ -/* - * 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.content; - -import java.util.function.BooleanSupplier; -import java.util.function.Function; - -import org.eclipse.swt.widgets.Composite; -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.Entity; -import ch.ethz.seb.sebserver.gbl.model.user.UserActivityLog; -import ch.ethz.seb.sebserver.gbl.model.user.UserInfo; -import ch.ethz.seb.sebserver.gbl.model.user.UserRole; -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.logs.GetUserLogPage; -import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.useraccount.GetUserAccount; -import ch.ethz.seb.sebserver.gui.service.remote.webservice.auth.CurrentUser; -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; - -@Lazy -@Component -@GuiProfile -public class UserActivityLogs implements TemplateComposer { - - private static final LocTextKey DETAILS_TITLE_TEXT_KEY = - new LocTextKey("sebserver.userlogs.details.title"); - private static final LocTextKey TITLE_TEXT_KEY = - new LocTextKey("sebserver.userlogs.list.title"); - private static final LocTextKey EMPTY_TEXT_KEY = - new LocTextKey("sebserver.userlogs.list.empty"); - private static final LocTextKey INSTITUTION_TEXT_KEY = - new LocTextKey("sebserver.userlogs.list.column.institution"); - private static final LocTextKey USER_TEXT_KEY = - new LocTextKey("sebserver.userlogs.list.column.user"); - private static final LocTextKey DATE_TEXT_KEY = - new LocTextKey("sebserver.userlogs.list.column.dateTime"); - private static final LocTextKey DETAILS_DATE_TEXT_KEY = - new LocTextKey("sebserver.seblogs.details.dateTime"); - private static final LocTextKey ACTIVITY_TEXT_KEY = - new LocTextKey("sebserver.userlogs.list.column.activityType"); - 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 = - new LocTextKey("sebserver.userlogs.info.pleaseSelect"); - - private final TableFilterAttribute institutionFilter; - private final TableFilterAttribute userNameFilter = - new TableFilterAttribute(CriteriaType.TEXT, UserActivityLog.FILTER_ATTR_USER_NAME); - private final TableFilterAttribute activityFilter; - private final TableFilterAttribute entityFilter; - - private final PageService pageService; - private final ResourceService resourceService; - private final I18nSupport i18nSupport; - private final WidgetFactory widgetFactory; - private final int pageSize; - - public UserActivityLogs( - final PageService pageService, - @Value("${sebserver.gui.list.page.size:20}") final Integer pageSize) { - - this.pageService = pageService; - this.resourceService = pageService.getResourceService(); - this.i18nSupport = this.resourceService.getI18nSupport(); - this.widgetFactory = pageService.getWidgetFactory(); - this.pageSize = pageSize; - - this.institutionFilter = new TableFilterAttribute( - CriteriaType.SINGLE_SELECTION, - Entity.FILTER_ATTR_INSTITUTION, - this.resourceService::institutionResource); - - this.activityFilter = new TableFilterAttribute( - CriteriaType.SINGLE_SELECTION, - UserActivityLog.FILTER_ATTR_ACTIVITY_TYPES, - this.resourceService::userActivityTypeResources); - - this.entityFilter = new TableFilterAttribute( - CriteriaType.SINGLE_SELECTION, - UserActivityLog.FILTER_ATTR_ENTITY_TYPES, - this.resourceService::entityTypeResources); - } - - @Override - public void compose(final PageContext pageContext) { - final CurrentUser currentUser = this.resourceService.getCurrentUser(); - 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()); - - final BooleanSupplier isSebAdmin = - () -> currentUser.get().hasRole(UserRole.SEB_SERVER_ADMIN); - - final Function institutionNameFunction = - this.resourceService.getInstitutionNameFunction() - .compose(log -> { - try { - final UserInfo user = restService.getBuilder(GetUserAccount.class) - .withURIVariable(API.PARAM_MODEL_ID, log.getUserUuid()) - .call().getOrThrow(); - return String.valueOf(user.getInstitutionId()); - } catch (final Exception e) { - return Constants.EMPTY_NOTE; - } - }); - - // table - final EntityTable table = this.pageService.entityTableBuilder( - restService.getRestCall(GetUserLogPage.class)) - .withEmptyMessage(EMPTY_TEXT_KEY) - .withPaging(this.pageSize) - - .withColumnIf( - isSebAdmin, - () -> new ColumnDefinition<>( - UserActivityLog.FILTER_ATTR_INSTITUTION, - INSTITUTION_TEXT_KEY, - institutionNameFunction) - .withFilter(this.institutionFilter)) - - .withColumn(new ColumnDefinition<>( - UserActivityLog.ATTR_USER_NAME, - USER_TEXT_KEY, - UserActivityLog::getUsername) - .withFilter(this.userNameFilter)) - - .withColumn(new ColumnDefinition( - Domain.USER_ACTIVITY_LOG.ATTR_ACTIVITY_TYPE, - ACTIVITY_TEXT_KEY, - this.resourceService::getUserActivityTypeName) - .withFilter(this.activityFilter) - .sortable()) - - .withColumn(new ColumnDefinition( - Domain.USER_ACTIVITY_LOG.ATTR_ENTITY_ID, - ENTITY_TYPE_TEXT_KEY, - this.resourceService::getEntityTypeName) - .withFilter(this.entityFilter) - .sortable()) - - .withColumn(new ColumnDefinition<>( - Domain.USER_ACTIVITY_LOG.ATTR_TIMESTAMP, - new LocTextKey(DATE_TEXT_KEY.name, this.i18nSupport.getUsersTimeZoneTitleSuffix()), - this::getLogTime) - .withFilter(new TableFilterAttribute( - CriteriaType.DATE_RANGE, - UserActivityLog.FILTER_ATTR_FROM_TO, - Utils.toDateTimeUTC(Utils.getMillisecondsNow()) - .minusYears(1) - .toString())) - .sortable()) - - .withDefaultAction(t -> actionBuilder - .newAction(ActionDefinition.LOGS_USER_ACTIVITY_SHOW_DETAILS) - .withExec(action -> this.showDetails(action, t.getSingleSelectedROWData())) - .noEventPropagation() - .create()) - - .compose(pageContext.copyOf(content)); - - actionBuilder - .newAction(ActionDefinition.LOGS_USER_ACTIVITY_SHOW_DETAILS) - .withSelect( - table::getSelection, - action -> this.showDetails(action, table.getSingleSelectedROWData()), - EMPTY_SELECTION_TEXT) - .noEventPropagation() - .publishIf(table::hasAnyContent); - - } - - private final String getLogTime(final UserActivityLog log) { - if (log == null || log.timestamp == null) { - return Constants.EMPTY_NOTE; - } - - return this.i18nSupport - .formatDisplayDateTime(Utils.toDateTimeUTC(log.timestamp)); - } - - private PageAction showDetails(final PageAction action, final UserActivityLog userActivityLog) { - action.getSingleSelection(); - - final ModalInputDialog dialog = new ModalInputDialog<>( - action.pageContext().getParent().getShell(), - this.widgetFactory); - dialog.setLargeDialogWidth(); - dialog.open( - DETAILS_TITLE_TEXT_KEY, - action.pageContext(), - pc -> createDetailsForm(userActivityLog, pc)); - - return action; - } - - private void createDetailsForm(final UserActivityLog userActivityLog, final PageContext pc) { - - final Composite parent = pc.getParent(); - final Composite grid = this.widgetFactory.createPopupScrollComposite(parent); - - this.pageService.formBuilder(pc.copyOf(grid)) - .withDefaultSpanInput(6) - .withEmptyCellSeparation(false) - .readonly(true) - .addField(FormBuilder.text( - UserActivityLog.ATTR_USER_NAME, - USER_TEXT_KEY, - String.valueOf(userActivityLog.getUsername()))) - .addField(FormBuilder.text( - Domain.USER_ACTIVITY_LOG.ATTR_ACTIVITY_TYPE, - ACTIVITY_TEXT_KEY, - this.resourceService.getUserActivityTypeName(userActivityLog))) - .addField(FormBuilder.text( - 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, - DETAILS_DATE_TEXT_KEY, - this.widgetFactory.getI18nSupport() - .formatDisplayDateTime(Utils.toDateTimeUTC(userActivityLog.timestamp)) + " " + - this.i18nSupport.getUsersTimeZoneTitleSuffix())) - .addField(FormBuilder.text( - Domain.USER_ACTIVITY_LOG.ATTR_MESSAGE, - MESSAGE_TEXT_KEY, - String.valueOf(userActivityLog.message)) - .asArea()) - .build(); - } - -} +/* + * 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.content; + +import java.util.function.BooleanSupplier; +import java.util.function.Function; + +import org.eclipse.swt.widgets.Composite; +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.Entity; +import ch.ethz.seb.sebserver.gbl.model.user.UserActivityLog; +import ch.ethz.seb.sebserver.gbl.model.user.UserInfo; +import ch.ethz.seb.sebserver.gbl.model.user.UserRole; +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.logs.GetUserLogPage; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.useraccount.GetUserAccount; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.auth.CurrentUser; +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; + +@Lazy +@Component +@GuiProfile +public class UserActivityLogs implements TemplateComposer { + + private static final LocTextKey DETAILS_TITLE_TEXT_KEY = + new LocTextKey("sebserver.userlogs.details.title"); + private static final LocTextKey TITLE_TEXT_KEY = + new LocTextKey("sebserver.userlogs.list.title"); + private static final LocTextKey EMPTY_TEXT_KEY = + new LocTextKey("sebserver.userlogs.list.empty"); + private static final LocTextKey INSTITUTION_TEXT_KEY = + new LocTextKey("sebserver.userlogs.list.column.institution"); + private static final LocTextKey USER_TEXT_KEY = + new LocTextKey("sebserver.userlogs.list.column.user"); + private static final LocTextKey DATE_TEXT_KEY = + new LocTextKey("sebserver.userlogs.list.column.dateTime"); + private static final LocTextKey DETAILS_DATE_TEXT_KEY = + new LocTextKey("sebserver.seblogs.details.dateTime"); + private static final LocTextKey ACTIVITY_TEXT_KEY = + new LocTextKey("sebserver.userlogs.list.column.activityType"); + 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 = + new LocTextKey("sebserver.userlogs.info.pleaseSelect"); + + private final TableFilterAttribute institutionFilter; + private final TableFilterAttribute userNameFilter = + new TableFilterAttribute(CriteriaType.TEXT, UserActivityLog.FILTER_ATTR_USER_NAME); + private final TableFilterAttribute activityFilter; + private final TableFilterAttribute entityFilter; + + private final PageService pageService; + private final ResourceService resourceService; + private final I18nSupport i18nSupport; + private final WidgetFactory widgetFactory; + private final int pageSize; + + public UserActivityLogs( + final PageService pageService, + @Value("${sebserver.gui.list.page.size:20}") final Integer pageSize) { + + this.pageService = pageService; + this.resourceService = pageService.getResourceService(); + this.i18nSupport = this.resourceService.getI18nSupport(); + this.widgetFactory = pageService.getWidgetFactory(); + this.pageSize = pageSize; + + this.institutionFilter = new TableFilterAttribute( + CriteriaType.SINGLE_SELECTION, + Entity.FILTER_ATTR_INSTITUTION, + this.resourceService::institutionResource); + + this.activityFilter = new TableFilterAttribute( + CriteriaType.SINGLE_SELECTION, + UserActivityLog.FILTER_ATTR_ACTIVITY_TYPES, + this.resourceService::userActivityTypeResources); + + this.entityFilter = new TableFilterAttribute( + CriteriaType.SINGLE_SELECTION, + UserActivityLog.FILTER_ATTR_ENTITY_TYPES, + this.resourceService::entityTypeResources); + } + + @Override + public void compose(final PageContext pageContext) { + final CurrentUser currentUser = this.resourceService.getCurrentUser(); + 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()); + + final BooleanSupplier isSebAdmin = + () -> currentUser.get().hasRole(UserRole.SEB_SERVER_ADMIN); + + final Function institutionNameFunction = + this.resourceService.getInstitutionNameFunction() + .compose(log -> { + try { + final UserInfo user = restService.getBuilder(GetUserAccount.class) + .withURIVariable(API.PARAM_MODEL_ID, log.getUserUuid()) + .call().getOrThrow(); + return String.valueOf(user.getInstitutionId()); + } catch (final Exception e) { + return Constants.EMPTY_NOTE; + } + }); + + // table + final EntityTable table = this.pageService.entityTableBuilder( + restService.getRestCall(GetUserLogPage.class)) + .withEmptyMessage(EMPTY_TEXT_KEY) + .withPaging(this.pageSize) + + .withColumnIf( + isSebAdmin, + () -> new ColumnDefinition<>( + UserActivityLog.FILTER_ATTR_INSTITUTION, + INSTITUTION_TEXT_KEY, + institutionNameFunction) + .withFilter(this.institutionFilter)) + + .withColumn(new ColumnDefinition<>( + UserActivityLog.ATTR_USER_NAME, + USER_TEXT_KEY, + UserActivityLog::getUsername) + .withFilter(this.userNameFilter)) + + .withColumn(new ColumnDefinition( + Domain.USER_ACTIVITY_LOG.ATTR_ACTIVITY_TYPE, + ACTIVITY_TEXT_KEY, + this.resourceService::getUserActivityTypeName) + .withFilter(this.activityFilter) + .sortable()) + + .withColumn(new ColumnDefinition( + Domain.USER_ACTIVITY_LOG.ATTR_ENTITY_ID, + ENTITY_TYPE_TEXT_KEY, + this.resourceService::getEntityTypeName) + .withFilter(this.entityFilter) + .sortable()) + + .withColumn(new ColumnDefinition<>( + Domain.USER_ACTIVITY_LOG.ATTR_TIMESTAMP, + new LocTextKey(DATE_TEXT_KEY.name, this.i18nSupport.getUsersTimeZoneTitleSuffix()), + this::getLogTime) + .withFilter(new TableFilterAttribute( + CriteriaType.DATE_RANGE, + UserActivityLog.FILTER_ATTR_FROM_TO, + Utils.toDateTimeUTC(Utils.getMillisecondsNow()) + .minusYears(1) + .toString())) + .sortable()) + + .withDefaultAction(t -> actionBuilder + .newAction(ActionDefinition.LOGS_USER_ACTIVITY_SHOW_DETAILS) + .withExec(action -> this.showDetails(action, t.getSingleSelectedROWData())) + .noEventPropagation() + .create()) + + .withSelectionListener(this.pageService.getSelectionPublisher( + pageContext, + ActionDefinition.LOGS_USER_ACTIVITY_SHOW_DETAILS)) + .compose(pageContext.copyOf(content)); + + actionBuilder + .newAction(ActionDefinition.LOGS_USER_ACTIVITY_SHOW_DETAILS) + .withSelect( + table::getSelection, + action -> this.showDetails(action, table.getSingleSelectedROWData()), + EMPTY_SELECTION_TEXT) + .noEventPropagation() + .publishIf(table::hasAnyContent, false); + + } + + private String getLogTime(final UserActivityLog log) { + if (log == null || log.timestamp == null) { + return Constants.EMPTY_NOTE; + } + + return this.i18nSupport + .formatDisplayDateTime(Utils.toDateTimeUTC(log.timestamp)); + } + + private PageAction showDetails(final PageAction action, final UserActivityLog userActivityLog) { + action.getSingleSelection(); + + final ModalInputDialog dialog = new ModalInputDialog<>( + action.pageContext().getParent().getShell(), + this.widgetFactory); + dialog.setLargeDialogWidth(); + dialog.open( + DETAILS_TITLE_TEXT_KEY, + action.pageContext(), + pc -> createDetailsForm(userActivityLog, pc)); + + return action; + } + + private void createDetailsForm(final UserActivityLog userActivityLog, final PageContext pc) { + + final Composite parent = pc.getParent(); + final Composite grid = this.widgetFactory.createPopupScrollComposite(parent); + + this.pageService.formBuilder(pc.copyOf(grid)) + .withDefaultSpanInput(6) + .withEmptyCellSeparation(false) + .readonly(true) + .addField(FormBuilder.text( + UserActivityLog.ATTR_USER_NAME, + USER_TEXT_KEY, + String.valueOf(userActivityLog.getUsername()))) + .addField(FormBuilder.text( + Domain.USER_ACTIVITY_LOG.ATTR_ACTIVITY_TYPE, + ACTIVITY_TEXT_KEY, + this.resourceService.getUserActivityTypeName(userActivityLog))) + .addField(FormBuilder.text( + 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, + DETAILS_DATE_TEXT_KEY, + this.widgetFactory.getI18nSupport() + .formatDisplayDateTime(Utils.toDateTimeUTC(userActivityLog.timestamp)) + " " + + this.i18nSupport.getUsersTimeZoneTitleSuffix())) + .addField(FormBuilder.text( + Domain.USER_ACTIVITY_LOG.ATTR_MESSAGE, + MESSAGE_TEXT_KEY, + String.valueOf(userActivityLog.message)) + .asArea()) + .build(); + } + +} diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/content/action/ActionDefinition.java b/src/main/java/ch/ethz/seb/sebserver/gui/content/action/ActionDefinition.java index 5af58e3b..d625df8d 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/content/action/ActionDefinition.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/content/action/ActionDefinition.java @@ -1,672 +1,690 @@ -/* - * Copyright (c) 2018 ETH Zürich, Educational Development and Technology (LET) - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. - */ - -package ch.ethz.seb.sebserver.gui.content.action; - -import ch.ethz.seb.sebserver.gui.content.activity.PageStateDefinitionImpl; -import ch.ethz.seb.sebserver.gui.service.i18n.LocTextKey; -import ch.ethz.seb.sebserver.gui.service.page.PageStateDefinition; -import ch.ethz.seb.sebserver.gui.widget.WidgetFactory.ImageIcon; - -/** Enumeration of static action data for each action within the SEB Server GUI */ -public enum ActionDefinition { - - INSTITUTION_VIEW_LIST( - new LocTextKey("sebserver.institution.action.list"), - PageStateDefinitionImpl.INSTITUTION_LIST), - INSTITUTION_VIEW_FORM( - new LocTextKey("sebserver.institution.action.form"), - PageStateDefinitionImpl.INSTITUTION_VIEW), - INSTITUTION_NEW( - new LocTextKey("sebserver.institution.action.new"), - ImageIcon.INSTITUTION, - PageStateDefinitionImpl.INSTITUTION_EDIT, - ActionCategory.FORM), - INSTITUTION_VIEW_FROM_LIST( - new LocTextKey("sebserver.institution.action.list.view"), - ImageIcon.SHOW, - PageStateDefinitionImpl.INSTITUTION_VIEW, - ActionCategory.INSTITUTION_LIST), - INSTITUTION_MODIFY_FROM_LIST( - new LocTextKey("sebserver.institution.action.list.modify"), - ImageIcon.EDIT, - PageStateDefinitionImpl.INSTITUTION_EDIT, - ActionCategory.INSTITUTION_LIST), - - INSTITUTION_MODIFY( - new LocTextKey("sebserver.institution.action.modify"), - ImageIcon.EDIT, - PageStateDefinitionImpl.INSTITUTION_EDIT, - ActionCategory.FORM), - INSTITUTION_CANCEL_MODIFY( - new LocTextKey("sebserver.overall.action.modify.cancel"), - ImageIcon.CANCEL, - PageStateDefinitionImpl.INSTITUTION_VIEW, - ActionCategory.FORM), - INSTITUTION_SAVE( - new LocTextKey("sebserver.institution.action.save"), - ImageIcon.SAVE, - PageStateDefinitionImpl.INSTITUTION_VIEW, - ActionCategory.FORM), - INSTITUTION_SAVE_AND_ACTIVATE( - new LocTextKey("sebserver.form.action.save.activate"), - ImageIcon.ACTIVE, - PageStateDefinitionImpl.INSTITUTION_VIEW, - ActionCategory.FORM), - INSTITUTION_ACTIVATE( - new LocTextKey("sebserver.institution.action.activate"), - ImageIcon.TOGGLE_OFF, - PageStateDefinitionImpl.INSTITUTION_VIEW, - ActionCategory.FORM), - INSTITUTION_DEACTIVATE( - new LocTextKey("sebserver.institution.action.deactivate"), - ImageIcon.TOGGLE_ON, - PageStateDefinitionImpl.INSTITUTION_VIEW, - ActionCategory.FORM), - INSTITUTION_TOGGLE_ACTIVITY( - new LocTextKey("sebserver.overall.action.toggle-activity"), - ImageIcon.SWITCH, - PageStateDefinitionImpl.INSTITUTION_LIST, - ActionCategory.INSTITUTION_LIST), - - USER_ACCOUNT_VIEW_LIST( - new LocTextKey("sebserver.useraccount.action.list"), - PageStateDefinitionImpl.USER_ACCOUNT_LIST), - USER_ACCOUNT_VIEW_FORM( - new LocTextKey("sebserver.useraccount.action.form"), - PageStateDefinitionImpl.USER_ACCOUNT_VIEW), - USER_ACCOUNT_NEW( - new LocTextKey("sebserver.useraccount.action.new"), - ImageIcon.USER, - PageStateDefinitionImpl.USER_ACCOUNT_EDIT), - USER_ACCOUNT_VIEW_FROM_LIST( - new LocTextKey("sebserver.useraccount.action.view"), - ImageIcon.SHOW, - PageStateDefinitionImpl.USER_ACCOUNT_VIEW, - ActionCategory.USER_ACCOUNT_LIST), - USER_ACCOUNT_MODIFY_FROM_LIST( - new LocTextKey("sebserver.useraccount.action.list.modify"), - ImageIcon.EDIT, - PageStateDefinitionImpl.USER_ACCOUNT_EDIT, - ActionCategory.USER_ACCOUNT_LIST), - USER_ACCOUNT_MODIFY( - new LocTextKey("sebserver.useraccount.action.modify"), - ImageIcon.EDIT, - PageStateDefinitionImpl.USER_ACCOUNT_EDIT, - ActionCategory.FORM), - USER_ACCOUNT_CANCEL_MODIFY( - new LocTextKey("sebserver.overall.action.modify.cancel"), - ImageIcon.CANCEL, - PageStateDefinitionImpl.USER_ACCOUNT_VIEW, - ActionCategory.FORM), - USER_ACCOUNT_SAVE( - new LocTextKey("sebserver.useraccount.action.save"), - ImageIcon.SAVE, - PageStateDefinitionImpl.USER_ACCOUNT_VIEW, - ActionCategory.FORM), - USER_ACCOUNT_ACTIVATE( - new LocTextKey("sebserver.useraccount.action.activate"), - ImageIcon.TOGGLE_OFF, - PageStateDefinitionImpl.USER_ACCOUNT_VIEW, - ActionCategory.FORM), - USER_ACCOUNT_DEACTIVATE( - new LocTextKey("sebserver.useraccount.action.deactivate"), - ImageIcon.TOGGLE_ON, - PageStateDefinitionImpl.USER_ACCOUNT_VIEW, - ActionCategory.FORM), - USER_ACCOUNT_CHANGE_PASSOWRD( - new LocTextKey("sebserver.useraccount.action.change.password"), - ImageIcon.SECURE, - PageStateDefinitionImpl.USER_ACCOUNT_PASSWORD_CHANGE, - ActionCategory.FORM), - USER_ACCOUNT_CHANGE_PASSOWRD_SAVE( - new LocTextKey("sebserver.useraccount.action.change.password.save"), - ImageIcon.SAVE, - PageStateDefinitionImpl.USER_ACCOUNT_VIEW, - ActionCategory.FORM), - - LMS_SETUP_VIEW_LIST( - new LocTextKey("sebserver.lmssetup.action.list"), - PageStateDefinitionImpl.LMS_SETUP_LIST), - LMS_SETUP_NEW( - new LocTextKey("sebserver.lmssetup.action.new"), - ImageIcon.LMS_SETUP, - PageStateDefinitionImpl.LMS_SETUP_EDIT), - LMS_SETUP_VIEW_FROM_LIST( - new LocTextKey("sebserver.lmssetup.action.list.view"), - ImageIcon.SHOW, - PageStateDefinitionImpl.LMS_SETUP_VIEW, - ActionCategory.LMS_SETUP_LIST), - LMS_SETUP_MODIFY_FROM_LIST( - new LocTextKey("sebserver.lmssetup.action.list.modify"), - ImageIcon.EDIT, - PageStateDefinitionImpl.LMS_SETUP_EDIT, - ActionCategory.LMS_SETUP_LIST), - LMS_SETUP_MODIFY( - new LocTextKey("sebserver.lmssetup.action.modify"), - ImageIcon.EDIT, - PageStateDefinitionImpl.LMS_SETUP_EDIT, - ActionCategory.FORM), - LMS_SETUP_SAVE_AND_TEST( - new LocTextKey("sebserver.lmssetup.action.savetest"), - ImageIcon.TEST, - PageStateDefinitionImpl.LMS_SETUP_VIEW, - ActionCategory.FORM), - LMS_SETUP_TEST_AND_SAVE( - new LocTextKey("sebserver.lmssetup.action.testsave"), - ImageIcon.TEST, - PageStateDefinitionImpl.LMS_SETUP_VIEW, - ActionCategory.FORM), - LMS_SETUP_CANCEL_MODIFY( - new LocTextKey("sebserver.overall.action.modify.cancel"), - ImageIcon.CANCEL, - PageStateDefinitionImpl.LMS_SETUP_VIEW, - ActionCategory.FORM), - LMS_SETUP_SAVE( - new LocTextKey("sebserver.lmssetup.action.save"), - ImageIcon.SAVE, - PageStateDefinitionImpl.LMS_SETUP_VIEW, - ActionCategory.FORM), - LMS_SETUP_ACTIVATE( - new LocTextKey("sebserver.lmssetup.action.activate"), - ImageIcon.TOGGLE_OFF, - PageStateDefinitionImpl.LMS_SETUP_VIEW, - ActionCategory.FORM), - LMS_SETUP_DEACTIVATE( - new LocTextKey("sebserver.lmssetup.action.deactivate"), - ImageIcon.TOGGLE_ON, - PageStateDefinitionImpl.LMS_SETUP_VIEW, - ActionCategory.FORM), - - QUIZ_DISCOVERY_VIEW_LIST( - new LocTextKey("sebserver.quizdiscovery.action.list"), - PageStateDefinitionImpl.QUIZ_LIST), - QUIZ_DISCOVERY_SHOW_DETAILS( - new LocTextKey("sebserver.quizdiscovery.action.details"), - ImageIcon.SHOW, - ActionCategory.QUIZ_LIST), - QUIZ_DISCOVERY_EXAM_IMPORT( - new LocTextKey("sebserver.quizdiscovery.action.import"), - ImageIcon.IMPORT, - PageStateDefinitionImpl.EXAM_EDIT, - ActionCategory.QUIZ_LIST), - - EXAM_VIEW_LIST( - new LocTextKey("sebserver.exam.action.list"), - PageStateDefinitionImpl.EXAM_LIST), - EXAM_IMPORT( - new LocTextKey("sebserver.exam.action.import"), - ImageIcon.IMPORT, - PageStateDefinitionImpl.QUIZ_LIST), - EXAM_VIEW_FROM_LIST( - new LocTextKey("sebserver.exam.action.list.view"), - ImageIcon.SHOW, - PageStateDefinitionImpl.EXAM_VIEW, - ActionCategory.EXAM_LIST), - EXAM_MODIFY_FROM_LIST( - new LocTextKey("sebserver.exam.action.list.modify"), - ImageIcon.EDIT, - PageStateDefinitionImpl.EXAM_EDIT, - ActionCategory.EXAM_LIST), - EXAM_MODIFY( - new LocTextKey("sebserver.exam.action.modify"), - ImageIcon.EDIT, - PageStateDefinitionImpl.EXAM_EDIT, - ActionCategory.FORM), - EXAM_CANCEL_MODIFY( - new LocTextKey("sebserver.overall.action.modify.cancel"), - ImageIcon.CANCEL, - PageStateDefinitionImpl.EXAM_VIEW, - ActionCategory.FORM), - EXAM_SAVE( - new LocTextKey("sebserver.exam.action.save"), - ImageIcon.SAVE, - PageStateDefinitionImpl.EXAM_VIEW, - ActionCategory.FORM), - EXAM_ACTIVATE( - new LocTextKey("sebserver.exam.action.activate"), - ImageIcon.TOGGLE_OFF, - PageStateDefinitionImpl.EXAM_VIEW, - ActionCategory.FORM), - EXAM_DEACTIVATE( - new LocTextKey("sebserver.exam.action.deactivate"), - ImageIcon.TOGGLE_ON, - PageStateDefinitionImpl.EXAM_VIEW, - ActionCategory.FORM), - - EXAM_MODIFY_SEB_RESTRICTION_DETAILS( - new LocTextKey("sebserver.exam.action.sebrestriction.details"), - ImageIcon.RESTRICTION, - PageStateDefinitionImpl.EXAM_VIEW, - ActionCategory.FORM), - EXAM_ENABLE_SEB_RESTRICTION( - new LocTextKey("sebserver.exam.action.sebrestriction.enable"), - ImageIcon.UNLOCK, - PageStateDefinitionImpl.EXAM_VIEW, - ActionCategory.FORM), - EXAM_DISABLE_SEB_RESTRICTION( - new LocTextKey("sebserver.exam.action.sebrestriction.disable"), - ImageIcon.LOCK, - PageStateDefinitionImpl.EXAM_VIEW, - ActionCategory.FORM), - - EXAM_CONFIGURATION_NEW( - new LocTextKey("sebserver.exam.configuration.action.list.new"), - ImageIcon.NEW, - PageStateDefinitionImpl.EXAM_VIEW, - ActionCategory.EXAM_CONFIG_MAPPING_LIST), - EXAM_CONFIGURATION_MODIFY_FROM_LIST( - new LocTextKey("sebserver.exam.configuration.action.list.modify"), - ImageIcon.EDIT, - PageStateDefinitionImpl.EXAM_VIEW, - ActionCategory.EXAM_CONFIG_MAPPING_LIST), - EXAM_CONFIGURATION_EXAM_CONFIG_VIEW_PROP( - new LocTextKey("sebserver.exam.configuration.action.list.view"), - ImageIcon.SHOW, - PageStateDefinitionImpl.SEB_EXAM_CONFIG_PROP_VIEW, - ActionCategory.EXAM_CONFIG_MAPPING_LIST), - EXAM_CONFIGURATION_DELETE_FROM_LIST( - new LocTextKey("sebserver.exam.configuration.action.list.delete"), - ImageIcon.DELETE, - PageStateDefinitionImpl.EXAM_VIEW, - ActionCategory.EXAM_CONFIG_MAPPING_LIST), - EXAM_CONFIGURATION_EXPORT( - new LocTextKey("sebserver.exam.configuration.action.export-config"), - ImageIcon.EXPORT, - PageStateDefinitionImpl.EXAM_VIEW, - ActionCategory.EXAM_CONFIG_MAPPING_LIST), - EXAM_CONFIGURATION_GET_CONFIG_KEY( - new LocTextKey("sebserver.exam.configuration.action.get-config-key"), - ImageIcon.SECURE, - ActionCategory.EXAM_CONFIG_MAPPING_LIST), - EXAM_CONFIGURATION_SAVE( - new LocTextKey("sebserver.exam.configuration.action.save"), - ImageIcon.SAVE, - PageStateDefinitionImpl.EXAM_VIEW, - ActionCategory.FORM), - EXAM_CONFIGURATION_CANCEL_MODIFY( - new LocTextKey("sebserver.overall.action.modify.cancel"), - ImageIcon.CANCEL, - PageStateDefinitionImpl.EXAM_VIEW, - ActionCategory.FORM), - - EXAM_INDICATOR_NEW( - new LocTextKey("sebserver.exam.indicator.action.list.new"), - ImageIcon.INDICATOR, - PageStateDefinitionImpl.INDICATOR_EDIT, - ActionCategory.INDICATOR_LIST), - EXAM_INDICATOR_MODIFY_FROM_LIST( - new LocTextKey("sebserver.exam.indicator.action.list.modify"), - ImageIcon.EDIT, - PageStateDefinitionImpl.INDICATOR_EDIT, - ActionCategory.INDICATOR_LIST), - EXAM_INDICATOR_DELETE_FROM_LIST( - new LocTextKey("sebserver.exam.indicator.action.list.delete"), - ImageIcon.DELETE, - PageStateDefinitionImpl.EXAM_VIEW, - ActionCategory.INDICATOR_LIST), - EXAM_INDICATOR_SAVE( - new LocTextKey("sebserver.exam.indicator.action.save"), - ImageIcon.SAVE, - PageStateDefinitionImpl.EXAM_VIEW, - ActionCategory.FORM), - EXAM_INDICATOR_CANCEL_MODIFY( - new LocTextKey("sebserver.overall.action.modify.cancel"), - ImageIcon.CANCEL, - PageStateDefinitionImpl.EXAM_VIEW, - ActionCategory.FORM), - - SEB_CLIENT_CONFIG_LIST( - new LocTextKey("sebserver.clientconfig.action.list"), - PageStateDefinitionImpl.SEB_CLIENT_CONFIG_LIST), - SEB_CLIENT_CONFIG_NEW( - new LocTextKey("sebserver.clientconfig.action.list.new"), - ImageIcon.NEW, - PageStateDefinitionImpl.SEB_CLIENT_CONFIG_EDIT), - SEB_CLIENT_CONFIG_VIEW_FROM_LIST( - new LocTextKey("sebserver.clientconfig.action.list.view"), - ImageIcon.SHOW, - PageStateDefinitionImpl.SEB_CLIENT_CONFIG_VIEW, - ActionCategory.SEB_CLIENT_CONFIG_LIST), - SEB_CLIENT_CONFIG_MODIFY_FROM_LIST( - new LocTextKey("sebserver.clientconfig.action.list.modify"), - ImageIcon.EDIT, - PageStateDefinitionImpl.SEB_CLIENT_CONFIG_EDIT, - ActionCategory.SEB_CLIENT_CONFIG_LIST), - SEB_CLIENT_CONFIG_MODIFY( - new LocTextKey("sebserver.clientconfig.action.modify"), - ImageIcon.EDIT, - PageStateDefinitionImpl.SEB_CLIENT_CONFIG_EDIT, - ActionCategory.FORM), - SEB_CLIENT_CONFIG_CANCEL_MODIFY( - new LocTextKey("sebserver.overall.action.modify.cancel"), - ImageIcon.CANCEL, - PageStateDefinitionImpl.SEB_CLIENT_CONFIG_VIEW, - ActionCategory.FORM), - SEB_CLIENT_CONFIG_SAVE( - new LocTextKey("sebserver.clientconfig.action.save"), - ImageIcon.SAVE, - PageStateDefinitionImpl.SEB_CLIENT_CONFIG_VIEW, - ActionCategory.FORM), - SEB_CLIENT_CONFIG_ACTIVATE( - new LocTextKey("sebserver.clientconfig.action.activate"), - ImageIcon.TOGGLE_OFF, - PageStateDefinitionImpl.SEB_CLIENT_CONFIG_VIEW, - ActionCategory.FORM), - SEB_CLIENT_CONFIG_DEACTIVATE( - new LocTextKey("sebserver.clientconfig.action.deactivate"), - ImageIcon.TOGGLE_ON, - PageStateDefinitionImpl.SEB_CLIENT_CONFIG_VIEW, - ActionCategory.FORM), - SEB_CLIENT_CONFIG_EXPORT( - new LocTextKey("sebserver.clientconfig.action.export"), - ImageIcon.EXPORT, - PageStateDefinitionImpl.SEB_CLIENT_CONFIG_VIEW, - ActionCategory.FORM), - - SEB_EXAM_CONFIG_LIST( - new LocTextKey("sebserver.examconfig.action.list"), - PageStateDefinitionImpl.SEB_EXAM_CONFIG_LIST), - SEB_EXAM_CONFIG_NEW( - new LocTextKey("sebserver.examconfig.action.list.new"), - ImageIcon.NEW, - PageStateDefinitionImpl.SEB_EXAM_CONFIG_PROP_EDIT), - SEB_EXAM_CONFIG_VIEW_PROP_FROM_LIST( - new LocTextKey("sebserver.examconfig.action.list.view"), - ImageIcon.SHOW, - PageStateDefinitionImpl.SEB_EXAM_CONFIG_PROP_VIEW, - ActionCategory.SEB_EXAM_CONFIG_LIST), - SEB_EXAM_CONFIG_VIEW_PROP( - new LocTextKey("sebserver.examconfig.action.view.properties"), - ImageIcon.SHOW, - PageStateDefinitionImpl.SEB_EXAM_CONFIG_PROP_VIEW, - ActionCategory.FORM), - SEB_EXAM_CONFIG_IMPORT_TO_NEW_CONFIG( - new LocTextKey("sebserver.examconfig.action.import-config"), - ImageIcon.IMPORT, - PageStateDefinitionImpl.SEB_EXAM_CONFIG_LIST, - ActionCategory.VARIA), - - SEB_EXAM_CONFIG_MODIFY_PROP_FROM_LIST( - new LocTextKey("sebserver.examconfig.action.list.modify.properties"), - ImageIcon.EDIT, - PageStateDefinitionImpl.SEB_EXAM_CONFIG_PROP_EDIT, - ActionCategory.SEB_EXAM_CONFIG_LIST), - SEB_EXAM_CONFIG_PROP_MODIFY( - new LocTextKey("sebserver.examconfig.action.modify.properties"), - ImageIcon.EDIT, - PageStateDefinitionImpl.SEB_EXAM_CONFIG_PROP_EDIT, - ActionCategory.FORM), - SEB_EXAM_CONFIG_MODIFY( - new LocTextKey("sebserver.examconfig.action.modify"), - ImageIcon.EDIT_SETTINGS, - PageStateDefinitionImpl.SEB_EXAM_CONFIG_EDIT, - ActionCategory.FORM), - SEB_EXAM_CONFIG_VIEW( - new LocTextKey("sebserver.examconfig.action.view"), - ImageIcon.EDIT_SETTINGS, - PageStateDefinitionImpl.SEB_EXAM_CONFIG_VIEW, - ActionCategory.FORM), - SEB_EXAM_CONFIG_PROP_CANCEL_MODIFY( - new LocTextKey("sebserver.overall.action.modify.cancel"), - ImageIcon.CANCEL, - PageStateDefinitionImpl.SEB_EXAM_CONFIG_PROP_VIEW, - ActionCategory.FORM), - SEB_EXAM_CONFIG_PROP_SAVE( - new LocTextKey("sebserver.examconfig.action.save"), - ImageIcon.SAVE, - PageStateDefinitionImpl.SEB_EXAM_CONFIG_PROP_VIEW, - ActionCategory.FORM), - SEB_EXAM_CONFIG_EXPORT_PLAIN_XML( - new LocTextKey("sebserver.examconfig.action.export.plainxml"), - ImageIcon.EXPORT, - ActionCategory.FORM), - SEB_EXAM_CONFIG_GET_CONFIG_KEY( - new LocTextKey("sebserver.examconfig.action.get-config-key"), - ImageIcon.SECURE, - ActionCategory.FORM), - SEB_EXAM_CONFIG_IMPORT_TO_EXISTING_CONFIG( - new LocTextKey("sebserver.examconfig.action.import-config"), - ImageIcon.IMPORT, - ActionCategory.FORM), - - SEB_EXAM_CONFIG_COPY_CONFIG( - new LocTextKey("sebserver.examconfig.action.copy"), - ImageIcon.COPY, - ActionCategory.FORM), - SEB_EXAM_CONFIG_COPY_CONFIG_AS_TEMPALTE( - new LocTextKey("sebserver.examconfig.action.copy-as-template"), - ImageIcon.TEMPLATE, - ActionCategory.FORM), - - SEB_EXAM_CONFIG_MODIFY_FROM_LIST( - new LocTextKey("sebserver.examconfig.action.list.modify"), - ImageIcon.EDIT_SETTINGS, - PageStateDefinitionImpl.SEB_EXAM_CONFIG_EDIT, - ActionCategory.SEB_EXAM_CONFIG_LIST), - - SEB_EXAM_CONFIG_SAVE_TO_HISTORY( - new LocTextKey("sebserver.examconfig.action.saveToHistory"), - ImageIcon.SAVE, - PageStateDefinitionImpl.SEB_EXAM_CONFIG_EDIT, - ActionCategory.FORM), - SEB_EXAM_CONFIG_UNDO( - new LocTextKey("sebserver.examconfig.action.undo"), - ImageIcon.UNDO, - PageStateDefinitionImpl.SEB_EXAM_CONFIG_EDIT, - ActionCategory.FORM), - - SEB_EXAM_CONFIG_TEMPLATE_LIST( - new LocTextKey("sebserver.configtemplate.action.list"), - PageStateDefinitionImpl.SEB_EXAM_CONFIG_TEMPLATE_LIST), - SEB_EXAM_CONFIG_TEMPLATE_NEW( - new LocTextKey("sebserver.configtemplate.action.list.new"), - ImageIcon.TEMPLATE, - PageStateDefinitionImpl.SEB_EXAM_CONFIG_TEMPLATE_EDIT, - ActionCategory.VARIA), - SEB_EXAM_CONFIG_TEMPLATE_VIEW_FROM_LIST( - new LocTextKey("sebserver.configtemplate.action.list.view"), - ImageIcon.SHOW, - PageStateDefinitionImpl.SEB_EXAM_CONFIG_TEMPLATE_VIEW, - ActionCategory.SEB_CONFIG_TEMPLATE_LIST), - SEB_EXAM_CONFIG_TEMPLATE_VIEW( - new LocTextKey("sebserver.configtemplate.action.view"), - ImageIcon.SHOW, - PageStateDefinitionImpl.SEB_EXAM_CONFIG_TEMPLATE_VIEW, - ActionCategory.FORM), - SEB_EXAM_CONFIG_TEMPLATE_MODIFY_FROM_LIST( - new LocTextKey("sebserver.configtemplate.action.list.modify"), - ImageIcon.EDIT, - PageStateDefinitionImpl.SEB_EXAM_CONFIG_TEMPLATE_EDIT, - ActionCategory.SEB_CONFIG_TEMPLATE_LIST), - SEB_EXAM_CONFIG_TEMPLATE_MODIFY( - new LocTextKey("sebserver.configtemplate.action.modify"), - ImageIcon.EDIT, - PageStateDefinitionImpl.SEB_EXAM_CONFIG_TEMPLATE_EDIT, - ActionCategory.FORM), - SEB_EXAM_CONFIG_TEMPLATE_CREATE_CONFIG( - new LocTextKey("sebserver.configtemplate.action.create-config"), - ImageIcon.NEW, - PageStateDefinitionImpl.SEB_EXAM_CONFIG_TEMPLATE_VIEW, - ActionCategory.FORM), - SEB_EXAM_CONFIG_TEMPLATE_CANCEL_MODIFY( - new LocTextKey("sebserver.overall.action.modify.cancel"), - ImageIcon.CANCEL, - PageStateDefinitionImpl.SEB_EXAM_CONFIG_TEMPLATE_VIEW, - ActionCategory.FORM), - SEB_EXAM_CONFIG_TEMPLATE_SAVE( - new LocTextKey("sebserver.configtemplate.action.save"), - ImageIcon.SAVE, - PageStateDefinitionImpl.SEB_EXAM_CONFIG_TEMPLATE_VIEW, - ActionCategory.FORM), - - SEB_EXAM_CONFIG_TEMPLATE_ATTR_EDIT( - new LocTextKey("sebserver.configtemplate.attr.list.actions.modify"), - ImageIcon.EDIT, - PageStateDefinitionImpl.SEB_EXAM_CONFIG_TEMPLATE_ATTRIBUTE_EDIT, - ActionCategory.SEB_CONFIG_TEMPLATE_ATTRIBUTE_LIST), - SEB_EXAM_CONFIG_TEMPLATE_ATTR_SET_DEFAULT( - new LocTextKey("sebserver.configtemplate.attr.list.actions.setdefault"), - ImageIcon.SAVE, - PageStateDefinitionImpl.SEB_EXAM_CONFIG_TEMPLATE_VIEW, - ActionCategory.SEB_CONFIG_TEMPLATE_ATTRIBUTE_LIST), - SEB_EXAM_CONFIG_TEMPLATE_ATTR_LIST_REMOVE_VIEW( - new LocTextKey("sebserver.configtemplate.attr.list.actions.removeview"), - ImageIcon.REMOVE, - PageStateDefinitionImpl.SEB_EXAM_CONFIG_TEMPLATE_VIEW, - ActionCategory.SEB_CONFIG_TEMPLATE_ATTRIBUTE_LIST), - SEB_EXAM_CONFIG_TEMPLATE_ATTR_LIST_ATTACH_DEFAULT_VIEW( - new LocTextKey("sebserver.configtemplate.attr.list.actions.attach-default-view"), - ImageIcon.ADD, - PageStateDefinitionImpl.SEB_EXAM_CONFIG_TEMPLATE_VIEW, - ActionCategory.SEB_CONFIG_TEMPLATE_ATTRIBUTE_LIST), - SEB_EXAM_CONFIG_TEMPLATE_ATTR_REMOVE_VIEW( - new LocTextKey("sebserver.configtemplate.attr.list.actions.removeview"), - ImageIcon.REMOVE, - PageStateDefinitionImpl.SEB_EXAM_CONFIG_TEMPLATE_ATTRIBUTE_EDIT, - ActionCategory.FORM), - SEB_EXAM_CONFIG_TEMPLATE_ATTR_ATTACH_DEFAULT_VIEW( - new LocTextKey("sebserver.configtemplate.attr.list.actions.attach-default-view"), - ImageIcon.ADD, - PageStateDefinitionImpl.SEB_EXAM_CONFIG_TEMPLATE_ATTRIBUTE_EDIT, - ActionCategory.FORM), - - SEB_EXAM_CONFIG_TEMPLATE_ATTR_FORM_SET_DEFAULT( - new LocTextKey("sebserver.configtemplate.attr.action.setdefault"), - ImageIcon.UNDO, - PageStateDefinitionImpl.SEB_EXAM_CONFIG_TEMPLATE_ATTRIBUTE_EDIT, - ActionCategory.FORM), - SEB_EXAM_CONFIG_TEMPLATE_ATTR_FORM_EDIT_TEMPLATE( - new LocTextKey("sebserver.configtemplate.attr.action.template"), - ImageIcon.SHOW, - PageStateDefinitionImpl.SEB_EXAM_CONFIG_TEMPLATE_VIEW, - ActionCategory.FORM), - - RUNNING_EXAM_VIEW_LIST( - new LocTextKey("sebserver.monitoring.action.list"), - PageStateDefinitionImpl.MONITORING_RUNNING_EXAM_LIST), - MONITOR_EXAM_FROM_LIST( - new LocTextKey("sebserver.monitoring.exam.action.list.view"), - ImageIcon.SHOW, - PageStateDefinitionImpl.MONITORING_RUNNING_EXAM, - ActionCategory.RUNNING_EXAM_LIST), - MONITOR_EXAM_CLIENT_CONNECTION( - new LocTextKey("sebserver.monitoring.exam.connection.action.view"), - ImageIcon.SHOW, - PageStateDefinitionImpl.MONITORING_CLIENT_CONNECTION, - ActionCategory.CLIENT_EVENT_LIST), - MONITOR_EXAM_CLIENT_CONNECTION_QUIT( - new LocTextKey("sebserver.monitoring.exam.connection.action.instruction.quit"), - ImageIcon.SEND_QUIT, - PageStateDefinitionImpl.MONITORING_CLIENT_CONNECTION, - ActionCategory.FORM), - MONITOR_EXAM_QUIT_SELECTED( - new LocTextKey("sebserver.monitoring.exam.connection.action.instruction.quit"), - ImageIcon.SEND_QUIT, - PageStateDefinitionImpl.MONITORING_RUNNING_EXAM, - ActionCategory.CLIENT_EVENT_LIST), - MONITOR_EXAM_QUIT_ALL( - new LocTextKey("sebserver.monitoring.exam.connection.action.instruction.quit.all"), - ImageIcon.SEND_QUIT, - PageStateDefinitionImpl.MONITORING_RUNNING_EXAM, - ActionCategory.FORM), - MONITOR_EXAM_FROM_DETAILS( - new LocTextKey("sebserver.monitoring.exam.action.detail.view"), - ImageIcon.SHOW, - PageStateDefinitionImpl.MONITORING_RUNNING_EXAM, - ActionCategory.FORM), - - MONITOR_EXAM_DISABLE_SELECTED_CONNECTION( - new LocTextKey("sebserver.monitoring.exam.connection.action.disable"), - ImageIcon.DISABLE, - PageStateDefinitionImpl.MONITORING_RUNNING_EXAM, - ActionCategory.CLIENT_EVENT_LIST), - - MONITOR_EXAM_HIDE_REQUESTED_CONNECTION( - new LocTextKey("sebserver.monitoring.exam.connection.action.hide.requested"), - ImageIcon.TOGGLE_OFF, - PageStateDefinitionImpl.MONITORING_RUNNING_EXAM, - ActionCategory.FILTER), - MONITOR_EXAM_SHOW_REQUESTED_CONNECTION( - new LocTextKey("sebserver.monitoring.exam.connection.action.show.requested"), - ImageIcon.TOGGLE_ON, - PageStateDefinitionImpl.MONITORING_RUNNING_EXAM, - ActionCategory.FILTER), - MONITOR_EXAM_HIDE_CLOSED_CONNECTION( - new LocTextKey("sebserver.monitoring.exam.connection.action.hide.closed"), - ImageIcon.TOGGLE_OFF, - PageStateDefinitionImpl.MONITORING_RUNNING_EXAM, - ActionCategory.FILTER), - MONITOR_EXAM_SHOW_CLOSED_CONNECTION( - new LocTextKey("sebserver.monitoring.exam.connection.action.show.closed"), - ImageIcon.TOGGLE_ON, - PageStateDefinitionImpl.MONITORING_RUNNING_EXAM, - ActionCategory.FILTER), - MONITOR_EXAM_HIDE_DISABLED_CONNECTION( - new LocTextKey("sebserver.monitoring.exam.connection.action.hide.disabled"), - ImageIcon.TOGGLE_OFF, - PageStateDefinitionImpl.MONITORING_RUNNING_EXAM, - ActionCategory.FILTER), - MONITOR_EXAM_SHOW_DISABLED_CONNECTION( - new LocTextKey("sebserver.monitoring.exam.connection.action.show.disabled"), - ImageIcon.TOGGLE_ON, - PageStateDefinitionImpl.MONITORING_RUNNING_EXAM, - ActionCategory.FILTER), - - LOGS_USER_ACTIVITY_LIST( - new LocTextKey("sebserver.logs.activity.userlogs"), - PageStateDefinitionImpl.USER_ACTIVITY_LOGS), - LOGS_USER_ACTIVITY_SHOW_DETAILS( - new LocTextKey("sebserver.logs.activity.userlogs.details"), - ImageIcon.SHOW, - ActionCategory.LOGS_USER_ACTIVITY_LIST), - - LOGS_SEB_CLIENT( - new LocTextKey("sebserver.logs.activity.seblogs"), - PageStateDefinitionImpl.SEB_CLIENT_LOGS), - LOGS_SEB_CLIENT_SHOW_DETAILS( - new LocTextKey("sebserver.logs.activity.seblogs.details"), - ImageIcon.SHOW, - ActionCategory.LOGS_SEB_CLIENT_LIST), - - ; - - public final LocTextKey title; - public final ImageIcon icon; - public final PageStateDefinition targetState; - public final ActionCategory category; - - private ActionDefinition(final LocTextKey title, final PageStateDefinition targetState) { - this(title, null, targetState, ActionCategory.VARIA); - } - - private ActionDefinition(final LocTextKey title, final ImageIcon icon, final PageStateDefinition targetState) { - this(title, icon, targetState, ActionCategory.VARIA); - } - - private ActionDefinition( - final LocTextKey title, - final ImageIcon icon, - final ActionCategory category) { - - this(title, icon, null, category); - } - - private ActionDefinition( - final LocTextKey title, - final ImageIcon icon, - final PageStateDefinition targetState, - final ActionCategory category) { - - this.title = title; - this.icon = icon; - this.targetState = targetState; - this.category = category; - } - -} +/* + * Copyright (c) 2018 ETH Zürich, Educational Development and Technology (LET) + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +package ch.ethz.seb.sebserver.gui.content.action; + +import ch.ethz.seb.sebserver.gui.content.activity.PageStateDefinitionImpl; +import ch.ethz.seb.sebserver.gui.service.i18n.LocTextKey; +import ch.ethz.seb.sebserver.gui.service.page.PageStateDefinition; +import ch.ethz.seb.sebserver.gui.widget.WidgetFactory.ImageIcon; + +/** Enumeration of static action data for each action within the SEB Server GUI */ +public enum ActionDefinition { + + INSTITUTION_VIEW_LIST( + new LocTextKey("sebserver.institution.action.list"), + PageStateDefinitionImpl.INSTITUTION_LIST), + INSTITUTION_VIEW_FORM( + new LocTextKey("sebserver.institution.action.form"), + PageStateDefinitionImpl.INSTITUTION_VIEW), + INSTITUTION_NEW( + new LocTextKey("sebserver.institution.action.new"), + ImageIcon.INSTITUTION, + PageStateDefinitionImpl.INSTITUTION_EDIT, + ActionCategory.FORM), + INSTITUTION_VIEW_FROM_LIST( + new LocTextKey("sebserver.institution.action.list.view"), + ImageIcon.SHOW, + PageStateDefinitionImpl.INSTITUTION_VIEW, + ActionCategory.INSTITUTION_LIST), + INSTITUTION_MODIFY_FROM_LIST( + new LocTextKey("sebserver.institution.action.list.modify"), + ImageIcon.EDIT, + PageStateDefinitionImpl.INSTITUTION_EDIT, + ActionCategory.INSTITUTION_LIST), + + INSTITUTION_MODIFY( + new LocTextKey("sebserver.institution.action.modify"), + ImageIcon.EDIT, + PageStateDefinitionImpl.INSTITUTION_EDIT, + ActionCategory.FORM), + INSTITUTION_CANCEL_MODIFY( + new LocTextKey("sebserver.overall.action.modify.cancel"), + ImageIcon.CANCEL, + PageStateDefinitionImpl.INSTITUTION_VIEW, + ActionCategory.FORM), + INSTITUTION_SAVE( + new LocTextKey("sebserver.institution.action.save"), + ImageIcon.SAVE, + PageStateDefinitionImpl.INSTITUTION_VIEW, + ActionCategory.FORM), + INSTITUTION_SAVE_AND_ACTIVATE( + new LocTextKey("sebserver.form.action.save.activate"), + ImageIcon.ACTIVE, + PageStateDefinitionImpl.INSTITUTION_VIEW, + ActionCategory.FORM), + INSTITUTION_ACTIVATE( + new LocTextKey("sebserver.institution.action.activate"), + ImageIcon.TOGGLE_OFF, + PageStateDefinitionImpl.INSTITUTION_VIEW, + ActionCategory.FORM), + INSTITUTION_DEACTIVATE( + new LocTextKey("sebserver.institution.action.deactivate"), + ImageIcon.TOGGLE_ON, + PageStateDefinitionImpl.INSTITUTION_VIEW, + ActionCategory.FORM), + INSTITUTION_TOGGLE_ACTIVITY( + new LocTextKey("sebserver.overall.action.toggle-activity"), + ImageIcon.SWITCH, + PageStateDefinitionImpl.INSTITUTION_LIST, + ActionCategory.INSTITUTION_LIST), + + USER_ACCOUNT_VIEW_LIST( + new LocTextKey("sebserver.useraccount.action.list"), + PageStateDefinitionImpl.USER_ACCOUNT_LIST), + USER_ACCOUNT_VIEW_FORM( + new LocTextKey("sebserver.useraccount.action.form"), + PageStateDefinitionImpl.USER_ACCOUNT_VIEW), + USER_ACCOUNT_NEW( + new LocTextKey("sebserver.useraccount.action.new"), + ImageIcon.USER, + PageStateDefinitionImpl.USER_ACCOUNT_EDIT), + USER_ACCOUNT_VIEW_FROM_LIST( + new LocTextKey("sebserver.useraccount.action.view"), + ImageIcon.SHOW, + PageStateDefinitionImpl.USER_ACCOUNT_VIEW, + ActionCategory.USER_ACCOUNT_LIST), + USER_ACCOUNT_MODIFY_FROM_LIST( + new LocTextKey("sebserver.useraccount.action.list.modify"), + ImageIcon.EDIT, + PageStateDefinitionImpl.USER_ACCOUNT_EDIT, + ActionCategory.USER_ACCOUNT_LIST), + USER_ACCOUNT_MODIFY( + new LocTextKey("sebserver.useraccount.action.modify"), + ImageIcon.EDIT, + PageStateDefinitionImpl.USER_ACCOUNT_EDIT, + ActionCategory.FORM), + USER_ACCOUNT_CANCEL_MODIFY( + new LocTextKey("sebserver.overall.action.modify.cancel"), + ImageIcon.CANCEL, + PageStateDefinitionImpl.USER_ACCOUNT_VIEW, + ActionCategory.FORM), + USER_ACCOUNT_SAVE( + new LocTextKey("sebserver.useraccount.action.save"), + ImageIcon.SAVE, + PageStateDefinitionImpl.USER_ACCOUNT_VIEW, + ActionCategory.FORM), + USER_ACCOUNT_SAVE_AND_ACTIVATE( + new LocTextKey("sebserver.form.action.save.activate"), + ImageIcon.ACTIVE, + PageStateDefinitionImpl.USER_ACCOUNT_VIEW, + ActionCategory.FORM), + USER_ACCOUNT_ACTIVATE( + new LocTextKey("sebserver.useraccount.action.activate"), + ImageIcon.TOGGLE_OFF, + PageStateDefinitionImpl.USER_ACCOUNT_VIEW, + ActionCategory.FORM), + USER_ACCOUNT_DEACTIVATE( + new LocTextKey("sebserver.useraccount.action.deactivate"), + ImageIcon.TOGGLE_ON, + PageStateDefinitionImpl.USER_ACCOUNT_VIEW, + ActionCategory.FORM), + USER_ACCOUNT_TOGGLE_ACTIVITY( + new LocTextKey("sebserver.overall.action.toggle-activity"), + ImageIcon.SWITCH, + PageStateDefinitionImpl.USER_ACCOUNT_LIST, + ActionCategory.USER_ACCOUNT_LIST), + + USER_ACCOUNT_CHANGE_PASSWORD( + new LocTextKey("sebserver.useraccount.action.change.password"), + ImageIcon.SECURE, + PageStateDefinitionImpl.USER_ACCOUNT_PASSWORD_CHANGE, + ActionCategory.FORM), + USER_ACCOUNT_CHANGE_PASSWORD_SAVE( + new LocTextKey("sebserver.useraccount.action.change.password.save"), + ImageIcon.SAVE, + PageStateDefinitionImpl.USER_ACCOUNT_VIEW, + ActionCategory.FORM), + + LMS_SETUP_VIEW_LIST( + new LocTextKey("sebserver.lmssetup.action.list"), + PageStateDefinitionImpl.LMS_SETUP_LIST), + LMS_SETUP_NEW( + new LocTextKey("sebserver.lmssetup.action.new"), + ImageIcon.LMS_SETUP, + PageStateDefinitionImpl.LMS_SETUP_EDIT), + LMS_SETUP_VIEW_FROM_LIST( + new LocTextKey("sebserver.lmssetup.action.list.view"), + ImageIcon.SHOW, + PageStateDefinitionImpl.LMS_SETUP_VIEW, + ActionCategory.LMS_SETUP_LIST), + LMS_SETUP_MODIFY_FROM_LIST( + new LocTextKey("sebserver.lmssetup.action.list.modify"), + ImageIcon.EDIT, + PageStateDefinitionImpl.LMS_SETUP_EDIT, + ActionCategory.LMS_SETUP_LIST), + LMS_SETUP_MODIFY( + new LocTextKey("sebserver.lmssetup.action.modify"), + ImageIcon.EDIT, + PageStateDefinitionImpl.LMS_SETUP_EDIT, + ActionCategory.FORM), + LMS_SETUP_SAVE_AND_TEST( + new LocTextKey("sebserver.lmssetup.action.savetest"), + ImageIcon.TEST, + PageStateDefinitionImpl.LMS_SETUP_VIEW, + ActionCategory.FORM), + LMS_SETUP_TEST_AND_SAVE( + new LocTextKey("sebserver.lmssetup.action.testsave"), + ImageIcon.TEST, + PageStateDefinitionImpl.LMS_SETUP_VIEW, + ActionCategory.FORM), + LMS_SETUP_CANCEL_MODIFY( + new LocTextKey("sebserver.overall.action.modify.cancel"), + ImageIcon.CANCEL, + PageStateDefinitionImpl.LMS_SETUP_VIEW, + ActionCategory.FORM), + LMS_SETUP_SAVE( + new LocTextKey("sebserver.lmssetup.action.save"), + ImageIcon.SAVE, + PageStateDefinitionImpl.LMS_SETUP_VIEW, + ActionCategory.FORM), + LMS_SETUP_ACTIVATE( + new LocTextKey("sebserver.lmssetup.action.activate"), + ImageIcon.TOGGLE_OFF, + PageStateDefinitionImpl.LMS_SETUP_VIEW, + ActionCategory.FORM), + LMS_SETUP_DEACTIVATE( + new LocTextKey("sebserver.lmssetup.action.deactivate"), + ImageIcon.TOGGLE_ON, + PageStateDefinitionImpl.LMS_SETUP_VIEW, + ActionCategory.FORM), + + QUIZ_DISCOVERY_VIEW_LIST( + new LocTextKey("sebserver.quizdiscovery.action.list"), + PageStateDefinitionImpl.QUIZ_LIST), + QUIZ_DISCOVERY_SHOW_DETAILS( + new LocTextKey("sebserver.quizdiscovery.action.details"), + ImageIcon.SHOW, + ActionCategory.QUIZ_LIST), + QUIZ_DISCOVERY_EXAM_IMPORT( + new LocTextKey("sebserver.quizdiscovery.action.import"), + ImageIcon.IMPORT, + PageStateDefinitionImpl.EXAM_EDIT, + ActionCategory.QUIZ_LIST), + + EXAM_VIEW_LIST( + new LocTextKey("sebserver.exam.action.list"), + PageStateDefinitionImpl.EXAM_LIST), + EXAM_IMPORT( + new LocTextKey("sebserver.exam.action.import"), + ImageIcon.IMPORT, + PageStateDefinitionImpl.QUIZ_LIST), + EXAM_VIEW_FROM_LIST( + new LocTextKey("sebserver.exam.action.list.view"), + ImageIcon.SHOW, + PageStateDefinitionImpl.EXAM_VIEW, + ActionCategory.EXAM_LIST), + EXAM_MODIFY_FROM_LIST( + new LocTextKey("sebserver.exam.action.list.modify"), + ImageIcon.EDIT, + PageStateDefinitionImpl.EXAM_EDIT, + ActionCategory.EXAM_LIST), + EXAM_MODIFY( + new LocTextKey("sebserver.exam.action.modify"), + ImageIcon.EDIT, + PageStateDefinitionImpl.EXAM_EDIT, + ActionCategory.FORM), + EXAM_CANCEL_MODIFY( + new LocTextKey("sebserver.overall.action.modify.cancel"), + ImageIcon.CANCEL, + PageStateDefinitionImpl.EXAM_VIEW, + ActionCategory.FORM), + EXAM_SAVE( + new LocTextKey("sebserver.exam.action.save"), + ImageIcon.SAVE, + PageStateDefinitionImpl.EXAM_VIEW, + ActionCategory.FORM), + EXAM_ACTIVATE( + new LocTextKey("sebserver.exam.action.activate"), + ImageIcon.TOGGLE_OFF, + PageStateDefinitionImpl.EXAM_VIEW, + ActionCategory.FORM), + EXAM_DEACTIVATE( + new LocTextKey("sebserver.exam.action.deactivate"), + ImageIcon.TOGGLE_ON, + PageStateDefinitionImpl.EXAM_VIEW, + ActionCategory.FORM), + + EXAM_MODIFY_SEB_RESTRICTION_DETAILS( + new LocTextKey("sebserver.exam.action.sebrestriction.details"), + ImageIcon.RESTRICTION, + PageStateDefinitionImpl.EXAM_VIEW, + ActionCategory.FORM), + EXAM_ENABLE_SEB_RESTRICTION( + new LocTextKey("sebserver.exam.action.sebrestriction.enable"), + ImageIcon.UNLOCK, + PageStateDefinitionImpl.EXAM_VIEW, + ActionCategory.FORM), + EXAM_DISABLE_SEB_RESTRICTION( + new LocTextKey("sebserver.exam.action.sebrestriction.disable"), + ImageIcon.LOCK, + PageStateDefinitionImpl.EXAM_VIEW, + ActionCategory.FORM), + + EXAM_CONFIGURATION_NEW( + new LocTextKey("sebserver.exam.configuration.action.list.new"), + ImageIcon.NEW, + PageStateDefinitionImpl.EXAM_VIEW, + ActionCategory.EXAM_CONFIG_MAPPING_LIST), + EXAM_CONFIGURATION_MODIFY_FROM_LIST( + new LocTextKey("sebserver.exam.configuration.action.list.modify"), + ImageIcon.EDIT, + PageStateDefinitionImpl.EXAM_VIEW, + ActionCategory.EXAM_CONFIG_MAPPING_LIST), + EXAM_CONFIGURATION_EXAM_CONFIG_VIEW_PROP( + new LocTextKey("sebserver.exam.configuration.action.list.view"), + ImageIcon.SHOW, + PageStateDefinitionImpl.SEB_EXAM_CONFIG_PROP_VIEW, + ActionCategory.EXAM_CONFIG_MAPPING_LIST), + EXAM_CONFIGURATION_DELETE_FROM_LIST( + new LocTextKey("sebserver.exam.configuration.action.list.delete"), + ImageIcon.DELETE, + PageStateDefinitionImpl.EXAM_VIEW, + ActionCategory.EXAM_CONFIG_MAPPING_LIST), + EXAM_CONFIGURATION_EXPORT( + new LocTextKey("sebserver.exam.configuration.action.export-config"), + ImageIcon.EXPORT, + PageStateDefinitionImpl.EXAM_VIEW, + ActionCategory.EXAM_CONFIG_MAPPING_LIST), + EXAM_CONFIGURATION_GET_CONFIG_KEY( + new LocTextKey("sebserver.exam.configuration.action.get-config-key"), + ImageIcon.SECURE, + ActionCategory.EXAM_CONFIG_MAPPING_LIST), + EXAM_CONFIGURATION_SAVE( + new LocTextKey("sebserver.exam.configuration.action.save"), + ImageIcon.SAVE, + PageStateDefinitionImpl.EXAM_VIEW, + ActionCategory.FORM), + EXAM_CONFIGURATION_CANCEL_MODIFY( + new LocTextKey("sebserver.overall.action.modify.cancel"), + ImageIcon.CANCEL, + PageStateDefinitionImpl.EXAM_VIEW, + ActionCategory.FORM), + + EXAM_INDICATOR_NEW( + new LocTextKey("sebserver.exam.indicator.action.list.new"), + ImageIcon.INDICATOR, + PageStateDefinitionImpl.INDICATOR_EDIT, + ActionCategory.INDICATOR_LIST), + EXAM_INDICATOR_MODIFY_FROM_LIST( + new LocTextKey("sebserver.exam.indicator.action.list.modify"), + ImageIcon.EDIT, + PageStateDefinitionImpl.INDICATOR_EDIT, + ActionCategory.INDICATOR_LIST), + EXAM_INDICATOR_DELETE_FROM_LIST( + new LocTextKey("sebserver.exam.indicator.action.list.delete"), + ImageIcon.DELETE, + PageStateDefinitionImpl.EXAM_VIEW, + ActionCategory.INDICATOR_LIST), + EXAM_INDICATOR_SAVE( + new LocTextKey("sebserver.exam.indicator.action.save"), + ImageIcon.SAVE, + PageStateDefinitionImpl.EXAM_VIEW, + ActionCategory.FORM), + EXAM_INDICATOR_CANCEL_MODIFY( + new LocTextKey("sebserver.overall.action.modify.cancel"), + ImageIcon.CANCEL, + PageStateDefinitionImpl.EXAM_VIEW, + ActionCategory.FORM), + + SEB_CLIENT_CONFIG_LIST( + new LocTextKey("sebserver.clientconfig.action.list"), + PageStateDefinitionImpl.SEB_CLIENT_CONFIG_LIST), + SEB_CLIENT_CONFIG_NEW( + new LocTextKey("sebserver.clientconfig.action.list.new"), + ImageIcon.NEW, + PageStateDefinitionImpl.SEB_CLIENT_CONFIG_EDIT), + SEB_CLIENT_CONFIG_VIEW_FROM_LIST( + new LocTextKey("sebserver.clientconfig.action.list.view"), + ImageIcon.SHOW, + PageStateDefinitionImpl.SEB_CLIENT_CONFIG_VIEW, + ActionCategory.SEB_CLIENT_CONFIG_LIST), + SEB_CLIENT_CONFIG_MODIFY_FROM_LIST( + new LocTextKey("sebserver.clientconfig.action.list.modify"), + ImageIcon.EDIT, + PageStateDefinitionImpl.SEB_CLIENT_CONFIG_EDIT, + ActionCategory.SEB_CLIENT_CONFIG_LIST), + SEB_CLIENT_CONFIG_MODIFY( + new LocTextKey("sebserver.clientconfig.action.modify"), + ImageIcon.EDIT, + PageStateDefinitionImpl.SEB_CLIENT_CONFIG_EDIT, + ActionCategory.FORM), + SEB_CLIENT_CONFIG_CANCEL_MODIFY( + new LocTextKey("sebserver.overall.action.modify.cancel"), + ImageIcon.CANCEL, + PageStateDefinitionImpl.SEB_CLIENT_CONFIG_VIEW, + ActionCategory.FORM), + SEB_CLIENT_CONFIG_SAVE( + new LocTextKey("sebserver.clientconfig.action.save"), + ImageIcon.SAVE, + PageStateDefinitionImpl.SEB_CLIENT_CONFIG_VIEW, + ActionCategory.FORM), + SEB_CLIENT_CONFIG_SAVE_AND_ACTIVATE( + new LocTextKey("sebserver.form.action.save.activate"), + ImageIcon.ACTIVE, + PageStateDefinitionImpl.SEB_CLIENT_CONFIG_VIEW, + ActionCategory.FORM), + SEB_CLIENT_CONFIG_ACTIVATE( + new LocTextKey("sebserver.clientconfig.action.activate"), + ImageIcon.TOGGLE_OFF, + PageStateDefinitionImpl.SEB_CLIENT_CONFIG_VIEW, + ActionCategory.FORM), + SEB_CLIENT_CONFIG_DEACTIVATE( + new LocTextKey("sebserver.clientconfig.action.deactivate"), + ImageIcon.TOGGLE_ON, + PageStateDefinitionImpl.SEB_CLIENT_CONFIG_VIEW, + ActionCategory.FORM), + SEB_CLIENT_CONFIG_TOGGLE_ACTIVITY( + new LocTextKey("sebserver.overall.action.toggle-activity"), + ImageIcon.SWITCH, + PageStateDefinitionImpl.SEB_CLIENT_CONFIG_LIST, + ActionCategory.SEB_CLIENT_CONFIG_LIST), + SEB_CLIENT_CONFIG_EXPORT( + new LocTextKey("sebserver.clientconfig.action.export"), + ImageIcon.EXPORT, + PageStateDefinitionImpl.SEB_CLIENT_CONFIG_VIEW, + ActionCategory.FORM), + + SEB_EXAM_CONFIG_LIST( + new LocTextKey("sebserver.examconfig.action.list"), + PageStateDefinitionImpl.SEB_EXAM_CONFIG_LIST), + SEB_EXAM_CONFIG_NEW( + new LocTextKey("sebserver.examconfig.action.list.new"), + ImageIcon.NEW, + PageStateDefinitionImpl.SEB_EXAM_CONFIG_PROP_EDIT), + SEB_EXAM_CONFIG_VIEW_PROP_FROM_LIST( + new LocTextKey("sebserver.examconfig.action.list.view"), + ImageIcon.SHOW, + PageStateDefinitionImpl.SEB_EXAM_CONFIG_PROP_VIEW, + ActionCategory.SEB_EXAM_CONFIG_LIST), + SEB_EXAM_CONFIG_VIEW_PROP( + new LocTextKey("sebserver.examconfig.action.view.properties"), + ImageIcon.SHOW, + PageStateDefinitionImpl.SEB_EXAM_CONFIG_PROP_VIEW, + ActionCategory.FORM), + SEB_EXAM_CONFIG_IMPORT_TO_NEW_CONFIG( + new LocTextKey("sebserver.examconfig.action.import-config"), + ImageIcon.IMPORT, + PageStateDefinitionImpl.SEB_EXAM_CONFIG_LIST, + ActionCategory.VARIA), + + SEB_EXAM_CONFIG_MODIFY_PROP_FROM_LIST( + new LocTextKey("sebserver.examconfig.action.list.modify.properties"), + ImageIcon.EDIT, + PageStateDefinitionImpl.SEB_EXAM_CONFIG_PROP_EDIT, + ActionCategory.SEB_EXAM_CONFIG_LIST), + SEB_EXAM_CONFIG_PROP_MODIFY( + new LocTextKey("sebserver.examconfig.action.modify.properties"), + ImageIcon.EDIT, + PageStateDefinitionImpl.SEB_EXAM_CONFIG_PROP_EDIT, + ActionCategory.FORM), + SEB_EXAM_CONFIG_MODIFY( + new LocTextKey("sebserver.examconfig.action.modify"), + ImageIcon.EDIT_SETTINGS, + PageStateDefinitionImpl.SEB_EXAM_CONFIG_EDIT, + ActionCategory.FORM), + + SEB_EXAM_CONFIG_PROP_CANCEL_MODIFY( + new LocTextKey("sebserver.overall.action.modify.cancel"), + ImageIcon.CANCEL, + PageStateDefinitionImpl.SEB_EXAM_CONFIG_PROP_VIEW, + ActionCategory.FORM), + SEB_EXAM_CONFIG_PROP_SAVE( + new LocTextKey("sebserver.examconfig.action.save"), + ImageIcon.SAVE, + PageStateDefinitionImpl.SEB_EXAM_CONFIG_PROP_VIEW, + ActionCategory.FORM), + + SEB_EXAM_CONFIG_EXPORT_PLAIN_XML( + new LocTextKey("sebserver.examconfig.action.export.plainxml"), + ImageIcon.EXPORT, + ActionCategory.FORM), + SEB_EXAM_CONFIG_GET_CONFIG_KEY( + new LocTextKey("sebserver.examconfig.action.get-config-key"), + ImageIcon.SECURE, + ActionCategory.FORM), + SEB_EXAM_CONFIG_IMPORT_TO_EXISTING_CONFIG( + new LocTextKey("sebserver.examconfig.action.import-config"), + ImageIcon.IMPORT, + ActionCategory.FORM), + + SEB_EXAM_CONFIG_COPY_CONFIG( + new LocTextKey("sebserver.examconfig.action.copy"), + ImageIcon.COPY, + ActionCategory.FORM), + SEB_EXAM_CONFIG_COPY_CONFIG_AS_TEMPALTE( + new LocTextKey("sebserver.examconfig.action.copy-as-template"), + ImageIcon.TEMPLATE, + ActionCategory.FORM), + + SEB_EXAM_CONFIG_MODIFY_FROM_LIST( + new LocTextKey("sebserver.examconfig.action.list.modify"), + ImageIcon.EDIT_SETTINGS, + PageStateDefinitionImpl.SEB_EXAM_CONFIG_EDIT, + ActionCategory.SEB_EXAM_CONFIG_LIST), + + SEB_EXAM_CONFIG_SAVE_TO_HISTORY( + new LocTextKey("sebserver.examconfig.action.saveToHistory"), + ImageIcon.SAVE, + PageStateDefinitionImpl.SEB_EXAM_CONFIG_EDIT, + ActionCategory.FORM), + SEB_EXAM_CONFIG_UNDO( + new LocTextKey("sebserver.examconfig.action.undo"), + ImageIcon.UNDO, + PageStateDefinitionImpl.SEB_EXAM_CONFIG_EDIT, + ActionCategory.FORM), + + SEB_EXAM_CONFIG_TEMPLATE_LIST( + new LocTextKey("sebserver.configtemplate.action.list"), + PageStateDefinitionImpl.SEB_EXAM_CONFIG_TEMPLATE_LIST), + SEB_EXAM_CONFIG_TEMPLATE_NEW( + new LocTextKey("sebserver.configtemplate.action.list.new"), + ImageIcon.TEMPLATE, + PageStateDefinitionImpl.SEB_EXAM_CONFIG_TEMPLATE_EDIT, + ActionCategory.VARIA), + SEB_EXAM_CONFIG_TEMPLATE_VIEW_FROM_LIST( + new LocTextKey("sebserver.configtemplate.action.list.view"), + ImageIcon.SHOW, + PageStateDefinitionImpl.SEB_EXAM_CONFIG_TEMPLATE_VIEW, + ActionCategory.SEB_CONFIG_TEMPLATE_LIST), + SEB_EXAM_CONFIG_TEMPLATE_VIEW( + new LocTextKey("sebserver.configtemplate.action.view"), + ImageIcon.SHOW, + PageStateDefinitionImpl.SEB_EXAM_CONFIG_TEMPLATE_VIEW, + ActionCategory.FORM), + SEB_EXAM_CONFIG_TEMPLATE_MODIFY_FROM_LIST( + new LocTextKey("sebserver.configtemplate.action.list.modify"), + ImageIcon.EDIT, + PageStateDefinitionImpl.SEB_EXAM_CONFIG_TEMPLATE_EDIT, + ActionCategory.SEB_CONFIG_TEMPLATE_LIST), + SEB_EXAM_CONFIG_TEMPLATE_MODIFY( + new LocTextKey("sebserver.configtemplate.action.modify"), + ImageIcon.EDIT, + PageStateDefinitionImpl.SEB_EXAM_CONFIG_TEMPLATE_EDIT, + ActionCategory.FORM), + SEB_EXAM_CONFIG_TEMPLATE_CREATE_CONFIG( + new LocTextKey("sebserver.configtemplate.action.create-config"), + ImageIcon.NEW, + PageStateDefinitionImpl.SEB_EXAM_CONFIG_TEMPLATE_VIEW, + ActionCategory.FORM), + SEB_EXAM_CONFIG_TEMPLATE_CANCEL_MODIFY( + new LocTextKey("sebserver.overall.action.modify.cancel"), + ImageIcon.CANCEL, + PageStateDefinitionImpl.SEB_EXAM_CONFIG_TEMPLATE_VIEW, + ActionCategory.FORM), + SEB_EXAM_CONFIG_TEMPLATE_SAVE( + new LocTextKey("sebserver.configtemplate.action.save"), + ImageIcon.SAVE, + PageStateDefinitionImpl.SEB_EXAM_CONFIG_TEMPLATE_VIEW, + ActionCategory.FORM), + + SEB_EXAM_CONFIG_TEMPLATE_ATTR_EDIT( + new LocTextKey("sebserver.configtemplate.attr.list.actions.modify"), + ImageIcon.EDIT, + PageStateDefinitionImpl.SEB_EXAM_CONFIG_TEMPLATE_ATTRIBUTE_EDIT, + ActionCategory.SEB_CONFIG_TEMPLATE_ATTRIBUTE_LIST), + SEB_EXAM_CONFIG_TEMPLATE_ATTR_SET_DEFAULT( + new LocTextKey("sebserver.configtemplate.attr.list.actions.setdefault"), + ImageIcon.SAVE, + PageStateDefinitionImpl.SEB_EXAM_CONFIG_TEMPLATE_VIEW, + ActionCategory.SEB_CONFIG_TEMPLATE_ATTRIBUTE_LIST), + SEB_EXAM_CONFIG_TEMPLATE_ATTR_LIST_REMOVE_VIEW( + new LocTextKey("sebserver.configtemplate.attr.list.actions.removeview"), + ImageIcon.REMOVE, + PageStateDefinitionImpl.SEB_EXAM_CONFIG_TEMPLATE_VIEW, + ActionCategory.SEB_CONFIG_TEMPLATE_ATTRIBUTE_LIST), + SEB_EXAM_CONFIG_TEMPLATE_ATTR_LIST_ATTACH_DEFAULT_VIEW( + new LocTextKey("sebserver.configtemplate.attr.list.actions.attach-default-view"), + ImageIcon.ADD, + PageStateDefinitionImpl.SEB_EXAM_CONFIG_TEMPLATE_VIEW, + ActionCategory.SEB_CONFIG_TEMPLATE_ATTRIBUTE_LIST), + SEB_EXAM_CONFIG_TEMPLATE_ATTR_REMOVE_VIEW( + new LocTextKey("sebserver.configtemplate.attr.list.actions.removeview"), + ImageIcon.REMOVE, + PageStateDefinitionImpl.SEB_EXAM_CONFIG_TEMPLATE_ATTRIBUTE_EDIT, + ActionCategory.FORM), + SEB_EXAM_CONFIG_TEMPLATE_ATTR_ATTACH_DEFAULT_VIEW( + new LocTextKey("sebserver.configtemplate.attr.list.actions.attach-default-view"), + ImageIcon.ADD, + PageStateDefinitionImpl.SEB_EXAM_CONFIG_TEMPLATE_ATTRIBUTE_EDIT, + ActionCategory.FORM), + + SEB_EXAM_CONFIG_TEMPLATE_ATTR_FORM_SET_DEFAULT( + new LocTextKey("sebserver.configtemplate.attr.action.setdefault"), + ImageIcon.UNDO, + PageStateDefinitionImpl.SEB_EXAM_CONFIG_TEMPLATE_ATTRIBUTE_EDIT, + ActionCategory.FORM), + SEB_EXAM_CONFIG_TEMPLATE_ATTR_FORM_EDIT_TEMPLATE( + new LocTextKey("sebserver.configtemplate.attr.action.template"), + ImageIcon.SHOW, + PageStateDefinitionImpl.SEB_EXAM_CONFIG_TEMPLATE_VIEW, + ActionCategory.FORM), + + RUNNING_EXAM_VIEW_LIST( + new LocTextKey("sebserver.monitoring.action.list"), + PageStateDefinitionImpl.MONITORING_RUNNING_EXAM_LIST), + MONITOR_EXAM_FROM_LIST( + new LocTextKey("sebserver.monitoring.exam.action.list.view"), + ImageIcon.SHOW, + PageStateDefinitionImpl.MONITORING_RUNNING_EXAM, + ActionCategory.RUNNING_EXAM_LIST), + MONITOR_EXAM_CLIENT_CONNECTION( + new LocTextKey("sebserver.monitoring.exam.connection.action.view"), + ImageIcon.SHOW, + PageStateDefinitionImpl.MONITORING_CLIENT_CONNECTION, + ActionCategory.CLIENT_EVENT_LIST), + MONITOR_EXAM_CLIENT_CONNECTION_QUIT( + new LocTextKey("sebserver.monitoring.exam.connection.action.instruction.quit"), + ImageIcon.SEND_QUIT, + PageStateDefinitionImpl.MONITORING_CLIENT_CONNECTION, + ActionCategory.FORM), + MONITOR_EXAM_QUIT_SELECTED( + new LocTextKey("sebserver.monitoring.exam.connection.action.instruction.quit"), + ImageIcon.SEND_QUIT, + PageStateDefinitionImpl.MONITORING_RUNNING_EXAM, + ActionCategory.CLIENT_EVENT_LIST), + MONITOR_EXAM_QUIT_ALL( + new LocTextKey("sebserver.monitoring.exam.connection.action.instruction.quit.all"), + ImageIcon.SEND_QUIT, + PageStateDefinitionImpl.MONITORING_RUNNING_EXAM, + ActionCategory.FORM), + MONITOR_EXAM_FROM_DETAILS( + new LocTextKey("sebserver.monitoring.exam.action.detail.view"), + ImageIcon.SHOW, + PageStateDefinitionImpl.MONITORING_RUNNING_EXAM, + ActionCategory.FORM), + + MONITOR_EXAM_DISABLE_SELECTED_CONNECTION( + new LocTextKey("sebserver.monitoring.exam.connection.action.disable"), + ImageIcon.DISABLE, + PageStateDefinitionImpl.MONITORING_RUNNING_EXAM, + ActionCategory.CLIENT_EVENT_LIST), + + MONITOR_EXAM_HIDE_REQUESTED_CONNECTION( + new LocTextKey("sebserver.monitoring.exam.connection.action.hide.requested"), + ImageIcon.TOGGLE_OFF, + PageStateDefinitionImpl.MONITORING_RUNNING_EXAM, + ActionCategory.FILTER), + MONITOR_EXAM_SHOW_REQUESTED_CONNECTION( + new LocTextKey("sebserver.monitoring.exam.connection.action.show.requested"), + ImageIcon.TOGGLE_ON, + PageStateDefinitionImpl.MONITORING_RUNNING_EXAM, + ActionCategory.FILTER), + MONITOR_EXAM_HIDE_CLOSED_CONNECTION( + new LocTextKey("sebserver.monitoring.exam.connection.action.hide.closed"), + ImageIcon.TOGGLE_OFF, + PageStateDefinitionImpl.MONITORING_RUNNING_EXAM, + ActionCategory.FILTER), + MONITOR_EXAM_SHOW_CLOSED_CONNECTION( + new LocTextKey("sebserver.monitoring.exam.connection.action.show.closed"), + ImageIcon.TOGGLE_ON, + PageStateDefinitionImpl.MONITORING_RUNNING_EXAM, + ActionCategory.FILTER), + MONITOR_EXAM_HIDE_DISABLED_CONNECTION( + new LocTextKey("sebserver.monitoring.exam.connection.action.hide.disabled"), + ImageIcon.TOGGLE_OFF, + PageStateDefinitionImpl.MONITORING_RUNNING_EXAM, + ActionCategory.FILTER), + MONITOR_EXAM_SHOW_DISABLED_CONNECTION( + new LocTextKey("sebserver.monitoring.exam.connection.action.show.disabled"), + ImageIcon.TOGGLE_ON, + PageStateDefinitionImpl.MONITORING_RUNNING_EXAM, + ActionCategory.FILTER), + + LOGS_USER_ACTIVITY_LIST( + new LocTextKey("sebserver.logs.activity.userlogs"), + PageStateDefinitionImpl.USER_ACTIVITY_LOGS), + LOGS_USER_ACTIVITY_SHOW_DETAILS( + new LocTextKey("sebserver.logs.activity.userlogs.details"), + ImageIcon.SHOW, + ActionCategory.LOGS_USER_ACTIVITY_LIST), + + LOGS_SEB_CLIENT( + new LocTextKey("sebserver.logs.activity.seblogs"), + PageStateDefinitionImpl.SEB_CLIENT_LOGS), + LOGS_SEB_CLIENT_SHOW_DETAILS( + new LocTextKey("sebserver.logs.activity.seblogs.details"), + ImageIcon.SHOW, + ActionCategory.LOGS_SEB_CLIENT_LIST), + + ; + + public final LocTextKey title; + public final ImageIcon icon; + public final PageStateDefinition targetState; + public final ActionCategory category; + + ActionDefinition(final LocTextKey title, final PageStateDefinition targetState) { + this(title, null, targetState, ActionCategory.VARIA); + } + + ActionDefinition(final LocTextKey title, final ImageIcon icon, final PageStateDefinition targetState) { + this(title, icon, targetState, ActionCategory.VARIA); + } + + ActionDefinition( + final LocTextKey title, + final ImageIcon icon, + final ActionCategory category) { + + this(title, icon, null, category); + } + + ActionDefinition( + final LocTextKey title, + final ImageIcon icon, + final PageStateDefinition targetState, + final ActionCategory category) { + + this.title = title; + this.icon = icon; + this.targetState = targetState; + this.category = category; + } + +} diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/content/activity/PageStateDefinitionImpl.java b/src/main/java/ch/ethz/seb/sebserver/gui/content/activity/PageStateDefinitionImpl.java index 493fb22c..c242cf22 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/content/activity/PageStateDefinitionImpl.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/content/activity/PageStateDefinitionImpl.java @@ -1,138 +1,137 @@ -/* - * 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.content.activity; - -import ch.ethz.seb.sebserver.gui.content.ConfigTemplateAttributeForm; -import ch.ethz.seb.sebserver.gui.content.ConfigTemplateForm; -import ch.ethz.seb.sebserver.gui.content.ConfigTemplateList; -import ch.ethz.seb.sebserver.gui.content.ExamForm; -import ch.ethz.seb.sebserver.gui.content.ExamList; -import ch.ethz.seb.sebserver.gui.content.IndicatorForm; -import ch.ethz.seb.sebserver.gui.content.InstitutionForm; -import ch.ethz.seb.sebserver.gui.content.InstitutionList; -import ch.ethz.seb.sebserver.gui.content.LmsSetupForm; -import ch.ethz.seb.sebserver.gui.content.LmsSetupList; -import ch.ethz.seb.sebserver.gui.content.MonitoringClientConnection; -import ch.ethz.seb.sebserver.gui.content.MonitoringRunningExam; -import ch.ethz.seb.sebserver.gui.content.MonitoringRunningExamList; -import ch.ethz.seb.sebserver.gui.content.QuizDiscoveryList; -import ch.ethz.seb.sebserver.gui.content.SebClientConfigForm; -import ch.ethz.seb.sebserver.gui.content.SebClientConfigList; -import ch.ethz.seb.sebserver.gui.content.SebClientLogs; -import ch.ethz.seb.sebserver.gui.content.SebExamConfigList; -import ch.ethz.seb.sebserver.gui.content.SebExamConfigPropForm; -import ch.ethz.seb.sebserver.gui.content.SebExamConfigSettingsForm; -import ch.ethz.seb.sebserver.gui.content.UserAccountChangePasswordForm; -import ch.ethz.seb.sebserver.gui.content.UserAccountForm; -import ch.ethz.seb.sebserver.gui.content.UserAccountList; -import ch.ethz.seb.sebserver.gui.content.UserActivityLogs; -import ch.ethz.seb.sebserver.gui.content.action.ActionPane; -import ch.ethz.seb.sebserver.gui.service.page.Activity; -import ch.ethz.seb.sebserver.gui.service.page.PageStateDefinition; -import ch.ethz.seb.sebserver.gui.service.page.TemplateComposer; - -public enum PageStateDefinitionImpl implements PageStateDefinition { - - INSTITUTION_LIST(Type.LIST_VIEW, InstitutionList.class, ActivityDefinition.INSTITUTION), - INSTITUTION_VIEW(Type.FORM_VIEW, InstitutionForm.class, ActivityDefinition.INSTITUTION), - INSTITUTION_EDIT(Type.FORM_EDIT, InstitutionForm.class, ActivityDefinition.INSTITUTION), - - USER_ACCOUNT_LIST(Type.LIST_VIEW, UserAccountList.class, ActivityDefinition.USER_ACCOUNT), - USER_ACCOUNT_VIEW(Type.FORM_VIEW, UserAccountForm.class, ActivityDefinition.USER_ACCOUNT), - USER_ACCOUNT_EDIT(Type.FORM_EDIT, UserAccountForm.class, ActivityDefinition.USER_ACCOUNT), - USER_ACCOUNT_PASSWORD_CHANGE(Type.FORM_EDIT, UserAccountChangePasswordForm.class, ActivityDefinition.USER_ACCOUNT), - - LMS_SETUP_LIST(Type.LIST_VIEW, LmsSetupList.class, ActivityDefinition.LMS_SETUP), - LMS_SETUP_VIEW(Type.FORM_VIEW, LmsSetupForm.class, ActivityDefinition.LMS_SETUP), - LMS_SETUP_EDIT(Type.FORM_EDIT, LmsSetupForm.class, ActivityDefinition.LMS_SETUP), - - QUIZ_LIST(Type.LIST_VIEW, QuizDiscoveryList.class, ActivityDefinition.QUIZ_DISCOVERY), - - EXAM_LIST(Type.LIST_VIEW, ExamList.class, ActivityDefinition.EXAM), - EXAM_VIEW(Type.FORM_VIEW, ExamForm.class, ActivityDefinition.EXAM), - EXAM_EDIT(Type.FORM_EDIT, ExamForm.class, ActivityDefinition.EXAM), - INDICATOR_EDIT(Type.FORM_EDIT, IndicatorForm.class, ActivityDefinition.EXAM), - - SEB_CLIENT_CONFIG_LIST(Type.LIST_VIEW, SebClientConfigList.class, ActivityDefinition.SEB_CLIENT_CONFIG), - SEB_CLIENT_CONFIG_VIEW(Type.FORM_VIEW, SebClientConfigForm.class, ActivityDefinition.SEB_CLIENT_CONFIG), - SEB_CLIENT_CONFIG_EDIT(Type.FORM_EDIT, SebClientConfigForm.class, ActivityDefinition.SEB_CLIENT_CONFIG), - - SEB_EXAM_CONFIG_LIST(Type.LIST_VIEW, SebExamConfigList.class, ActivityDefinition.SEB_EXAM_CONFIG), - SEB_EXAM_CONFIG_PROP_VIEW(Type.FORM_VIEW, SebExamConfigPropForm.class, ActivityDefinition.SEB_EXAM_CONFIG), - SEB_EXAM_CONFIG_PROP_EDIT(Type.FORM_EDIT, SebExamConfigPropForm.class, ActivityDefinition.SEB_EXAM_CONFIG), - SEB_EXAM_CONFIG_EDIT(Type.FORM_IN_TIME_EDIT, SebExamConfigSettingsForm.class, ActivityDefinition.SEB_EXAM_CONFIG), - SEB_EXAM_CONFIG_VIEW(Type.FORM_VIEW, SebExamConfigSettingsForm.class, ActivityDefinition.SEB_EXAM_CONFIG), - - SEB_EXAM_CONFIG_TEMPLATE_LIST(Type.LIST_VIEW, ConfigTemplateList.class, - ActivityDefinition.SEB_EXAM_CONFIG_TEMPLATE), - SEB_EXAM_CONFIG_TEMPLATE_VIEW(Type.FORM_VIEW, ConfigTemplateForm.class, - ActivityDefinition.SEB_EXAM_CONFIG_TEMPLATE), - SEB_EXAM_CONFIG_TEMPLATE_EDIT(Type.FORM_EDIT, ConfigTemplateForm.class, - ActivityDefinition.SEB_EXAM_CONFIG_TEMPLATE), - - SEB_EXAM_CONFIG_TEMPLATE_ATTRIBUTE_EDIT( - Type.FORM_EDIT, - ConfigTemplateAttributeForm.class, - ActivityDefinition.SEB_EXAM_CONFIG), - - MONITORING_RUNNING_EXAM_LIST(Type.LIST_VIEW, MonitoringRunningExamList.class, ActivityDefinition.MONITORING_EXAMS), - MONITORING_RUNNING_EXAM(Type.FORM_VIEW, MonitoringRunningExam.class, ActivityDefinition.MONITORING_EXAMS), - MONITORING_CLIENT_CONNECTION(Type.FORM_VIEW, MonitoringClientConnection.class, ActivityDefinition.MONITORING_EXAMS), - - USER_ACTIVITY_LOGS(Type.LIST_VIEW, UserActivityLogs.class, ActivityDefinition.USER_ACTIVITY_LOGS), - SEB_CLIENT_LOGS(Type.LIST_VIEW, SebClientLogs.class, ActivityDefinition.SEB_CLIENT_LOGS) - - ; - - public final Type type; - public final Class contentPaneComposer; - public final Class actionPaneComposer; - public final Activity activityAnchor; - - private PageStateDefinitionImpl( - final Type type, - final Class contentPaneComposer, - final Activity activityAnchor) { - - this(type, contentPaneComposer, ActionPane.class, activityAnchor); - } - - private PageStateDefinitionImpl( - final Type type, - final Class contentPaneComposer, - final Class actionPaneComposer, - final Activity activityAnchor) { - - this.type = type; - this.contentPaneComposer = contentPaneComposer; - this.actionPaneComposer = actionPaneComposer; - this.activityAnchor = activityAnchor; - } - - @Override - public Type type() { - return this.type; - } - - @Override - public Class contentPaneComposer() { - return this.contentPaneComposer; - } - - @Override - public Class actionPaneComposer() { - return this.actionPaneComposer; - } - - @Override - public Activity activityAnchor() { - return this.activityAnchor; - } - -} +/* + * 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.content.activity; + +import ch.ethz.seb.sebserver.gui.content.ConfigTemplateAttributeForm; +import ch.ethz.seb.sebserver.gui.content.ConfigTemplateForm; +import ch.ethz.seb.sebserver.gui.content.ConfigTemplateList; +import ch.ethz.seb.sebserver.gui.content.ExamForm; +import ch.ethz.seb.sebserver.gui.content.ExamList; +import ch.ethz.seb.sebserver.gui.content.IndicatorForm; +import ch.ethz.seb.sebserver.gui.content.InstitutionForm; +import ch.ethz.seb.sebserver.gui.content.InstitutionList; +import ch.ethz.seb.sebserver.gui.content.LmsSetupForm; +import ch.ethz.seb.sebserver.gui.content.LmsSetupList; +import ch.ethz.seb.sebserver.gui.content.MonitoringClientConnection; +import ch.ethz.seb.sebserver.gui.content.MonitoringRunningExam; +import ch.ethz.seb.sebserver.gui.content.MonitoringRunningExamList; +import ch.ethz.seb.sebserver.gui.content.QuizDiscoveryList; +import ch.ethz.seb.sebserver.gui.content.SebClientConfigForm; +import ch.ethz.seb.sebserver.gui.content.SebClientConfigList; +import ch.ethz.seb.sebserver.gui.content.SebClientLogs; +import ch.ethz.seb.sebserver.gui.content.SebExamConfigList; +import ch.ethz.seb.sebserver.gui.content.SebExamConfigPropForm; +import ch.ethz.seb.sebserver.gui.content.SebExamConfigSettingsForm; +import ch.ethz.seb.sebserver.gui.content.UserAccountChangePasswordForm; +import ch.ethz.seb.sebserver.gui.content.UserAccountForm; +import ch.ethz.seb.sebserver.gui.content.UserAccountList; +import ch.ethz.seb.sebserver.gui.content.UserActivityLogs; +import ch.ethz.seb.sebserver.gui.content.action.ActionPane; +import ch.ethz.seb.sebserver.gui.service.page.Activity; +import ch.ethz.seb.sebserver.gui.service.page.PageStateDefinition; +import ch.ethz.seb.sebserver.gui.service.page.TemplateComposer; + +public enum PageStateDefinitionImpl implements PageStateDefinition { + + INSTITUTION_LIST(Type.LIST_VIEW, InstitutionList.class, ActivityDefinition.INSTITUTION), + INSTITUTION_VIEW(Type.FORM_VIEW, InstitutionForm.class, ActivityDefinition.INSTITUTION), + INSTITUTION_EDIT(Type.FORM_EDIT, InstitutionForm.class, ActivityDefinition.INSTITUTION), + + USER_ACCOUNT_LIST(Type.LIST_VIEW, UserAccountList.class, ActivityDefinition.USER_ACCOUNT), + USER_ACCOUNT_VIEW(Type.FORM_VIEW, UserAccountForm.class, ActivityDefinition.USER_ACCOUNT), + USER_ACCOUNT_EDIT(Type.FORM_EDIT, UserAccountForm.class, ActivityDefinition.USER_ACCOUNT), + USER_ACCOUNT_PASSWORD_CHANGE(Type.FORM_EDIT, UserAccountChangePasswordForm.class, ActivityDefinition.USER_ACCOUNT), + + LMS_SETUP_LIST(Type.LIST_VIEW, LmsSetupList.class, ActivityDefinition.LMS_SETUP), + LMS_SETUP_VIEW(Type.FORM_VIEW, LmsSetupForm.class, ActivityDefinition.LMS_SETUP), + LMS_SETUP_EDIT(Type.FORM_EDIT, LmsSetupForm.class, ActivityDefinition.LMS_SETUP), + + QUIZ_LIST(Type.LIST_VIEW, QuizDiscoveryList.class, ActivityDefinition.QUIZ_DISCOVERY), + + EXAM_LIST(Type.LIST_VIEW, ExamList.class, ActivityDefinition.EXAM), + EXAM_VIEW(Type.FORM_VIEW, ExamForm.class, ActivityDefinition.EXAM), + EXAM_EDIT(Type.FORM_EDIT, ExamForm.class, ActivityDefinition.EXAM), + INDICATOR_EDIT(Type.FORM_EDIT, IndicatorForm.class, ActivityDefinition.EXAM), + + SEB_CLIENT_CONFIG_LIST(Type.LIST_VIEW, SebClientConfigList.class, ActivityDefinition.SEB_CLIENT_CONFIG), + SEB_CLIENT_CONFIG_VIEW(Type.FORM_VIEW, SebClientConfigForm.class, ActivityDefinition.SEB_CLIENT_CONFIG), + SEB_CLIENT_CONFIG_EDIT(Type.FORM_EDIT, SebClientConfigForm.class, ActivityDefinition.SEB_CLIENT_CONFIG), + + SEB_EXAM_CONFIG_LIST(Type.LIST_VIEW, SebExamConfigList.class, ActivityDefinition.SEB_EXAM_CONFIG), + SEB_EXAM_CONFIG_PROP_VIEW(Type.FORM_VIEW, SebExamConfigPropForm.class, ActivityDefinition.SEB_EXAM_CONFIG), + SEB_EXAM_CONFIG_PROP_EDIT(Type.FORM_EDIT, SebExamConfigPropForm.class, ActivityDefinition.SEB_EXAM_CONFIG), + SEB_EXAM_CONFIG_EDIT(Type.FORM_IN_TIME_EDIT, SebExamConfigSettingsForm.class, ActivityDefinition.SEB_EXAM_CONFIG), + SEB_EXAM_CONFIG_VIEW(Type.FORM_VIEW, SebExamConfigSettingsForm.class, ActivityDefinition.SEB_EXAM_CONFIG), + + SEB_EXAM_CONFIG_TEMPLATE_LIST(Type.LIST_VIEW, ConfigTemplateList.class, + ActivityDefinition.SEB_EXAM_CONFIG_TEMPLATE), + SEB_EXAM_CONFIG_TEMPLATE_VIEW(Type.FORM_VIEW, ConfigTemplateForm.class, + ActivityDefinition.SEB_EXAM_CONFIG_TEMPLATE), + SEB_EXAM_CONFIG_TEMPLATE_EDIT(Type.FORM_EDIT, ConfigTemplateForm.class, + ActivityDefinition.SEB_EXAM_CONFIG_TEMPLATE), + SEB_EXAM_CONFIG_TEMPLATE_ATTRIBUTE_EDIT( + Type.FORM_EDIT, + ConfigTemplateAttributeForm.class, + ActivityDefinition.SEB_EXAM_CONFIG_TEMPLATE), + + MONITORING_RUNNING_EXAM_LIST(Type.LIST_VIEW, MonitoringRunningExamList.class, ActivityDefinition.MONITORING_EXAMS), + MONITORING_RUNNING_EXAM(Type.FORM_VIEW, MonitoringRunningExam.class, ActivityDefinition.MONITORING_EXAMS), + MONITORING_CLIENT_CONNECTION(Type.FORM_VIEW, MonitoringClientConnection.class, ActivityDefinition.MONITORING_EXAMS), + + USER_ACTIVITY_LOGS(Type.LIST_VIEW, UserActivityLogs.class, ActivityDefinition.USER_ACTIVITY_LOGS), + SEB_CLIENT_LOGS(Type.LIST_VIEW, SebClientLogs.class, ActivityDefinition.SEB_CLIENT_LOGS) + + ; + + public final Type type; + public final Class contentPaneComposer; + public final Class actionPaneComposer; + public final Activity activityAnchor; + + private PageStateDefinitionImpl( + final Type type, + final Class contentPaneComposer, + final Activity activityAnchor) { + + this(type, contentPaneComposer, ActionPane.class, activityAnchor); + } + + private PageStateDefinitionImpl( + final Type type, + final Class contentPaneComposer, + final Class actionPaneComposer, + final Activity activityAnchor) { + + this.type = type; + this.contentPaneComposer = contentPaneComposer; + this.actionPaneComposer = actionPaneComposer; + this.activityAnchor = activityAnchor; + } + + @Override + public Type type() { + return this.type; + } + + @Override + public Class contentPaneComposer() { + return this.contentPaneComposer; + } + + @Override + public Class actionPaneComposer() { + return this.actionPaneComposer; + } + + @Override + public Activity activityAnchor() { + return this.activityAnchor; + } + +} diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/service/ResourceService.java b/src/main/java/ch/ethz/seb/sebserver/gui/service/ResourceService.java index 3cca59d5..432ac387 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/service/ResourceService.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/service/ResourceService.java @@ -1,650 +1,665 @@ -/* - * 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; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.Comparator; -import java.util.EnumSet; -import java.util.List; -import java.util.Locale; -import java.util.Map; -import java.util.function.Function; -import java.util.function.Supplier; -import java.util.stream.Collectors; - -import org.apache.commons.lang3.StringUtils; -import org.joda.time.DateTimeZone; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.context.annotation.Lazy; -import org.springframework.stereotype.Service; - -import ch.ethz.seb.sebserver.gbl.Constants; -import ch.ethz.seb.sebserver.gbl.api.API; -import ch.ethz.seb.sebserver.gbl.api.EntityType; -import ch.ethz.seb.sebserver.gbl.model.Activatable; -import ch.ethz.seb.sebserver.gbl.model.Entity; -import ch.ethz.seb.sebserver.gbl.model.EntityName; -import ch.ethz.seb.sebserver.gbl.model.exam.Exam; -import ch.ethz.seb.sebserver.gbl.model.exam.Exam.ExamStatus; -import ch.ethz.seb.sebserver.gbl.model.exam.Exam.ExamType; -import ch.ethz.seb.sebserver.gbl.model.exam.ExamConfigurationMap; -import ch.ethz.seb.sebserver.gbl.model.exam.Indicator.IndicatorType; -import ch.ethz.seb.sebserver.gbl.model.exam.OpenEdxSebRestriction.PermissionComponent; -import ch.ethz.seb.sebserver.gbl.model.exam.OpenEdxSebRestriction.WhiteListPath; -import ch.ethz.seb.sebserver.gbl.model.institution.LmsSetup.LmsType; -import ch.ethz.seb.sebserver.gbl.model.sebconfig.AttributeType; -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.sebconfig.TemplateAttribute; -import ch.ethz.seb.sebserver.gbl.model.sebconfig.View; -import ch.ethz.seb.sebserver.gbl.model.session.ClientConnection.ConnectionStatus; -import ch.ethz.seb.sebserver.gbl.model.session.ClientConnectionData; -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; -import ch.ethz.seb.sebserver.gbl.model.user.UserLogActivityType; -import ch.ethz.seb.sebserver.gbl.model.user.UserRole; -import ch.ethz.seb.sebserver.gbl.profile.GuiProfile; -import ch.ethz.seb.sebserver.gbl.util.Result; -import ch.ethz.seb.sebserver.gbl.util.Tuple; -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.GetExamNames; -import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.GetExams; -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.seb.examconfig.GetExamConfigNodeNames; -import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.seb.examconfig.GetExamConfigNodes; -import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.seb.examconfig.GetViews; -import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.useraccount.GetUserAccountNames; -import ch.ethz.seb.sebserver.gui.service.remote.webservice.auth.CurrentUser; - -@Lazy -@Service -@GuiProfile -/** Defines functionality to get resources or functions of resources to feed e.g. selection or - * combo-box content. */ -public class ResourceService { - - private static final String MISSING_CLIENT_PING_NAME_KEY = "MISSING"; - - public static final Comparator> RESOURCE_COMPARATOR = (t1, t2) -> t1._2.compareTo(t2._2); - - public static final EnumSet ENTITY_TYPE_EXCLUDE_MAP = EnumSet.of( - EntityType.ADDITIONAL_ATTRIBUTES, - EntityType.CLIENT_CONNECTION, - EntityType.CLIENT_EVENT, - EntityType.CONFIGURATION_ATTRIBUTE, - EntityType.CONFIGURATION_VALUE, - EntityType.CONFIGURATION, - EntityType.ORIENTATION, - EntityType.USER_ACTIVITY_LOG, - EntityType.USER_ROLE, - EntityType.WEBSERVICE_SERVER_INFO); - - public static final EnumSet 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."; - public static final String EXAM_INDICATOR_TYPE_PREFIX = "sebserver.exam.indicator.type."; - public static final String LMSSETUP_TYPE_PREFIX = "sebserver.lmssetup.type."; - public static final String LMSSETUP_PROXY_AUTH_TYPE_PREFIX = "sebserver.lmssetup.form.proxy.auth-type."; - public static final String CONFIG_ATTRIBUTE_TYPE_PREFIX = "sebserver.configtemplate.attr.type."; - public static final String SEB_RESTRICTION_WHITE_LIST_PREFIX = "sebserver.exam.form.sebrestriction.whiteListPaths."; - public static final String SEB_RESTRICTION_PERMISSIONS_PREFIX = "sebserver.exam.form.sebrestriction.permissions."; - - public static final EnumSet ATTRIBUTE_TYPES_NOT_DISPLAYED = EnumSet.of( - AttributeType.LABEL, - AttributeType.COMPOSITE_TABLE, - AttributeType.INLINE_TABLE); - - 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"); - - private final I18nSupport i18nSupport; - private final RestService restService; - private final CurrentUser currentUser; - private final boolean mock_lms_enabled; - - protected ResourceService( - final I18nSupport i18nSupport, - final RestService restService, - final CurrentUser currentUser, - @Value("${sebserver.gui.webservice.mock-lms-enabled:true}") final boolean mock_lms_enabled) { - - this.i18nSupport = i18nSupport; - this.restService = restService; - this.currentUser = currentUser; - this.mock_lms_enabled = mock_lms_enabled; - } - - public I18nSupport getI18nSupport() { - return this.i18nSupport; - } - - public RestService getRestService() { - return this.restService; - } - - public CurrentUser getCurrentUser() { - return this.currentUser; - } - - public Supplier>> localizedResourceSupplier(final List> source) { - return () -> source.stream() - .map(tuple -> new Tuple<>(tuple._1, this.i18nSupport.getText(tuple._2))) - .collect(Collectors.toList()); - } - - public List> activityResources() { - final List> result = new ArrayList<>(); - result.add(new Tuple<>(Constants.TRUE_STRING, this.i18nSupport.getText("sebserver.overall.status.active"))); - result.add(new Tuple<>(Constants.FALSE_STRING, this.i18nSupport.getText("sebserver.overall.status.inactive"))); - result.add(new Tuple<>(StringUtils.EMPTY, this.i18nSupport.getText("sebserver.overall.status.all"))); - return result; - } - - public List> lmsTypeResources() { - return Arrays.asList(LmsType.values()) - .stream() - .filter(lmsType -> lmsType != LmsType.MOCKUP || this.mock_lms_enabled) - .map(lmsType -> new Tuple<>( - lmsType.name(), - this.i18nSupport.getText(LMSSETUP_TYPE_PREFIX + lmsType.name(), lmsType.name()))) - .sorted(RESOURCE_COMPARATOR) - .collect(Collectors.toList()); - } - - public List> clientEventTypeResources() { - return Arrays.asList(EventType.values()) - .stream() - .filter(eventType -> !CLIENT_EVENT_TYPE_EXCLUDE_MAP.contains(eventType)) - .map(eventType -> new Tuple<>( - eventType.name(), - getEventTypeName(eventType))) - .sorted(RESOURCE_COMPARATOR) - .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; - } - return this.i18nSupport.getText(CLIENT_EVENT_TYPE_PREFIX + eventType.name(), eventType.name()); - } - - public List> indicatorTypeResources() { - return Arrays.asList(IndicatorType.values()) - .stream() - .map(type -> new Tuple<>( - type.name(), - this.i18nSupport.getText(EXAM_INDICATOR_TYPE_PREFIX + type.name(), type.name()))) - .sorted(RESOURCE_COMPARATOR) - .collect(Collectors.toList()); - } - - public List> examConfigurationSelectionResources() { - return getExamConfigurationSelection() - .getOr(Collections.emptyList()) - .stream() - .map(entityName -> new Tuple<>(entityName.modelId, entityName.name)) - .sorted(RESOURCE_COMPARATOR) - .collect(Collectors.toList()); - } - - public List> userRoleResources() { - return UserRole.publicRolesForUser(this.currentUser.get()) - .stream() - .map(ur -> new Tuple<>( - ur.name(), - this.i18nSupport.getText(USERACCOUNT_ROLE_PREFIX + ur.name()))) - .sorted(RESOURCE_COMPARATOR) - .collect(Collectors.toList()); - } - - public List> institutionResource() { - return this.restService.getBuilder(GetInstitutionNames.class) - .withQueryParam(Entity.FILTER_ATTR_ACTIVE, Constants.TRUE_STRING) - .call() - .getOr(Collections.emptyList()) - .stream() - .map(entityName -> new Tuple<>(entityName.modelId, entityName.name)) - .sorted(RESOURCE_COMPARATOR) - .collect(Collectors.toList()); - } - - public Function getInstitutionNameFunction() { - final Map idNameMap = this.restService.getBuilder(GetInstitutionNames.class) - .withQueryParam(Entity.FILTER_ATTR_ACTIVE, Constants.TRUE_STRING) - .call() - .getOr(Collections.emptyList()) - .stream() - .collect(Collectors.toMap(e -> e.modelId, e -> e.name)); - - return id -> { - if (!idNameMap.containsKey(id)) { - return Constants.EMPTY_NOTE; - } - return idNameMap.get(id); - }; - } - - public List> lmsSetupResource() { - final boolean isSEBAdmin = this.currentUser.get().hasRole(UserRole.SEB_SERVER_ADMIN); - final String institutionId = (isSEBAdmin) ? "" : String.valueOf(this.currentUser.get().institutionId); - return this.restService.getBuilder(GetLmsSetupNames.class) - .withQueryParam(Entity.FILTER_ATTR_INSTITUTION, institutionId) - .withQueryParam(Entity.FILTER_ATTR_ACTIVE, Constants.TRUE_STRING) - .call() - .getOr(Collections.emptyList()) - .stream() - .map(entityName -> new Tuple<>(entityName.modelId, entityName.name)) - .sorted(RESOURCE_COMPARATOR) - .collect(Collectors.toList()); - } - - public Function getLmsSetupNameFunction() { - final boolean isSEBAdmin = this.currentUser.get().hasRole(UserRole.SEB_SERVER_ADMIN); - final String institutionId = (isSEBAdmin) ? "" : String.valueOf(this.currentUser.get().institutionId); - final Map idNameMap = this.restService.getBuilder(GetLmsSetupNames.class) - .withQueryParam(Entity.FILTER_ATTR_INSTITUTION, institutionId) - .withQueryParam(Entity.FILTER_ATTR_ACTIVE, Constants.TRUE_STRING) - .call() - .getOr(Collections.emptyList()) - .stream() - .collect(Collectors.toMap(e -> e.modelId, e -> e.name)); - - return id -> { - if (!idNameMap.containsKey(id)) { - return Constants.EMPTY_NOTE; - } - return idNameMap.get(id); - }; - } - - public List> entityTypeResources() { - return Arrays.asList(EntityType.values()) - .stream() - .filter(type -> !ENTITY_TYPE_EXCLUDE_MAP.contains(type)) - .map(type -> new Tuple<>(type.name(), getEntityTypeName(type))) - .sorted(RESOURCE_COMPARATOR) - .collect(Collectors.toList()); - } - - public static LocTextKey getEntityTypeNameKey(final EntityType type) { - return new LocTextKey(ENTITY_TYPE_PREFIX + type.name()); - } - - public String getEntityTypeName(final EntityType type) { - if (type == null) { - return Constants.EMPTY_NOTE; - } - return this.i18nSupport.getText(getEntityTypeNameKey(type)); - } - - public String getEntityTypeName(final UserActivityLog userLog) { - return getEntityTypeName(userLog.entityType); - } - - public List> userActivityTypeResources() { - return Arrays.asList(UserLogActivityType.values()) - .stream() - .map(type -> new Tuple<>(type.name(), getUserActivityTypeName(type))) - .sorted(RESOURCE_COMPARATOR) - .collect(Collectors.toList()); - } - - public String getUserActivityTypeName(final UserLogActivityType type) { - if (type == null) { - return Constants.EMPTY_NOTE; - } - return this.i18nSupport.getText(USER_ACTIVITY_TYPE_PREFIX + type.name()); - } - - public String getUserActivityTypeName(final UserActivityLog userLog) { - return getUserActivityTypeName(userLog.activityType); - } - - /** Get a list of language key/name tuples for all supported languages in the - * language of the current users locale. - * - * @param i18nSupport I18nSupport to get the actual current users locale - * @return list of language key/name tuples for all supported languages in the language of the current users - * locale */ - public List> languageResources() { - final Locale currentLocale = this.i18nSupport.getUsersLanguageLocale(); - return this.i18nSupport.supportedLanguages() - .stream() - .map(locale -> new Tuple<>(locale.toLanguageTag(), locale.getDisplayLanguage(currentLocale))) - .filter(tuple -> StringUtils.isNotBlank(tuple._2)) - .sorted(RESOURCE_COMPARATOR) - .collect(Collectors.toList()); - } - - public List> timeZoneResources() { - final Locale currentLocale = this.i18nSupport.getUsersLanguageLocale(); - return DateTimeZone - .getAvailableIDs() - .stream() - .map(id -> new Tuple<>(id, id + " (" + DateTimeZone.forID(id).getName(0, currentLocale) + ")")) - .sorted(RESOURCE_COMPARATOR) - .collect(Collectors.toList()); - } - - public List> examTypeResources() { - return Arrays.asList(ExamType.values()) - .stream() - .filter(type -> type != ExamType.UNDEFINED) - .map(type -> new Tuple<>( - type.name(), - this.i18nSupport.getText(EXAM_TYPE_PREFIX + type.name()))) - .sorted(RESOURCE_COMPARATOR) - .collect(Collectors.toList()); - } - - public List> examConfigStatusResources() { - return examConfigStatusResources(false); - } - - public List> examConfigStatusResources(final boolean isAttachedToExam) { - return Arrays.asList(ConfigurationStatus.values()) - .stream() - .filter(status -> { - if (isAttachedToExam) { - return status != ConfigurationStatus.READY_TO_USE; - } else { - return status != ConfigurationStatus.IN_USE; - } - }) - .map(type -> new Tuple<>( - type.name(), - this.i18nSupport.getText(EXAMCONFIG_STATUS_PREFIX + type.name()))) - .sorted(RESOURCE_COMPARATOR) - .collect(Collectors.toList()); - } - - public List> examSupporterResources() { - final UserInfo userInfo = this.currentUser.get(); - final List selection = this.restService.getBuilder(GetUserAccountNames.class) - .withQueryParam(Entity.FILTER_ATTR_INSTITUTION, String.valueOf(userInfo.institutionId)) - .withQueryParam(Entity.FILTER_ATTR_ACTIVE, Constants.TRUE_STRING) - .withQueryParam(UserInfo.FILTER_ATTR_ROLE, UserRole.EXAM_SUPPORTER.name()) - .call() - .getOr(Collections.emptyList()); - return selection - .stream() - .map(entityName -> new Tuple<>(entityName.modelId, entityName.name)) - .sorted(RESOURCE_COMPARATOR) - .collect(Collectors.toList()); - } - - public List> userResources() { - final UserInfo userInfo = this.currentUser.get(); - return this.restService.getBuilder(GetUserAccountNames.class) - .withQueryParam(Entity.FILTER_ATTR_INSTITUTION, String.valueOf(userInfo.institutionId)) - .withQueryParam(Entity.FILTER_ATTR_ACTIVE, Constants.TRUE_STRING) - .call() - .getOr(Collections.emptyList()) - .stream() - .map(entityName -> new Tuple<>(entityName.modelId, entityName.name)) - .sorted(RESOURCE_COMPARATOR) - .collect(Collectors.toList()); - } - - public Function localizedActivityFunction() { - final Function localizedActivityResource = localizedActivityResource(); - return activatable -> localizedActivityResource.apply(activatable.isActive()); - } - - public Function localizedActivityResource() { - return activity -> activity - ? this.i18nSupport.getText(ACTIVE_TEXT_KEY) - : this.i18nSupport.getText(INACTIVE_TEXT_KEY); - } - - public String localizedExamConfigInstitutionName(final ConfigurationNode config) { - return getInstitutionNameFunction() - .apply(String.valueOf(config.institutionId)); - } - - public String localizedExamConfigStatusName(final ConfigurationNode config) { - if (config.status == null) { - return Constants.EMPTY_NOTE; - } - - return this.i18nSupport - .getText(ResourceService.EXAMCONFIG_STATUS_PREFIX + config.status.name()); - } - - public String localizedExamConfigStatusName(final ExamConfigurationMap config) { - if (config.configStatus == null) { - return Constants.EMPTY_NOTE; - } - - return this.i18nSupport - .getText(ResourceService.EXAMCONFIG_STATUS_PREFIX + config.configStatus.name()); - } - - public String localizedClientConnectionStatusName(final ClientConnectionData connectionData) { - if (connectionData == null) { - final String name = ConnectionStatus.UNDEFINED.name(); - return this.i18nSupport.getText( - SEB_CONNECTION_STATUS_KEY_PREFIX + name, - name); - } - if (connectionData.missingPing) { - return this.i18nSupport.getText( - SEB_CONNECTION_STATUS_KEY_PREFIX + MISSING_CLIENT_PING_NAME_KEY, - MISSING_CLIENT_PING_NAME_KEY); - } else { - return localizedClientConnectionStatusName((connectionData.clientConnection != null) - ? connectionData.clientConnection.status - : ConnectionStatus.UNDEFINED); - } - } - - 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 ExamConfigurationMap examMap) { - if (examMap.examType == null) { - return Constants.EMPTY_NOTE; - } - - return this.i18nSupport - .getText(ResourceService.EXAM_TYPE_PREFIX + examMap.examType.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> getExamLogSelectionResources() { - final UserInfo userInfo = this.currentUser.get(); - return this.restService.getBuilder(GetExams.class) - .withQueryParam(Entity.FILTER_ATTR_INSTITUTION, String.valueOf(userInfo.getInstitutionId())) - .withQueryParam(Exam.FILTER_CACHED_QUIZZES, Constants.TRUE_STRING) - .call() - .getOr(Collections.emptyList()) - .stream() - .filter(exam -> exam != null - && (exam.getStatus() == ExamStatus.RUNNING || exam.getStatus() == ExamStatus.FINISHED)) - .map(exam -> new Tuple<>(exam.getModelId(), exam.name)) - .sorted(RESOURCE_COMPARATOR) - .collect(Collectors.toList()); - } - - public List> getExamResources() { - final UserInfo userInfo = this.currentUser.get(); - return this.restService.getBuilder(GetExamNames.class) - .withQueryParam(Entity.FILTER_ATTR_INSTITUTION, String.valueOf(userInfo.getInstitutionId())) - .withQueryParam(Exam.FILTER_CACHED_QUIZZES, Constants.TRUE_STRING) - .call() - .getOr(Collections.emptyList()) - .stream() - .map(entityName -> new Tuple<>(entityName.modelId, entityName.name)) - .sorted(RESOURCE_COMPARATOR) - .collect(Collectors.toList()); - } - - public Map getExamNameMapping() { - final UserInfo userInfo = this.currentUser.get(); - return this.restService.getBuilder(GetExamNames.class) - .withQueryParam(Entity.FILTER_ATTR_INSTITUTION, String.valueOf(userInfo.getInstitutionId())) - .withQueryParam(Exam.FILTER_CACHED_QUIZZES, Constants.TRUE_STRING) - .call() - .getOr(Collections.emptyList()) - .stream() - .filter(k -> StringUtils.isNotBlank(k.modelId)) - .collect(Collectors.toMap( - k -> Long.valueOf(k.modelId), - k -> k.name)); - } - - public List> getViewResources() { - return getViewResources(API.DEFAULT_CONFIG_TEMPLATE_ID); - } - - public List> getViewResources(final String templateId) { - return this.restService.getBuilder(GetViews.class) - .withQueryParam( - View.FILTER_ATTR_TEMPLATE, - templateId) - .call() - .getOr(Collections.emptyList()) - .stream() - .map(view -> new Tuple<>(view.getModelId(), view.name)) - .sorted(RESOURCE_COMPARATOR) - .collect(Collectors.toList()); - } - - public final Function getViewNameFunction(final String templateId) { - final Map mapping = this.getViewResources(templateId) - .stream() - .collect(Collectors.toMap(tuple -> tuple._1, tuple -> tuple._2)); - - return attr -> mapping.get(attr.getViewModelId()); - } - - public List> getAttributeTypeResources() { - return Arrays.asList(AttributeType.values()) - .stream() - .filter(type -> !ATTRIBUTE_TYPES_NOT_DISPLAYED.contains(type)) - .map(type -> new Tuple<>(getAttributeTypeFilterName(type), getAttributeTypeName(type))) - .sorted(RESOURCE_COMPARATOR) - .collect(Collectors.toList()); - } - - public String getAttributeTypeName(final TemplateAttribute attribute) { - if (attribute != null && attribute.getConfigAttribute() != null) { - return getAttributeTypeName(attribute.getConfigAttribute().type); - } - - return Constants.EMPTY_NOTE; - } - - private String getAttributeTypeFilterName(final AttributeType type) { - if (type == AttributeType.TABLE) { - return type.name() - + Constants.LIST_SEPARATOR - + AttributeType.COMPOSITE_TABLE.name() - + Constants.LIST_SEPARATOR - + AttributeType.INLINE_TABLE.name(); - } else { - return type.name(); - } - } - - public String getAttributeTypeName(final AttributeType type) { - if (type == null) { - return Constants.EMPTY_NOTE; - } - return this.i18nSupport - .getText(CONFIG_ATTRIBUTE_TYPE_PREFIX + type.name()); - } - - public List> getExamConfigTemplateResources() { - final UserInfo userInfo = this.currentUser.get(); - return this.restService.getBuilder(GetExamConfigNodes.class) - .withQueryParam(Entity.FILTER_ATTR_INSTITUTION, String.valueOf(userInfo.getInstitutionId())) - .withQueryParam(ConfigurationNode.FILTER_ATTR_TYPE, ConfigurationType.TEMPLATE.name()) - .call() - .getOr(Collections.emptyList()) - .stream() - .map(node -> new Tuple<>(node.getModelId(), node.name)) - .sorted(RESOURCE_COMPARATOR) - .collect(Collectors.toList()); - } - - public List> sebRestrictionWhiteListResources() { - return Arrays.asList(WhiteListPath.values()) - .stream() - .map(type -> new Tuple<>( - type.key, - this.i18nSupport.getText(SEB_RESTRICTION_WHITE_LIST_PREFIX + type.name(), type.key))) - .sorted(RESOURCE_COMPARATOR) - .collect(Collectors.toList()); - } - - public List> sebRestrictionPermissionResources() { - return Arrays.asList(PermissionComponent.values()) - .stream() - .map(type -> new Tuple<>( - type.key, - this.i18nSupport.getText(SEB_RESTRICTION_PERMISSIONS_PREFIX + type.name(), type.key))) - .sorted(RESOURCE_COMPARATOR) - .collect(Collectors.toList()); - } - - private Result> getExamConfigurationSelection() { - return this.restService.getBuilder(GetExamConfigNodeNames.class) - .withQueryParam( - Entity.FILTER_ATTR_INSTITUTION, - String.valueOf(this.currentUser.get().institutionId)) - .withQueryParam( - ConfigurationNode.FILTER_ATTR_TYPE, - ConfigurationType.EXAM_CONFIG.name()) - .withQueryParam( - ConfigurationNode.FILTER_ATTR_STATUS, - ConfigurationStatus.READY_TO_USE.name()) - .call(); - } - -} +/* + * 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; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.Comparator; +import java.util.EnumSet; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.function.Function; +import java.util.function.Supplier; +import java.util.stream.Collectors; + +import ch.ethz.seb.sebserver.gbl.util.Tuple3; +import ch.ethz.seb.sebserver.gbl.util.Utils; +import org.apache.commons.lang3.StringUtils; +import org.joda.time.DateTimeZone; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Lazy; +import org.springframework.stereotype.Service; + +import ch.ethz.seb.sebserver.gbl.Constants; +import ch.ethz.seb.sebserver.gbl.api.API; +import ch.ethz.seb.sebserver.gbl.api.EntityType; +import ch.ethz.seb.sebserver.gbl.model.Activatable; +import ch.ethz.seb.sebserver.gbl.model.Entity; +import ch.ethz.seb.sebserver.gbl.model.EntityName; +import ch.ethz.seb.sebserver.gbl.model.exam.Exam; +import ch.ethz.seb.sebserver.gbl.model.exam.Exam.ExamStatus; +import ch.ethz.seb.sebserver.gbl.model.exam.Exam.ExamType; +import ch.ethz.seb.sebserver.gbl.model.exam.ExamConfigurationMap; +import ch.ethz.seb.sebserver.gbl.model.exam.Indicator.IndicatorType; +import ch.ethz.seb.sebserver.gbl.model.exam.OpenEdxSebRestriction.PermissionComponent; +import ch.ethz.seb.sebserver.gbl.model.exam.OpenEdxSebRestriction.WhiteListPath; +import ch.ethz.seb.sebserver.gbl.model.institution.LmsSetup.LmsType; +import ch.ethz.seb.sebserver.gbl.model.sebconfig.AttributeType; +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.sebconfig.TemplateAttribute; +import ch.ethz.seb.sebserver.gbl.model.sebconfig.View; +import ch.ethz.seb.sebserver.gbl.model.session.ClientConnection.ConnectionStatus; +import ch.ethz.seb.sebserver.gbl.model.session.ClientConnectionData; +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; +import ch.ethz.seb.sebserver.gbl.model.user.UserLogActivityType; +import ch.ethz.seb.sebserver.gbl.model.user.UserRole; +import ch.ethz.seb.sebserver.gbl.profile.GuiProfile; +import ch.ethz.seb.sebserver.gbl.util.Result; +import ch.ethz.seb.sebserver.gbl.util.Tuple; +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.GetExamNames; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.GetExams; +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.seb.examconfig.GetExamConfigNodeNames; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.seb.examconfig.GetExamConfigNodes; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.seb.examconfig.GetViews; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.useraccount.GetUserAccountNames; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.auth.CurrentUser; + +@Lazy +@Service +@GuiProfile +/** Defines functionality to get resources or functions of resources to feed e.g. selection or + * combo-box content. */ +public class ResourceService { + + private static final String MISSING_CLIENT_PING_NAME_KEY = "MISSING"; + + public static final Comparator> RESOURCE_COMPARATOR = (t1, t2) -> t1._2.compareTo(t2._2); + public static final Comparator> RESOURCE_COMPARATOR_TUPLE_3 = (t1, t2) -> t1._2.compareTo(t2._2); + + public static final EnumSet ENTITY_TYPE_EXCLUDE_MAP = EnumSet.of( + EntityType.ADDITIONAL_ATTRIBUTES, + EntityType.CLIENT_CONNECTION, + EntityType.CLIENT_EVENT, + EntityType.CONFIGURATION_ATTRIBUTE, + EntityType.CONFIGURATION_VALUE, + EntityType.CONFIGURATION, + EntityType.ORIENTATION, + EntityType.USER_ACTIVITY_LOG, + EntityType.USER_ROLE, + EntityType.WEBSERVICE_SERVER_INFO); + + public static final EnumSet 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."; + public static final String EXAM_INDICATOR_TYPE_PREFIX = "sebserver.exam.indicator.type."; + public static final String LMSSETUP_TYPE_PREFIX = "sebserver.lmssetup.type."; + public static final String CONFIG_ATTRIBUTE_TYPE_PREFIX = "sebserver.configtemplate.attr.type."; + public static final String SEB_RESTRICTION_WHITE_LIST_PREFIX = "sebserver.exam.form.sebrestriction.whiteListPaths."; + public static final String SEB_RESTRICTION_PERMISSIONS_PREFIX = "sebserver.exam.form.sebrestriction.permissions."; + + public static final EnumSet ATTRIBUTE_TYPES_NOT_DISPLAYED = EnumSet.of( + AttributeType.LABEL, + AttributeType.COMPOSITE_TABLE, + AttributeType.INLINE_TABLE); + + 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"); + + private final I18nSupport i18nSupport; + private final RestService restService; + private final CurrentUser currentUser; + private final boolean mock_lms_enabled; + + protected ResourceService( + final I18nSupport i18nSupport, + final RestService restService, + final CurrentUser currentUser, + @Value("${sebserver.gui.webservice.mock-lms-enabled:true}") final boolean mock_lms_enabled) { + + this.i18nSupport = i18nSupport; + this.restService = restService; + this.currentUser = currentUser; + this.mock_lms_enabled = mock_lms_enabled; + } + + public I18nSupport getI18nSupport() { + return this.i18nSupport; + } + + public RestService getRestService() { + return this.restService; + } + + public CurrentUser getCurrentUser() { + return this.currentUser; + } + + public Supplier>> localizedResourceSupplier(final List> source) { + return () -> source.stream() + .map(tuple -> new Tuple<>(tuple._1, this.i18nSupport.getText(tuple._2))) + .collect(Collectors.toList()); + } + + public List> activityResources() { + final List> result = new ArrayList<>(); + result.add(new Tuple<>(Constants.TRUE_STRING, this.i18nSupport.getText("sebserver.overall.status.active"))); + result.add(new Tuple<>(Constants.FALSE_STRING, this.i18nSupport.getText("sebserver.overall.status.inactive"))); + result.add(new Tuple<>(StringUtils.EMPTY, this.i18nSupport.getText("sebserver.overall.status.all"))); + return result; + } + + public List> lmsTypeResources() { + return Arrays.asList(LmsType.values()) + .stream() + .filter(lmsType -> lmsType != LmsType.MOCKUP || this.mock_lms_enabled) + .map(lmsType -> new Tuple<>( + lmsType.name(), + this.i18nSupport.getText(LMSSETUP_TYPE_PREFIX + lmsType.name(), lmsType.name()))) + .sorted(RESOURCE_COMPARATOR) + .collect(Collectors.toList()); + } + + public List> clientEventTypeResources() { + return Arrays.asList(EventType.values()) + .stream() + .filter(eventType -> !CLIENT_EVENT_TYPE_EXCLUDE_MAP.contains(eventType)) + .map(eventType -> new Tuple<>( + eventType.name(), + getEventTypeName(eventType))) + .sorted(RESOURCE_COMPARATOR) + .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; + } + return this.i18nSupport.getText(CLIENT_EVENT_TYPE_PREFIX + eventType.name(), eventType.name()); + } + + public List> indicatorTypeResources() { + return Arrays.asList(IndicatorType.values()) + .stream() + .map(type -> new Tuple3<>( + type.name(), + this.i18nSupport.getText(EXAM_INDICATOR_TYPE_PREFIX + type.name(), type.name()), + Utils.formatLineBreaks(this.i18nSupport.getText( + EXAM_INDICATOR_TYPE_PREFIX + type.name() + Constants.TOOLTIP_TEXT_KEY_SUFFIX, + StringUtils.EMPTY)))) + .sorted(RESOURCE_COMPARATOR_TUPLE_3) + .collect(Collectors.toList()); + } + + public List> examConfigurationSelectionResources() { + return getExamConfigurationSelection() + .getOr(Collections.emptyList()) + .stream() + .map(entityName -> new Tuple<>(entityName.modelId, entityName.name)) + .sorted(RESOURCE_COMPARATOR) + .collect(Collectors.toList()); + } + + public List> userRoleResources() { + return UserRole.publicRolesForUser(this.currentUser.get()) + .stream() + .map(ur -> new Tuple3<>( + ur.name(), + this.i18nSupport.getText(USERACCOUNT_ROLE_PREFIX + ur.name()), + Utils.formatLineBreaks(this.i18nSupport.getText( + USERACCOUNT_ROLE_PREFIX + ur.name() + Constants.TOOLTIP_TEXT_KEY_SUFFIX, + StringUtils.EMPTY)))) + .sorted(RESOURCE_COMPARATOR) + .collect(Collectors.toList()); + } + + public List> institutionResource() { + return this.restService.getBuilder(GetInstitutionNames.class) + .withQueryParam(Entity.FILTER_ATTR_ACTIVE, Constants.TRUE_STRING) + .call() + .getOr(Collections.emptyList()) + .stream() + .map(entityName -> new Tuple<>(entityName.modelId, entityName.name)) + .sorted(RESOURCE_COMPARATOR) + .collect(Collectors.toList()); + } + + public Function getInstitutionNameFunction() { + final Map idNameMap = this.restService.getBuilder(GetInstitutionNames.class) + .withQueryParam(Entity.FILTER_ATTR_ACTIVE, Constants.TRUE_STRING) + .call() + .getOr(Collections.emptyList()) + .stream() + .collect(Collectors.toMap(e -> e.modelId, e -> e.name)); + + return id -> { + if (!idNameMap.containsKey(id)) { + return Constants.EMPTY_NOTE; + } + return idNameMap.get(id); + }; + } + + public List> lmsSetupResource() { + final boolean isSEBAdmin = this.currentUser.get().hasRole(UserRole.SEB_SERVER_ADMIN); + final String institutionId = (isSEBAdmin) ? "" : String.valueOf(this.currentUser.get().institutionId); + return this.restService.getBuilder(GetLmsSetupNames.class) + .withQueryParam(Entity.FILTER_ATTR_INSTITUTION, institutionId) + .withQueryParam(Entity.FILTER_ATTR_ACTIVE, Constants.TRUE_STRING) + .call() + .getOr(Collections.emptyList()) + .stream() + .map(entityName -> new Tuple<>(entityName.modelId, entityName.name)) + .sorted(RESOURCE_COMPARATOR) + .collect(Collectors.toList()); + } + + public Function getLmsSetupNameFunction() { + final boolean isSEBAdmin = this.currentUser.get().hasRole(UserRole.SEB_SERVER_ADMIN); + final String institutionId = (isSEBAdmin) ? "" : String.valueOf(this.currentUser.get().institutionId); + final Map idNameMap = this.restService.getBuilder(GetLmsSetupNames.class) + .withQueryParam(Entity.FILTER_ATTR_INSTITUTION, institutionId) + .withQueryParam(Entity.FILTER_ATTR_ACTIVE, Constants.TRUE_STRING) + .call() + .getOr(Collections.emptyList()) + .stream() + .collect(Collectors.toMap(e -> e.modelId, e -> e.name)); + + return id -> { + if (!idNameMap.containsKey(id)) { + return Constants.EMPTY_NOTE; + } + return idNameMap.get(id); + }; + } + + public List> entityTypeResources() { + return Arrays.asList(EntityType.values()) + .stream() + .filter(type -> !ENTITY_TYPE_EXCLUDE_MAP.contains(type)) + .map(type -> new Tuple<>(type.name(), getEntityTypeName(type))) + .sorted(RESOURCE_COMPARATOR) + .collect(Collectors.toList()); + } + + public static LocTextKey getEntityTypeNameKey(final EntityType type) { + return new LocTextKey(ENTITY_TYPE_PREFIX + type.name()); + } + + public String getEntityTypeName(final EntityType type) { + if (type == null) { + return Constants.EMPTY_NOTE; + } + return this.i18nSupport.getText(getEntityTypeNameKey(type)); + } + + public String getEntityTypeName(final UserActivityLog userLog) { + return getEntityTypeName(userLog.entityType); + } + + public List> userActivityTypeResources() { + return Arrays.asList(UserLogActivityType.values()) + .stream() + .map(type -> new Tuple<>(type.name(), getUserActivityTypeName(type))) + .sorted(RESOURCE_COMPARATOR) + .collect(Collectors.toList()); + } + + public String getUserActivityTypeName(final UserLogActivityType type) { + if (type == null) { + return Constants.EMPTY_NOTE; + } + return this.i18nSupport.getText(USER_ACTIVITY_TYPE_PREFIX + type.name()); + } + + public String getUserActivityTypeName(final UserActivityLog userLog) { + return getUserActivityTypeName(userLog.activityType); + } + + /** Get a list of language key/name tuples for all supported languages in the + * language of the current users locale. + * + * @return list of language key/name tuples for all supported languages in the language of the current users + * locale */ + public List> languageResources() { + final Locale currentLocale = this.i18nSupport.getUsersLanguageLocale(); + return this.i18nSupport.supportedLanguages() + .stream() + .map(locale -> new Tuple<>(locale.toLanguageTag(), locale.getDisplayLanguage(currentLocale))) + .filter(tuple -> StringUtils.isNotBlank(tuple._2)) + .sorted(RESOURCE_COMPARATOR) + .collect(Collectors.toList()); + } + + public List> timeZoneResources() { + final Locale currentLocale = this.i18nSupport.getUsersLanguageLocale(); + return DateTimeZone + .getAvailableIDs() + .stream() + .map(id -> new Tuple<>(id, id + " (" + DateTimeZone.forID(id).getName(0, currentLocale) + ")")) + .sorted(RESOURCE_COMPARATOR) + .collect(Collectors.toList()); + } + + public List> examTypeResources() { + return Arrays.asList(ExamType.values()) + .stream() + .filter(type -> type != ExamType.UNDEFINED) + .map(type -> new Tuple3<>( + type.name(), + this.i18nSupport.getText(EXAM_TYPE_PREFIX + type.name()), + Utils.formatLineBreaks(this.i18nSupport.getText( + this.i18nSupport.getText(EXAM_TYPE_PREFIX + type.name()) + Constants.TOOLTIP_TEXT_KEY_SUFFIX, + StringUtils.EMPTY)) + )) + .sorted(RESOURCE_COMPARATOR) + .collect(Collectors.toList()); + } + + public List> examConfigStatusResources() { + return examConfigStatusResources(false); + } + + public List> examConfigStatusResources(final boolean isAttachedToExam) { + return Arrays.asList(ConfigurationStatus.values()) + .stream() + .filter(status -> { + if (isAttachedToExam) { + return status != ConfigurationStatus.READY_TO_USE; + } else { + return status != ConfigurationStatus.IN_USE; + } + }) + .map(type -> new Tuple3<>( + type.name(), + this.i18nSupport.getText(EXAMCONFIG_STATUS_PREFIX + type.name()), + Utils.formatLineBreaks(this.i18nSupport.getText( + this.i18nSupport.getText(EXAMCONFIG_STATUS_PREFIX + type.name()) + Constants.TOOLTIP_TEXT_KEY_SUFFIX, + StringUtils.EMPTY)) + )) + .sorted(RESOURCE_COMPARATOR) + .collect(Collectors.toList()); + } + + public List> examSupporterResources() { + final UserInfo userInfo = this.currentUser.get(); + final List selection = this.restService.getBuilder(GetUserAccountNames.class) + .withQueryParam(Entity.FILTER_ATTR_INSTITUTION, String.valueOf(userInfo.institutionId)) + .withQueryParam(Entity.FILTER_ATTR_ACTIVE, Constants.TRUE_STRING) + .withQueryParam(UserInfo.FILTER_ATTR_ROLE, UserRole.EXAM_SUPPORTER.name()) + .call() + .getOr(Collections.emptyList()); + return selection + .stream() + .map(entityName -> new Tuple<>(entityName.modelId, entityName.name)) + .sorted(RESOURCE_COMPARATOR) + .collect(Collectors.toList()); + } + + public List> userResources() { + final UserInfo userInfo = this.currentUser.get(); + return this.restService.getBuilder(GetUserAccountNames.class) + .withQueryParam(Entity.FILTER_ATTR_INSTITUTION, String.valueOf(userInfo.institutionId)) + .withQueryParam(Entity.FILTER_ATTR_ACTIVE, Constants.TRUE_STRING) + .call() + .getOr(Collections.emptyList()) + .stream() + .map(entityName -> new Tuple<>(entityName.modelId, entityName.name)) + .sorted(RESOURCE_COMPARATOR) + .collect(Collectors.toList()); + } + + public Function localizedActivityFunction() { + final Function localizedActivityResource = localizedActivityResource(); + return activatable -> localizedActivityResource.apply(activatable.isActive()); + } + + public Function localizedActivityResource() { + return activity -> activity + ? this.i18nSupport.getText(ACTIVE_TEXT_KEY) + : this.i18nSupport.getText(INACTIVE_TEXT_KEY); + } + + public String localizedExamConfigInstitutionName(final ConfigurationNode config) { + return getInstitutionNameFunction() + .apply(String.valueOf(config.institutionId)); + } + + public String localizedExamConfigStatusName(final ConfigurationNode config) { + if (config.status == null) { + return Constants.EMPTY_NOTE; + } + + return this.i18nSupport + .getText(ResourceService.EXAMCONFIG_STATUS_PREFIX + config.status.name()); + } + + public String localizedExamConfigStatusName(final ExamConfigurationMap config) { + if (config.configStatus == null) { + return Constants.EMPTY_NOTE; + } + + return this.i18nSupport + .getText(ResourceService.EXAMCONFIG_STATUS_PREFIX + config.configStatus.name()); + } + + public String localizedClientConnectionStatusName(final ClientConnectionData connectionData) { + if (connectionData == null) { + final String name = ConnectionStatus.UNDEFINED.name(); + return this.i18nSupport.getText( + SEB_CONNECTION_STATUS_KEY_PREFIX + name, + name); + } + if (connectionData.missingPing) { + return this.i18nSupport.getText( + SEB_CONNECTION_STATUS_KEY_PREFIX + MISSING_CLIENT_PING_NAME_KEY, + MISSING_CLIENT_PING_NAME_KEY); + } else { + return localizedClientConnectionStatusName((connectionData.clientConnection != null) + ? connectionData.clientConnection.status + : ConnectionStatus.UNDEFINED); + } + } + + 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 ExamConfigurationMap examMap) { + if (examMap.examType == null) { + return Constants.EMPTY_NOTE; + } + + return this.i18nSupport + .getText(ResourceService.EXAM_TYPE_PREFIX + examMap.examType.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> getExamLogSelectionResources() { + final UserInfo userInfo = this.currentUser.get(); + return this.restService.getBuilder(GetExams.class) + .withQueryParam(Entity.FILTER_ATTR_INSTITUTION, String.valueOf(userInfo.getInstitutionId())) + .withQueryParam(Exam.FILTER_CACHED_QUIZZES, Constants.TRUE_STRING) + .call() + .getOr(Collections.emptyList()) + .stream() + .filter(exam -> exam != null + && (exam.getStatus() == ExamStatus.RUNNING || exam.getStatus() == ExamStatus.FINISHED)) + .map(exam -> new Tuple<>(exam.getModelId(), exam.name)) + .sorted(RESOURCE_COMPARATOR) + .collect(Collectors.toList()); + } + + public List> getExamResources() { + final UserInfo userInfo = this.currentUser.get(); + return this.restService.getBuilder(GetExamNames.class) + .withQueryParam(Entity.FILTER_ATTR_INSTITUTION, String.valueOf(userInfo.getInstitutionId())) + .withQueryParam(Exam.FILTER_CACHED_QUIZZES, Constants.TRUE_STRING) + .call() + .getOr(Collections.emptyList()) + .stream() + .map(entityName -> new Tuple<>(entityName.modelId, entityName.name)) + .sorted(RESOURCE_COMPARATOR) + .collect(Collectors.toList()); + } + + public Map getExamNameMapping() { + final UserInfo userInfo = this.currentUser.get(); + return this.restService.getBuilder(GetExamNames.class) + .withQueryParam(Entity.FILTER_ATTR_INSTITUTION, String.valueOf(userInfo.getInstitutionId())) + .withQueryParam(Exam.FILTER_CACHED_QUIZZES, Constants.TRUE_STRING) + .call() + .getOr(Collections.emptyList()) + .stream() + .filter(k -> StringUtils.isNotBlank(k.modelId)) + .collect(Collectors.toMap( + k -> Long.valueOf(k.modelId), + k -> k.name)); + } + + public List> getViewResources() { + return getViewResources(API.DEFAULT_CONFIG_TEMPLATE_ID); + } + + public List> getViewResources(final String templateId) { + return this.restService.getBuilder(GetViews.class) + .withQueryParam( + View.FILTER_ATTR_TEMPLATE, + templateId) + .call() + .getOr(Collections.emptyList()) + .stream() + .map(view -> new Tuple<>(view.getModelId(), view.name)) + .sorted(RESOURCE_COMPARATOR) + .collect(Collectors.toList()); + } + + public final Function getViewNameFunction(final String templateId) { + final Map mapping = this.getViewResources(templateId) + .stream() + .collect(Collectors.toMap(tuple -> tuple._1, tuple -> tuple._2)); + + return attr -> mapping.get(attr.getViewModelId()); + } + + public List> getAttributeTypeResources() { + return Arrays.asList(AttributeType.values()) + .stream() + .filter(type -> !ATTRIBUTE_TYPES_NOT_DISPLAYED.contains(type)) + .map(type -> new Tuple<>(getAttributeTypeFilterName(type), getAttributeTypeName(type))) + .sorted(RESOURCE_COMPARATOR) + .collect(Collectors.toList()); + } + + public String getAttributeTypeName(final TemplateAttribute attribute) { + if (attribute != null && attribute.getConfigAttribute() != null) { + return getAttributeTypeName(attribute.getConfigAttribute().type); + } + + return Constants.EMPTY_NOTE; + } + + private String getAttributeTypeFilterName(final AttributeType type) { + if (type == AttributeType.TABLE) { + return type.name() + + Constants.LIST_SEPARATOR + + AttributeType.COMPOSITE_TABLE.name() + + Constants.LIST_SEPARATOR + + AttributeType.INLINE_TABLE.name(); + } else { + return type.name(); + } + } + + public String getAttributeTypeName(final AttributeType type) { + if (type == null) { + return Constants.EMPTY_NOTE; + } + return this.i18nSupport + .getText(CONFIG_ATTRIBUTE_TYPE_PREFIX + type.name()); + } + + public List> getExamConfigTemplateResources() { + final UserInfo userInfo = this.currentUser.get(); + return this.restService.getBuilder(GetExamConfigNodes.class) + .withQueryParam(Entity.FILTER_ATTR_INSTITUTION, String.valueOf(userInfo.getInstitutionId())) + .withQueryParam(ConfigurationNode.FILTER_ATTR_TYPE, ConfigurationType.TEMPLATE.name()) + .call() + .getOr(Collections.emptyList()) + .stream() + .map(node -> new Tuple<>(node.getModelId(), node.name)) + .sorted(RESOURCE_COMPARATOR) + .collect(Collectors.toList()); + } + + public List> sebRestrictionWhiteListResources() { + return Arrays.asList(WhiteListPath.values()) + .stream() + .map(type -> new Tuple<>( + type.key, + this.i18nSupport.getText(SEB_RESTRICTION_WHITE_LIST_PREFIX + type.name(), type.key))) + .sorted(RESOURCE_COMPARATOR) + .collect(Collectors.toList()); + } + + public List> sebRestrictionPermissionResources() { + return Arrays.asList(PermissionComponent.values()) + .stream() + .map(type -> new Tuple<>( + type.key, + this.i18nSupport.getText(SEB_RESTRICTION_PERMISSIONS_PREFIX + type.name(), type.key))) + .sorted(RESOURCE_COMPARATOR) + .collect(Collectors.toList()); + } + + private Result> getExamConfigurationSelection() { + return this.restService.getBuilder(GetExamConfigNodeNames.class) + .withQueryParam( + Entity.FILTER_ATTR_INSTITUTION, + String.valueOf(this.currentUser.get().institutionId)) + .withQueryParam( + ConfigurationNode.FILTER_ATTR_TYPE, + ConfigurationType.EXAM_CONFIG.name()) + .withQueryParam( + ConfigurationNode.FILTER_ATTR_STATUS, + ConfigurationStatus.READY_TO_USE.name()) + .call(); + } + +} diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/service/i18n/I18nSupport.java b/src/main/java/ch/ethz/seb/sebserver/gui/service/i18n/I18nSupport.java index f94d4f33..1376f307 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/service/i18n/I18nSupport.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/service/i18n/I18nSupport.java @@ -1,181 +1,186 @@ -/* - * Copyright (c) 2018 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.i18n; - -import java.util.Collection; -import java.util.Locale; - -import org.joda.time.DateTime; - -import ch.ethz.seb.sebserver.gbl.util.Utils; - -public interface I18nSupport { - - public static final String SUPPORTED_LANGUAGES_KEY = "sebserver.gui.supported.languages"; - public static final String MULTILINGUAL_KEY = "sebserver.gui.multilingual"; - public static final String FORMAL_LOCALE_KEY = "sebserver.gui.date.displayformat"; - public static final String ATTR_CURRENT_SESSION_LOCALE = "CURRENT_SESSION_LOCALE"; - - /** Get all supported languages as a collection of Locale - * - * @return all supported languages as a collection of Locale */ - Collection supportedLanguages(); - - /** Get the current users language based Locale (from user info language selection) - * Or the default language Locale if the user has not defined any language - * - * @return the current user language Locale to use in context */ - Locale getUsersLanguageLocale(); - - /** Get the current users format based Locale (from user info format selection) - * Or the default format Locale if the user has not defined any language - * - * @return the current user format Locale to use in context */ - Locale getUsersFormatLocale(); - - /** Format a DateTime 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 offset information if the currents user time-zone is different form UTC - * - * @param date the DateTime instance - * @return date formatted date String to display */ - String formatDisplayDate(DateTime date); - - /** Format a DateTime to a text format to display with additional time zone name at the end. - * This uses the date-format defined by either the attribute 'sebserver.gui.date.displayformat' - * or the Constants.DEFAULT_DISPLAY_DATE_FORMAT - * - * Adds time-zone offset 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 formatDisplayDateWithTimeZone(final DateTime date) { - return formatDisplayDateTime(date) + " " + this.getUsersTimeZoneTitleSuffix(); - } - - /** 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 - * - * 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 */ - 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 - * - * 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 */ - 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. - * - * @return date/time column title suffix for current user */ - String getUsersTimeZoneTitleSuffix(); - - /** Get localized text of specified key for currently set Locale. - * - * @param key LocTextKey instance - * @param def default text - * @return the text in current language parsed from localized text */ - default String getText(final LocTextKey key, final String def) { - return getText(key.name, def, key.args); - } - - /** Get localized text of specified key for currently set Locale. - * - * @param key LocTextKey instance - * @return the text in current language parsed from localized text */ - default String getText(final LocTextKey key) { - return getText(key.name, key.name, key.args); - } - - /** Get localized text of specified key for currently set Locale. - * - * @param key the key name of localized text - * @param args additional arguments to parse the localized text - * @return the text in current language parsed from localized text */ - default String getText(final String key, final Object... args) { - return getText(key, key, args); - } - - /** Get localized text of specified key for currently set Locale. - * - * @param key the key name of localized text - * @param def default text that is returned if no localized test with specified key was found - * @param args additional arguments to parse the localized text - * @return the text in current language parsed from localized text */ - String getText(final String key, String def, Object... args); - - /** Get localized text of specified key and Locale. - * - * @param key the key name of localized text - * @param locale the Locale - * @param args additional arguments to parse the localized text - * @return the text in current language parsed from localized text */ - default String getText(final String key, final Locale locale, final Object... args) { - return getText(key, locale, key, args); - } - - /** Get localized text of specified key and Locale. - * - * @param key the key name of localized text - * @param locale the Locale - * @param def default text that is returned if no localized test with specified key was found - * @param args additional arguments to parse the localized text - * @return the text in current language parsed from localized text */ - String getText(String key, Locale locale, String def, Object... args); - - boolean hasText(LocTextKey locTextKey); - -} +/* + * Copyright (c) 2018 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.i18n; + +import java.util.Collection; +import java.util.Locale; + +import org.joda.time.DateTime; + +import ch.ethz.seb.sebserver.gbl.util.Utils; + +public interface I18nSupport { + + public static final String SUPPORTED_LANGUAGES_KEY = "sebserver.gui.supported.languages"; + public static final String MULTILINGUAL_KEY = "sebserver.gui.multilingual"; + public static final String FORMAL_LOCALE_KEY = "sebserver.gui.date.displayformat"; + public static final String ATTR_CURRENT_SESSION_LOCALE = "CURRENT_SESSION_LOCALE"; + + /** Get all supported languages as a collection of Locale + * + * @return all supported languages as a collection of Locale */ + Collection supportedLanguages(); + + /** Get the current users language based Locale (from user info language selection) + * Or the default language Locale if the user has not defined any language + * + * @return the current user language Locale to use in context */ + Locale getUsersLanguageLocale(); + + /** Get the current users format based Locale (from user info format selection) + * Or the default format Locale if the user has not defined any language + * + * @return the current user format Locale to use in context */ + Locale getUsersFormatLocale(); + + /** Format a DateTime 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 offset information if the currents user time-zone is different form UTC + * + * @param date the DateTime instance + * @return date formatted date String to display */ + String formatDisplayDate(DateTime date); + + /** Format a DateTime to a text format to display with additional time zone name at the end. + * This uses the date-format defined by either the attribute 'sebserver.gui.date.displayformat' + * or the Constants.DEFAULT_DISPLAY_DATE_FORMAT + * + * Adds time-zone offset 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 formatDisplayDateWithTimeZone(final DateTime date) { + return formatDisplayDateTime(date) + " " + this.getUsersTimeZoneTitleSuffix(); + } + + /** 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 + * + * 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 */ + 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 + * + * 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 */ + 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. + * + * @return date/time column title suffix for current user */ + String getUsersTimeZoneTitleSuffix(); + + /** Get localized text of specified key for currently set Locale. + * + * @param key LocTextKey instance + * @param def default text + * @return the text in current language parsed from localized text */ + default String getText(final LocTextKey key, final String def) { + return getText(key.name, def, key.args); + } + + /** Get localized text of specified key for currently set Locale. + * + * @param key LocTextKey instance + * @return the text in current language parsed from localized text */ + default String getText(final LocTextKey key) { + return getText(key.name, key.name, key.args); + } + + /** Get localized text of specified key for currently set Locale. + * + * @param key the key name of localized text + * @param args additional arguments to parse the localized text + * @return the text in current language parsed from localized text */ + default String getText(final String key, final Object... args) { + return getText(key, key, args); + } + + /** Get localized text of specified key for currently set Locale. + * + * @param key the key name of localized text + * @param def default text that is returned if no localized test with specified key was found + * @param args additional arguments to parse the localized text + * @return the text in current language parsed from localized text */ + String getText(final String key, String def, Object... args); + + /** Get localized text of specified key and Locale. + * + * @param key the key name of localized text + * @param locale the Locale + * @param args additional arguments to parse the localized text + * @return the text in current language parsed from localized text */ + default String getText(final String key, final Locale locale, final Object... args) { + return getText(key, locale, key, args); + } + + /** Get localized text of specified key and Locale. + * + * @param key the key name of localized text + * @param locale the Locale + * @param def default text that is returned if no localized test with specified key was found + * @param args additional arguments to parse the localized text + * @return the text in current language parsed from localized text */ + String getText(String key, Locale locale, String def, Object... args); + + /** Indicates if there is a localized text defined for a specified LocTextKey + * + * @param locTextKey the LocTextKey instance + * @return true if there is a localized text defined for a specified LocTextKey, false otherwise + */ + boolean hasText(LocTextKey locTextKey); + +} diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/service/i18n/impl/PolyglotPageServiceImpl.java b/src/main/java/ch/ethz/seb/sebserver/gui/service/i18n/impl/PolyglotPageServiceImpl.java index b37c1b37..3e3d5a5d 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/service/i18n/impl/PolyglotPageServiceImpl.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/service/i18n/impl/PolyglotPageServiceImpl.java @@ -1,296 +1,294 @@ -/* - * Copyright (c) 2018 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.i18n.impl; - -import java.util.Locale; -import java.util.function.Consumer; - -import org.apache.commons.lang3.StringUtils; -import org.eclipse.rap.rwt.RWT; -import org.eclipse.swt.SWT; -import org.eclipse.swt.widgets.Button; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Control; -import org.eclipse.swt.widgets.Group; -import org.eclipse.swt.widgets.Label; -import org.eclipse.swt.widgets.TabFolder; -import org.eclipse.swt.widgets.TabItem; -import org.eclipse.swt.widgets.Table; -import org.eclipse.swt.widgets.TableColumn; -import org.eclipse.swt.widgets.TableItem; -import org.eclipse.swt.widgets.Tree; -import org.eclipse.swt.widgets.TreeItem; -import org.springframework.context.annotation.Lazy; -import org.springframework.stereotype.Service; - -import ch.ethz.seb.sebserver.gbl.profile.GuiProfile; -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.i18n.PolyglotPageService; -import ch.ethz.seb.sebserver.gui.service.page.ComposerService; -import ch.ethz.seb.sebserver.gui.service.page.PageContext; -import ch.ethz.seb.sebserver.gui.widget.ImageUploadSelection; - -/** Service that supports page language change on the fly */ -@Lazy -@Service -@GuiProfile -public final class PolyglotPageServiceImpl implements PolyglotPageService { - - private final I18nSupport i18nSupport; - - public PolyglotPageServiceImpl(final I18nSupport i18nSupport) { - this.i18nSupport = i18nSupport; - } - - @Override - public I18nSupport getI18nSupport() { - return this.i18nSupport; - } - - @Override - public void setDefaultPageLocale(final Composite root) { - setPageLocale(root, this.i18nSupport.getUsersLanguageLocale()); - } - - @Override - @SuppressWarnings("unchecked") - public void setPageLocale(final Composite root, final Locale locale) { - RWT.getUISession() - .getHttpSession() - .setAttribute(I18nSupport.ATTR_CURRENT_SESSION_LOCALE, locale); - - ComposerService.traversePageTree( - root, - comp -> comp.getData(POLYGLOT_WIDGET_FUNCTION_KEY) != null, - comp -> ((Consumer) comp.getData(POLYGLOT_WIDGET_FUNCTION_KEY)).accept(comp)); - - root.layout(true, true); - } - - @Override - public void injectI18n(final ImageUploadSelection imageUpload, final LocTextKey locTextKey) { - final Consumer imageUploadFunction = iu -> { - if (locTextKey != null) { - iu.setSelectionText(this.i18nSupport.getText(locTextKey)); - } - }; - imageUpload.setData(POLYGLOT_WIDGET_FUNCTION_KEY, imageUploadFunction); - imageUploadFunction.accept(imageUpload); - } - - @Override - public void injectI18n(final Label label, final LocTextKey locTextKey) { - injectI18n(label, locTextKey, null); - } - - @Override - public void injectI18n(final Label label, final LocTextKey locTextKey, final LocTextKey locToolTipKey) { - final Consumer