GUI improvements

This commit is contained in:
anhefti 2020-02-13 16:46:36 +01:00
parent 8ea0def877
commit 25485b1fd4
28 changed files with 7160 additions and 6921 deletions

View file

@ -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 =
"<?xml version=\"1.0\" encoding=\"utf-8\"?>";
public static final String XML_DOCTYPE_HEADER =
"<!DOCTYPE plist PUBLIC \"-//Apple Computer//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">";
public static final String XML_PLIST_START_V1 =
"<plist version=\"1.0\">";
public static final String XML_PLIST_END =
"</plist>";
public static final String XML_DICT_START =
"<dict>";
public static final String XML_DICT_END =
"</dict>";
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<Collection<APIMessage>> TYPE_REFERENCE_API_MESSAGE =
new TypeReferenceAPIMessage();
public static final ParameterizedTypeReference<Collection<Privilege>> TYPE_REFERENCE_PRIVILEGES =
new TypeReferencePrivileges();
public static final class TypeReferenceAPIMessage extends TypeReference<Collection<APIMessage>> {
}
public static final class TypeReferencePrivileges extends ParameterizedTypeReference<Collection<Privilege>> {
}
}
/*
* 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 =
"<?xml version=\"1.0\" encoding=\"utf-8\"?>";
public static final String XML_DOCTYPE_HEADER =
"<!DOCTYPE plist PUBLIC \"-//Apple Computer//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">";
public static final String XML_PLIST_START_V1 =
"<plist version=\"1.0\">";
public static final String XML_PLIST_END =
"</plist>";
public static final String XML_DICT_START =
"<dict>";
public static final String XML_DICT_END =
"</dict>";
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<Collection<APIMessage>> TYPE_REFERENCE_API_MESSAGE =
new TypeReferenceAPIMessage();
public static final ParameterizedTypeReference<Collection<Privilege>> TYPE_REFERENCE_PRIVILEGES =
new TypeReferencePrivileges();
public static final class TypeReferenceAPIMessage extends TypeReference<Collection<APIMessage>> {
}
public static final class TypeReferencePrivileges extends ParameterizedTypeReference<Collection<Privilege>> {
}
}

View file

@ -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<String> 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<String> 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<String> getRoles() {
return this.roles;
}
@Override
@JsonIgnore
public EnumSet<UserRole> 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<String> 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<String> 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<String> getRoles() {
return this.roles;
}
@Override
@JsonIgnore
public EnumSet<UserRole> 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);
}
}

View file

@ -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<String> 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<String> 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<String> getRoles() {
return this.roles;
}
@Override
@JsonIgnore
public EnumSet<UserRole> getUserRoles() {
final List<UserRole> 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<String> 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<String> 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<String> getRoles() {
return this.roles;
}
@Override
@JsonIgnore
public EnumSet<UserRole> getUserRoles() {
final List<UserRole> 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);
}
}

View file

@ -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<T> {
/** 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<T> {
/** 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 extends Tuple<T>> TT adaptTo(Class<TT> 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 + ")";
}
}

View file

@ -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<T> extends Tuple<T> {
/** 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;
}
}

View file

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

View file

@ -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<ConfigurationNode> 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<TemplateAttribute> 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<TemplateAttribute>(
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<TemplateAttribute> attrTable) {
final PageAction resetToDefaults = this.examConfigurationService.resetToDefaults(action);
// reload the list
attrTable.applyFilter();
return resetToDefaults;
}
private final PageAction removeFormView(
final PageAction action,
final EntityTable<TemplateAttribute> attrTable) {
final PageAction removeFormView = this.examConfigurationService.removeFromView(action);
// reload the list
attrTable.applyFilter();
return removeFormView;
}
private final PageAction attachView(
final PageAction action,
final EntityTable<TemplateAttribute> 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<ConfigurationNode> 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<TemplateAttribute> 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<TemplateAttribute>(
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<TemplateAttribute> attrTable) {
final PageAction resetToDefaults = this.examConfigurationService.resetToDefaults(action);
// reload the list
attrTable.applyFilter();
return resetToDefaults;
}
private PageAction removeFormView(
final PageAction action,
final EntityTable<TemplateAttribute> attrTable) {
final PageAction removeFormView = this.examConfigurationService.removeFromView(action);
// reload the list
attrTable.applyFilter();
return removeFormView;
}
private PageAction attachView(
final PageAction action,
final EntityTable<TemplateAttribute> attrTable) {
final PageAction attachView = this.examConfigurationService.attachToDefaultView(action);
// reload the list
attrTable.applyFilter();
return attachView;
}
}

View file

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

File diff suppressed because it is too large Load diff

View file

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

View file

@ -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<Institution> 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().<Institution> 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<Institution> 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().<Institution> 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);
}
}

View file

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

View file

@ -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<SebClientConfig> 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().<SebClientConfig> 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<SebClientConfig, String> 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<SebClientConfig> 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().<SebClientConfig> 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<SebClientConfig, String> clientConfigInstitutionNameFunction(
final ResourceService resourceService) {
return config -> resourceService.getInstitutionNameFunction()
.apply(String.valueOf(config.institutionId));
}
}

View file

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

View file

@ -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<Tuple<String>> examConfigTemplateResources = resourceService.getExamConfigTemplateResources();
final FormHandle<ConfigurationNode> 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<ExamConfigurationMap> 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<ExamConfigurationMap>(
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<ExamConfigurationMap> 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<ExamConfigurationMap> 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<ConfigurationNode> 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<PageAction, PageAction> 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<Void> 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<Tuple<String>> examConfigTemplateResources = resourceService.getExamConfigTemplateResources();
final FormHandle<ConfigurationNode> 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<ExamConfigurationMap> 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<ExamConfigurationMap> 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<ExamConfigurationMap> 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<ConfigurationNode> 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<PageAction, PageAction> 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<Void> 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;
};
}
}

View file

@ -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<View> views = this.examConfigurationService.getViews(attributes);
final TabFolder tabFolder = widgetFactory.tabFolderLocalized(content);
tabFolder.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false));
final List<ViewContext> 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<APIMessage> 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<View> views = this.examConfigurationService.getViews(attributes);
final TabFolder tabFolder = widgetFactory.tabFolderLocalized(content);
tabFolder.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false));
final List<ViewContext> 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<APIMessage> 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);
}
}
}
}

View file

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

View file

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

View file

@ -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<UserInfo> 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().<UserInfo> 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<UserInfo, String> userInstitutionNameFunction(final ResourceService resourceService) {
final Function<String, String> 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<UserInfo> 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().<UserInfo> 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<UserInfo, String> userInstitutionNameFunction(final ResourceService resourceService) {
final Function<String, String> institutionNameFunction = resourceService.getInstitutionNameFunction();
return userInfo -> institutionNameFunction.apply(String.valueOf(userInfo.institutionId));
}
}

View file

@ -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<UserActivityLog, String> 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<UserActivityLog> 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<UserActivityLog>(
Domain.USER_ACTIVITY_LOG.ATTR_ACTIVITY_TYPE,
ACTIVITY_TEXT_KEY,
this.resourceService::getUserActivityTypeName)
.withFilter(this.activityFilter)
.sortable())
.withColumn(new ColumnDefinition<UserActivityLog>(
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<Void> 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<UserActivityLog, String> 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<UserActivityLog> 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<UserActivityLog>(
Domain.USER_ACTIVITY_LOG.ATTR_ACTIVITY_TYPE,
ACTIVITY_TEXT_KEY,
this.resourceService::getUserActivityTypeName)
.withFilter(this.activityFilter)
.sortable())
.withColumn(new ColumnDefinition<UserActivityLog>(
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<Void> 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();
}
}

View file

@ -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<? extends TemplateComposer> contentPaneComposer;
public final Class<? extends TemplateComposer> actionPaneComposer;
public final Activity activityAnchor;
private PageStateDefinitionImpl(
final Type type,
final Class<? extends TemplateComposer> contentPaneComposer,
final Activity activityAnchor) {
this(type, contentPaneComposer, ActionPane.class, activityAnchor);
}
private PageStateDefinitionImpl(
final Type type,
final Class<? extends TemplateComposer> contentPaneComposer,
final Class<? extends TemplateComposer> 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<? extends TemplateComposer> contentPaneComposer() {
return this.contentPaneComposer;
}
@Override
public Class<? extends TemplateComposer> 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<? extends TemplateComposer> contentPaneComposer;
public final Class<? extends TemplateComposer> actionPaneComposer;
public final Activity activityAnchor;
private PageStateDefinitionImpl(
final Type type,
final Class<? extends TemplateComposer> contentPaneComposer,
final Activity activityAnchor) {
this(type, contentPaneComposer, ActionPane.class, activityAnchor);
}
private PageStateDefinitionImpl(
final Type type,
final Class<? extends TemplateComposer> contentPaneComposer,
final Class<? extends TemplateComposer> 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<? extends TemplateComposer> contentPaneComposer() {
return this.contentPaneComposer;
}
@Override
public Class<? extends TemplateComposer> actionPaneComposer() {
return this.actionPaneComposer;
}
@Override
public Activity activityAnchor() {
return this.activityAnchor;
}
}

View file

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

View file

@ -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<Control>) comp.getData(POLYGLOT_WIDGET_FUNCTION_KEY)).accept(comp));
root.layout(true, true);
}
@Override
public void injectI18n(final ImageUploadSelection imageUpload, final LocTextKey locTextKey) {
final Consumer<ImageUploadSelection> 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<Label> labelFunction = labelFunction(locTextKey, locToolTipKey, this.i18nSupport);
label.setData(POLYGLOT_WIDGET_FUNCTION_KEY, labelFunction);
labelFunction.accept(label);
}
@Override
public void injectI18n(final Group group, final LocTextKey locTextKey, final LocTextKey locTooltipKey) {
final Consumer<Group> groupFunction = groupFunction(locTextKey, locTooltipKey, this.i18nSupport);
group.setData(POLYGLOT_WIDGET_FUNCTION_KEY, groupFunction);
groupFunction.accept(group);
}
@Override
public void injectI18n(final Button button, final LocTextKey locTextKey) {
injectI18n(button, locTextKey, null);
}
@Override
public void injectI18n(final Button button, final LocTextKey locTextKey, final LocTextKey locToolTipKey) {
final Consumer<Button> buttonFunction = b -> {
if (locTextKey != null) {
b.setText(this.i18nSupport.getText(locTextKey));
}
if (locToolTipKey != null) {
b.setToolTipText(Utils.formatLineBreaks(this.i18nSupport.getText(locToolTipKey)));
}
};
button.setData(POLYGLOT_WIDGET_FUNCTION_KEY, buttonFunction);
buttonFunction.accept(button);
}
@Override
public void injectI18n(final Tree tree) {
tree.setData(
POLYGLOT_WIDGET_FUNCTION_KEY,
(Consumer<Tree>) t -> updateLocale(t.getItems(), this.i18nSupport));
}
@Override
public void injectI18n(final TreeItem treeItem, final LocTextKey locTextKey) {
treeItem.setData(POLYGLOT_ITEM_TEXT_DATA_KEY, locTextKey);
treeItem.setText(this.i18nSupport.getText(locTextKey));
}
@Override
public void injectI18n(final Table table) {
table.setData(
POLYGLOT_WIDGET_FUNCTION_KEY,
(Consumer<Table>) t -> {
updateLocale(t.getColumns(), this.i18nSupport);
updateLocale(t.getItems(), this.i18nSupport);
});
}
@Override
public void injectI18n(final TabFolder tabFolder) {
tabFolder.setData(
POLYGLOT_WIDGET_FUNCTION_KEY,
(Consumer<TabFolder>) t -> updateLocale(t.getItems(), this.i18nSupport));
}
@Override
public void injectI18n(final TableColumn tableColumn, final LocTextKey locTextKey, final LocTextKey locTooltipKey) {
tableColumn.setData(POLYGLOT_ITEM_TEXT_DATA_KEY, locTextKey);
tableColumn.setText(this.i18nSupport.getText(locTextKey));
if (this.i18nSupport.hasText(locTooltipKey)) {
tableColumn.setData(POLYGLOT_ITEM_TOOLTIP_DATA_KEY, locTooltipKey);
tableColumn.setToolTipText(Utils.formatLineBreaks(this.i18nSupport.getText(locTooltipKey)));
}
}
@Override
public void injectI18n(final TableItem tableItem, final LocTextKey... locTextKey) {
if (locTextKey == null) {
return;
}
tableItem.setData(POLYGLOT_ITEM_TEXT_DATA_KEY, locTextKey);
for (int i = 0; i < locTextKey.length; i++) {
tableItem.setText(i, this.i18nSupport.getText(locTextKey[i]));
}
}
@Override
public void injectI18n(final TabItem tabItem, final LocTextKey locTextKey, final LocTextKey locTooltipKey) {
tabItem.setData(POLYGLOT_ITEM_TEXT_DATA_KEY, locTextKey);
tabItem.setText(this.i18nSupport.getText(locTextKey));
if (this.i18nSupport.hasText(locTooltipKey)) {
tabItem.setData(POLYGLOT_ITEM_TOOLTIP_DATA_KEY, locTooltipKey);
tabItem.setToolTipText(Utils.formatLineBreaks(this.i18nSupport.getText(locTooltipKey)));
}
}
@Override
public void createLanguageSelector(final PageContext composerCtx) {
for (final Locale locale : this.i18nSupport.supportedLanguages()) {
final Label languageSelection = new Label(composerCtx.getParent(), SWT.NONE);
languageSelection.setData(
POLYGLOT_WIDGET_FUNCTION_KEY,
(Consumer<Label>) label -> label.setVisible(
!this.i18nSupport.getUsersLanguageLocale()
.getLanguage()
.equals(locale.getLanguage())));
languageSelection.setData(RWT.CUSTOM_VARIANT, "header");
languageSelection.setText("| " + locale.getLanguage().toUpperCase(locale));
languageSelection.addListener(SWT.MouseDown, event -> {
this.setPageLocale(composerCtx.getRoot(), locale);
});
}
}
private static final Consumer<Label> labelFunction(
final LocTextKey locTextKey,
final LocTextKey locToolTipKey,
final I18nSupport i18nSupport) {
return label -> {
if (locTextKey != null) {
label.setText(i18nSupport.getText(locTextKey));
}
if (locToolTipKey != null) {
label.setToolTipText(Utils.formatLineBreaks(i18nSupport.getText(locToolTipKey)));
}
};
}
private static final Consumer<Group> groupFunction(
final LocTextKey locTextKey,
final LocTextKey locToolTipKey,
final I18nSupport i18nSupport) {
return group -> {
if (locTextKey != null) {
group.setText(i18nSupport.getText(locTextKey));
}
if (locToolTipKey != null) {
group.setToolTipText(Utils.formatLineBreaks(i18nSupport.getText(locToolTipKey, StringUtils.EMPTY)));
}
};
}
private static final void updateLocale(final TabItem[] items, final I18nSupport i18nSupport) {
if (items == null) {
return;
}
for (final TabItem childItem : items) {
final LocTextKey locTextKey = (LocTextKey) childItem.getData(POLYGLOT_ITEM_TEXT_DATA_KEY);
if (locTextKey != null) {
childItem.setText(i18nSupport.getText(locTextKey));
}
}
}
private static final void updateLocale(final TreeItem[] items, final I18nSupport i18nSupport) {
if (items == null) {
return;
}
for (final TreeItem childItem : items) {
final LocTextKey locTextKey = (LocTextKey) childItem.getData(POLYGLOT_ITEM_TEXT_DATA_KEY);
if (locTextKey != null) {
childItem.setText(i18nSupport.getText(locTextKey));
}
updateLocale(childItem.getItems(), i18nSupport);
}
}
private static void updateLocale(final TableItem[] items, final I18nSupport i18nSupport) {
if (items == null) {
return;
}
for (final TableItem childItem : items) {
final LocTextKey[] locTextKey = (LocTextKey[]) childItem.getData(POLYGLOT_ITEM_TEXT_DATA_KEY);
if (locTextKey != null) {
for (int i = 0; i < locTextKey.length; i++) {
if (locTextKey[i] != null) {
childItem.setText(i, i18nSupport.getText(locTextKey[i]));
}
}
}
}
}
private static void updateLocale(final TableColumn[] columns, final I18nSupport i18nSupport) {
if (columns == null) {
return;
}
for (final TableColumn childItem : columns) {
final LocTextKey locTextKey = (LocTextKey) childItem.getData(POLYGLOT_ITEM_TEXT_DATA_KEY);
if (locTextKey != null) {
childItem.setText(i18nSupport.getText(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.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<Control>) comp.getData(POLYGLOT_WIDGET_FUNCTION_KEY)).accept(comp));
root.layout(true, true);
}
@Override
public void injectI18n(final ImageUploadSelection imageUpload, final LocTextKey locTextKey) {
final Consumer<ImageUploadSelection> 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<Label> labelFunction = labelFunction(locTextKey, locToolTipKey, this.i18nSupport);
label.setData(POLYGLOT_WIDGET_FUNCTION_KEY, labelFunction);
labelFunction.accept(label);
}
@Override
public void injectI18n(final Group group, final LocTextKey locTextKey, final LocTextKey locTooltipKey) {
final Consumer<Group> groupFunction = groupFunction(locTextKey, locTooltipKey, this.i18nSupport);
group.setData(POLYGLOT_WIDGET_FUNCTION_KEY, groupFunction);
groupFunction.accept(group);
}
@Override
public void injectI18n(final Button button, final LocTextKey locTextKey) {
injectI18n(button, locTextKey, null);
}
@Override
public void injectI18n(final Button button, final LocTextKey locTextKey, final LocTextKey locToolTipKey) {
final Consumer<Button> buttonFunction = b -> {
if (locTextKey != null) {
b.setText(this.i18nSupport.getText(locTextKey));
}
if (i18nSupport.hasText(locToolTipKey)) {
b.setToolTipText(Utils.formatLineBreaks(this.i18nSupport.getText(locToolTipKey)));
}
};
button.setData(POLYGLOT_WIDGET_FUNCTION_KEY, buttonFunction);
buttonFunction.accept(button);
}
@Override
public void injectI18n(final Tree tree) {
tree.setData(
POLYGLOT_WIDGET_FUNCTION_KEY,
(Consumer<Tree>) t -> updateLocale(t.getItems(), this.i18nSupport));
}
@Override
public void injectI18n(final TreeItem treeItem, final LocTextKey locTextKey) {
treeItem.setData(POLYGLOT_ITEM_TEXT_DATA_KEY, locTextKey);
treeItem.setText(this.i18nSupport.getText(locTextKey));
}
@Override
public void injectI18n(final Table table) {
table.setData(
POLYGLOT_WIDGET_FUNCTION_KEY,
(Consumer<Table>) t -> {
updateLocale(t.getColumns(), this.i18nSupport);
updateLocale(t.getItems(), this.i18nSupport);
});
}
@Override
public void injectI18n(final TabFolder tabFolder) {
tabFolder.setData(
POLYGLOT_WIDGET_FUNCTION_KEY,
(Consumer<TabFolder>) t -> updateLocale(t.getItems(), this.i18nSupport));
}
@Override
public void injectI18n(final TableColumn tableColumn, final LocTextKey locTextKey, final LocTextKey locTooltipKey) {
tableColumn.setData(POLYGLOT_ITEM_TEXT_DATA_KEY, locTextKey);
tableColumn.setText(this.i18nSupport.getText(locTextKey));
if (this.i18nSupport.hasText(locTooltipKey)) {
tableColumn.setData(POLYGLOT_ITEM_TOOLTIP_DATA_KEY, locTooltipKey);
tableColumn.setToolTipText(Utils.formatLineBreaks(this.i18nSupport.getText(locTooltipKey)));
}
}
@Override
public void injectI18n(final TableItem tableItem, final LocTextKey... locTextKey) {
if (locTextKey == null) {
return;
}
tableItem.setData(POLYGLOT_ITEM_TEXT_DATA_KEY, locTextKey);
for (int i = 0; i < locTextKey.length; i++) {
tableItem.setText(i, this.i18nSupport.getText(locTextKey[i]));
}
}
@Override
public void injectI18n(final TabItem tabItem, final LocTextKey locTextKey, final LocTextKey locTooltipKey) {
tabItem.setData(POLYGLOT_ITEM_TEXT_DATA_KEY, locTextKey);
tabItem.setText(this.i18nSupport.getText(locTextKey));
if (this.i18nSupport.hasText(locTooltipKey)) {
tabItem.setData(POLYGLOT_ITEM_TOOLTIP_DATA_KEY, locTooltipKey);
tabItem.setToolTipText(Utils.formatLineBreaks(this.i18nSupport.getText(locTooltipKey)));
}
}
@Override
public void createLanguageSelector(final PageContext composerCtx) {
for (final Locale locale : this.i18nSupport.supportedLanguages()) {
final Label languageSelection = new Label(composerCtx.getParent(), SWT.NONE);
languageSelection.setData(
POLYGLOT_WIDGET_FUNCTION_KEY,
(Consumer<Label>) label -> label.setVisible(
!this.i18nSupport.getUsersLanguageLocale()
.getLanguage()
.equals(locale.getLanguage())));
languageSelection.setData(RWT.CUSTOM_VARIANT, "header");
languageSelection.setText("| " + locale.getLanguage().toUpperCase(locale));
languageSelection.addListener(SWT.MouseDown, event -> this.setPageLocale(composerCtx.getRoot(), locale));
}
}
private static Consumer<Label> labelFunction(
final LocTextKey locTextKey,
final LocTextKey locToolTipKey,
final I18nSupport i18nSupport) {
return label -> {
if (locTextKey != null) {
label.setText(i18nSupport.getText(locTextKey));
}
if (i18nSupport.hasText(locToolTipKey)) {
label.setToolTipText(Utils.formatLineBreaks(i18nSupport.getText(locToolTipKey)));
}
};
}
private static Consumer<Group> groupFunction(
final LocTextKey locTextKey,
final LocTextKey locToolTipKey,
final I18nSupport i18nSupport) {
return group -> {
if (locTextKey != null) {
group.setText(i18nSupport.getText(locTextKey));
}
if (i18nSupport.hasText(locToolTipKey)) {
group.setToolTipText(Utils.formatLineBreaks(i18nSupport.getText(locToolTipKey, StringUtils.EMPTY)));
}
};
}
private static void updateLocale(final TabItem[] items, final I18nSupport i18nSupport) {
if (items == null) {
return;
}
for (final TabItem childItem : items) {
final LocTextKey locTextKey = (LocTextKey) childItem.getData(POLYGLOT_ITEM_TEXT_DATA_KEY);
if (locTextKey != null) {
childItem.setText(i18nSupport.getText(locTextKey));
}
}
}
private static void updateLocale(final TreeItem[] items, final I18nSupport i18nSupport) {
if (items == null) {
return;
}
for (final TreeItem childItem : items) {
final LocTextKey locTextKey = (LocTextKey) childItem.getData(POLYGLOT_ITEM_TEXT_DATA_KEY);
if (locTextKey != null) {
childItem.setText(i18nSupport.getText(locTextKey));
}
updateLocale(childItem.getItems(), i18nSupport);
}
}
private static void updateLocale(final TableItem[] items, final I18nSupport i18nSupport) {
if (items == null) {
return;
}
for (final TableItem childItem : items) {
final LocTextKey[] locTextKey = (LocTextKey[]) childItem.getData(POLYGLOT_ITEM_TEXT_DATA_KEY);
if (locTextKey != null) {
for (int i = 0; i < locTextKey.length; i++) {
if (locTextKey[i] != null) {
childItem.setText(i, i18nSupport.getText(locTextKey[i]));
}
}
}
}
}
private static void updateLocale(final TableColumn[] columns, final I18nSupport i18nSupport) {
if (columns == null) {
return;
}
for (final TableColumn childItem : columns) {
final LocTextKey locTextKey = (LocTextKey) childItem.getData(POLYGLOT_ITEM_TEXT_DATA_KEY);
if (locTextKey != null) {
childItem.setText(i18nSupport.getText(locTextKey));
}
}
}
}

View file

@ -1,134 +1,141 @@
/*
* 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.widget;
import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
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.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Listener;
import ch.ethz.seb.sebserver.gbl.Constants;
import ch.ethz.seb.sebserver.gbl.util.Tuple;
import ch.ethz.seb.sebserver.gbl.util.Utils;
import ch.ethz.seb.sebserver.gui.service.page.PageService;
public final class MultiSelectionCheckbox extends Composite implements Selection {
private static final long serialVersionUID = -8507565817745610126L;
private Listener listener = null;
private final Map<String, Button> checkboxes;
MultiSelectionCheckbox(final Composite parent) {
super(parent, SWT.NONE);
final GridLayout gridLayout = new GridLayout(1, true);
gridLayout.verticalSpacing = 1;
gridLayout.marginLeft = 0;
gridLayout.marginHeight = 0;
gridLayout.marginWidth = 0;
setLayout(gridLayout);
this.checkboxes = new LinkedHashMap<>();
}
@Override
public Type type() {
return Type.MULTI_CHECKBOX;
}
@Override
public void applyNewMapping(final List<Tuple<String>> mapping) {
final String selectionValue = getSelectionValue();
this.checkboxes.clear();
PageService.clearComposite(this);
for (final Tuple<String> tuple : mapping) {
final Button button = new Button(this, SWT.CHECK);
button.setText(tuple._2);
final GridData gridData = new GridData(SWT.FILL, SWT.CENTER, true, true);
button.setLayoutData(gridData);
button.setData(OPTION_VALUE, tuple._1);
button.addListener(SWT.Selection, event -> {
if (this.listener != null) {
this.listener.handleEvent(event);
}
});
this.checkboxes.put(tuple._1, button);
}
if (StringUtils.isNotBlank(selectionValue)) {
select(selectionValue);
}
}
@Override
public void applyToolTipsForItems(final List<Tuple<String>> mapping) {
mapping
.stream()
.filter(tuple -> StringUtils.isNotBlank(tuple._2))
.forEach(tuple -> {
final Button button = this.checkboxes.get(tuple._1);
if (button != null) {
button.setToolTipText(Utils.formatLineBreaks(tuple._2));
}
});
}
@Override
public void select(final String keys) {
clear();
if (StringUtils.isBlank(keys)) {
return;
}
Arrays.asList(StringUtils.split(keys, Constants.LIST_SEPARATOR))
.stream()
.forEach(key -> {
final Button button = this.checkboxes.get(key);
if (button != null) {
button.setSelection(true);
}
});
}
@Override
public String getSelectionValue() {
return StringUtils.joinWith(
Constants.LIST_SEPARATOR,
this.checkboxes
.values()
.stream()
.filter(button -> button.getSelection())
.map(button -> (String) button.getData(OPTION_VALUE))
.collect(Collectors.toList()).toArray());
}
@Override
public void clear() {
this.checkboxes
.values()
.stream()
.forEach(button -> button.setSelection(false));
}
@Override
public void setSelectionListener(final Listener listener) {
this.listener = listener;
}
}
/*
* 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.widget;
import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import ch.ethz.seb.sebserver.gbl.util.Tuple3;
import ch.ethz.seb.sebserver.gui.service.i18n.I18nSupport;
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.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Listener;
import ch.ethz.seb.sebserver.gbl.Constants;
import ch.ethz.seb.sebserver.gbl.util.Tuple;
import ch.ethz.seb.sebserver.gbl.util.Utils;
import ch.ethz.seb.sebserver.gui.service.page.PageService;
public final class MultiSelectionCheckbox extends Composite implements Selection {
private static final long serialVersionUID = -8507565817745610126L;
private Listener listener = null;
private final Map<String, Button> checkboxes;
MultiSelectionCheckbox(final Composite parent) {
super(parent, SWT.NONE);
final GridLayout gridLayout = new GridLayout(1, true);
gridLayout.verticalSpacing = 1;
gridLayout.marginLeft = 0;
gridLayout.marginHeight = 0;
gridLayout.marginWidth = 0;
setLayout(gridLayout);
this.checkboxes = new LinkedHashMap<>();
}
@Override
public Type type() {
return Type.MULTI_CHECKBOX;
}
@Override
public void applyNewMapping(final List<Tuple<String>> mapping) {
final String selectionValue = getSelectionValue();
this.checkboxes.clear();
PageService.clearComposite(this);
for (final Tuple<String> tuple : mapping) {
final Button button = new Button(this, SWT.CHECK);
button.setText(tuple._2);
final GridData gridData = new GridData(SWT.FILL, SWT.CENTER, true, true);
button.setLayoutData(gridData);
button.setData(OPTION_VALUE, tuple._1);
button.addListener(SWT.Selection, event -> {
if (this.listener != null) {
this.listener.handleEvent(event);
}
});
this.checkboxes.put(tuple._1, button);
Tuple3<String> tuple3 = tuple.adaptTo(Tuple3.class);
if (tuple3 != null && StringUtils.isNotBlank(tuple3._3)) {
button.setToolTipText(tuple3._3);
}
}
if (StringUtils.isNotBlank(selectionValue)) {
select(selectionValue);
}
}
@Override
public void applyToolTipsForItems(final List<Tuple<String>> mapping) {
mapping
.stream()
.filter(tuple -> StringUtils.isNotBlank(tuple._2))
.forEach(tuple -> {
final Button button = this.checkboxes.get(tuple._1);
if (button != null) {
button.setToolTipText(Utils.formatLineBreaks(tuple._2));
}
});
}
@Override
public void select(final String keys) {
clear();
if (StringUtils.isBlank(keys)) {
return;
}
Arrays.asList(StringUtils.split(keys, Constants.LIST_SEPARATOR))
.stream()
.forEach(key -> {
final Button button = this.checkboxes.get(key);
if (button != null) {
button.setSelection(true);
}
});
}
@Override
public String getSelectionValue() {
return StringUtils.joinWith(
Constants.LIST_SEPARATOR,
this.checkboxes
.values()
.stream()
.filter(button -> button.getSelection())
.map(button -> (String) button.getData(OPTION_VALUE))
.collect(Collectors.toList()).toArray());
}
@Override
public void clear() {
this.checkboxes
.values()
.stream()
.forEach(button -> button.setSelection(false));
}
@Override
public void setSelectionListener(final Listener listener) {
this.listener = listener;
}
}

View file

@ -59,6 +59,8 @@ sebserver.overall.types.entityType.INSTITUTION=Institution
sebserver.overall.types.entityType.SEB_CLIENT_CONFIGURATION=Client Configuration
sebserver.overall.types.entityType.LMS_SETUP=LMS Setup
sebserver.overall.types.entityType.USER=User Account
sebserver.overall.types.entityType.CLIENT_INSTRUCTION=SEB Client Instruction
sebserver.overall.types.entityType.EXAM_SEB_RESTRICTION=SEB Exam Restriction
sebserver.overall.activity.title.serveradmin=SEB Server Administration
sebserver.overall.activity.title.sebconfig=SEB Configuration
@ -174,18 +176,27 @@ sebserver.institution.form.logoImage.unsupportedFileType=The selected file is no
sebserver.useraccount.list.actions=
sebserver.useraccount.role.SEB_SERVER_ADMIN=SEB Server Administrator
sebserver.useraccount.role.SEB_SERVER_ADMIN.tooltip=A SEB server administrator has overall read privileges</br>and is able to create new institutions and user accounts
sebserver.useraccount.role.INSTITUTIONAL_ADMIN=Institutional Administrator
sebserver.useraccount.role.INSTITUTIONAL_ADMIN.tooltip=An institutional administrator has overall read privileges on the assigned institution</br>and is able to edit the institution and create new user accounts for the institution.</br>Furthermore an institutional administrator is able to create new LMS bindings and SEB client configurations for the institution.
sebserver.useraccount.role.EXAM_ADMIN=Exam Administrator
sebserver.useraccount.role.EXAM_ADMIN.tooltip=An exam administrator has overall read privileges for the institution but cannot see or manage other user accounts.</br>An exam administrator is able to create new SEB configurations and import and setup exams.
sebserver.useraccount.role.EXAM_SUPPORTER=Exam Supporter
sebserver.useraccount.role.EXAM_SUPPORTER.tooltip=An exam supporter can only see and edit the own user account</br> and monitor exams for that he/she was attached by an exam administrator.
sebserver.useraccount.list.empty=No user account has been found. Please adapt the filter or create a new user account
sebserver.useraccount.list.title=User Accounts
sebserver.useraccount.list.column.institution=Institution
sebserver.useraccount.list.column.institution.tooltip=The institution of the user account.</br></br>Use the filter above to specify the institution.</br>{0}
sebserver.useraccount.list.column.name=First Name
sebserver.useraccount.list.column.name.tooltip=The first name of the user.</br></br>Use the filter above to narrow down a specific first name.</br>{0}
sebserver.useraccount.list.column.username=User Name
sebserver.useraccount.list.column.username.tooltip=The internal user name of the user.</br></br>Use the filter above to narrow down a specific user name.</br>{0}
sebserver.useraccount.list.column.email=Mail
sebserver.useraccount.list.column.email.tooltip=The e-mail address of the user.</br></br>Use the filter above to narrow down a specific e-mail address.</br>{0}
sebserver.useraccount.list.column.language=Language
sebserver.useraccount.list.column.active=Active
sebserver.useraccount.list.column.active.tooltip=The activity of the user.</br></br>Use the filter above to specify the activity.</br>{0}
sebserver.useraccount.action.list=User Account
sebserver.useraccount.action.form=User Account of {0}
@ -206,19 +217,32 @@ sebserver.useraccount.info.notEditable=You have no edit rights for this User Acc
sebserver.useraccount.form.title=User Account
sebserver.useraccount.form.title.new=Add User Account
sebserver.useraccount.form.institution=Institution
sebserver.useraccount.form.institution.tooltip=The institution the user belongs to.
sebserver.useraccount.form.creationdate=Creation Date
sebserver.useraccount.form.creationdate.tooltip=The date when the user-account was first created.
sebserver.useraccount.form.name=First Name
sebserver.useraccount.form.name.tooltip=The first name of the user.
sebserver.useraccount.form.surname=Surname
sebserver.useraccount.form.surname.tooltip=The last- or surname of the user.
sebserver.useraccount.form.username=Username
sebserver.useraccount.form.username.tooltip=The internal user name of the account.
sebserver.useraccount.form.mail=E-Mail
sebserver.useraccount.form.mail.tooltip=The e-mail address of the user.
sebserver.useraccount.form.language=Language
sebserver.useraccount.form.language.tooltip=The users language.
sebserver.useraccount.form.timezone=Time Zone
sebserver.useraccount.form.timezone.tooltip=The time-zone of the user.</br></br>Note that this also defines the exact time and date that is displayed to the user.
sebserver.useraccount.form.roles=User Roles
sebserver.useraccount.form.roles.tooltip=Select the roles for the user.</br>A user can have more then one role but must have at least one.</br></br>Please use the tooltip on the role name for more information about a specific role.
sebserver.useraccount.form.password=Password
sebserver.useraccount.form.password.tooltip=The password of the user account
sebserver.useraccount.form.password.confirm=Confirm Password
sebserver.useraccount.form.password.confirm.tooltip=Please confirm the password
sebserver.useraccount.form.pwchange.title=Change Password : {0}
sebserver.useraccount.form.password.new=New Password
sebserver.useraccount.form.password.new.tooltip=The new password for the user account
sebserver.useraccount.form.password.new.confirm=Confirm New Password
sebserver.useraccount.form.password.new.confirm.tooltip=Please confirm the password
################################
# LMS Setup
@ -465,19 +489,28 @@ sebserver.clientconfig.list.empty=There is currently no SEB-Client configuration
sebserver.clientconfig.list.title=SEB Client Configurations
sebserver.clientconfig.list.actions=
sebserver.clientconfig.list.column.institution=Institution
sebserver.clientconfig.list.column.institution.tooltip=The institution of the SEB client configuration.</br></br>Use the filter above to specify the institution.</br>{0}
sebserver.clientconfig.list.column.name=Name
sebserver.clientconfig.list.column.name.tooltip=The name of the SEB client configuration.</br></br>Use the filter above to narrow down a specific name.</br>{0}
sebserver.clientconfig.list.column.date=Creation Date {0}
sebserver.clientconfig.list.column.date.tooltip=The date when the SEB client configuration was first created.</br></br>Use the filter above to specify a from-date.</br>{0}
sebserver.clientconfig.list.column.active=Active
sebserver.clientconfig.list.column.active.tooltip=The activity of SEB client configuration.</br></br>Use the filter above to specify the activity.</br>{0}
sebserver.clientconfig.info.pleaseSelect=Please select first a Client Configuration from the list
sebserver.clientconfig.list.action.no.modify.privilege=No Access: A SEB Client Configuration from other institution cannot be modified.
sebserver.clientconfig.form.title.new=Add Client Configuration
sebserver.clientconfig.form.title=SEB Client Configuration
sebserver.clientconfig.form.name=Name
sebserver.clientconfig.form.name.tooltip=The name of the SEB Client Configuration.</br>Can be any name that not already exists for another SEB Client Configuration
sebserver.clientconfig.form.fallback-url=Fallback Start URL
sebserver.clientconfig.form.fallback-url.tooltip=A fallback URL that tells the SEB where to go when the SEB Server service is unavailable.
sebserver.clientconfig.form.date=Creation Date
sebserver.clientconfig.form.date.tooltip=The date when the SEB client configuration was first created.
sebserver.clientconfig.form.encryptSecret=Configuration Password
sebserver.clientconfig.form.encryptSecret.tooltip=Define a password if the SEB client configuration shall be password-encrypted
sebserver.clientconfig.form.encryptSecret.confirm=Confirm Password
sebserver.clientconfig.form.encryptSecret.confirm.tooltip=Please retype the given password for configrmation
sebserver.clientconfig.action.list.new=Add Configuration
sebserver.clientconfig.action.list.view=View Configuration
@ -494,9 +527,13 @@ sebserver.clientconfig.action.deactivate=Deactivate Configuration
sebserver.examconfig.action.list=Exam Configuration
sebserver.examconfig.list.title=Exam Configurations
sebserver.examconfig.list.column.institution=Institution
sebserver.examconfig.list.column.institution.tooltip=The institution of the SEB exam configuration.</br></br>Use the filter above to specify the institution.</br>{0}
sebserver.examconfig.list.column.name=Name
sebserver.examconfig.list.column.name.tooltip=The name of the SEB exam configuration.</br></br>Use the filter above to narrow down a specific name.</br>{0}
sebserver.examconfig.list.column.description=Description
sebserver.examconfig.list.column.description.tooltip=The description of the SEB exam configuration.</br></br>Use the filter above to find configurations that contains specific words or phrases within the description.</br>{0}
sebserver.examconfig.list.column.status=Status
sebserver.examconfig.list.column.status.tooltip=The status of the SEB exam configuration.</br></br>Use the filter above to specify a status.</br>{0}
sebserver.examconfig.list.actions=
@ -506,10 +543,9 @@ sebserver.examconfig.list.action.no.modify.privilege=No Access: An Exam Configur
sebserver.examconfig.action.list.new=Add Exam Configuration
sebserver.examconfig.action.list.view=View Configuration
sebserver.examconfig.action.list.modify=Edit Settings
sebserver.examconfig.action.list.modify=Configuration Settings
sebserver.examconfig.action.list.modify.properties=Edit Configuration
sebserver.examconfig.action.view=View Settings
sebserver.examconfig.action.modify=Edit Settings
sebserver.examconfig.action.modify=Configuration Settings
sebserver.examconfig.action.modify.properties=Edit Configuration
sebserver.examconfig.action.view.properties=View Configuration
sebserver.examconfig.action.save=Save
@ -534,12 +570,17 @@ sebserver.examconfig.action.state-change.confirm=This configuration is already a
sebserver.examconfig.form.title.new=Add Exam Configuration
sebserver.examconfig.form.title=Exam Configuration
sebserver.examconfig.form.name=Name
sebserver.examconfig.form.name.tooltip=The name of the SEB exam configuration.
sebserver.examconfig.form.description=Description
sebserver.examconfig.form.description.tooltip=The description text of the SEB exam configuration.
sebserver.examconfig.form.with-history=With History
sebserver.examconfig.form.template=Template
sebserver.examconfig.form.template.tooltip=The template this SEB exam configuration depends on.
sebserver.examconfig.form.status=Status
sebserver.examconfig.form.status.tooltip=The status of this SEB exam configuration.</br></br>Under Construction marks a SEB exam configuration to not be able to attach to an exam so far.</br></br>Ready to use marks an SEB exam configuration to be able to attach to an exam.</br></br>In Use marks a SEB exam configuration is already been used from one or more exam(s)
sebserver.examconfig.form.config-key.title=Config Key
sebserver.examconfig.form.attched-to=Attached To Exam
sebserver.examconfig.form.attached-to=Attached To Exam
sebserver.examconfig.form.attached-to.tooltip=This SEB exam configuration is currently attached to the following exams.</br></br>Select an exam from the list and use the "View Exam" or Double-Click on the list to go to a specific exam.
sebserver.examconfig.status.CONSTRUCTION=Under Construction
sebserver.examconfig.status.READY_TO_USE=Ready To Use
@ -1037,7 +1078,9 @@ sebserver.configtemplate.action.create-config.dialog=Exam Configuration
sebserver.configtemplate.form.title.new=Add Template
sebserver.configtemplate.form.title=Configuration Template
sebserver.configtemplate.form.name=Name
sebserver.configtemplate.form.name.tooltip=The name of the SEB exam configuration template
sebserver.configtemplate.form.description=Description
sebserver.configtemplate.form.description.tooltip=The description of the SEB exam configuration template
sebserver.configtemplate.action.save=Save Template
sebserver.configtemplate.attr.type.TEXT_FIELD=Text Field
@ -1058,10 +1101,15 @@ sebserver.configtemplate.attr.type.INLINE_TABLE=Table
sebserver.configtemplate.attr.type.COMPOSITE_TABLE=Table
sebserver.configtemplate.attrs.list.title=Configuration Attributes
sebserver.configtemplate.attrs.list.title.tooltip=Table of all SEB exam configuration attributes of this template
sebserver.configtemplate.attrs.list.name=Name
sebserver.configtemplate.attrs.list.name.tooltip=The technical name of the SEB exam configuration attribute with the display name in brackets if available.</br></br>{0}
sebserver.configtemplate.attrs.list.view=View
sebserver.configtemplate.attrs.list.view.tooltip=The view/tab where the SEB exam configuration attribute belongs to.</br></br>{0}
sebserver.configtemplate.attrs.list.group=Group
sebserver.configtemplate.attrs.list.group.tooltip=The group on the view/tab where the SEB exam configuration attribute belongs to.</br></br>{0}
sebserver.configtemplate.attrs.list.type=Type
sebserver.configtemplate.attrs.list.type.tooltip=The type of the SEB exam configuration attribute.</br></br>{0}
sebserver.configtemplate.attr.list.actions=
sebserver.configtemplate.attr.list.actions.modify=Edit Attribute
@ -1072,10 +1120,15 @@ sebserver.configtemplate.attr.info.pleaseSelect=Please select first an Attribute
sebserver.configtemplate.attr.form.title=Template Attribute
sebserver.configtemplate.attr.form.name=Name
sebserver.configtemplate.attr.form.name.tooltip=The technical name of the SEB exam configuration attribute with the display name in brackets if available.
sebserver.configtemplate.attr.form.type=Type
sebserver.configtemplate.attr.form.type.tooltip=The type of the SEB exam configuration attribute.
sebserver.configtemplate.attr.form.view=View
sebserver.configtemplate.attr.form.view.tooltip=The view/tab where the SEB exam configuration attribute belongs to.
sebserver.configtemplate.attr.form.group=Group
sebserver.configtemplate.attr.form.group.tooltip=The group on the view/tab where the SEB exam configuration attribute belongs to.
sebserver.configtemplate.attr.form.value=Template Attribute Value
sebserver.configtemplate.attr.form.value.tooltip=The SEB exam configuration attribute value that can be set as default for this template
sebserver.configtemplate.attr.action.setdefault=Set Default Values
sebserver.configtemplate.attr.action.template=View Template
@ -1165,10 +1218,15 @@ sebserver.logs.activity.seblogs.details=Show Details
sebserver.userlogs.list.title=User Activity Logs
sebserver.userlogs.list.column.institution=Institution
sebserver.userlogs.list.column.institution.tooltip=The institution of the user activity log.</br></br>Use the filter above to specify the institution.</br>{0}
sebserver.userlogs.list.column.user=User
sebserver.userlogs.list.column.user.tooltip=The user account of the user activity log.</br></br>Use the filter above to specify a user account.</br>{0}
sebserver.userlogs.list.column.dateTime=Date {0}
sebserver.userlogs.list.column.dateTime.tooltip=The date when the user activity log happened.</br></br>Use the filter above to specify a from- and to-date range.</br>{0}
sebserver.userlogs.list.column.activityType=User Activity
sebserver.userlogs.list.column.entityType=Entity Type
sebserver.userlogs.list.column.activityType.tooltip=The type of the user activity.</br></br>Use the filter above to specify a activity type.</br>{0}
sebserver.userlogs.list.column.entityType=Domain Type
sebserver.userlogs.list.column.entityType.tooltip=The domain type of the user activity.</br></br>Use the filter above to specify a domain type.</br>{0}
sebserver.userlogs.list.column.entityId=Entity-ID
sebserver.userlogs.list.column.message=Message

Binary file not shown.

Before

Width:  |  Height:  |  Size: 678 B

After

Width:  |  Height:  |  Size: 1.7 KiB