SEBSERV-18 SEBSERV-19 #ported from prototype
This commit is contained in:
		
							parent
							
								
									f21f959ad2
								
							
						
					
					
						commit
						d5f7752e98
					
				
					 92 changed files with 4016 additions and 336 deletions
				
			
		
							
								
								
									
										6
									
								
								pom.xml
									
										
									
									
									
								
							
							
						
						
									
										6
									
								
								pom.xml
									
										
									
									
									
								
							|  | @ -225,6 +225,12 @@ | |||
|       <version>1.0.9.RELEASE</version> | ||||
|     </dependency> | ||||
| 
 | ||||
|     <!-- Apache HTTP --> | ||||
|     <dependency> | ||||
|       <groupId>org.apache.httpcomponents</groupId> | ||||
|       <artifactId>httpclient</artifactId> | ||||
|     </dependency> | ||||
| 
 | ||||
|     <!-- Eclipse RAP / RWT --> | ||||
|     <dependency> | ||||
|       <groupId>org.eclipse.rap</groupId> | ||||
|  |  | |||
|  | @ -19,6 +19,7 @@ import org.springframework.boot.web.servlet.error.ErrorController; | |||
| import org.springframework.context.annotation.Bean; | ||||
| import org.springframework.context.annotation.Configuration; | ||||
| import org.springframework.core.annotation.Order; | ||||
| import org.springframework.http.HttpStatus; | ||||
| import org.springframework.security.config.annotation.web.builders.HttpSecurity; | ||||
| import org.springframework.security.config.annotation.web.builders.WebSecurity; | ||||
| import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; | ||||
|  | @ -90,6 +91,7 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter implements E | |||
|                                     final HttpServletResponse response, | ||||
|                                     final AuthenticationException authException) throws IOException, ServletException { | ||||
| 
 | ||||
|                                 response.setStatus(HttpStatus.UNAUTHORIZED.value()); | ||||
|                                 response.sendRedirect(WebSecurityConfig.this.unauthorizedRedirect); | ||||
|                             } | ||||
|                         }) | ||||
|  | @ -104,6 +106,7 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter implements E | |||
| 
 | ||||
|     @RequestMapping("/error") | ||||
|     public void handleError(final HttpServletResponse response) throws IOException { | ||||
|         response.setStatus(HttpStatus.NOT_FOUND.value()); | ||||
|         response.sendRedirect(this.unauthorizedRedirect); | ||||
|     } | ||||
| 
 | ||||
|  |  | |||
|  | @ -6,9 +6,11 @@ | |||
|  * file, You can obtain one at http://mozilla.org/MPL/2.0/. | ||||
|  */ | ||||
| 
 | ||||
| package ch.ethz.seb.sebserver.gbl.model; | ||||
| package ch.ethz.seb.sebserver.gbl.api; | ||||
| 
 | ||||
| import java.io.Serializable; | ||||
| import java.util.Arrays; | ||||
| import java.util.Collection; | ||||
| import java.util.Collections; | ||||
| import java.util.List; | ||||
| 
 | ||||
|  | @ -72,12 +74,14 @@ public class APIMessage implements Serializable { | |||
|             return new APIMessage(this.messageCode, this.systemMessage, error.getMessage()); | ||||
|         } | ||||
| 
 | ||||
|         public ResponseEntity<APIMessage> createErrorResponse() { | ||||
|             return new ResponseEntity<>(of(), this.httpStatus); | ||||
|         public ResponseEntity<List<APIMessage>> createErrorResponse() { | ||||
|             final APIMessage message = of(); | ||||
|             return new ResponseEntity<>(Arrays.asList(message), this.httpStatus); | ||||
|         } | ||||
| 
 | ||||
|         public ResponseEntity<Object> createErrorResponse(final String details, final String... attributes) { | ||||
|             return new ResponseEntity<>(of(details, attributes), this.httpStatus); | ||||
|             final APIMessage message = of(details, attributes); | ||||
|             return new ResponseEntity<>(Arrays.asList(message), this.httpStatus); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|  | @ -134,6 +138,24 @@ public class APIMessage implements Serializable { | |||
|         return ErrorMessage.FIELD_VALIDATION.of(error.toString(), args); | ||||
|     } | ||||
| 
 | ||||
|     public static String toHTML(final Collection<APIMessage> messages) { | ||||
|         final StringBuilder builder = new StringBuilder(); | ||||
|         builder.append("<b>Messages:</b><br/><br/>"); | ||||
|         messages.stream().forEach(message -> { | ||||
|             builder | ||||
|                     .append("  code : ") | ||||
|                     .append(message.messageCode) | ||||
|                     .append("<br/>") | ||||
|                     .append("  system message : ") | ||||
|                     .append(message.systemMessage) | ||||
|                     .append("<br/>") | ||||
|                     .append("  details : ") | ||||
|                     .append(StringUtils.abbreviate(message.details, 100)) | ||||
|                     .append("<br/><br/>"); | ||||
|         }); | ||||
|         return builder.toString(); | ||||
|     } | ||||
| 
 | ||||
|     public static class APIMessageException extends RuntimeException { | ||||
| 
 | ||||
|         private static final long serialVersionUID = 1453431210820677296L; | ||||
|  | @ -0,0 +1,17 @@ | |||
| /* | ||||
|  * Copyright (c) 2019 ETH Zürich, Educational Development and Technology (LET) | ||||
|  * | ||||
|  * This Source Code Form is subject to the terms of the Mozilla Public | ||||
|  * License, v. 2.0. If a copy of the MPL was not distributed with this | ||||
|  * file, You can obtain one at http://mozilla.org/MPL/2.0/. | ||||
|  */ | ||||
| 
 | ||||
| package ch.ethz.seb.sebserver.gbl.api; | ||||
| 
 | ||||
| import java.util.List; | ||||
| 
 | ||||
| public interface APIMessageError { | ||||
| 
 | ||||
|     List<APIMessage> getErrorMessages(); | ||||
| 
 | ||||
| } | ||||
|  | @ -6,7 +6,7 @@ | |||
|  * file, You can obtain one at http://mozilla.org/MPL/2.0/. | ||||
|  */ | ||||
| 
 | ||||
| package ch.ethz.seb.sebserver.gbl; | ||||
| package ch.ethz.seb.sebserver.gbl.api; | ||||
| 
 | ||||
| import org.springframework.context.annotation.Lazy; | ||||
| import org.springframework.stereotype.Component; | ||||
|  | @ -6,7 +6,7 @@ | |||
|  * file, You can obtain one at http://mozilla.org/MPL/2.0/. | ||||
|  */ | ||||
| 
 | ||||
| package ch.ethz.seb.sebserver.gbl; | ||||
| package ch.ethz.seb.sebserver.gbl.api; | ||||
| 
 | ||||
| import java.util.Collections; | ||||
| import java.util.List; | ||||
|  | @ -6,9 +6,9 @@ | |||
|  * file, You can obtain one at http://mozilla.org/MPL/2.0/. | ||||
|  */ | ||||
| 
 | ||||
| package ch.ethz.seb.sebserver.webservice.weblayer.api; | ||||
| package ch.ethz.seb.sebserver.gbl.api; | ||||
| 
 | ||||
| public class RestAPI { | ||||
| public class SEBServerRestEndpoints { | ||||
| 
 | ||||
|     public static final String ENDPOINT_INSTITUTION = "/institution"; | ||||
| 
 | ||||
|  | @ -22,8 +22,8 @@ public interface Entity extends ModelIdAware { | |||
|     @JsonIgnore | ||||
|     String getName(); | ||||
| 
 | ||||
|     public static EntityKeyAndName toName(final Entity entity) { | ||||
|         return new EntityKeyAndName( | ||||
|     public static EntityName toName(final Entity entity) { | ||||
|         return new EntityName( | ||||
|                 entity.entityType(), | ||||
|                 entity.getModelId(), | ||||
|                 entity.getName()); | ||||
|  |  | |||
|  | @ -14,7 +14,7 @@ import com.fasterxml.jackson.annotation.JsonIgnoreProperties; | |||
| import com.fasterxml.jackson.annotation.JsonProperty; | ||||
| 
 | ||||
| @JsonIgnoreProperties(ignoreUnknown = true) | ||||
| public class EntityKeyAndName implements ModelIdAware, ModelNameAware { | ||||
| public class EntityName implements ModelIdAware, ModelNameAware { | ||||
| 
 | ||||
|     @JsonProperty(value = "entityType", required = true) | ||||
|     public final EntityType entityType; | ||||
|  | @ -24,7 +24,7 @@ public class EntityKeyAndName implements ModelIdAware, ModelNameAware { | |||
|     public final String name; | ||||
| 
 | ||||
|     @JsonCreator | ||||
|     public EntityKeyAndName( | ||||
|     public EntityName( | ||||
|             @JsonProperty(value = "entityType", required = true) final EntityType entityType, | ||||
|             @JsonProperty(value = Domain.ATTR_ID, required = true) final String id, | ||||
|             @JsonProperty(value = "name", required = true) final String name) { | ||||
|  | @ -34,7 +34,7 @@ public class EntityKeyAndName implements ModelIdAware, ModelNameAware { | |||
|         this.name = name; | ||||
|     } | ||||
| 
 | ||||
|     public EntityKeyAndName(final EntityKey entityKey, final String name) { | ||||
|     public EntityName(final EntityKey entityKey, final String name) { | ||||
| 
 | ||||
|         this.entityType = entityKey.entityType; | ||||
|         this.modelId = entityKey.modelId; | ||||
|  | @ -74,7 +74,7 @@ public class EntityKeyAndName implements ModelIdAware, ModelNameAware { | |||
|             return false; | ||||
|         if (getClass() != obj.getClass()) | ||||
|             return false; | ||||
|         final EntityKeyAndName other = (EntityKeyAndName) obj; | ||||
|         final EntityName other = (EntityName) obj; | ||||
|         if (this.entityType != other.entityType) | ||||
|             return false; | ||||
|         if (this.modelId == null) { | ||||
|  | @ -14,6 +14,7 @@ import java.util.Set; | |||
| import com.fasterxml.jackson.annotation.JsonCreator; | ||||
| import com.fasterxml.jackson.annotation.JsonProperty; | ||||
| 
 | ||||
| import ch.ethz.seb.sebserver.gbl.api.APIMessage; | ||||
| import ch.ethz.seb.sebserver.gbl.util.Utils; | ||||
| 
 | ||||
| public class EntityProcessingReport { | ||||
|  |  | |||
|  | @ -19,7 +19,7 @@ import com.fasterxml.jackson.annotation.JsonCreator; | |||
| import com.fasterxml.jackson.annotation.JsonIgnore; | ||||
| import com.fasterxml.jackson.annotation.JsonProperty; | ||||
| 
 | ||||
| import ch.ethz.seb.sebserver.gbl.POSTMapper; | ||||
| import ch.ethz.seb.sebserver.gbl.api.POSTMapper; | ||||
| import ch.ethz.seb.sebserver.gbl.model.Activatable; | ||||
| import ch.ethz.seb.sebserver.gbl.model.Domain.EXAM; | ||||
| import ch.ethz.seb.sebserver.gbl.model.EntityType; | ||||
|  |  | |||
|  | @ -13,7 +13,7 @@ import javax.validation.constraints.Size; | |||
| 
 | ||||
| import com.fasterxml.jackson.annotation.JsonProperty; | ||||
| 
 | ||||
| import ch.ethz.seb.sebserver.gbl.POSTMapper; | ||||
| import ch.ethz.seb.sebserver.gbl.api.POSTMapper; | ||||
| import ch.ethz.seb.sebserver.gbl.model.Activatable; | ||||
| import ch.ethz.seb.sebserver.gbl.model.Domain; | ||||
| import ch.ethz.seb.sebserver.gbl.model.Domain.INSTITUTION; | ||||
|  |  | |||
|  | @ -15,12 +15,12 @@ import com.fasterxml.jackson.annotation.JsonCreator; | |||
| import com.fasterxml.jackson.annotation.JsonIgnore; | ||||
| import com.fasterxml.jackson.annotation.JsonProperty; | ||||
| 
 | ||||
| import ch.ethz.seb.sebserver.gbl.POSTMapper; | ||||
| import ch.ethz.seb.sebserver.gbl.api.POSTMapper; | ||||
| import ch.ethz.seb.sebserver.gbl.model.Activatable; | ||||
| import ch.ethz.seb.sebserver.gbl.model.Domain; | ||||
| import ch.ethz.seb.sebserver.gbl.model.Domain.INSTITUTION; | ||||
| import ch.ethz.seb.sebserver.gbl.model.Domain.LMS_SETUP; | ||||
| import ch.ethz.seb.sebserver.gbl.model.EntityKeyAndName; | ||||
| import ch.ethz.seb.sebserver.gbl.model.EntityName; | ||||
| import ch.ethz.seb.sebserver.gbl.model.EntityType; | ||||
| import ch.ethz.seb.sebserver.webservice.servicelayer.authorization.GrantEntity; | ||||
| 
 | ||||
|  | @ -198,8 +198,8 @@ public final class LmsSetup implements GrantEntity, Activatable { | |||
|                 + this.sebAuthSecret + ", active=" + this.active + "]"; | ||||
|     } | ||||
| 
 | ||||
|     public static EntityKeyAndName toName(final LmsSetup lmsSetup) { | ||||
|         return new EntityKeyAndName( | ||||
|     public static EntityName toName(final LmsSetup lmsSetup) { | ||||
|         return new EntityName( | ||||
|                 EntityType.LMS_SETUP, | ||||
|                 String.valueOf(lmsSetup.id), | ||||
|                 lmsSetup.name); | ||||
|  |  | |||
|  | @ -156,6 +156,13 @@ public final class UserInfo implements GrantEntity, Activatable, Serializable { | |||
|         return this.roles; | ||||
|     } | ||||
| 
 | ||||
|     public boolean hasRole(final UserRole userRole) { | ||||
|         if (userRole == null) { | ||||
|             return false; | ||||
|         } | ||||
|         return this.roles.contains(userRole.name()); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public int hashCode() { | ||||
|         final int prime = 31; | ||||
|  |  | |||
|  | @ -22,9 +22,9 @@ import com.fasterxml.jackson.annotation.JsonCreator; | |||
| import com.fasterxml.jackson.annotation.JsonIgnoreProperties; | ||||
| import com.fasterxml.jackson.annotation.JsonProperty; | ||||
| 
 | ||||
| import ch.ethz.seb.sebserver.gbl.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.api.POSTMapper; | ||||
| import ch.ethz.seb.sebserver.gbl.model.EntityType; | ||||
| import ch.ethz.seb.sebserver.webservice.servicelayer.authorization.GrantEntity; | ||||
| 
 | ||||
|  |  | |||
|  | @ -69,23 +69,58 @@ public final class Result<T> { | |||
|         return this.value; | ||||
|     } | ||||
| 
 | ||||
|     /** @return the error if some was reporter or null if there was no error */ | ||||
|     public Throwable getError() { | ||||
|         return this.error; | ||||
|     /** Use this to get the resulting value. In an error case, a given error handling | ||||
|      * function is used that receives the error and returns a resulting value instead | ||||
|      * (or throw some error instead) | ||||
|      * | ||||
|      * @param errorHandler the error handling function | ||||
|      * @return */ | ||||
|     public T get(final Function<Throwable, T> errorHandler) { | ||||
|         return this.error != null ? errorHandler.apply(this.error) : this.value; | ||||
|     } | ||||
| 
 | ||||
|     public boolean hasError() { | ||||
|         return this.error != null; | ||||
|     public T get(final Consumer<Throwable> errorHandler, final Supplier<T> supplier) { | ||||
|         if (this.error != null) { | ||||
|             errorHandler.accept(this.error); | ||||
|             return supplier.get(); | ||||
|         } else { | ||||
|             return this.value; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /** Use this to get the resulting value or (if null) to get a given other value | ||||
|      * | ||||
|      * @param other the other value to get if the computed value is null | ||||
|      * @return return either the computed value if existing or a given other value */ | ||||
|     public T getOrElse(final T other) { | ||||
|     public T getOr(final T other) { | ||||
|         return this.value != null ? this.value : other; | ||||
|     } | ||||
| 
 | ||||
|     /** Use this to get the resulting value if existing or throw an Runtime exception with | ||||
|      * given message otherwise. | ||||
|      * | ||||
|      * @param message the message for the RuntimeException in error case | ||||
|      * @return the resulting value */ | ||||
|     public T getOrThrowRuntime(final String message) { | ||||
|         if (this.error != null) { | ||||
|             throw new RuntimeException(message, this.error); | ||||
|         } | ||||
| 
 | ||||
|         return this.value; | ||||
|     } | ||||
| 
 | ||||
|     public T getOrThrow() { | ||||
|         if (this.error != null) { | ||||
|             if (this.error instanceof RuntimeException) { | ||||
|                 throw (RuntimeException) this.error; | ||||
|             } else { | ||||
|                 throw new RuntimeException("RuntimeExceptionWrapper cause: ", this.error); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         return this.value; | ||||
|     } | ||||
| 
 | ||||
|     /** Use this to get the resulting value or (if null) to get a given other value | ||||
|      * | ||||
|      * @param supplier supplier to get the value from if the computed value is null | ||||
|  | @ -94,6 +129,15 @@ public final class Result<T> { | |||
|         return this.value != null ? this.value : supplier.get(); | ||||
|     } | ||||
| 
 | ||||
|     /** @return the error if some was reporter or null if there was no error */ | ||||
|     public Throwable getError() { | ||||
|         return this.error; | ||||
|     } | ||||
| 
 | ||||
|     public boolean hasError() { | ||||
|         return this.error != null; | ||||
|     } | ||||
| 
 | ||||
|     /** If a value is present, performs the given action with the value, | ||||
|      * otherwise performs the given empty-based action. | ||||
|      * | ||||
|  | @ -155,16 +199,6 @@ public final class Result<T> { | |||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /** Use this to get the resulting value. In an error case, a given error handling | ||||
|      * function is used that receives the error and returns a resulting value instead | ||||
|      * (or throw some error instead) | ||||
|      * | ||||
|      * @param errorHandler the error handling function | ||||
|      * @return */ | ||||
|     public T getOrHandleError(final Function<Throwable, T> errorHandler) { | ||||
|         return this.error != null ? errorHandler.apply(this.error) : this.value; | ||||
|     } | ||||
| 
 | ||||
|     public Result<T> onErrorDo(final Consumer<Throwable> block) { | ||||
|         if (this.error != null) { | ||||
|             block.accept(this.error); | ||||
|  | @ -172,31 +206,6 @@ public final class Result<T> { | |||
|         return this; | ||||
|     } | ||||
| 
 | ||||
|     /** Use this to get the resulting value if existing or throw an Runtime exception with | ||||
|      * given message otherwise. | ||||
|      * | ||||
|      * @param message the message for the RuntimeException in error case | ||||
|      * @return the resulting value */ | ||||
|     public T getOrThrowRuntime(final String message) { | ||||
|         if (this.error != null) { | ||||
|             throw new RuntimeException(message, this.error); | ||||
|         } | ||||
| 
 | ||||
|         return this.value; | ||||
|     } | ||||
| 
 | ||||
|     public T getOrThrow() { | ||||
|         if (this.error != null) { | ||||
|             if (this.error instanceof RuntimeException) { | ||||
|                 throw (RuntimeException) this.error; | ||||
|             } else { | ||||
|                 throw new RuntimeException("RuntimeExceptionWrapper cause: ", this.error); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         return this.value; | ||||
|     } | ||||
| 
 | ||||
|     /** Use this to create a Result of a given resulting value. | ||||
|      * | ||||
|      * @param value resulting value | ||||
|  |  | |||
|  | @ -103,7 +103,7 @@ public final class Utils { | |||
| 
 | ||||
|     public static Long dateTimeStringToTimestamp(final String startTime, final Long defaultValue) { | ||||
|         return dateTimeStringToTimestamp(startTime) | ||||
|                 .getOrElse(defaultValue); | ||||
|                 .getOr(defaultValue); | ||||
|     } | ||||
| 
 | ||||
|     public static <M extends Map<K, V>, K, V> M mapPut(final M map, final K key, final V value) { | ||||
|  |  | |||
|  | @ -11,7 +11,6 @@ package ch.ethz.seb.sebserver.gui; | |||
| import org.springframework.beans.factory.annotation.Value; | ||||
| import org.springframework.context.annotation.Configuration; | ||||
| import org.springframework.core.annotation.Order; | ||||
| import org.springframework.security.config.annotation.web.builders.HttpSecurity; | ||||
| import org.springframework.security.config.annotation.web.builders.WebSecurity; | ||||
| import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; | ||||
| import org.springframework.security.web.util.matcher.AntPathRequestMatcher; | ||||
|  | @ -43,38 +42,4 @@ public class GuiWebsecurityConfig extends WebSecurityConfigurerAdapter { | |||
|                 .requestMatchers(PUBLIC_URLS); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     protected void configure(final HttpSecurity http) throws Exception { | ||||
|         System.out.println("**************** GuiWebConfig: "); | ||||
| //        //@formatter:off | ||||
| //        http | ||||
| //            .sessionManagement() | ||||
| //            .sessionCreationPolicy(SessionCreationPolicy.STATELESS) | ||||
| //        .and() | ||||
| //            .antMatcher("/**"/*this.guiEndpointPath + "/**"*/) | ||||
| //            .authorizeRequests() | ||||
| //            .anyRequest() | ||||
| //            .authenticated() | ||||
| //            .and() | ||||
| //            .exceptionHandling() | ||||
| //            .authenticationEntryPoint( | ||||
| //                    new AuthenticationEntryPoint() { | ||||
| // | ||||
| //                        @Override | ||||
| //                        public void commence(final HttpServletRequest request, final HttpServletResponse response, | ||||
| //                                final AuthenticationException authException) throws IOException, ServletException { | ||||
| //                            response.sendRedirect("/gui"); | ||||
| //                        } | ||||
| // | ||||
| //                    }) | ||||
| //        .and() | ||||
| //            .formLogin().disable() | ||||
| //            .httpBasic().disable() | ||||
| //            .logout().disable() | ||||
| //            .headers().frameOptions().disable() | ||||
| //         .and() | ||||
| //            .csrf().disable(); | ||||
|       //@formatter:on | ||||
|     } | ||||
| 
 | ||||
| } | ||||
|  |  | |||
|  | @ -0,0 +1,72 @@ | |||
| /* | ||||
|  * 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; | ||||
| 
 | ||||
| public interface I18nSupport { | ||||
| 
 | ||||
|     Collection<Locale> supportedLanguages(); | ||||
| 
 | ||||
|     /** Get the current Locale either form a user if this is called from a logged in user context or the | ||||
|      * applications default locale. | ||||
|      * | ||||
|      * @return the current Locale to use in context */ | ||||
|     Locale getCurrentLocale(); | ||||
| 
 | ||||
|     void setSessionLocale(Locale locale); | ||||
| 
 | ||||
|     /** Format a DateTime to a text format to display. | ||||
|      * | ||||
|      * @param date | ||||
|      * @return */ | ||||
|     String formatDisplayDate(DateTime date); | ||||
| 
 | ||||
|     /** 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 */ | ||||
|     String getText(String key, Object... 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 */ | ||||
|     String getText(LocTextKey key); | ||||
| 
 | ||||
|     /** 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 */ | ||||
|     String getText(String key, Locale locale, Object... 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); | ||||
| 
 | ||||
| } | ||||
|  | @ -0,0 +1,58 @@ | |||
| /* | ||||
|  * 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.Arrays; | ||||
| 
 | ||||
| public class LocTextKey { | ||||
| 
 | ||||
|     public final String name; | ||||
|     public final Object[] args; | ||||
| 
 | ||||
|     public LocTextKey(final String name) { | ||||
|         this.name = name; | ||||
|         this.args = null; | ||||
|     } | ||||
| 
 | ||||
|     public LocTextKey(final String name, final Object... args) { | ||||
|         this.name = name; | ||||
|         this.args = args; | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public int hashCode() { | ||||
|         final int prime = 31; | ||||
|         int result = 1; | ||||
|         result = prime * result + ((this.name == null) ? 0 : this.name.hashCode()); | ||||
|         return result; | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public boolean equals(final Object obj) { | ||||
|         if (this == obj) | ||||
|             return true; | ||||
|         if (obj == null) | ||||
|             return false; | ||||
|         if (getClass() != obj.getClass()) | ||||
|             return false; | ||||
|         final LocTextKey other = (LocTextKey) obj; | ||||
|         if (this.name == null) { | ||||
|             if (other.name != null) | ||||
|                 return false; | ||||
|         } else if (!this.name.equals(other.name)) | ||||
|             return false; | ||||
|         return true; | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public String toString() { | ||||
|         return "LocTextKey [name=" + this.name + ", args=" + Arrays.toString(this.args) + "]"; | ||||
|     } | ||||
| 
 | ||||
| } | ||||
|  | @ -0,0 +1,38 @@ | |||
| /* | ||||
|  * Copyright (c) 2019 ETH Zürich, Educational Development and Technology (LET) | ||||
|  * | ||||
|  * This Source Code Form is subject to the terms of the Mozilla Public | ||||
|  * License, v. 2.0. If a copy of the MPL was not distributed with this | ||||
|  * file, You can obtain one at http://mozilla.org/MPL/2.0/. | ||||
|  */ | ||||
| 
 | ||||
| package ch.ethz.seb.sebserver.gui.service.i18n; | ||||
| 
 | ||||
| import java.util.Locale; | ||||
| 
 | ||||
| import org.eclipse.swt.widgets.Composite; | ||||
| 
 | ||||
| public interface PolyglotPageService { | ||||
| 
 | ||||
|     String POLYGLOT_WIDGET_FUNCTION_KEY = "POLYGLOT_WIDGET_FUNCTION"; | ||||
|     String POLYGLOT_TREE_ITEM_TEXT_DATA_KEY = "POLYGLOT_TREE_ITEM_TEXT_DATA"; | ||||
| 
 | ||||
|     /** Gets the underling I18nSupport | ||||
|      *  | ||||
|      * @return the underling I18nSupport */ | ||||
|     I18nSupport getI18nSupport(); | ||||
| 
 | ||||
|     /** The default locale for the page. | ||||
|      * Uses I18nSupport.getCurrentLocale to do so. | ||||
|      *  | ||||
|      * @param root the root Composite of the page to change the language */ | ||||
|     void setDefaultPageLocale(Composite root); | ||||
| 
 | ||||
|     /** Sets the given Locale and if needed, updates the page language according to the | ||||
|      * given Locale | ||||
|      *  | ||||
|      * @param root root the root Composite of the page to change the language | ||||
|      * @param locale the Locale to set */ | ||||
|     void setPageLocale(Composite root, Locale locale); | ||||
| 
 | ||||
| } | ||||
|  | @ -0,0 +1,131 @@ | |||
| /* | ||||
|  * 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.Arrays; | ||||
| import java.util.Collection; | ||||
| import java.util.Locale; | ||||
| 
 | ||||
| import org.eclipse.rap.rwt.RWT; | ||||
| import org.joda.time.DateTime; | ||||
| import org.joda.time.format.DateTimeFormat; | ||||
| import org.joda.time.format.DateTimeFormatter; | ||||
| import org.slf4j.Logger; | ||||
| import org.slf4j.LoggerFactory; | ||||
| import org.springframework.beans.factory.annotation.Value; | ||||
| import org.springframework.context.MessageSource; | ||||
| import org.springframework.context.annotation.Lazy; | ||||
| import org.springframework.stereotype.Component; | ||||
| 
 | ||||
| import ch.ethz.seb.sebserver.gui.service.i18n.I18nSupport; | ||||
| import ch.ethz.seb.sebserver.gui.service.i18n.LocTextKey; | ||||
| import ch.ethz.seb.sebserver.gui.service.remote.webservice.auth.CurrentUser; | ||||
| 
 | ||||
| @Lazy | ||||
| @Component | ||||
| public class I18nSupportImpl implements I18nSupport { | ||||
| 
 | ||||
|     private static final Logger log = LoggerFactory.getLogger(I18nSupportImpl.class); | ||||
| 
 | ||||
|     public static final String EMPTY_DISPLAY_VALUE = "--"; | ||||
|     private static final String ATTR_CURRENT_SESSION_LOCALE = "CURRENT_SESSION_LOCALE"; | ||||
| 
 | ||||
|     private final DateTimeFormatter displayDateFormatter; | ||||
|     private final CurrentUser currentUser; | ||||
|     private final MessageSource messageSource; | ||||
|     private final Locale defaultLocale = Locale.ENGLISH; | ||||
| 
 | ||||
|     public I18nSupportImpl( | ||||
|             final CurrentUser currentUser, | ||||
|             final MessageSource messageSource, | ||||
|             @Value("${sebserver.gui.date.displayformat}") final String displayDateFormat) { | ||||
| 
 | ||||
|         this.currentUser = currentUser; | ||||
|         this.messageSource = messageSource; | ||||
|         this.displayDateFormatter = DateTimeFormat | ||||
|                 .forPattern(displayDateFormat) | ||||
|                 .withZoneUTC(); | ||||
|     } | ||||
| 
 | ||||
|     private static final Collection<Locale> SUPPORTED = Arrays.asList(Locale.ENGLISH, Locale.GERMANY); | ||||
| 
 | ||||
|     @Override | ||||
|     public Collection<Locale> supportedLanguages() { | ||||
|         return SUPPORTED; | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void setSessionLocale(final Locale locale) { | ||||
|         try { | ||||
|             RWT.getUISession() | ||||
|                     .getHttpSession() | ||||
|                     .setAttribute(ATTR_CURRENT_SESSION_LOCALE, locale); | ||||
|             RWT.setLocale(locale); | ||||
|         } catch (final IllegalStateException e) { | ||||
|             log.error("Set current locale for session failed: ", e); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public Locale getCurrentLocale() { | ||||
|         // first session-locale if available | ||||
|         try { | ||||
|             final Locale sessionLocale = (Locale) RWT.getUISession() | ||||
|                     .getHttpSession() | ||||
|                     .getAttribute(ATTR_CURRENT_SESSION_LOCALE); | ||||
|             if (sessionLocale != null) { | ||||
|                 return sessionLocale; | ||||
|             } | ||||
|         } catch (final IllegalStateException e) { | ||||
|             log.warn("Get current locale for session failed: {}", e.getMessage()); | ||||
|         } | ||||
| 
 | ||||
|         // second user-locale if available | ||||
|         if (this.currentUser.isAvailable()) { | ||||
|             return this.currentUser.get().locale; | ||||
|         } | ||||
| 
 | ||||
|         // last the default locale | ||||
|         return this.defaultLocale; | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public String formatDisplayDate(final DateTime date) { | ||||
|         if (date == null) { | ||||
|             return EMPTY_DISPLAY_VALUE; | ||||
|         } | ||||
|         return date.toString(this.displayDateFormatter); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public String getText(final LocTextKey key) { | ||||
|         return getText(key.name, key.args); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public String getText(final String key, final Object... args) { | ||||
|         return getText(key, key, args); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public String getText(final String key, final String def, final Object... args) { | ||||
|         return this.messageSource.getMessage(key, args, def, this.getCurrentLocale()); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public String getText(final String key, final Locale locale, final Object... args) { | ||||
|         return getText(key, locale, key, args); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public String getText(final String key, final Locale locale, final String def, final Object... args) { | ||||
|         return this.messageSource.getMessage(key, args, def, locale); | ||||
|     } | ||||
| 
 | ||||
| } | ||||
|  | @ -0,0 +1,58 @@ | |||
| /* | ||||
|  * 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.eclipse.swt.widgets.Composite; | ||||
| import org.eclipse.swt.widgets.Control; | ||||
| import org.springframework.context.annotation.Lazy; | ||||
| import org.springframework.stereotype.Service; | ||||
| 
 | ||||
| import ch.ethz.seb.sebserver.gbl.profile.GuiProfile; | ||||
| import ch.ethz.seb.sebserver.gui.service.i18n.I18nSupport; | ||||
| import ch.ethz.seb.sebserver.gui.service.i18n.PolyglotPageService; | ||||
| import ch.ethz.seb.sebserver.gui.service.page.ComposerService; | ||||
| 
 | ||||
| /** 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.getCurrentLocale()); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     @SuppressWarnings("unchecked") | ||||
|     public void setPageLocale(final Composite root, final Locale locale) { | ||||
|         this.i18nSupport.setSessionLocale(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); | ||||
|     } | ||||
| 
 | ||||
| } | ||||
|  | @ -0,0 +1,66 @@ | |||
| /* | ||||
|  * Copyright (c) 2019 ETH Zürich, Educational Development and Technology (LET) | ||||
|  * | ||||
|  * This Source Code Form is subject to the terms of the Mozilla Public | ||||
|  * License, v. 2.0. If a copy of the MPL was not distributed with this | ||||
|  * file, You can obtain one at http://mozilla.org/MPL/2.0/. | ||||
|  */ | ||||
| 
 | ||||
| package ch.ethz.seb.sebserver.gui.service.page; | ||||
| 
 | ||||
| import java.util.function.Consumer; | ||||
| import java.util.function.Predicate; | ||||
| 
 | ||||
| import org.eclipse.swt.widgets.Composite; | ||||
| import org.eclipse.swt.widgets.Control; | ||||
| 
 | ||||
| import ch.ethz.seb.sebserver.gui.RAPConfiguration.EntryPointService; | ||||
| 
 | ||||
| public interface ComposerService extends EntryPointService { | ||||
| 
 | ||||
|     boolean validate(String composerName, PageContext pageContext); | ||||
| 
 | ||||
|     PageDefinition mainPage(); | ||||
| 
 | ||||
|     PageDefinition loginPage(); | ||||
| 
 | ||||
|     void compose( | ||||
|             String composerName, | ||||
|             PageContext pageContext); | ||||
| 
 | ||||
|     void compose( | ||||
|             Class<? extends TemplateComposer> composerType, | ||||
|             PageContext pageContext); | ||||
| 
 | ||||
|     void composePage( | ||||
|             Class<? extends PageDefinition> pageType, | ||||
|             Composite root); | ||||
| 
 | ||||
|     void composePage( | ||||
|             PageDefinition pageDefinition, | ||||
|             Composite root); | ||||
| 
 | ||||
|     static void traversePageTree( | ||||
|             final Composite root, | ||||
|             final Predicate<Control> predicate, | ||||
|             final Consumer<Control> f) { | ||||
| 
 | ||||
|         if (predicate.test(root)) { | ||||
|             f.accept(root); | ||||
|         } | ||||
| 
 | ||||
|         final Control[] children = root.getChildren(); | ||||
|         if (children != null) { | ||||
|             for (final Control control : children) { | ||||
|                 if (!(control instanceof Composite)) { | ||||
|                     if (predicate.test(control)) { | ||||
|                         f.accept(control); | ||||
|                     } | ||||
|                 } else { | ||||
|                     traversePageTree((Composite) control, predicate, f); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
| } | ||||
|  | @ -0,0 +1,128 @@ | |||
| /* | ||||
|  * Copyright (c) 2019 ETH Zürich, Educational Development and Technology (LET) | ||||
|  * | ||||
|  * This Source Code Form is subject to the terms of the Mozilla Public | ||||
|  * License, v. 2.0. If a copy of the MPL was not distributed with this | ||||
|  * file, You can obtain one at http://mozilla.org/MPL/2.0/. | ||||
|  */ | ||||
| 
 | ||||
| package ch.ethz.seb.sebserver.gui.service.page; | ||||
| 
 | ||||
| import org.eclipse.swt.widgets.Composite; | ||||
| import org.eclipse.swt.widgets.Shell; | ||||
| 
 | ||||
| import ch.ethz.seb.sebserver.gui.service.page.event.PageEvent; | ||||
| 
 | ||||
| public interface PageContext { | ||||
| 
 | ||||
|     public static final class PageAttr { | ||||
| 
 | ||||
|         public final String name; | ||||
|         public final String value; | ||||
| 
 | ||||
|         public PageAttr(final String name, final String value) { | ||||
|             this.name = name; | ||||
|             this.value = value; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     public interface AttributeKeys { | ||||
| 
 | ||||
|         public static final String ATTR_PAGE_TEMPLATE_COMPOSER_NAME = "ATTR_PAGE_TEMPLATE_COMPOSER_NAME"; | ||||
| 
 | ||||
|         public static final String INSTITUTION_ID = "INSTITUTION_ID"; | ||||
| 
 | ||||
| //        public static final String USER_NAME = "USER_NAME"; | ||||
| //        public static final String PASSWORD = "PASSWORD"; | ||||
| // | ||||
| 
 | ||||
| // | ||||
| //        public static final String CONFIG_ID = "CONFIG_ID"; | ||||
| //        public static final String CONFIG_VIEW_NAME = "CONFIG_VIEW_NAME"; | ||||
| //        public static final String CONFIG_ATTRIBUTE_SAVE_TYPE = "CONFIG_ATTRIBUTE_SAVE_TYPE"; | ||||
| //        public static final String CONFIG_ATTRIBUTE_VALUE = "CONFIG_ATTRIBUTE_VALUE"; | ||||
| // | ||||
| //        public static final String EXAM_ID = "EXAM_ID"; | ||||
| //        public static final String STATE_NAME = "STATE_NAME"; | ||||
| // | ||||
| //        public static final String AUTHORIZATION_CONTEXT = "AUTHORIZATION_CONTEXT"; | ||||
| //        public static final String AUTHORIZATION_HEADER = "AUTHORIZATION_HEADER"; | ||||
|         public static final String AUTHORIZATION_FAILURE = "AUTHORIZATION_FAILURE"; | ||||
|         public static final String LGOUT_SUCCESS = "LGOUT_SUCCESS"; | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
|     ComposerService composerService(); | ||||
| 
 | ||||
|     Shell getShell(); | ||||
| 
 | ||||
|     /** Get the page root Component. | ||||
|      * | ||||
|      * @return the page root Component. */ | ||||
|     Composite getRoot(); | ||||
| 
 | ||||
|     /** Get the Component that is currently set as parent during page tree compose | ||||
|      * | ||||
|      * @return the parent Component */ | ||||
|     Composite getParent(); | ||||
| 
 | ||||
|     /** Create a copy of this PageContext with a new parent Composite. | ||||
|      * | ||||
|      * @param parent the new parent Composite | ||||
|      * @return a copy of this PageContext with a new parent Composite. */ | ||||
|     PageContext copyOf(Composite parent); | ||||
| 
 | ||||
|     /** Create a copy of this PageContext with and additionally page context attributes. | ||||
|      * The additionally page context attributes will get merged with them already defined | ||||
|      * | ||||
|      * @param attributes additionally page context attributes. | ||||
|      * @return a copy of this PageContext with with and additionally page context attributes. */ | ||||
|     PageContext copyOfAttributes(PageContext otherContext); | ||||
| 
 | ||||
|     /** Adds the specified attribute to the existing page context attributes. | ||||
|      * | ||||
|      * @param key the key of the attribute | ||||
|      * @param value the value of the attribute | ||||
|      * @return this PageContext instance (builder pattern) */ | ||||
|     PageContext withAttr(String key, String value); | ||||
| 
 | ||||
|     String getAttribute(String name); | ||||
| 
 | ||||
|     String getAttribute(String name, String def); | ||||
| 
 | ||||
|     boolean hasAttribute(String name); | ||||
| 
 | ||||
|     /** Publishes a given PageEvent to the current page tree | ||||
|      * This goes through the page-tree and collects all listeners the are listen to | ||||
|      * the specified page event type. | ||||
|      * | ||||
|      * @param event the concrete PageEvent instance */ | ||||
|     <T extends PageEvent> void publishPageEvent(T event); | ||||
| 
 | ||||
|     /** Apply a confirm dialog with a specified confirm message and a callback code | ||||
|      * block that will be executed on users OK selection. | ||||
|      * | ||||
|      * @param confirmMessage | ||||
|      * @param onOK callback code block that will be executed on users OK selection */ | ||||
|     void applyConfirmDialog(String confirmMessage, Runnable onOK); | ||||
| 
 | ||||
|     void forwardToPage( | ||||
|             PageDefinition pageDefinition, | ||||
|             PageContext pageContext); | ||||
| 
 | ||||
|     void forwardToMainPage(PageContext pageContext); | ||||
| 
 | ||||
|     void forwardToLoginPage(PageContext pageContext); | ||||
| 
 | ||||
|     /** Notify an error dialog to the user with specified error message and | ||||
|      * optional exception instance | ||||
|      * | ||||
|      * @param errorMessage the error message to display | ||||
|      * @param error the error as Throwable */ | ||||
|     void notifyError(String errorMessage, Throwable error); | ||||
| 
 | ||||
|     void notifyError(Throwable error); | ||||
| 
 | ||||
|     <T> T logoutOnError(Throwable error); | ||||
| 
 | ||||
| } | ||||
|  | @ -0,0 +1,16 @@ | |||
| /* | ||||
|  * Copyright (c) 2019 ETH Zürich, Educational Development and Technology (LET) | ||||
|  * | ||||
|  * This Source Code Form is subject to the terms of the Mozilla Public | ||||
|  * License, v. 2.0. If a copy of the MPL was not distributed with this | ||||
|  * file, You can obtain one at http://mozilla.org/MPL/2.0/. | ||||
|  */ | ||||
| 
 | ||||
| package ch.ethz.seb.sebserver.gui.service.page; | ||||
| 
 | ||||
| public interface PageDefinition { | ||||
| 
 | ||||
|     Class<? extends TemplateComposer> composer(); | ||||
| 
 | ||||
|     PageContext applyPageContext(PageContext pageContext); | ||||
| } | ||||
|  | @ -0,0 +1,25 @@ | |||
| /* | ||||
|  * 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.page; | ||||
| 
 | ||||
| import ch.ethz.seb.sebserver.gui.service.page.event.PageEvent; | ||||
| 
 | ||||
| public interface PageEventListener<T extends PageEvent> { | ||||
| 
 | ||||
|     String LISTENER_ATTRIBUTE_KEY = "PageEventListener"; | ||||
| 
 | ||||
|     boolean match(Class<? extends PageEvent> eventType); | ||||
| 
 | ||||
|     default int priority() { | ||||
|         return 1; | ||||
|     } | ||||
| 
 | ||||
|     void notify(T event); | ||||
| 
 | ||||
| } | ||||
|  | @ -0,0 +1,17 @@ | |||
| /* | ||||
|  * 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.page; | ||||
| 
 | ||||
| import org.eclipse.swt.widgets.Event; | ||||
| 
 | ||||
| public interface PopupMenuComposer { | ||||
| 
 | ||||
|     void onMenuEvent(final Event event); | ||||
| 
 | ||||
| } | ||||
|  | @ -0,0 +1,19 @@ | |||
| /* | ||||
|  * 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.page; | ||||
| 
 | ||||
| public interface TemplateComposer { | ||||
| 
 | ||||
|     default boolean validate(final PageContext context) { | ||||
|         return true; | ||||
|     } | ||||
| 
 | ||||
|     void compose(PageContext composerCtx); | ||||
| 
 | ||||
| } | ||||
|  | @ -0,0 +1,77 @@ | |||
| /* | ||||
|  * 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.page.action; | ||||
| 
 | ||||
| import ch.ethz.seb.sebserver.gui.service.widget.WidgetFactory.IconButtonType; | ||||
| 
 | ||||
| public enum ActionDefinition { | ||||
| 
 | ||||
|     INSTITUTION_NEW( | ||||
|             "actions.new.institution", | ||||
|             IconButtonType.NEW_ACTION), | ||||
| 
 | ||||
|     INSTITUTION_MODIFY( | ||||
|             "actions.modify.institution", | ||||
|             IconButtonType.SAVE_ACTION), | ||||
| 
 | ||||
|     INSTITUTION_DELETE( | ||||
|             "actions.delete.institution", | ||||
|             IconButtonType.DELETE_ACTION), | ||||
| 
 | ||||
|     LMS_SETUP_NEW( | ||||
|             "New LMS Setup", | ||||
|             IconButtonType.NEW_ACTION), | ||||
| 
 | ||||
|     LMS_SETUP_MODIFY( | ||||
|             "Save LMS Setup", | ||||
|             IconButtonType.SAVE_ACTION), | ||||
| 
 | ||||
|     LMS_SETUP_DELETE( | ||||
|             "Delete LMS Setup", | ||||
|             IconButtonType.DELETE_ACTION), | ||||
| 
 | ||||
|     LMS_SETUP_TEST( | ||||
|             "Test LMS Setup", | ||||
|             IconButtonType.SAVE_ACTION), | ||||
| 
 | ||||
|     SEB_CONFIG_NEW( | ||||
|             "New Configuration", | ||||
|             IconButtonType.NEW_ACTION), | ||||
| 
 | ||||
|     SEB_CONFIG_MODIFY( | ||||
|             "Save Configuration", | ||||
|             IconButtonType.SAVE_ACTION), | ||||
| 
 | ||||
|     SEB_CONFIG_DELETE( | ||||
|             "Delete Configuration", | ||||
|             IconButtonType.DELETE_ACTION), | ||||
| 
 | ||||
|     EXAM_IMPORT( | ||||
|             "Import Exam", | ||||
|             IconButtonType.SAVE_ACTION), | ||||
| 
 | ||||
|     EXAM_EDIT( | ||||
|             "Edit Selected Exam", | ||||
|             IconButtonType.NEW_ACTION), | ||||
| 
 | ||||
|     EXAM_DELETE( | ||||
|             "Delete Selected Exam", | ||||
|             IconButtonType.DELETE_ACTION), | ||||
| 
 | ||||
|     ; | ||||
| 
 | ||||
|     public final String name; | ||||
|     public final IconButtonType icon; | ||||
| 
 | ||||
|     private ActionDefinition(final String name, final IconButtonType icon) { | ||||
|         this.name = name; | ||||
|         this.icon = icon; | ||||
|     } | ||||
| 
 | ||||
| } | ||||
|  | @ -0,0 +1,96 @@ | |||
| /* | ||||
|  * 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.page.action; | ||||
| 
 | ||||
| import org.eclipse.rap.rwt.RWT; | ||||
| import org.eclipse.rap.rwt.template.ImageCell; | ||||
| import org.eclipse.rap.rwt.template.Template; | ||||
| import org.eclipse.rap.rwt.template.TextCell; | ||||
| import org.eclipse.swt.SWT; | ||||
| import org.eclipse.swt.layout.GridData; | ||||
| import org.eclipse.swt.widgets.Label; | ||||
| import org.eclipse.swt.widgets.Tree; | ||||
| import org.eclipse.swt.widgets.TreeItem; | ||||
| import org.springframework.context.annotation.Lazy; | ||||
| import org.springframework.stereotype.Component; | ||||
| 
 | ||||
| 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.PageEventListener; | ||||
| import ch.ethz.seb.sebserver.gui.service.page.TemplateComposer; | ||||
| import ch.ethz.seb.sebserver.gui.service.page.event.ActionPublishEvent; | ||||
| import ch.ethz.seb.sebserver.gui.service.page.event.ActionPublishEventListener; | ||||
| import ch.ethz.seb.sebserver.gui.service.widget.WidgetFactory; | ||||
| 
 | ||||
| @Lazy | ||||
| @Component | ||||
| public class ActionPane implements TemplateComposer { | ||||
| 
 | ||||
|     private static final String ACTION_EVENT_CALL_KEY = "ACTION_EVENT_CALL"; | ||||
| 
 | ||||
|     private final WidgetFactory widgetFactory; | ||||
| 
 | ||||
|     public ActionPane(final WidgetFactory widgetFactory) { | ||||
|         super(); | ||||
|         this.widgetFactory = widgetFactory; | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void compose(final PageContext composerCtx) { | ||||
| 
 | ||||
|         final Label label = this.widgetFactory.labelLocalized( | ||||
|                 composerCtx.getParent(), | ||||
|                 "h3", | ||||
|                 new LocTextKey("sebserver.actionpane.title")); | ||||
|         final GridData titleLayout = new GridData(SWT.FILL, SWT.TOP, true, false); | ||||
|         titleLayout.verticalIndent = 10; | ||||
|         titleLayout.horizontalIndent = 10; | ||||
|         label.setLayoutData(titleLayout); | ||||
| 
 | ||||
|         final Tree actions = this.widgetFactory.treeLocalized(composerCtx.getParent(), SWT.SINGLE | SWT.FULL_SELECTION); | ||||
|         actions.setData(RWT.CUSTOM_VARIANT, "actions"); | ||||
|         final GridData gridData = new GridData(SWT.FILL, SWT.FILL, true, true); | ||||
|         actions.setLayoutData(gridData); | ||||
|         final Template template = new Template(); | ||||
|         final ImageCell imageCell = new ImageCell(template); | ||||
|         imageCell.setLeft(0, 0).setWidth(40).setTop(0).setBottom(0, 0).setHorizontalAlignment(SWT.LEFT); | ||||
|         imageCell.setBindingIndex(0); | ||||
|         final TextCell textCell = new TextCell(template); | ||||
|         textCell.setLeft(0, 30).setWidth(150).setTop(7).setBottom(0, 0).setHorizontalAlignment(SWT.LEFT); | ||||
|         textCell.setBindingIndex(0); | ||||
|         actions.setData(RWT.ROW_TEMPLATE, template); | ||||
| 
 | ||||
|         actions.addListener(SWT.Selection, event -> { | ||||
|             final TreeItem treeItem = (TreeItem) event.item; | ||||
| 
 | ||||
|             final Runnable action = (Runnable) treeItem.getData(ACTION_EVENT_CALL_KEY); | ||||
|             action.run(); | ||||
| 
 | ||||
|             if (!treeItem.isDisposed()) { | ||||
|                 treeItem.getParent().deselectAll(); | ||||
|             } | ||||
|         }); | ||||
| 
 | ||||
|         actions.setData( | ||||
|                 PageEventListener.LISTENER_ATTRIBUTE_KEY, | ||||
|                 new ActionPublishEventListener() { | ||||
|                     @Override | ||||
|                     public void notify(final ActionPublishEvent event) { | ||||
|                         final TreeItem actionItem = ActionPane.this.widgetFactory.treeItemLocalized( | ||||
|                                 actions, | ||||
|                                 event.actionDefinition.name); | ||||
|                         actionItem.setImage(event.actionDefinition.icon.getImage(composerCtx.getParent().getDisplay())); | ||||
|                         actionItem.setData(ACTION_EVENT_CALL_KEY, | ||||
|                                 new SafeActionExecution(composerCtx, event, event.run)); | ||||
|                     } | ||||
|                 }); | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
| } | ||||
|  | @ -0,0 +1,41 @@ | |||
| /* | ||||
|  * 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.page.action; | ||||
| 
 | ||||
| public interface InstitutionActions { | ||||
| 
 | ||||
| //    /** Use this higher-order function to create a new Institution action Runnable. | ||||
| //     * | ||||
| //     * @return */ | ||||
| //    static Runnable newInstitution(final PageContext composerCtx, final RestServices restServices) { | ||||
| //        return () -> { | ||||
| //            final IdAndName newInstitutionId = restServices | ||||
| //                    .sebServerAPICall(NewInstitution.class) | ||||
| //                    .doAPICall() | ||||
| //                    .onErrorThrow("Unexpected Error"); | ||||
| //            composerCtx.notify(new ActionEvent(ActionDefinition.INSTITUTION_NEW, newInstitutionId)); | ||||
| //        }; | ||||
| //    } | ||||
| // | ||||
| //    /** Use this higher-order function to create a delete Institution action Runnable. | ||||
| //     * | ||||
| //     * @return */ | ||||
| //    static Runnable deleteInstitution(final PageContext composerCtx, final RestServices restServices, | ||||
| //            final String instId) { | ||||
| //        return () -> { | ||||
| //            restServices | ||||
| //                    .sebServerAPICall(DeleteInstitution.class) | ||||
| //                    .attribute(AttributeKeys.INSTITUTION_ID, instId) | ||||
| //                    .doAPICall() | ||||
| //                    .onErrorThrow("Unexpected Error"); | ||||
| //            composerCtx.notify(new ActionEvent(ActionDefinition.INSTITUTION_DELETE, instId)); | ||||
| //        }; | ||||
| //    } | ||||
| 
 | ||||
| } | ||||
|  | @ -0,0 +1,68 @@ | |||
| /* | ||||
|  * 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.page.action; | ||||
| 
 | ||||
| import org.apache.commons.lang3.StringUtils; | ||||
| import org.slf4j.Logger; | ||||
| import org.slf4j.LoggerFactory; | ||||
| 
 | ||||
| import ch.ethz.seb.sebserver.gui.service.page.PageContext; | ||||
| import ch.ethz.seb.sebserver.gui.service.page.event.ActionPublishEvent; | ||||
| 
 | ||||
| public class SafeActionExecution implements Runnable { | ||||
| 
 | ||||
|     private static final Logger log = LoggerFactory.getLogger(SafeActionExecution.class); | ||||
| 
 | ||||
|     private final PageContext pageContext; | ||||
|     private final ActionPublishEvent actionEvent; | ||||
|     private final Runnable actionExecution; | ||||
| 
 | ||||
|     public SafeActionExecution( | ||||
|             final PageContext pageContext, | ||||
|             final ActionPublishEvent actionEvent, | ||||
|             final Runnable actionExecution) { | ||||
| 
 | ||||
|         this.pageContext = pageContext; | ||||
|         this.actionEvent = actionEvent; | ||||
|         this.actionExecution = actionExecution; | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void run() { | ||||
|         try { | ||||
|             if (StringUtils.isNotBlank(this.actionEvent.confirmationMessage)) { | ||||
|                 this.pageContext.applyConfirmDialog( | ||||
|                         this.actionEvent.confirmationMessage, | ||||
|                         createConfirmationCallback()); | ||||
|             } else { | ||||
|                 this.actionExecution.run(); | ||||
|             } | ||||
|         } catch (final Throwable t) { | ||||
|             log.error("Failed to execute action: {}", this.actionEvent, t); | ||||
|             this.pageContext.notifyError("action.error.unexpected.message", t); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     private final Runnable createConfirmationCallback() { | ||||
|         return new Runnable() { | ||||
| 
 | ||||
|             @Override | ||||
|             public void run() { | ||||
|                 try { | ||||
|                     SafeActionExecution.this.actionExecution.run(); | ||||
|                 } catch (final Throwable t) { | ||||
|                     log.error("Failed to execute action: {}", SafeActionExecution.this.actionEvent, t); | ||||
|                     SafeActionExecution.this.pageContext.notifyError("action.error.unexpected.message", t); | ||||
|                 } | ||||
| 
 | ||||
|             } | ||||
|         }; | ||||
|     } | ||||
| 
 | ||||
| } | ||||
|  | @ -0,0 +1,281 @@ | |||
| /* | ||||
|  * 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.page.activity; | ||||
| 
 | ||||
| import java.util.Collection; | ||||
| import java.util.Collections; | ||||
| import java.util.EnumMap; | ||||
| import java.util.List; | ||||
| import java.util.Map; | ||||
| 
 | ||||
| import org.eclipse.swt.SWT; | ||||
| import org.eclipse.swt.layout.GridData; | ||||
| import org.eclipse.swt.widgets.Event; | ||||
| import org.eclipse.swt.widgets.Label; | ||||
| import org.eclipse.swt.widgets.Tree; | ||||
| import org.eclipse.swt.widgets.TreeItem; | ||||
| import org.springframework.context.annotation.Lazy; | ||||
| import org.springframework.stereotype.Component; | ||||
| 
 | ||||
| import ch.ethz.seb.sebserver.gbl.model.EntityName; | ||||
| import ch.ethz.seb.sebserver.gbl.model.user.UserInfo; | ||||
| import ch.ethz.seb.sebserver.gbl.model.user.UserRole; | ||||
| 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.PageEventListener; | ||||
| import ch.ethz.seb.sebserver.gui.service.page.TemplateComposer; | ||||
| import ch.ethz.seb.sebserver.gui.service.page.action.ActionDefinition; | ||||
| import ch.ethz.seb.sebserver.gui.service.page.activity.ActivitySelection.Activity; | ||||
| import ch.ethz.seb.sebserver.gui.service.page.event.ActionEvent; | ||||
| import ch.ethz.seb.sebserver.gui.service.page.event.ActionEventListener; | ||||
| import ch.ethz.seb.sebserver.gui.service.page.event.ActivitySelectionEvent; | ||||
| import ch.ethz.seb.sebserver.gui.service.page.impl.MainPageState; | ||||
| import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestService; | ||||
| import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.institution.GetInstitutionNames; | ||||
| import ch.ethz.seb.sebserver.gui.service.remote.webservice.auth.AuthorizationContextHolder; | ||||
| import ch.ethz.seb.sebserver.gui.service.widget.WidgetFactory; | ||||
| 
 | ||||
| @Lazy | ||||
| @Component | ||||
| public class ActivitiesPane implements TemplateComposer { | ||||
| 
 | ||||
|     private final WidgetFactory widgetFactory; | ||||
|     private final RestService restService; | ||||
|     private final AuthorizationContextHolder authorizationContextHolder; | ||||
| 
 | ||||
|     private final Map<ActionDefinition, ActivityActionHandler> activityActionHandler = | ||||
|             new EnumMap<>(ActionDefinition.class); | ||||
| 
 | ||||
|     public ActivitiesPane( | ||||
|             final WidgetFactory widgetFactory, | ||||
|             final RestService restService, | ||||
|             final AuthorizationContextHolder authorizationContextHolder, | ||||
|             final Collection<ActivityActionHandler> activityActionHandler) { | ||||
| 
 | ||||
|         this.widgetFactory = widgetFactory; | ||||
|         this.restService = restService; | ||||
|         this.authorizationContextHolder = authorizationContextHolder; | ||||
| 
 | ||||
|         for (final ActivityActionHandler aah : activityActionHandler) { | ||||
|             this.activityActionHandler.put(aah.handlesAction(), aah); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void compose(final PageContext pageContext) { | ||||
|         final UserInfo userInfo = this.authorizationContextHolder | ||||
|                 .getAuthorizationContext() | ||||
|                 .getLoggedInUser() | ||||
|                 .get(pageContext::logoutOnError); | ||||
| 
 | ||||
|         final Label activities = this.widgetFactory.labelLocalized( | ||||
|                 pageContext.getParent(), | ||||
|                 "h3", | ||||
|                 new LocTextKey("sebserver.activitiespane.title")); | ||||
|         final GridData activitiesGridData = new GridData(SWT.FILL, SWT.TOP, true, false); | ||||
|         activitiesGridData.horizontalIndent = 20; | ||||
|         activities.setLayoutData(activitiesGridData); | ||||
| 
 | ||||
|         final Tree navigation = | ||||
|                 this.widgetFactory.treeLocalized(pageContext.getParent(), SWT.SINGLE | SWT.FULL_SELECTION); | ||||
|         final GridData navigationGridData = new GridData(SWT.FILL, SWT.FILL, true, true); | ||||
|         navigationGridData.horizontalIndent = 10; | ||||
|         navigation.setLayoutData(navigationGridData); | ||||
| 
 | ||||
|         final List<EntityName> insitutionNames = this.restService | ||||
|                 .getBuilder(GetInstitutionNames.class) | ||||
|                 .call() | ||||
|                 .get(pageContext::notifyError, () -> Collections.emptyList()); | ||||
| 
 | ||||
|         if (userInfo.hasRole(UserRole.SEB_SERVER_ADMIN)) { | ||||
|             // institutions (list) as root | ||||
|             final TreeItem institutions = this.widgetFactory.treeItemLocalized( | ||||
|                     navigation, | ||||
|                     Activity.INSTITUTION_ROOT.title); | ||||
|             ActivitySelection.inject(institutions, Activity.INSTITUTION_ROOT.createSelection()); | ||||
| 
 | ||||
|             for (final EntityName inst : insitutionNames) { | ||||
|                 createInstitutionItem(institutions, inst); | ||||
|             } | ||||
|         } else { | ||||
|             final EntityName inst = insitutionNames.iterator().next(); | ||||
|             createInstitutionItem(navigation, inst); | ||||
|         } | ||||
| 
 | ||||
| //        final TreeItem user = this.widgetFactory.treeItemLocalized( | ||||
| //                navigation, | ||||
| //                "org.sebserver.activities.user"); | ||||
| //        ActivitySelection.set(user, Activity.USERS.createSelection()); | ||||
| // | ||||
| //        final TreeItem configs = this.widgetFactory.treeItemLocalized( | ||||
| //                navigation, | ||||
| //                "org.sebserver.activities.sebconfigs"); | ||||
| //        ActivitySelection.set(configs, Activity.SEB_CONFIGS.createSelection()); | ||||
| // | ||||
| //        final TreeItem config = this.widgetFactory.treeItemLocalized( | ||||
| //                configs, | ||||
| //                "org.sebserver.activities.sebconfig"); | ||||
| //        ActivitySelection.set(config, Activity.SEB_CONFIG.createSelection()); | ||||
| // | ||||
| //        final TreeItem configTemplates = this.widgetFactory.treeItemLocalized( | ||||
| //                configs, | ||||
| //                "org.sebserver.activities.sebconfig.templates"); | ||||
| //        ActivitySelection.set(configTemplates, Activity.SEB_CONFIG_TEMPLATES.createSelection()); | ||||
| // | ||||
| //        final TreeItem exams = this.widgetFactory.treeItemLocalized( | ||||
| //                navigation, | ||||
| //                "org.sebserver.activities.exam"); | ||||
| //        ActivitySelection.set(exams, Activity.EXAMS.createSelection()); | ||||
| // | ||||
| //        final TreeItem monitoring = this.widgetFactory.treeItemLocalized( | ||||
| //                navigation, | ||||
| //                "org.sebserver.activities.monitoring"); | ||||
| //        ActivitySelection.set(monitoring, Activity.MONITORING.createSelection()); | ||||
| // | ||||
| //        final TreeItem runningExams = this.widgetFactory.treeItemLocalized( | ||||
| //                monitoring, | ||||
| //                "org.sebserver.activities.runningExams"); | ||||
| //        ActivitySelection.set(runningExams, Activity.RUNNING_EXAMS.createSelection() | ||||
| //                .withExpandFunction(this::runningExamExpand)); | ||||
| //        runningExams.setItemCount(1); | ||||
| // | ||||
| //        final TreeItem logs = this.widgetFactory.treeItemLocalized( | ||||
| //                monitoring, | ||||
| //                "org.sebserver.activities.logs"); | ||||
| //        ActivitySelection.set(logs, Activity.LOGS.createSelection()); | ||||
| 
 | ||||
|         navigation.addListener(SWT.Expand, this::handleExpand); | ||||
|         navigation.addListener(SWT.Selection, event -> handleSelection(pageContext, event)); | ||||
| 
 | ||||
|         navigation.setData( | ||||
|                 PageEventListener.LISTENER_ATTRIBUTE_KEY, | ||||
|                 new ActionEventListener() { | ||||
|                     @Override | ||||
|                     public void notify(final ActionEvent event) { | ||||
|                         final ActivityActionHandler aah = | ||||
|                                 ActivitiesPane.this.activityActionHandler.get(event.actionDefinition); | ||||
|                         if (aah != null) { | ||||
|                             aah.notifyAction(event, navigation, pageContext); | ||||
|                         } | ||||
|                     } | ||||
|                 }); | ||||
| 
 | ||||
|         // page-selection on (re)load | ||||
|         final MainPageState mainPageState = MainPageState.get(); | ||||
| 
 | ||||
|         if (mainPageState.activitySelection == null) { | ||||
|             mainPageState.activitySelection = ActivitySelection.get(navigation.getItem(0)); | ||||
|         } | ||||
|         pageContext.publishPageEvent( | ||||
|                 new ActivitySelectionEvent(mainPageState.activitySelection)); | ||||
|     } | ||||
| 
 | ||||
| //    private void runningExamExpand(final TreeItem item) { | ||||
| //        item.removeAll(); | ||||
| //        final List<EntityName> runningExamNames = this.restService | ||||
| //                .sebServerCall(GetRunningExamNames.class) | ||||
| //                .onError(t -> { | ||||
| //                    throw new RuntimeException(t); | ||||
| //                }); | ||||
| // | ||||
| //        if (runningExamNames != null) { | ||||
| //            for (final EntityName runningExamName : runningExamNames) { | ||||
| //                final TreeItem runningExams = this.widgetFactory.treeItemLocalized( | ||||
| //                        item, | ||||
| //                        runningExamName.name); | ||||
| //                ActivitySelection.set(runningExams, Activity.RUNNING_EXAM.createSelection(runningExamName)); | ||||
| //            } | ||||
| //        } | ||||
| //    } | ||||
| 
 | ||||
|     private void handleExpand(final Event event) { | ||||
|         final TreeItem treeItem = (TreeItem) event.item; | ||||
| 
 | ||||
|         System.out.println("opened: " + treeItem); | ||||
| 
 | ||||
|         final ActivitySelection activity = ActivitySelection.get(treeItem); | ||||
|         if (activity != null) { | ||||
|             activity.processExpand(treeItem); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     private void handleSelection(final PageContext composerCtx, final Event event) { | ||||
|         final TreeItem treeItem = (TreeItem) event.item; | ||||
| 
 | ||||
|         System.out.println("selected: " + treeItem); | ||||
| 
 | ||||
|         final MainPageState mainPageState = MainPageState.get(); | ||||
|         final ActivitySelection activitySelection = ActivitySelection.get(treeItem); | ||||
|         if (mainPageState.activitySelection == null) { | ||||
|             mainPageState.activitySelection = Activity.NONE.createSelection(); | ||||
|         } | ||||
|         if (!mainPageState.activitySelection.equals(activitySelection)) { | ||||
|             mainPageState.activitySelection = activitySelection; | ||||
|             composerCtx.publishPageEvent( | ||||
|                     new ActivitySelectionEvent(mainPageState.activitySelection)); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     static TreeItem createInstitutionItem(final Tree parent, final EntityName idAndName) { | ||||
|         final TreeItem institution = new TreeItem(parent, SWT.NONE); | ||||
|         createInstitutionItem(idAndName, institution); | ||||
|         return institution; | ||||
|     } | ||||
| 
 | ||||
|     static TreeItem createInstitutionItem(final TreeItem parent, final EntityName idAndName) { | ||||
|         final TreeItem institution = new TreeItem(parent, SWT.NONE); | ||||
|         createInstitutionItem(idAndName, institution); | ||||
|         return institution; | ||||
|     } | ||||
| 
 | ||||
|     static void createInstitutionItem(final EntityName idAndName, final TreeItem institution) { | ||||
|         institution.setText(idAndName.name); | ||||
|         ActivitySelection.inject(institution, Activity.INSTITUTION_NODE.createSelection(idAndName)); | ||||
|     } | ||||
| 
 | ||||
|     static final TreeItem findItemByActivity( | ||||
|             final TreeItem[] items, | ||||
|             final Activity activity, | ||||
|             final String objectId) { | ||||
| 
 | ||||
|         if (items == null) { | ||||
|             return null; | ||||
|         } | ||||
| 
 | ||||
|         for (final TreeItem item : items) { | ||||
|             final ActivitySelection activitySelection = ActivitySelection.get(item); | ||||
|             final String id = activitySelection.getObjectIdentifier(); | ||||
|             if (activitySelection != null && activitySelection.activity == activity && | ||||
|                     (id == null || (objectId != null && objectId.equals(id)))) { | ||||
|                 return item; | ||||
|             } | ||||
| 
 | ||||
|             final TreeItem _item = findItemByActivity(item.getItems(), activity, objectId); | ||||
|             if (_item != null) { | ||||
|                 return _item; | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         return null; | ||||
|     } | ||||
| 
 | ||||
|     static final TreeItem findItemByActivity(final TreeItem[] items, final Activity activity) { | ||||
|         return findItemByActivity(items, activity, null); | ||||
|     } | ||||
| 
 | ||||
|     static final void expand(final TreeItem item) { | ||||
|         if (item == null) { | ||||
|             return; | ||||
|         } | ||||
| 
 | ||||
|         item.setExpanded(true); | ||||
|         expand(item.getParentItem()); | ||||
|     } | ||||
| 
 | ||||
| } | ||||
|  | @ -0,0 +1,26 @@ | |||
| /* | ||||
|  * 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.page.activity; | ||||
| 
 | ||||
| import org.eclipse.swt.widgets.Tree; | ||||
| 
 | ||||
| import ch.ethz.seb.sebserver.gui.service.page.PageContext; | ||||
| import ch.ethz.seb.sebserver.gui.service.page.action.ActionDefinition; | ||||
| import ch.ethz.seb.sebserver.gui.service.page.event.ActionEvent; | ||||
| 
 | ||||
| public interface ActivityActionHandler { | ||||
| 
 | ||||
|     public ActionDefinition handlesAction(); | ||||
| 
 | ||||
|     void notifyAction( | ||||
|             final ActionEvent event, | ||||
|             final Tree navigation, | ||||
|             final PageContext composerCtx); | ||||
| 
 | ||||
| } | ||||
|  | @ -0,0 +1,162 @@ | |||
| /* | ||||
|  * 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.page.activity; | ||||
| 
 | ||||
| import java.util.function.Consumer; | ||||
| 
 | ||||
| import org.eclipse.swt.widgets.TreeItem; | ||||
| 
 | ||||
| import ch.ethz.seb.sebserver.gbl.model.EntityName; | ||||
| import ch.ethz.seb.sebserver.gui.service.i18n.LocTextKey; | ||||
| import ch.ethz.seb.sebserver.gui.service.page.PageContext.AttributeKeys; | ||||
| import ch.ethz.seb.sebserver.gui.service.page.TemplateComposer; | ||||
| import ch.ethz.seb.sebserver.gui.service.page.action.ActionPane; | ||||
| import ch.ethz.seb.sebserver.gui.service.page.impl.TODOTemplate; | ||||
| 
 | ||||
| public class ActivitySelection { | ||||
| 
 | ||||
|     public static final Consumer<TreeItem> EMPTY_FUNCTION = ti -> { | ||||
|     }; | ||||
|     public static final Consumer<TreeItem> COLLAPSE_NONE_EMPTY = ti -> { | ||||
|         ti.removeAll(); | ||||
|         ti.setItemCount(1); | ||||
|     }; | ||||
| 
 | ||||
|     public enum Activity { | ||||
|         NONE(TODOTemplate.class, TODOTemplate.class, (String) null), | ||||
|         INSTITUTION_ROOT( | ||||
|                 TODOTemplate.class, | ||||
|                 ActionPane.class, | ||||
|                 new LocTextKey("sebserver.activities.inst")), | ||||
|         INSTITUTION_NODE( | ||||
|                 TODOTemplate.class, | ||||
|                 ActionPane.class, | ||||
|                 AttributeKeys.INSTITUTION_ID), | ||||
| // | ||||
| //        USERS(UserAccountsForm.class, ActionPane.class), | ||||
| // | ||||
| //        EXAMS(ExamsListPage.class, ActionPane.class), | ||||
| //        SEB_CONFIGS(SEBConfigurationForm.class, ActionPane.class), | ||||
| //        SEB_CONFIG(SEBConfigurationPage.class, ActionPane.class), | ||||
| //        SEB_CONFIG_TEMPLATES(TODOTemplate.class, ActionPane.class), | ||||
| //        MONITORING(MonitoringForm.class, ActionPane.class), | ||||
| //        RUNNING_EXAMS(RunningExamForm.class, ActionPane.class), | ||||
| //        RUNNING_EXAM(RunningExamPage.class, ActionPane.class, AttributeKeys.EXAM_ID), | ||||
| //        LOGS(TODOTemplate.class, ActionPane.class), | ||||
|         ; | ||||
| 
 | ||||
|         public final LocTextKey title; | ||||
|         public final Class<? extends TemplateComposer> contentPaneComposer; | ||||
|         public final Class<? extends TemplateComposer> actionPaneComposer; | ||||
|         public final String objectIdentifierAttribute; | ||||
| 
 | ||||
|         private Activity( | ||||
|                 final Class<? extends TemplateComposer> objectPaneComposer, | ||||
|                 final Class<? extends TemplateComposer> selectionPaneComposer, | ||||
|                 final LocTextKey title) { | ||||
| 
 | ||||
|             this.title = title; | ||||
|             this.contentPaneComposer = objectPaneComposer; | ||||
|             this.actionPaneComposer = selectionPaneComposer; | ||||
|             this.objectIdentifierAttribute = null; | ||||
|         } | ||||
| 
 | ||||
|         private Activity( | ||||
|                 final Class<? extends TemplateComposer> objectPaneComposer, | ||||
|                 final Class<? extends TemplateComposer> selectionPaneComposer, | ||||
|                 final String objectIdentifierAttribute) { | ||||
| 
 | ||||
|             this.title = null; | ||||
|             this.contentPaneComposer = objectPaneComposer; | ||||
|             this.actionPaneComposer = selectionPaneComposer; | ||||
|             this.objectIdentifierAttribute = objectIdentifierAttribute; | ||||
|         } | ||||
| 
 | ||||
|         public final ActivitySelection createSelection() { | ||||
|             return new ActivitySelection(this); | ||||
|         } | ||||
| 
 | ||||
|         public final ActivitySelection createSelection(final EntityName entityName) { | ||||
|             return new ActivitySelection(this, entityName); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     private static final String ATTR_ACTIVITY_SELECTION = "ACTIVITY_SELECTION"; | ||||
| 
 | ||||
|     public final Activity activity; | ||||
|     public final EntityName entityName; | ||||
|     Consumer<TreeItem> expandFunction = EMPTY_FUNCTION; | ||||
| 
 | ||||
|     ActivitySelection(final Activity activity) { | ||||
|         this(activity, null); | ||||
|     } | ||||
| 
 | ||||
|     ActivitySelection(final Activity activity, final EntityName entityName) { | ||||
|         this.activity = activity; | ||||
|         this.entityName = entityName; | ||||
|         this.expandFunction = EMPTY_FUNCTION; | ||||
|     } | ||||
| 
 | ||||
|     public ActivitySelection withExpandFunction(final Consumer<TreeItem> expandFunction) { | ||||
|         if (expandFunction == null) { | ||||
|             this.expandFunction = EMPTY_FUNCTION; | ||||
|         } | ||||
|         this.expandFunction = expandFunction; | ||||
|         return this; | ||||
|     } | ||||
| 
 | ||||
|     public String getObjectIdentifier() { | ||||
|         if (this.entityName == null) { | ||||
|             return null; | ||||
|         } | ||||
| 
 | ||||
|         return this.entityName.modelId; | ||||
|     } | ||||
| 
 | ||||
|     public void processExpand(final TreeItem item) { | ||||
|         this.expandFunction.accept(item); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public int hashCode() { | ||||
|         final int prime = 31; | ||||
|         int result = 1; | ||||
|         result = prime * result + ((this.activity == null) ? 0 : this.activity.hashCode()); | ||||
|         result = prime * result + ((this.entityName == null) ? 0 : this.entityName.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 ActivitySelection other = (ActivitySelection) obj; | ||||
|         if (this.activity != other.activity) | ||||
|             return false; | ||||
|         if (this.entityName == null) { | ||||
|             if (other.entityName != null) | ||||
|                 return false; | ||||
|         } else if (!this.entityName.equals(other.entityName)) | ||||
|             return false; | ||||
|         return true; | ||||
|     } | ||||
| 
 | ||||
|     public static ActivitySelection get(final TreeItem item) { | ||||
|         return (ActivitySelection) item.getData(ATTR_ACTIVITY_SELECTION); | ||||
|     } | ||||
| 
 | ||||
|     public static void inject(final TreeItem item, final ActivitySelection selection) { | ||||
|         item.setData(ATTR_ACTIVITY_SELECTION, selection); | ||||
|     } | ||||
| 
 | ||||
| } | ||||
|  | @ -0,0 +1,26 @@ | |||
| /* | ||||
|  * 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.page.event; | ||||
| 
 | ||||
| import ch.ethz.seb.sebserver.gui.service.page.action.ActionDefinition; | ||||
| 
 | ||||
| public final class ActionEvent implements PageEvent { | ||||
| 
 | ||||
|     public final ActionDefinition actionDefinition; | ||||
|     public final Object source; | ||||
| 
 | ||||
|     public ActionEvent( | ||||
|             final ActionDefinition actionDefinition, | ||||
|             final Object source) { | ||||
| 
 | ||||
|         this.actionDefinition = actionDefinition; | ||||
|         this.source = source; | ||||
|     } | ||||
| 
 | ||||
| } | ||||
|  | @ -0,0 +1,73 @@ | |||
| /* | ||||
|  * 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.page.event; | ||||
| 
 | ||||
| import java.util.function.Consumer; | ||||
| import java.util.function.Predicate; | ||||
| 
 | ||||
| import org.eclipse.swt.widgets.Widget; | ||||
| 
 | ||||
| import ch.ethz.seb.sebserver.gui.service.page.PageEventListener; | ||||
| import ch.ethz.seb.sebserver.gui.service.page.action.ActionDefinition; | ||||
| 
 | ||||
| public interface ActionEventListener extends PageEventListener<ActionEvent> { | ||||
| 
 | ||||
|     @Override | ||||
|     default boolean match(final Class<? extends PageEvent> type) { | ||||
|         return type == ActionEvent.class; | ||||
|     } | ||||
| 
 | ||||
|     static ActionEventListener of(final Consumer<ActionEvent> eventConsumer) { | ||||
|         return new ActionEventListener() { | ||||
|             @Override | ||||
|             public void notify(final ActionEvent event) { | ||||
|                 eventConsumer.accept(event); | ||||
|             } | ||||
|         }; | ||||
|     } | ||||
| 
 | ||||
|     static ActionEventListener of( | ||||
|             final Predicate<ActionEvent> predicate, | ||||
|             final Consumer<ActionEvent> eventConsumer) { | ||||
| 
 | ||||
|         return new ActionEventListener() { | ||||
|             @Override | ||||
|             public void notify(final ActionEvent event) { | ||||
|                 if (predicate.test(event)) { | ||||
|                     eventConsumer.accept(event); | ||||
|                 } | ||||
|             } | ||||
|         }; | ||||
|     } | ||||
| 
 | ||||
|     static ActionEventListener of( | ||||
|             final ActionDefinition actionDefinition, | ||||
|             final Consumer<ActionEvent> eventConsumer) { | ||||
| 
 | ||||
|         return new ActionEventListener() { | ||||
|             @Override | ||||
|             public void notify(final ActionEvent event) { | ||||
|                 if (event.actionDefinition == actionDefinition) { | ||||
|                     eventConsumer.accept(event); | ||||
|                 } | ||||
|             } | ||||
|         }; | ||||
|     } | ||||
| 
 | ||||
|     static void injectListener( | ||||
|             final Widget widget, | ||||
|             final ActionDefinition actionDefinition, | ||||
|             final Consumer<ActionEvent> eventConsumer) { | ||||
| 
 | ||||
|         widget.setData( | ||||
|                 PageEventListener.LISTENER_ATTRIBUTE_KEY, | ||||
|                 of(actionDefinition, eventConsumer)); | ||||
|     } | ||||
| 
 | ||||
| } | ||||
|  | @ -0,0 +1,53 @@ | |||
| /* | ||||
|  * 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.page.event; | ||||
| 
 | ||||
| import ch.ethz.seb.sebserver.gui.service.page.action.ActionDefinition; | ||||
| 
 | ||||
| public class ActionPublishEvent implements PageEvent { | ||||
| 
 | ||||
|     public final ActionDefinition actionDefinition; | ||||
|     public final Runnable run; | ||||
|     public final String confirmationMessage; | ||||
|     public final String successMessage; | ||||
| 
 | ||||
|     public ActionPublishEvent( | ||||
|             final ActionDefinition actionDefinition, | ||||
|             final Runnable run) { | ||||
| 
 | ||||
|         this(actionDefinition, run, null, null); | ||||
|     } | ||||
| 
 | ||||
|     public ActionPublishEvent( | ||||
|             final ActionDefinition actionDefinition, | ||||
|             final Runnable run, | ||||
|             final String confirmationMessage) { | ||||
| 
 | ||||
|         this(actionDefinition, run, confirmationMessage, null); | ||||
|     } | ||||
| 
 | ||||
|     public ActionPublishEvent( | ||||
|             final ActionDefinition actionDefinition, | ||||
|             final Runnable run, | ||||
|             final String confirmationMessage, | ||||
|             final String successMessage) { | ||||
| 
 | ||||
|         this.actionDefinition = actionDefinition; | ||||
|         this.run = run; | ||||
|         this.confirmationMessage = confirmationMessage; | ||||
|         this.successMessage = successMessage; | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public String toString() { | ||||
|         return "ActionPublishEvent [actionDefinition=" + this.actionDefinition + ", confirmationMessage=" | ||||
|                 + this.confirmationMessage + ", successMessage=" + this.successMessage + "]"; | ||||
|     } | ||||
| 
 | ||||
| } | ||||
|  | @ -0,0 +1,20 @@ | |||
| /* | ||||
|  * 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.page.event; | ||||
| 
 | ||||
| import ch.ethz.seb.sebserver.gui.service.page.PageEventListener; | ||||
| 
 | ||||
| public interface ActionPublishEventListener extends PageEventListener<ActionPublishEvent> { | ||||
| 
 | ||||
|     @Override | ||||
|     default boolean match(final Class<? extends PageEvent> type) { | ||||
|         return type == ActionPublishEvent.class; | ||||
|     } | ||||
| 
 | ||||
| } | ||||
|  | @ -0,0 +1,21 @@ | |||
| /* | ||||
|  * 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.page.event; | ||||
| 
 | ||||
| import ch.ethz.seb.sebserver.gui.service.page.activity.ActivitySelection; | ||||
| 
 | ||||
| public class ActivitySelectionEvent implements PageEvent { | ||||
| 
 | ||||
|     public final ActivitySelection selection; | ||||
| 
 | ||||
|     public ActivitySelectionEvent(final ActivitySelection selection) { | ||||
|         this.selection = selection; | ||||
|     } | ||||
| 
 | ||||
| } | ||||
|  | @ -0,0 +1,20 @@ | |||
| /* | ||||
|  * 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.page.event; | ||||
| 
 | ||||
| import ch.ethz.seb.sebserver.gui.service.page.PageEventListener; | ||||
| 
 | ||||
| public interface ActivitySelectionListener extends PageEventListener<ActivitySelectionEvent> { | ||||
| 
 | ||||
|     @Override | ||||
|     default boolean match(final Class<? extends PageEvent> eventType) { | ||||
|         return eventType == ActivitySelectionEvent.class; | ||||
|     } | ||||
| 
 | ||||
| } | ||||
|  | @ -0,0 +1,21 @@ | |||
| /* | ||||
|  * Copyright (c) 2019 ETH Zürich, Educational Development and Technology (LET) | ||||
|  * | ||||
|  * This Source Code Form is subject to the terms of the Mozilla Public | ||||
|  * License, v. 2.0. If a copy of the MPL was not distributed with this | ||||
|  * file, You can obtain one at http://mozilla.org/MPL/2.0/. | ||||
|  */ | ||||
| 
 | ||||
| package ch.ethz.seb.sebserver.gui.service.page.event; | ||||
| 
 | ||||
| import ch.ethz.seb.sebserver.gui.service.page.PageContext; | ||||
| 
 | ||||
| public final class LogoutEvent implements PageEvent { | ||||
| 
 | ||||
|     public final PageContext pageContext; | ||||
| 
 | ||||
|     public LogoutEvent(final PageContext pageContext) { | ||||
|         this.pageContext = pageContext; | ||||
|     } | ||||
| 
 | ||||
| } | ||||
|  | @ -0,0 +1,20 @@ | |||
| /* | ||||
|  * Copyright (c) 2019 ETH Zürich, Educational Development and Technology (LET) | ||||
|  * | ||||
|  * This Source Code Form is subject to the terms of the Mozilla Public | ||||
|  * License, v. 2.0. If a copy of the MPL was not distributed with this | ||||
|  * file, You can obtain one at http://mozilla.org/MPL/2.0/. | ||||
|  */ | ||||
| 
 | ||||
| package ch.ethz.seb.sebserver.gui.service.page.event; | ||||
| 
 | ||||
| import ch.ethz.seb.sebserver.gui.service.page.PageEventListener; | ||||
| 
 | ||||
| public interface LogoutEventListener extends PageEventListener<LogoutEvent> { | ||||
| 
 | ||||
|     @Override | ||||
|     default boolean match(final Class<? extends PageEvent> eventType) { | ||||
|         return eventType == LogoutEvent.class; | ||||
|     } | ||||
| 
 | ||||
| } | ||||
|  | @ -0,0 +1,13 @@ | |||
| /* | ||||
|  * Copyright (c) 2019 ETH Zürich, Educational Development and Technology (LET) | ||||
|  * | ||||
|  * This Source Code Form is subject to the terms of the Mozilla Public | ||||
|  * License, v. 2.0. If a copy of the MPL was not distributed with this | ||||
|  * file, You can obtain one at http://mozilla.org/MPL/2.0/. | ||||
|  */ | ||||
| 
 | ||||
| package ch.ethz.seb.sebserver.gui.service.page.event; | ||||
| 
 | ||||
| public interface PageEvent { | ||||
| 
 | ||||
| } | ||||
|  | @ -0,0 +1,186 @@ | |||
| /* | ||||
|  * Copyright (c) 2019 ETH Zürich, Educational Development and Technology (LET) | ||||
|  * | ||||
|  * This Source Code Form is subject to the terms of the Mozilla Public | ||||
|  * License, v. 2.0. If a copy of the MPL was not distributed with this | ||||
|  * file, You can obtain one at http://mozilla.org/MPL/2.0/. | ||||
|  */ | ||||
| 
 | ||||
| package ch.ethz.seb.sebserver.gui.service.page.impl; | ||||
| 
 | ||||
| import java.util.Collection; | ||||
| import java.util.Map; | ||||
| import java.util.function.Function; | ||||
| import java.util.stream.Collectors; | ||||
| 
 | ||||
| import org.eclipse.swt.widgets.Composite; | ||||
| import org.eclipse.swt.widgets.Control; | ||||
| import org.slf4j.Logger; | ||||
| import org.slf4j.LoggerFactory; | ||||
| import org.springframework.context.annotation.Lazy; | ||||
| import org.springframework.stereotype.Service; | ||||
| 
 | ||||
| import ch.ethz.seb.sebserver.gbl.profile.GuiProfile; | ||||
| import ch.ethz.seb.sebserver.gui.service.i18n.I18nSupport; | ||||
| import ch.ethz.seb.sebserver.gui.service.page.ComposerService; | ||||
| import ch.ethz.seb.sebserver.gui.service.page.PageContext; | ||||
| import ch.ethz.seb.sebserver.gui.service.page.PageDefinition; | ||||
| import ch.ethz.seb.sebserver.gui.service.page.TemplateComposer; | ||||
| import ch.ethz.seb.sebserver.gui.service.remote.webservice.auth.AuthorizationContextHolder; | ||||
| 
 | ||||
| @Lazy | ||||
| @Service | ||||
| @GuiProfile | ||||
| public class ComposerServiceImpl implements ComposerService { | ||||
| 
 | ||||
|     private static final Logger log = LoggerFactory.getLogger(ComposerServiceImpl.class); | ||||
| 
 | ||||
|     // TODO configurable | ||||
|     private final Class<? extends PageDefinition> loginPageType = DefaultLoginPage.class; | ||||
|     private final Class<? extends PageDefinition> mainPageType = DefaultMainPage.class; | ||||
| 
 | ||||
|     final AuthorizationContextHolder authorizationContextHolder; | ||||
|     private final I18nSupport i18nSupport; | ||||
|     private final Map<String, TemplateComposer> composer; | ||||
|     private final Map<String, PageDefinition> pages; | ||||
| 
 | ||||
|     public ComposerServiceImpl( | ||||
|             final AuthorizationContextHolder authorizationContextHolder, | ||||
|             final I18nSupport i18nSupport, | ||||
|             final Collection<TemplateComposer> composer, | ||||
|             final Collection<PageDefinition> pageDefinitions) { | ||||
| 
 | ||||
|         this.authorizationContextHolder = authorizationContextHolder; | ||||
|         this.i18nSupport = i18nSupport; | ||||
|         this.composer = composer | ||||
|                 .stream() | ||||
|                 .collect(Collectors.toMap( | ||||
|                         comp -> comp.getClass().getName(), | ||||
|                         Function.identity())); | ||||
| 
 | ||||
|         this.pages = pageDefinitions | ||||
|                 .stream() | ||||
|                 .collect(Collectors.toMap( | ||||
|                         page -> page.getClass().getName(), | ||||
|                         Function.identity())); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public PageDefinition mainPage() { | ||||
|         return this.pages.get(this.mainPageType.getName()); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public PageDefinition loginPage() { | ||||
|         return this.pages.get(this.loginPageType.getName()); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public boolean validate(final String composerName, final PageContext pageContext) { | ||||
|         if (!this.composer.containsKey(composerName)) { | ||||
|             return false; | ||||
|         } | ||||
| 
 | ||||
|         return this.composer | ||||
|                 .get(composerName) | ||||
|                 .validate(pageContext); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void compose( | ||||
|             final Class<? extends TemplateComposer> composerType, | ||||
|             final PageContext pageContext) { | ||||
| 
 | ||||
|         compose(composerType.getName(), pageContext); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void compose( | ||||
|             final String name, | ||||
|             final PageContext pageContext) { | ||||
| 
 | ||||
|         if (!this.composer.containsKey(name)) { | ||||
|             log.error("No TemplateComposer with name: " + name + " found. Check Spring confiuration and beans"); | ||||
|             return; | ||||
|         } | ||||
| 
 | ||||
|         final TemplateComposer composer = this.composer.get(name); | ||||
| 
 | ||||
|         if (composer.validate(pageContext)) { | ||||
| 
 | ||||
|             clear(pageContext.getParent()); | ||||
| 
 | ||||
|             try { | ||||
|                 composer.compose(pageContext); | ||||
|             } catch (final Exception e) { | ||||
|                 log.warn("Failed to compose: {}, pageContext: {}", name, pageContext, e); | ||||
|             } | ||||
| 
 | ||||
|             try { | ||||
|                 pageContext.getParent().layout(); | ||||
|             } catch (final Exception e) { | ||||
|                 log.warn("Failed to layout new composition: {}, pageContext: {}", name, pageContext, e); | ||||
|             } | ||||
| 
 | ||||
|         } else { | ||||
|             log.error( | ||||
|                     "Invalid or missing mandatory attributes to handle compose request of ViewComposer: {} pageContext: {}", | ||||
|                     name, | ||||
|                     pageContext); | ||||
|         } | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void composePage( | ||||
|             final PageDefinition pageDefinition, | ||||
|             final Composite root) { | ||||
| 
 | ||||
|         compose( | ||||
|                 pageDefinition.composer(), | ||||
|                 pageDefinition.applyPageContext(createPageContext(root))); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void composePage( | ||||
|             final Class<? extends PageDefinition> pageType, | ||||
|             final Composite root) { | ||||
| 
 | ||||
|         final String pageName = pageType.getName(); | ||||
|         if (!this.pages.containsKey(pageName)) { | ||||
|             log.error("Unknown page with name: {}", pageName); | ||||
|             return; | ||||
|         } | ||||
| 
 | ||||
|         final PageDefinition pageDefinition = this.pages.get(pageName); | ||||
|         compose( | ||||
|                 pageDefinition.composer(), | ||||
|                 pageDefinition.applyPageContext(createPageContext(root))); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void loadLoginPage(final Composite parent) { | ||||
|         composePage(this.loginPageType, parent); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void loadMainPage(final Composite parent) { | ||||
|         composePage(this.mainPageType, parent); | ||||
|     } | ||||
| 
 | ||||
|     private PageContext createPageContext(final Composite root) { | ||||
|         return new PageContextImpl( | ||||
|                 this.i18nSupport, this, root, root, null); | ||||
|     } | ||||
| 
 | ||||
|     private void clear(final Composite parent) { | ||||
|         if (parent == null) { | ||||
|             return; | ||||
|         } | ||||
| 
 | ||||
|         for (final Control control : parent.getChildren()) { | ||||
|             control.dispose(); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
| } | ||||
|  | @ -0,0 +1,37 @@ | |||
| /* | ||||
|  * Copyright (c) 2019 ETH Zürich, Educational Development and Technology (LET) | ||||
|  * | ||||
|  * This Source Code Form is subject to the terms of the Mozilla Public | ||||
|  * License, v. 2.0. If a copy of the MPL was not distributed with this | ||||
|  * file, You can obtain one at http://mozilla.org/MPL/2.0/. | ||||
|  */ | ||||
| 
 | ||||
| package ch.ethz.seb.sebserver.gui.service.page.impl; | ||||
| 
 | ||||
| import org.springframework.context.annotation.Lazy; | ||||
| import org.springframework.stereotype.Component; | ||||
| 
 | ||||
| import ch.ethz.seb.sebserver.gui.service.page.PageContext; | ||||
| import ch.ethz.seb.sebserver.gui.service.page.PageContext.AttributeKeys; | ||||
| import ch.ethz.seb.sebserver.gui.service.page.PageDefinition; | ||||
| import ch.ethz.seb.sebserver.gui.service.page.TemplateComposer; | ||||
| 
 | ||||
| /** Default login page works with the DefaultPageLayout and the | ||||
|  * SEBLogin template */ | ||||
| @Lazy | ||||
| @Component | ||||
| public class DefaultLoginPage implements PageDefinition { | ||||
| 
 | ||||
|     @Override | ||||
|     public Class<? extends TemplateComposer> composer() { | ||||
|         return DefaultPageLayout.class; | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public PageContext applyPageContext(final PageContext pageContext) { | ||||
|         return pageContext.withAttr( | ||||
|                 AttributeKeys.ATTR_PAGE_TEMPLATE_COMPOSER_NAME, | ||||
|                 SEBLogin.class.getName()); | ||||
|     } | ||||
| 
 | ||||
| } | ||||
|  | @ -0,0 +1,37 @@ | |||
| /* | ||||
|  * Copyright (c) 2019 ETH Zürich, Educational Development and Technology (LET) | ||||
|  * | ||||
|  * This Source Code Form is subject to the terms of the Mozilla Public | ||||
|  * License, v. 2.0. If a copy of the MPL was not distributed with this | ||||
|  * file, You can obtain one at http://mozilla.org/MPL/2.0/. | ||||
|  */ | ||||
| 
 | ||||
| package ch.ethz.seb.sebserver.gui.service.page.impl; | ||||
| 
 | ||||
| import org.springframework.context.annotation.Lazy; | ||||
| import org.springframework.stereotype.Component; | ||||
| 
 | ||||
| import ch.ethz.seb.sebserver.gui.service.page.PageContext; | ||||
| import ch.ethz.seb.sebserver.gui.service.page.PageContext.AttributeKeys; | ||||
| import ch.ethz.seb.sebserver.gui.service.page.PageDefinition; | ||||
| import ch.ethz.seb.sebserver.gui.service.page.TemplateComposer; | ||||
| 
 | ||||
| /** Default main page works with the DefaultPageLayout and the | ||||
|  * SEBMainPage template */ | ||||
| @Lazy | ||||
| @Component | ||||
| public class DefaultMainPage implements PageDefinition { | ||||
| 
 | ||||
|     @Override | ||||
|     public Class<? extends TemplateComposer> composer() { | ||||
|         return DefaultPageLayout.class; | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public PageContext applyPageContext(final PageContext pageContext) { | ||||
|         return pageContext.withAttr( | ||||
|                 AttributeKeys.ATTR_PAGE_TEMPLATE_COMPOSER_NAME, | ||||
|                 SEBMainPage.class.getName()); | ||||
|     } | ||||
| 
 | ||||
| } | ||||
|  | @ -0,0 +1,275 @@ | |||
| /* | ||||
|  * 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.page.impl; | ||||
| 
 | ||||
| import org.eclipse.rap.rwt.RWT; | ||||
| import org.eclipse.swt.SWT; | ||||
| import org.eclipse.swt.layout.GridData; | ||||
| import org.eclipse.swt.layout.GridLayout; | ||||
| import org.eclipse.swt.layout.RowLayout; | ||||
| import org.eclipse.swt.widgets.Button; | ||||
| import org.eclipse.swt.widgets.Composite; | ||||
| import org.eclipse.swt.widgets.Label; | ||||
| import org.springframework.beans.factory.annotation.Value; | ||||
| import org.springframework.context.annotation.Lazy; | ||||
| import org.springframework.stereotype.Component; | ||||
| 
 | ||||
| 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.PageContext; | ||||
| import ch.ethz.seb.sebserver.gui.service.page.PageContext.AttributeKeys; | ||||
| import ch.ethz.seb.sebserver.gui.service.page.TemplateComposer; | ||||
| import ch.ethz.seb.sebserver.gui.service.remote.webservice.auth.AuthorizationContextHolder; | ||||
| import ch.ethz.seb.sebserver.gui.service.widget.WidgetFactory; | ||||
| 
 | ||||
| @Lazy | ||||
| @Component | ||||
| public class DefaultPageLayout implements TemplateComposer { | ||||
| 
 | ||||
|     private final WidgetFactory widgetFactory; | ||||
|     private final PolyglotPageService polyglotPageService; | ||||
|     private final AuthorizationContextHolder authorizationContextHolder; | ||||
|     private final String sebServerVersion; | ||||
| 
 | ||||
|     public DefaultPageLayout( | ||||
|             final WidgetFactory widgetFactory, | ||||
|             final PolyglotPageService polyglotPageService, | ||||
|             final AuthorizationContextHolder authorizationContextHolder, | ||||
|             @Value("${sebserver.version}") final String sebServerVersion) { | ||||
| 
 | ||||
|         this.widgetFactory = widgetFactory; | ||||
|         this.polyglotPageService = polyglotPageService; | ||||
|         this.authorizationContextHolder = authorizationContextHolder; | ||||
|         this.sebServerVersion = sebServerVersion; | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public boolean validate(final PageContext pageContext) { | ||||
|         return pageContext.hasAttribute(AttributeKeys.ATTR_PAGE_TEMPLATE_COMPOSER_NAME); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void compose(final PageContext pageContext) { | ||||
| 
 | ||||
|         final GridLayout skeletonLayout = new GridLayout(); | ||||
|         skeletonLayout.marginBottom = 0; | ||||
|         skeletonLayout.marginLeft = 0; | ||||
|         skeletonLayout.marginRight = 0; | ||||
|         skeletonLayout.marginTop = 0; | ||||
|         skeletonLayout.marginHeight = 0; | ||||
|         skeletonLayout.marginWidth = 0; | ||||
|         skeletonLayout.verticalSpacing = 0; | ||||
|         skeletonLayout.horizontalSpacing = 0; | ||||
|         pageContext.getParent().setLayout(skeletonLayout); | ||||
| 
 | ||||
|         composeHeader(pageContext); | ||||
|         composeLogoBar(pageContext); | ||||
|         composeContent(pageContext); | ||||
|         composeFooter(pageContext); | ||||
| 
 | ||||
|         this.polyglotPageService.setDefaultPageLocale(pageContext.getRoot()); | ||||
|     } | ||||
| 
 | ||||
|     private void composeHeader(final PageContext pageContext) { | ||||
|         final Composite header = new Composite(pageContext.getParent(), SWT.NONE); | ||||
|         final GridLayout gridLayout = new GridLayout(); | ||||
|         gridLayout.marginRight = 50; | ||||
|         gridLayout.marginLeft = 50; | ||||
|         header.setLayout(gridLayout); | ||||
|         final GridData headerCell = new GridData(SWT.FILL, SWT.TOP, true, false); | ||||
|         headerCell.minimumHeight = 40; | ||||
|         headerCell.heightHint = 40; | ||||
|         header.setLayoutData(headerCell); | ||||
|         header.setData(RWT.CUSTOM_VARIANT, "header"); | ||||
| 
 | ||||
|         final Composite headerRight = new Composite(header, SWT.NONE); | ||||
|         headerRight.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, true, true)); | ||||
|         final GridLayout headerRightGrid = new GridLayout(2, false); | ||||
|         headerRightGrid.marginHeight = 0; | ||||
|         headerRightGrid.marginWidth = 0; | ||||
|         headerRightGrid.horizontalSpacing = 20; | ||||
|         headerRight.setLayout(headerRightGrid); | ||||
|         headerRight.setData(RWT.CUSTOM_VARIANT, "header"); | ||||
| 
 | ||||
|         if (this.authorizationContextHolder.getAuthorizationContext().isLoggedIn()) { | ||||
|             final Label username = new Label(headerRight, SWT.NONE); | ||||
|             username.setData(RWT.CUSTOM_VARIANT, "header"); | ||||
|             username.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, true, true)); | ||||
|             username.setText(this.authorizationContextHolder | ||||
|                     .getAuthorizationContext() | ||||
|                     .getLoggedInUser() | ||||
|                     .get(pageContext::logoutOnError).username); | ||||
| 
 | ||||
|             final Button logout = this.widgetFactory.buttonLocalized(headerRight, "sebserver.logout"); | ||||
|             logout.setLayoutData(new GridData(SWT.RIGHT, SWT.FILL, true, true)); | ||||
|             logout.setData(RWT.CUSTOM_VARIANT, "header"); | ||||
|             logout.addListener(SWT.Selection, event -> { | ||||
|                 final boolean logoutSuccessful = this.authorizationContextHolder | ||||
|                         .getAuthorizationContext() | ||||
|                         .logout(); | ||||
| 
 | ||||
|                 if (!logoutSuccessful) { | ||||
|                     // TODO error handling | ||||
|                 } | ||||
| 
 | ||||
|                 MainPageState.clear(); | ||||
| 
 | ||||
|                 // forward to login page with success message | ||||
|                 pageContext.forwardToLoginPage( | ||||
|                         pageContext.withAttr(AttributeKeys.LGOUT_SUCCESS, "true")); | ||||
|             }); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     private void composeLogoBar(final PageContext pageContext) { | ||||
|         final Composite logoBar = new Composite(pageContext.getParent(), SWT.NONE); | ||||
|         final GridData logoBarCell = new GridData(SWT.FILL, SWT.TOP, false, false); | ||||
|         logoBarCell.minimumHeight = 80; | ||||
|         logoBarCell.heightHint = 80; | ||||
|         logoBar.setLayoutData(logoBarCell); | ||||
|         logoBar.setData(RWT.CUSTOM_VARIANT, "logo"); | ||||
|         final GridLayout logoBarLayout = new GridLayout(2, false); | ||||
|         logoBarLayout.horizontalSpacing = 0; | ||||
|         logoBarLayout.marginHeight = 0; | ||||
|         logoBar.setLayout(logoBarLayout); | ||||
| 
 | ||||
|         final Composite logo = new Composite(logoBar, SWT.NONE); | ||||
|         final GridData logoCell = new GridData(SWT.LEFT, SWT.CENTER, true, true); | ||||
|         logoCell.minimumHeight = 80; | ||||
|         logoCell.heightHint = 80; | ||||
|         logoCell.minimumWidth = 400; | ||||
|         logoCell.horizontalIndent = 50; | ||||
|         logo.setLayoutData(logoCell); | ||||
|         logo.setData(RWT.CUSTOM_VARIANT, "bgLogo"); | ||||
| 
 | ||||
|         final Composite langSupport = new Composite(logoBar, SWT.NONE); | ||||
|         final GridData langSupportCell = new GridData(SWT.RIGHT, SWT.CENTER, false, false); | ||||
|         langSupportCell.heightHint = 20; | ||||
|         logoCell.horizontalIndent = 50; | ||||
|         langSupport.setLayoutData(langSupportCell); | ||||
|         langSupport.setData(RWT.CUSTOM_VARIANT, "logo"); | ||||
|         final RowLayout rowLayout = new RowLayout(SWT.HORIZONTAL); | ||||
|         rowLayout.spacing = 7; | ||||
|         rowLayout.marginRight = 70; | ||||
|         langSupport.setLayout(rowLayout); | ||||
| 
 | ||||
|         this.widgetFactory.createLanguageSelector(pageContext.copyOf(langSupport)); | ||||
| //        for (final Locale locale : this.i18nSupport.supportedLanguages()) { | ||||
| //            final LanguageSelection languageSelection = new LanguageSelection(langSupport, locale); | ||||
| //            languageSelection.updateLocale(this.i18nSupport); | ||||
| //            languageSelection.addListener(SWT.MouseDown, event -> { | ||||
| //                this.polyglotPageService.setPageLocale(pageContext.root, languageSelection.locale); | ||||
| // | ||||
| //            }); | ||||
| //        } | ||||
|     } | ||||
| 
 | ||||
|     private void composeContent(final PageContext pageContext) { | ||||
|         final Composite contentBackground = new Composite(pageContext.getParent(), SWT.NONE); | ||||
|         contentBackground.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); | ||||
|         contentBackground.setData(RWT.CUSTOM_VARIANT, "bgContent"); | ||||
|         final GridLayout innerGrid = new GridLayout(); | ||||
|         innerGrid.marginLeft = 50; | ||||
|         innerGrid.marginRight = 50; | ||||
|         innerGrid.marginHeight = 0; | ||||
|         innerGrid.marginWidth = 0; | ||||
| 
 | ||||
|         contentBackground.setLayout(innerGrid); | ||||
| 
 | ||||
|         final Composite content = new Composite(contentBackground, SWT.NONE); | ||||
|         content.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); | ||||
|         content.setData(RWT.CUSTOM_VARIANT, "content"); | ||||
|         final GridLayout contentGrid = new GridLayout(); | ||||
|         contentGrid.marginHeight = 0; | ||||
|         contentGrid.marginWidth = 0; | ||||
|         content.setLayout(contentGrid); | ||||
| 
 | ||||
|         final Composite contentInner = new Composite(content, SWT.NONE); | ||||
|         contentInner.setLayoutData(new GridData(SWT.CENTER, SWT.CENTER, true, true)); | ||||
|         final GridLayout gridLayout = new GridLayout(); | ||||
|         gridLayout.marginHeight = 0; | ||||
|         gridLayout.marginWidth = 0; | ||||
|         contentInner.setLayout(gridLayout); | ||||
| 
 | ||||
|         final String contentComposerName = pageContext.getAttribute( | ||||
|                 AttributeKeys.ATTR_PAGE_TEMPLATE_COMPOSER_NAME); | ||||
|         pageContext.composerService().compose( | ||||
|                 contentComposerName, | ||||
|                 pageContext.copyOf(contentInner)); | ||||
|     } | ||||
| 
 | ||||
|     private void composeFooter(final PageContext pageContext) { | ||||
|         final Composite footerBar = new Composite(pageContext.getParent(), SWT.NONE); | ||||
|         final GridData footerCell = new GridData(SWT.FILL, SWT.BOTTOM, false, false); | ||||
|         footerCell.minimumHeight = 30; | ||||
|         footerCell.heightHint = 30; | ||||
|         footerBar.setLayoutData(footerCell); | ||||
|         footerBar.setData(RWT.CUSTOM_VARIANT, "bgFooter"); | ||||
|         final GridLayout innerBarGrid = new GridLayout(); | ||||
|         innerBarGrid.marginHeight = 0; | ||||
|         innerBarGrid.marginWidth = 0; | ||||
|         innerBarGrid.marginLeft = 50; | ||||
|         innerBarGrid.marginRight = 50; | ||||
|         footerBar.setLayout(innerBarGrid); | ||||
| 
 | ||||
|         final Composite footer = new Composite(footerBar, SWT.NONE); | ||||
|         final GridData gridData = new GridData(SWT.FILL, SWT.FILL, true, true); | ||||
|         footer.setLayoutData(gridData); | ||||
|         final GridLayout footerGrid = new GridLayout(2, false); | ||||
|         footerGrid.marginHeight = 0; | ||||
|         footerGrid.marginWidth = 0; | ||||
|         footerGrid.horizontalSpacing = 0; | ||||
|         footer.setLayout(footerGrid); | ||||
|         footer.setData(RWT.CUSTOM_VARIANT, "footer"); | ||||
| 
 | ||||
|         final Composite footerLeft = new Composite(footer, SWT.NONE); | ||||
|         footerLeft.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, true, true)); | ||||
|         footerLeft.setData(RWT.CUSTOM_VARIANT, "footer"); | ||||
|         RowLayout rowLayout = new RowLayout(SWT.HORIZONTAL); | ||||
|         rowLayout.marginLeft = 20; | ||||
|         rowLayout.spacing = 20; | ||||
|         footerLeft.setLayout(rowLayout); | ||||
| 
 | ||||
|         final Composite footerRight = new Composite(footer, SWT.NONE); | ||||
|         footerRight.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, true, true)); | ||||
|         footerRight.setData(RWT.CUSTOM_VARIANT, "footer"); | ||||
|         rowLayout = new RowLayout(SWT.HORIZONTAL); | ||||
|         rowLayout.marginRight = 20; | ||||
|         footerRight.setLayout(rowLayout); | ||||
| 
 | ||||
|         this.widgetFactory.labelLocalized(footerLeft, "footer", new LocTextKey("sebserver.overall.imprint")); | ||||
|         this.widgetFactory.labelLocalized(footerLeft, "footer", new LocTextKey("sebserver.overall.about")); | ||||
|         this.widgetFactory.labelLocalized( | ||||
|                 footerRight, | ||||
|                 "footer", | ||||
|                 new LocTextKey("sebserver.overall.version", this.sebServerVersion)); | ||||
|     } | ||||
| 
 | ||||
| //    private final class LanguageSelection extends Label implements Polyglot { | ||||
| // | ||||
| //        private static final long serialVersionUID = 8110167162843383940L; | ||||
| //        private final Locale locale; | ||||
| // | ||||
| //        public LanguageSelection(final Composite parent, final Locale locale) { | ||||
| //            super(parent, SWT.NONE); | ||||
| //            this.locale = locale; | ||||
| //            super.setData(RWT.CUSTOM_VARIANT, "header"); | ||||
| //            super.setText("|  " + locale.getLanguage().toUpperCase()); | ||||
| //        } | ||||
| // | ||||
| //        @Override | ||||
| //        public void updateLocale(final I18nSupport i18nSupport) { | ||||
| //            super.setVisible( | ||||
| //                    !i18nSupport.getCurrentLocale() | ||||
| //                            .getLanguage() | ||||
| //                            .equals(this.locale.getLanguage())); | ||||
| //        } | ||||
| //    } | ||||
| 
 | ||||
| } | ||||
|  | @ -0,0 +1,49 @@ | |||
| /* | ||||
|  * Copyright (c) 2019 ETH Zürich, Educational Development and Technology (LET) | ||||
|  *  | ||||
|  * This Source Code Form is subject to the terms of the Mozilla Public | ||||
|  * License, v. 2.0. If a copy of the MPL was not distributed with this | ||||
|  * file, You can obtain one at http://mozilla.org/MPL/2.0/. | ||||
|  */ | ||||
| 
 | ||||
| package ch.ethz.seb.sebserver.gui.service.page.impl; | ||||
| 
 | ||||
| import javax.servlet.http.HttpSession; | ||||
| 
 | ||||
| import org.eclipse.rap.rwt.RWT; | ||||
| 
 | ||||
| import ch.ethz.seb.sebserver.gui.service.page.activity.ActivitySelection; | ||||
| import ch.ethz.seb.sebserver.gui.service.page.activity.ActivitySelection.Activity; | ||||
| 
 | ||||
| public final class MainPageState { | ||||
| 
 | ||||
|     public ActivitySelection activitySelection = Activity.NONE.createSelection(); | ||||
| 
 | ||||
|     private MainPageState() { | ||||
|     } | ||||
| 
 | ||||
|     public static MainPageState get() { | ||||
|         try { | ||||
|             final HttpSession httpSession = RWT | ||||
|                     .getUISession() | ||||
|                     .getHttpSession(); | ||||
| 
 | ||||
|             MainPageState mainPageState = (MainPageState) httpSession.getAttribute(SEBMainPage.ATTR_MAIN_PAGE_STATE); | ||||
|             if (mainPageState == null) { | ||||
|                 mainPageState = new MainPageState(); | ||||
|                 httpSession.setAttribute(SEBMainPage.ATTR_MAIN_PAGE_STATE, mainPageState); | ||||
|             } | ||||
| 
 | ||||
|             return mainPageState; | ||||
|         } catch (final Exception e) { | ||||
|             SEBMainPage.log.error("Unexpected error while trying to get MainPageState from user-session"); | ||||
|         } | ||||
| 
 | ||||
|         return null; | ||||
|     } | ||||
| 
 | ||||
|     public static void clear() { | ||||
|         final MainPageState mainPageState = get(); | ||||
|         mainPageState.activitySelection = Activity.NONE.createSelection(); | ||||
|     } | ||||
| } | ||||
|  | @ -0,0 +1,275 @@ | |||
| /* | ||||
|  * Copyright (c) 2019 ETH Zürich, Educational Development and Technology (LET) | ||||
|  * | ||||
|  * This Source Code Form is subject to the terms of the Mozilla Public | ||||
|  * License, v. 2.0. If a copy of the MPL was not distributed with this | ||||
|  * file, You can obtain one at http://mozilla.org/MPL/2.0/. | ||||
|  */ | ||||
| 
 | ||||
| package ch.ethz.seb.sebserver.gui.service.page.impl; | ||||
| 
 | ||||
| import java.util.ArrayList; | ||||
| import java.util.Comparator; | ||||
| import java.util.HashMap; | ||||
| import java.util.List; | ||||
| import java.util.Map; | ||||
| 
 | ||||
| import org.eclipse.rap.rwt.widgets.DialogCallback; | ||||
| import org.eclipse.swt.SWT; | ||||
| import org.eclipse.swt.widgets.Composite; | ||||
| import org.eclipse.swt.widgets.MessageBox; | ||||
| import org.eclipse.swt.widgets.Shell; | ||||
| import org.slf4j.Logger; | ||||
| import org.slf4j.LoggerFactory; | ||||
| 
 | ||||
| import ch.ethz.seb.sebserver.gbl.api.APIMessage; | ||||
| import ch.ethz.seb.sebserver.gbl.api.APIMessageError; | ||||
| import ch.ethz.seb.sebserver.gbl.util.Utils; | ||||
| import ch.ethz.seb.sebserver.gui.service.i18n.I18nSupport; | ||||
| import ch.ethz.seb.sebserver.gui.service.page.ComposerService; | ||||
| import ch.ethz.seb.sebserver.gui.service.page.PageContext; | ||||
| import ch.ethz.seb.sebserver.gui.service.page.PageDefinition; | ||||
| import ch.ethz.seb.sebserver.gui.service.page.PageEventListener; | ||||
| import ch.ethz.seb.sebserver.gui.service.page.event.PageEvent; | ||||
| import ch.ethz.seb.sebserver.gui.service.widget.Message; | ||||
| 
 | ||||
| public class PageContextImpl implements PageContext { | ||||
| 
 | ||||
|     private static final Logger log = LoggerFactory.getLogger(PageContextImpl.class); | ||||
| 
 | ||||
|     private final I18nSupport i18nSupport; | ||||
|     private final ComposerService composerService; | ||||
|     private final Composite root; | ||||
|     private final Composite parent; | ||||
|     private final Map<String, String> attributes; | ||||
| 
 | ||||
|     PageContextImpl( | ||||
|             final I18nSupport i18nSupport, | ||||
|             final ComposerService composerService, | ||||
|             final Composite root, | ||||
|             final Composite parent, | ||||
|             final Map<String, String> attributes) { | ||||
| 
 | ||||
|         this.i18nSupport = i18nSupport; | ||||
|         this.composerService = composerService; | ||||
|         this.root = root; | ||||
|         this.parent = parent; | ||||
|         this.attributes = Utils.immutableMapOf(attributes); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public Shell getShell() { | ||||
|         if (this.root == null) { | ||||
|             return null; | ||||
|         } | ||||
| 
 | ||||
|         return this.root.getShell(); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public ComposerService composerService() { | ||||
|         return this.composerService; | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public Composite getRoot() { | ||||
|         return this.root; | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public Composite getParent() { | ||||
|         return this.parent; | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public PageContext copyOf(final Composite parent) { | ||||
|         return new PageContextImpl( | ||||
|                 this.i18nSupport, | ||||
|                 this.composerService, | ||||
|                 this.root, | ||||
|                 parent, | ||||
|                 this.attributes); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public PageContext copyOfAttributes(final PageContext otherContext) { | ||||
|         final Map<String, String> attrs = new HashMap<>(); | ||||
|         attrs.putAll(this.attributes); | ||||
|         attrs.putAll(((PageContextImpl) otherContext).attributes); | ||||
|         return new PageContextImpl( | ||||
|                 this.i18nSupport, | ||||
|                 this.composerService, | ||||
|                 this.root, | ||||
|                 this.parent, | ||||
|                 attrs); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public PageContext withAttr(final String key, final String value) { | ||||
|         final Map<String, String> attrs = new HashMap<>(); | ||||
|         attrs.putAll(this.attributes); | ||||
|         attrs.put(key, value); | ||||
|         return new PageContextImpl( | ||||
|                 this.i18nSupport, | ||||
|                 this.composerService, | ||||
|                 this.root, | ||||
|                 this.parent, attrs); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public String getAttribute(final String name) { | ||||
|         return this.attributes.get(name); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public String getAttribute(final String name, final String def) { | ||||
|         if (this.attributes.containsKey(name)) { | ||||
|             return this.attributes.get(name); | ||||
|         } else { | ||||
|             return def; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public boolean hasAttribute(final String name) { | ||||
|         return this.attributes.containsKey(name); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     @SuppressWarnings("unchecked") | ||||
|     public <T extends PageEvent> void publishPageEvent(final T event) { | ||||
|         final Class<? extends PageEvent> typeClass = event.getClass(); | ||||
|         final List<PageEventListener<T>> listeners = new ArrayList<>(); | ||||
|         ComposerService.traversePageTree( | ||||
|                 this.root, | ||||
|                 c -> { | ||||
|                     final PageEventListener<?> listener = | ||||
|                             (PageEventListener<?>) c.getData(PageEventListener.LISTENER_ATTRIBUTE_KEY); | ||||
|                     return listener != null && listener.match(typeClass); | ||||
|                 }, | ||||
|                 c -> listeners.add(((PageEventListener<T>) c.getData(PageEventListener.LISTENER_ATTRIBUTE_KEY)))); | ||||
| 
 | ||||
|         if (listeners.isEmpty()) { | ||||
|             return; | ||||
|         } | ||||
| 
 | ||||
|         listeners.stream() | ||||
|                 .sorted(LISTENER_COMPARATOR) | ||||
|                 .forEach(listener -> listener.notify(event)); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     @SuppressWarnings("serial") | ||||
|     public void applyConfirmDialog(final String confirmMessage, final Runnable onOK) { | ||||
|         final Message messageBox = new Message( | ||||
|                 this.root.getShell(), | ||||
|                 this.i18nSupport.getText("org.sebserver.dialog.confirm.title"), | ||||
|                 this.i18nSupport.getText(confirmMessage), | ||||
|                 SWT.OK | SWT.CANCEL); | ||||
|         messageBox.open(new DialogCallback() { | ||||
|             @Override | ||||
|             public void dialogClosed(final int returnCode) { | ||||
|                 if (returnCode == SWT.OK) { | ||||
|                     try { | ||||
|                         onOK.run(); | ||||
|                     } catch (final Throwable t) { | ||||
|                         log.error( | ||||
|                                 "Unexpected on confirm callback execution. This should not happen, plase secure the given onOK Runnable", | ||||
|                                 t); | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         }); | ||||
|     } | ||||
| 
 | ||||
| //    public void applyValidationErrorDialog(final Collection<FieldValidationError> validationErrors) { | ||||
| //        final Message messageBox = new Message( | ||||
| //                this.root.getShell(), | ||||
| //                this.i18nSupport.getText("org.sebserver.dialog.validationErrors.title"), | ||||
| //                this.i18nSupport.getText(confirmMessage), | ||||
| //                SWT.OK); | ||||
| //    } | ||||
| 
 | ||||
|     @Override | ||||
|     public void forwardToPage( | ||||
|             final PageDefinition pageDefinition, | ||||
|             final PageContext pageContext) { | ||||
| 
 | ||||
|         this.composerService.compose( | ||||
|                 pageDefinition.composer(), | ||||
|                 pageDefinition.applyPageContext(pageContext.copyOf(pageContext.getRoot()))); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void forwardToMainPage(final PageContext pageContext) { | ||||
|         forwardToPage(this.composerService.mainPage(), pageContext); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void forwardToLoginPage(final PageContext pageContext) { | ||||
|         forwardToPage(this.composerService.loginPage(), pageContext); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void notifyError(final String errorMessage, final Throwable error) { | ||||
|         if (error instanceof APIMessageError) { | ||||
|             final List<APIMessage> errorMessages = ((APIMessageError) error).getErrorMessages(); | ||||
|             final MessageBox messageBox = new Message( | ||||
|                     getShell(), | ||||
|                     this.i18nSupport.getText("sebserver.error.unexpected"), | ||||
|                     APIMessage.toHTML(errorMessages), | ||||
|                     SWT.ERROR); | ||||
|             messageBox.setMarkupEnabled(true); | ||||
|             messageBox.open(null); | ||||
|             return; | ||||
|         } | ||||
| 
 | ||||
|         final MessageBox messageBox = new Message( | ||||
|                 getShell(), | ||||
|                 this.i18nSupport.getText("sebserver.error.unexpected"), | ||||
|                 error.toString(), | ||||
|                 SWT.ERROR); | ||||
|         messageBox.open(null); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void notifyError(final Throwable error) { | ||||
|         notifyError(error.getMessage(), error); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public <T> T logoutOnError(final Throwable error) { | ||||
|         // just to be sure we leave a clean and proper authorizationContext | ||||
|         try { | ||||
|             ((ComposerServiceImpl) this.composerService).authorizationContextHolder | ||||
|                     .getAuthorizationContext() | ||||
|                     .logout(); | ||||
|         } catch (final Exception e) { | ||||
|             log.info("Cleanup logout failed: {}", e.getMessage()); | ||||
|         } | ||||
| 
 | ||||
|         MainPageState.clear(); | ||||
|         forwardToLoginPage(this.withAttr( | ||||
|                 AttributeKeys.AUTHORIZATION_FAILURE, | ||||
|                 error.getMessage())); | ||||
| 
 | ||||
|         return null; | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public String toString() { | ||||
|         return "PageContextImpl [root=" + this.root + ", parent=" + this.parent + ", attributes=" + this.attributes | ||||
|                 + "]"; | ||||
|     } | ||||
| 
 | ||||
|     private static final Comparator<PageEventListener<?>> LISTENER_COMPARATOR = | ||||
|             new Comparator<>() { | ||||
|                 @Override | ||||
|                 public int compare(final PageEventListener<?> o1, final PageEventListener<?> o2) { | ||||
|                     final int x = o1.priority(); | ||||
|                     final int y = o2.priority(); | ||||
|                     return (x < y) ? -1 : ((x == y) ? 0 : 1); | ||||
|                 } | ||||
|             }; | ||||
| 
 | ||||
| } | ||||
|  | @ -0,0 +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.service.page.impl; | ||||
| 
 | ||||
| import org.eclipse.rap.rwt.RWT; | ||||
| 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.Label; | ||||
| import org.eclipse.swt.widgets.MessageBox; | ||||
| import org.eclipse.swt.widgets.Text; | ||||
| 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.profile.GuiProfile; | ||||
| import ch.ethz.seb.sebserver.gui.service.i18n.I18nSupport; | ||||
| import ch.ethz.seb.sebserver.gui.service.page.PageContext; | ||||
| import ch.ethz.seb.sebserver.gui.service.page.PageContext.AttributeKeys; | ||||
| import ch.ethz.seb.sebserver.gui.service.page.TemplateComposer; | ||||
| import ch.ethz.seb.sebserver.gui.service.remote.webservice.auth.AuthorizationContextHolder; | ||||
| import ch.ethz.seb.sebserver.gui.service.remote.webservice.auth.SEBServerAuthorizationContext; | ||||
| import ch.ethz.seb.sebserver.gui.service.widget.Message; | ||||
| import ch.ethz.seb.sebserver.gui.service.widget.WidgetFactory; | ||||
| 
 | ||||
| @Lazy | ||||
| @Component | ||||
| @GuiProfile | ||||
| public class SEBLogin implements TemplateComposer { | ||||
| 
 | ||||
|     private static final Logger log = LoggerFactory.getLogger(SEBLogin.class); | ||||
| 
 | ||||
|     private final AuthorizationContextHolder authorizationContextHolder; | ||||
|     private final WidgetFactory widgetFactory; | ||||
|     private final I18nSupport i18nSupport; | ||||
| 
 | ||||
|     public SEBLogin( | ||||
|             final AuthorizationContextHolder authorizationContextHolder, | ||||
|             final WidgetFactory widgetFactory, | ||||
|             final I18nSupport i18nSupport) { | ||||
| 
 | ||||
|         this.authorizationContextHolder = authorizationContextHolder; | ||||
|         this.widgetFactory = widgetFactory; | ||||
|         this.i18nSupport = i18nSupport; | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void compose(final PageContext pageContext) { | ||||
|         final Composite parent = pageContext.getParent(); | ||||
| 
 | ||||
|         if (pageContext.hasAttribute((AttributeKeys.LGOUT_SUCCESS))) { | ||||
|             final MessageBox logoutSuccess = new Message( | ||||
|                     pageContext.getShell(), | ||||
|                     this.i18nSupport.getText("org.sebserver.logout"), | ||||
|                     this.i18nSupport.getText("org.sebserver.logout.success.message"), | ||||
|                     SWT.ICON_INFORMATION); | ||||
|             logoutSuccess.open(null); | ||||
|         } | ||||
| 
 | ||||
|         final Composite loginGroup = new Composite(parent, SWT.NONE); | ||||
|         final GridLayout rowLayout = new GridLayout(); | ||||
|         rowLayout.marginWidth = 20; | ||||
|         rowLayout.marginRight = 100; | ||||
|         loginGroup.setLayout(rowLayout); | ||||
|         loginGroup.setData(RWT.CUSTOM_VARIANT, "login"); | ||||
| 
 | ||||
|         final Label name = this.widgetFactory.labelLocalized(loginGroup, "sebserver.login.username"); | ||||
|         name.setLayoutData(new GridData(300, -1)); | ||||
|         name.setAlignment(SWT.BOTTOM); | ||||
|         final Text loginName = new Text(loginGroup, SWT.LEFT | SWT.BORDER); | ||||
|         loginName.setLayoutData(new GridData(SWT.FILL, SWT.TOP, false, false)); | ||||
|         GridData gridData = new GridData(SWT.FILL, SWT.TOP, false, false); | ||||
|         gridData.verticalIndent = 10; | ||||
|         final Label pwd = this.widgetFactory.labelLocalized(loginGroup, "sebserver.login.pwd"); | ||||
|         pwd.setLayoutData(gridData); | ||||
|         final Text loginPassword = new Text(loginGroup, SWT.LEFT | SWT.PASSWORD | SWT.BORDER); | ||||
|         loginPassword.setLayoutData(new GridData(SWT.FILL, SWT.TOP, false, false)); | ||||
| 
 | ||||
|         final Button button = this.widgetFactory.buttonLocalized(loginGroup, "sebserver.login.login"); | ||||
|         gridData = new GridData(SWT.LEFT, SWT.TOP, false, false); | ||||
|         gridData.verticalIndent = 10; | ||||
|         button.setLayoutData(gridData); | ||||
| 
 | ||||
|         final SEBServerAuthorizationContext authorizationContext = this.authorizationContextHolder | ||||
|                 .getAuthorizationContext(RWT.getUISession().getHttpSession()); | ||||
| 
 | ||||
|         button.addListener(SWT.Selection, event -> { | ||||
|             final String username = loginName.getText(); | ||||
|             try { | ||||
| 
 | ||||
|                 final boolean loggedIn = authorizationContext.login( | ||||
|                         username, | ||||
|                         loginPassword.getText()); | ||||
| 
 | ||||
|                 if (loggedIn) { | ||||
|                     // Set users locale on page after successful login | ||||
|                     this.i18nSupport.setSessionLocale( | ||||
|                             authorizationContext | ||||
|                                     .getLoggedInUser() | ||||
|                                     .get(pageContext::logoutOnError).locale); | ||||
| 
 | ||||
|                     pageContext.forwardToMainPage(pageContext); | ||||
| 
 | ||||
|                 } else { | ||||
|                     loginError(pageContext, "sebserver.login.failed.message"); | ||||
|                 } | ||||
|             } catch (final Exception e) { | ||||
|                 log.error("Unexpected error while trying to login with user: {}", username, e); | ||||
|                 loginError(pageContext, "Unexpected Error. Please call an Administrator"); | ||||
|             } | ||||
|         }); | ||||
|         loginName.addListener(SWT.KeyDown, event -> { | ||||
|             if (event.character == '\n' || event.character == '\r') { | ||||
|                 loginPassword.setFocus(); | ||||
|             } | ||||
|         }); | ||||
|         loginPassword.addListener(SWT.KeyDown, event -> { | ||||
|             if (event.character == '\n' || event.character == '\r') { | ||||
|                 button.setFocus(); | ||||
|             } | ||||
|         }); | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
|     private void loginError( | ||||
|             final PageContext pageContext, | ||||
|             final String message) { | ||||
| 
 | ||||
|         final MessageBox error = new Message( | ||||
|                 pageContext.getShell(), | ||||
|                 this.i18nSupport.getText("sebserver.login.failed.title"), | ||||
|                 this.i18nSupport.getText(message, message), | ||||
|                 SWT.ERROR); | ||||
|         error.open(null); | ||||
|         pageContext.logoutOnError(new RuntimeException(message)); | ||||
|     } | ||||
| 
 | ||||
| } | ||||
|  | @ -0,0 +1,166 @@ | |||
| /* | ||||
|  * Copyright (c) 2019 ETH Zürich, Educational Development and Technology (LET) | ||||
|  * | ||||
|  * This Source Code Form is subject to the terms of the Mozilla Public | ||||
|  * License, v. 2.0. If a copy of the MPL was not distributed with this | ||||
|  * file, You can obtain one at http://mozilla.org/MPL/2.0/. | ||||
|  */ | ||||
| 
 | ||||
| package ch.ethz.seb.sebserver.gui.service.page.impl; | ||||
| 
 | ||||
| import org.eclipse.rap.rwt.RWT; | ||||
| import org.eclipse.swt.SWT; | ||||
| import org.eclipse.swt.custom.SashForm; | ||||
| import org.eclipse.swt.layout.GridData; | ||||
| import org.eclipse.swt.layout.GridLayout; | ||||
| import org.eclipse.swt.widgets.Composite; | ||||
| import org.eclipse.swt.widgets.Label; | ||||
| 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.profile.GuiProfile; | ||||
| 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.PageEventListener; | ||||
| import ch.ethz.seb.sebserver.gui.service.page.TemplateComposer; | ||||
| import ch.ethz.seb.sebserver.gui.service.page.activity.ActivitiesPane; | ||||
| import ch.ethz.seb.sebserver.gui.service.page.event.ActivitySelectionEvent; | ||||
| import ch.ethz.seb.sebserver.gui.service.page.event.ActivitySelectionListener; | ||||
| import ch.ethz.seb.sebserver.gui.service.widget.WidgetFactory; | ||||
| import ch.ethz.seb.sebserver.gui.service.widget.WidgetFactory.IconButtonType; | ||||
| 
 | ||||
| @Lazy | ||||
| @Component | ||||
| @GuiProfile | ||||
| public class SEBMainPage implements TemplateComposer { | ||||
| 
 | ||||
|     static final Logger log = LoggerFactory.getLogger(SEBMainPage.class); | ||||
| 
 | ||||
|     public static final String ATTR_MAIN_PAGE_STATE = "MAIN_PAGE_STATE"; | ||||
| 
 | ||||
|     private static final int ACTIVITY_PANE_WEIGHT = 20; | ||||
|     private static final int CONTENT_PANE_WEIGHT = 65; | ||||
|     private static final int ACTION_PANE_WEIGHT = 15; | ||||
|     private static final int[] DEFAULT_SASH_WEIGHTS = new int[] { | ||||
|             ACTIVITY_PANE_WEIGHT, | ||||
|             CONTENT_PANE_WEIGHT, | ||||
|             ACTION_PANE_WEIGHT | ||||
|     }; | ||||
|     private static final int[] OPENED_SASH_WEIGHTS = new int[] { 0, 100, 0 }; | ||||
| 
 | ||||
|     private final WidgetFactory widgetFactory; | ||||
| 
 | ||||
|     public SEBMainPage(final WidgetFactory widgetFactory) { | ||||
|         this.widgetFactory = widgetFactory; | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void compose(final PageContext pageContext) { | ||||
|         MainPageState.clear(); | ||||
| 
 | ||||
|         final Composite parent = pageContext.getParent(); | ||||
|         parent.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); | ||||
| 
 | ||||
|         final SashForm mainSash = new SashForm(parent, SWT.HORIZONTAL); | ||||
|         final GridLayout gridLayout = new GridLayout(); | ||||
| 
 | ||||
|         mainSash.setLayout(gridLayout); | ||||
|         mainSash.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); | ||||
| 
 | ||||
|         final Composite nav = new Composite(mainSash, SWT.NONE); | ||||
|         nav.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); | ||||
|         final GridLayout navLayout = new GridLayout(); | ||||
|         navLayout.marginHeight = 20; | ||||
|         navLayout.marginWidth = 0; | ||||
|         nav.setLayout(navLayout); | ||||
| 
 | ||||
|         final Composite content = new Composite(mainSash, SWT.NONE); | ||||
|         content.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); | ||||
|         final GridLayout contentOuterlayout = new GridLayout(); | ||||
|         contentOuterlayout.marginHeight = 0; | ||||
|         contentOuterlayout.marginWidth = 0; | ||||
|         content.setLayout(contentOuterlayout); | ||||
| 
 | ||||
|         final Label toggleView = this.widgetFactory.imageButton( | ||||
|                 IconButtonType.MAXIMIZE, | ||||
|                 content, | ||||
|                 new LocTextKey("sebserver.mainpage.maximize.tooltip"), | ||||
|                 event -> { | ||||
|                     final Label ib = (Label) event.widget; | ||||
|                     if ((Boolean) ib.getData("fullScreen")) { | ||||
|                         mainSash.setWeights(DEFAULT_SASH_WEIGHTS); | ||||
|                         ib.setData("fullScreen", false); | ||||
|                         ib.setImage(WidgetFactory.IconButtonType.MAXIMIZE.getImage(ib.getDisplay())); | ||||
|                         this.widgetFactory.injectI18n( | ||||
|                                 ib, | ||||
|                                 null, | ||||
|                                 new LocTextKey("sebserver.mainpage.maximize.tooltip")); | ||||
|                     } else { | ||||
|                         mainSash.setWeights(OPENED_SASH_WEIGHTS); | ||||
|                         ib.setData("fullScreen", true); | ||||
|                         ib.setImage(WidgetFactory.IconButtonType.MINIMIZE.getImage(ib.getDisplay())); | ||||
|                         this.widgetFactory.injectI18n( | ||||
|                                 ib, | ||||
|                                 null, | ||||
|                                 new LocTextKey("sebserver.mainpage.minimize.tooltip")); | ||||
|                     } | ||||
|                 }); | ||||
|         final GridData gridData = new GridData(SWT.RIGHT, SWT.TOP, true, false); | ||||
|         toggleView.setLayoutData(gridData); | ||||
|         toggleView.setData("fullScreen", false); | ||||
| 
 | ||||
|         final Composite contentObjects = new Composite(content, SWT.NONE); | ||||
|         contentObjects.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); | ||||
|         final GridLayout contentObjectslayout = new GridLayout(); | ||||
|         contentObjectslayout.marginHeight = 0; | ||||
|         contentObjectslayout.marginWidth = 0; | ||||
|         contentObjects.setLayout(contentObjectslayout); | ||||
|         contentObjects.setData(PageEventListener.LISTENER_ATTRIBUTE_KEY, | ||||
|                 new ActivitySelectionListener() { | ||||
|                     @Override | ||||
|                     public int priority() { | ||||
|                         return 2; | ||||
|                     } | ||||
| 
 | ||||
|                     @Override | ||||
|                     public void notify(final ActivitySelectionEvent event) { | ||||
|                         pageContext.composerService().compose( | ||||
|                                 event.selection.activity.contentPaneComposer, | ||||
|                                 pageContext.copyOf(contentObjects).withAttr( | ||||
|                                         event.selection.activity.objectIdentifierAttribute, | ||||
|                                         event.selection.getObjectIdentifier())); | ||||
|                     } | ||||
|                 }); | ||||
| 
 | ||||
|         final Composite actionPane = new Composite(mainSash, SWT.NONE); | ||||
|         actionPane.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); | ||||
|         final GridLayout actionPaneGrid = new GridLayout(); | ||||
|         actionPane.setLayout(actionPaneGrid); | ||||
|         actionPane.setData(RWT.CUSTOM_VARIANT, "actionPane"); | ||||
|         actionPane.setData(PageEventListener.LISTENER_ATTRIBUTE_KEY, | ||||
|                 new ActivitySelectionListener() { | ||||
|                     @Override | ||||
|                     public int priority() { | ||||
|                         return 1; | ||||
|                     } | ||||
| 
 | ||||
|                     @Override | ||||
|                     public void notify(final ActivitySelectionEvent event) { | ||||
|                         pageContext.composerService().compose( | ||||
|                                 event.selection.activity.actionPaneComposer, | ||||
|                                 pageContext.copyOf(actionPane).withAttr( | ||||
|                                         event.selection.activity.objectIdentifierAttribute, | ||||
|                                         event.selection.getObjectIdentifier())); | ||||
|                     } | ||||
|                 }); | ||||
| 
 | ||||
|         pageContext.composerService().compose( | ||||
|                 ActivitiesPane.class, | ||||
|                 pageContext.copyOf(nav)); | ||||
| 
 | ||||
|         mainSash.setWeights(DEFAULT_SASH_WEIGHTS); | ||||
|     } | ||||
| 
 | ||||
| } | ||||
|  | @ -0,0 +1,33 @@ | |||
| /* | ||||
|  * 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.page.impl; | ||||
| 
 | ||||
| import org.eclipse.swt.SWT; | ||||
| import org.eclipse.swt.layout.GridData; | ||||
| import org.eclipse.swt.widgets.Label; | ||||
| import org.springframework.context.annotation.Lazy; | ||||
| import org.springframework.stereotype.Component; | ||||
| 
 | ||||
| import ch.ethz.seb.sebserver.gui.service.page.PageContext; | ||||
| import ch.ethz.seb.sebserver.gui.service.page.TemplateComposer; | ||||
| 
 | ||||
| @Lazy | ||||
| @Component | ||||
| public class TODOTemplate implements TemplateComposer { | ||||
| 
 | ||||
|     @Override | ||||
|     public void compose(final PageContext composerCtx) { | ||||
| 
 | ||||
|         final Label tree = new Label(composerCtx.getParent(), SWT.NONE); | ||||
|         tree.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); | ||||
|         tree.setText("[TODO]"); | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
| } | ||||
|  | @ -0,0 +1,96 @@ | |||
| /* | ||||
|  * Copyright (c) 2019 ETH Zürich, Educational Development and Technology (LET) | ||||
|  * | ||||
|  * This Source Code Form is subject to the terms of the Mozilla Public | ||||
|  * License, v. 2.0. If a copy of the MPL was not distributed with this | ||||
|  * file, You can obtain one at http://mozilla.org/MPL/2.0/. | ||||
|  */ | ||||
| 
 | ||||
| package ch.ethz.seb.sebserver.gui.service.remote.webservice; | ||||
| 
 | ||||
| import java.io.FileNotFoundException; | ||||
| import java.io.IOException; | ||||
| import java.net.HttpURLConnection; | ||||
| import java.security.KeyManagementException; | ||||
| import java.security.KeyStoreException; | ||||
| import java.security.NoSuchAlgorithmException; | ||||
| import java.security.cert.CertificateException; | ||||
| 
 | ||||
| import javax.net.ssl.SSLContext; | ||||
| 
 | ||||
| import org.apache.http.client.HttpClient; | ||||
| import org.apache.http.impl.client.HttpClients; | ||||
| import org.apache.http.ssl.SSLContextBuilder; | ||||
| import org.springframework.context.annotation.Bean; | ||||
| import org.springframework.context.annotation.Configuration; | ||||
| import org.springframework.core.env.Environment; | ||||
| import org.springframework.http.client.ClientHttpRequestFactory; | ||||
| import org.springframework.http.client.HttpComponentsClientHttpRequestFactory; | ||||
| import org.springframework.http.client.SimpleClientHttpRequestFactory; | ||||
| import org.springframework.util.ResourceUtils; | ||||
| 
 | ||||
| import ch.ethz.seb.sebserver.gbl.profile.DevGuiProfile; | ||||
| import ch.ethz.seb.sebserver.gbl.profile.GuiProfile; | ||||
| import ch.ethz.seb.sebserver.gbl.profile.ProdGuiProfile; | ||||
| 
 | ||||
| @Configuration | ||||
| @GuiProfile | ||||
| public class WebserviceConnectionConfig { | ||||
| 
 | ||||
|     /** A ClientHttpRequestFactory for development profile with no TSL SSL protocol and | ||||
|      * not following redirects on redirect responses. | ||||
|      * | ||||
|      * @return ClientHttpRequestFactory bean for development profiles */ | ||||
|     @Bean | ||||
|     @DevGuiProfile | ||||
|     public ClientHttpRequestFactory clientHttpRequestFactory() { | ||||
|         return new SimpleClientHttpRequestFactory() { | ||||
| 
 | ||||
|             @Override | ||||
|             protected void prepareConnection(final HttpURLConnection connection, final String httpMethod) | ||||
|                     throws IOException { | ||||
|                 super.prepareConnection(connection, httpMethod); | ||||
|                 connection.setInstanceFollowRedirects(false); | ||||
|             } | ||||
|         }; | ||||
|     } | ||||
| 
 | ||||
|     /** A ClientHttpRequestFactory used in production with TSL SSL configuration. | ||||
|      * | ||||
|      * NOTE: | ||||
|      * environment property: sebserver.gui.truststore.pwd is expected to have the correct truststore password set | ||||
|      * environment property: sebserver.gui.truststore.type is expected to set to the correct type of truststore | ||||
|      * truststore.jks is expected to be on the classpath containing all trusted certificates for request | ||||
|      * to SSL secured SEB Server webservice | ||||
|      * | ||||
|      * @return ClientHttpRequestFactory with TLS / SSL configuration | ||||
|      * @throws IOException | ||||
|      * @throws FileNotFoundException | ||||
|      * @throws CertificateException | ||||
|      * @throws KeyStoreException | ||||
|      * @throws NoSuchAlgorithmException | ||||
|      * @throws KeyManagementException */ | ||||
|     @Bean | ||||
|     @ProdGuiProfile | ||||
|     public ClientHttpRequestFactory clientHttpRequestFactoryTLS(final Environment env) throws KeyManagementException, | ||||
|             NoSuchAlgorithmException, KeyStoreException, CertificateException, FileNotFoundException, IOException { | ||||
| 
 | ||||
|         final char[] password = env | ||||
|                 .getProperty("sebserver.gui.truststore.pwd") | ||||
|                 .toCharArray(); | ||||
| 
 | ||||
|         final SSLContext sslContext = SSLContextBuilder | ||||
|                 .create() | ||||
|                 .loadTrustMaterial(ResourceUtils.getFile( | ||||
|                         "classpath:truststore.jks"), | ||||
|                         password) | ||||
|                 .build(); | ||||
| 
 | ||||
|         final HttpClient client = HttpClients.custom() | ||||
|                 .setSSLContext(sslContext) | ||||
|                 .build(); | ||||
| 
 | ||||
|         return new HttpComponentsClientHttpRequestFactory(client); | ||||
|     } | ||||
| 
 | ||||
| } | ||||
|  | @ -28,8 +28,8 @@ import org.springframework.web.client.RestClientResponseException; | |||
| import com.fasterxml.jackson.core.JsonProcessingException; | ||||
| import com.fasterxml.jackson.core.type.TypeReference; | ||||
| 
 | ||||
| import ch.ethz.seb.sebserver.gbl.JSONMapper; | ||||
| import ch.ethz.seb.sebserver.gbl.model.APIMessage; | ||||
| import ch.ethz.seb.sebserver.gbl.api.APIMessage; | ||||
| import ch.ethz.seb.sebserver.gbl.api.JSONMapper; | ||||
| import ch.ethz.seb.sebserver.gbl.model.Page; | ||||
| import ch.ethz.seb.sebserver.gbl.util.Result; | ||||
| import ch.ethz.seb.sebserver.webservice.servicelayer.PaginationService.SortOrder; | ||||
|  | @ -58,9 +58,10 @@ public abstract class RestCall<T> { | |||
| 
 | ||||
|     } | ||||
| 
 | ||||
|     void init(final RestService restService, final JSONMapper jsonMapper) { | ||||
|     RestCall<T> init(final RestService restService, final JSONMapper jsonMapper) { | ||||
|         this.restService = restService; | ||||
|         this.jsonMapper = jsonMapper; | ||||
|         return this; | ||||
|     } | ||||
| 
 | ||||
|     protected Result<T> exchange(final RestCallBuilder builder) { | ||||
|  | @ -107,6 +108,10 @@ public abstract class RestCall<T> { | |||
|         } | ||||
|     } | ||||
| 
 | ||||
|     public RestCallBuilder newBuilder() { | ||||
|         return new RestCallBuilder(); | ||||
|     } | ||||
| 
 | ||||
|     public final class RestCallBuilder { | ||||
| 
 | ||||
|         private final HttpHeaders httpHeaders = new HttpHeaders(); | ||||
|  | @ -117,7 +122,7 @@ public abstract class RestCall<T> { | |||
|         RestCallBuilder() { | ||||
|             this.httpHeaders.set( | ||||
|                     HttpHeaders.CONTENT_TYPE, | ||||
|                     RestCall.this.contentType.getType()); | ||||
|                     RestCall.this.contentType.toString()); | ||||
|         } | ||||
| 
 | ||||
|         public RestCallBuilder withHeaders(final HttpHeaders headers) { | ||||
|  | @ -165,7 +170,7 @@ public abstract class RestCall<T> { | |||
|             return this; | ||||
|         } | ||||
| 
 | ||||
|         public final Result<T> exchange() { | ||||
|         public final Result<T> call() { | ||||
|             return RestCall.this.exchange(this); | ||||
|         } | ||||
| 
 | ||||
|  |  | |||
|  | @ -11,9 +11,10 @@ package ch.ethz.seb.sebserver.gui.service.remote.webservice.api; | |||
| import java.util.ArrayList; | ||||
| import java.util.List; | ||||
| 
 | ||||
| import ch.ethz.seb.sebserver.gbl.model.APIMessage; | ||||
| import ch.ethz.seb.sebserver.gbl.api.APIMessage; | ||||
| import ch.ethz.seb.sebserver.gbl.api.APIMessageError; | ||||
| 
 | ||||
| public class RestCallError extends RuntimeException { | ||||
| public class RestCallError extends RuntimeException implements APIMessageError { | ||||
| 
 | ||||
|     private static final long serialVersionUID = -5201349295667957490L; | ||||
| 
 | ||||
|  | @ -27,6 +28,7 @@ public class RestCallError extends RuntimeException { | |||
|         super(message); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public List<APIMessage> getErrorMessages() { | ||||
|         return this.errors; | ||||
|     } | ||||
|  |  | |||
|  | @ -8,6 +8,10 @@ | |||
| 
 | ||||
| package ch.ethz.seb.sebserver.gui.service.remote.webservice.api; | ||||
| 
 | ||||
| import java.util.Collection; | ||||
| import java.util.Map; | ||||
| import java.util.stream.Collectors; | ||||
| 
 | ||||
| import org.slf4j.Logger; | ||||
| import org.slf4j.LoggerFactory; | ||||
| import org.springframework.context.annotation.Lazy; | ||||
|  | @ -15,10 +19,10 @@ import org.springframework.stereotype.Service; | |||
| import org.springframework.web.client.RestTemplate; | ||||
| import org.springframework.web.util.UriComponentsBuilder; | ||||
| 
 | ||||
| import ch.ethz.seb.sebserver.gbl.JSONMapper; | ||||
| import ch.ethz.seb.sebserver.gbl.api.JSONMapper; | ||||
| import ch.ethz.seb.sebserver.gbl.profile.GuiProfile; | ||||
| import ch.ethz.seb.sebserver.gui.service.remote.webservice.auth.AuthorizationContextHolder; | ||||
| import ch.ethz.seb.sebserver.gui.service.remote.webservice.auth.WebserviceURIBuilderSupplier; | ||||
| import ch.ethz.seb.sebserver.gui.service.remote.webservice.auth.WebserviceURIService; | ||||
| 
 | ||||
| @Lazy | ||||
| @Service | ||||
|  | @ -28,17 +32,23 @@ public class RestService { | |||
|     private static final Logger log = LoggerFactory.getLogger(RestService.class); | ||||
| 
 | ||||
|     private final AuthorizationContextHolder authorizationContextHolder; | ||||
|     private final WebserviceURIBuilderSupplier webserviceURIBuilderSupplier; | ||||
|     private final JSONMapper jsonMapper; | ||||
|     private final WebserviceURIService webserviceURIBuilderSupplier; | ||||
|     private final Map<String, RestCall<?>> calls; | ||||
| 
 | ||||
|     public RestService( | ||||
|             final AuthorizationContextHolder authorizationContextHolder, | ||||
|             final WebserviceURIBuilderSupplier webserviceURIBuilderSupplier, | ||||
|             final JSONMapper jsonMapper) { | ||||
|             final WebserviceURIService webserviceURIBuilderSupplier, | ||||
|             final JSONMapper jsonMapper, | ||||
|             final Collection<RestCall<?>> calls) { | ||||
| 
 | ||||
|         this.authorizationContextHolder = authorizationContextHolder; | ||||
|         this.webserviceURIBuilderSupplier = webserviceURIBuilderSupplier; | ||||
|         this.jsonMapper = jsonMapper; | ||||
| 
 | ||||
|         this.calls = calls | ||||
|                 .stream() | ||||
|                 .collect(Collectors.toMap( | ||||
|                         call -> call.getClass().getName(), | ||||
|                         call -> call.init(this, jsonMapper))); | ||||
|     } | ||||
| 
 | ||||
|     public RestTemplate getWebserviceAPIRestTemplate() { | ||||
|  | @ -51,15 +61,19 @@ public class RestService { | |||
|         return this.webserviceURIBuilderSupplier.getBuilder(); | ||||
|     } | ||||
| 
 | ||||
|     @SuppressWarnings("unchecked") | ||||
|     public <T> RestCall<T> getRestCall(final Class<? extends RestCall<T>> type) { | ||||
|         try { | ||||
|             final RestCall<T> restCall = type.getDeclaredConstructor().newInstance(); | ||||
|             restCall.init(this, this.jsonMapper); | ||||
|             return restCall; | ||||
|         } catch (final Exception e) { | ||||
|             log.error("Error while trying to create RestCall of type: {}", type, e); | ||||
|             return new BuildErrorCall<>(e); | ||||
|         return (RestCall<T>) this.calls.get(type.getName()); | ||||
|     } | ||||
| 
 | ||||
|     public <T> RestCall<T>.RestCallBuilder getBuilder(final Class<? extends RestCall<T>> type) { | ||||
|         @SuppressWarnings("unchecked") | ||||
|         final RestCall<T> restCall = (RestCall<T>) this.calls.get(type.getName()); | ||||
|         if (restCall == null) { | ||||
|             return null; | ||||
|         } | ||||
| 
 | ||||
|         return restCall.newBuilder(); | ||||
|     } | ||||
| 
 | ||||
| } | ||||
|  |  | |||
|  | @ -0,0 +1,39 @@ | |||
| /* | ||||
|  * Copyright (c) 2019 ETH Zürich, Educational Development and Technology (LET) | ||||
|  * | ||||
|  * This Source Code Form is subject to the terms of the Mozilla Public | ||||
|  * License, v. 2.0. If a copy of the MPL was not distributed with this | ||||
|  * file, You can obtain one at http://mozilla.org/MPL/2.0/. | ||||
|  */ | ||||
| 
 | ||||
| package ch.ethz.seb.sebserver.gui.service.remote.webservice.api.institution; | ||||
| 
 | ||||
| import java.util.List; | ||||
| 
 | ||||
| import org.springframework.context.annotation.Lazy; | ||||
| import org.springframework.http.HttpMethod; | ||||
| import org.springframework.http.MediaType; | ||||
| import org.springframework.stereotype.Component; | ||||
| 
 | ||||
| import com.fasterxml.jackson.core.type.TypeReference; | ||||
| 
 | ||||
| import ch.ethz.seb.sebserver.gbl.api.SEBServerRestEndpoints; | ||||
| import ch.ethz.seb.sebserver.gbl.model.EntityName; | ||||
| import ch.ethz.seb.sebserver.gbl.profile.GuiProfile; | ||||
| import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestCall; | ||||
| 
 | ||||
| @Lazy | ||||
| @Component | ||||
| @GuiProfile | ||||
| public class GetInstitutionNames extends RestCall<List<EntityName>> { | ||||
| 
 | ||||
|     protected GetInstitutionNames() { | ||||
|         super( | ||||
|                 new TypeReference<List<EntityName>>() { | ||||
|                 }, | ||||
|                 HttpMethod.GET, | ||||
|                 MediaType.APPLICATION_FORM_URLENCODED, | ||||
|                 SEBServerRestEndpoints.ENDPOINT_INSTITUTION + "/names"); | ||||
|     } | ||||
| 
 | ||||
| } | ||||
|  | @ -34,7 +34,9 @@ public class CurrentUser { | |||
| 
 | ||||
|     public UserInfo get() { | ||||
|         if (isAvailable()) { | ||||
|             return this.authContext.getLoggedInUser(); | ||||
|             return this.authContext | ||||
|                     .getLoggedInUser() | ||||
|                     .getOrThrow(); | ||||
|         } | ||||
| 
 | ||||
|         log.warn("Current user requested but no user is currently logged in"); | ||||
|  |  | |||
|  | @ -22,7 +22,9 @@ import org.springframework.beans.factory.annotation.Autowired; | |||
| import org.springframework.beans.factory.annotation.Value; | ||||
| import org.springframework.context.annotation.Lazy; | ||||
| import org.springframework.http.HttpMethod; | ||||
| import org.springframework.http.HttpStatus; | ||||
| import org.springframework.http.ResponseEntity; | ||||
| import org.springframework.http.client.ClientHttpRequestFactory; | ||||
| import org.springframework.security.access.AccessDeniedException; | ||||
| import org.springframework.security.oauth2.client.DefaultOAuth2ClientContext; | ||||
| import org.springframework.security.oauth2.client.OAuth2RestTemplate; | ||||
|  | @ -41,6 +43,7 @@ import org.springframework.web.client.RestTemplate; | |||
| 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.Result; | ||||
| 
 | ||||
| @Lazy | ||||
| @Component | ||||
|  | @ -50,23 +53,23 @@ public class OAuth2AuthorizationContextHolder implements AuthorizationContextHol | |||
|     private static final Logger log = LoggerFactory.getLogger(OAuth2AuthorizationContextHolder.class); | ||||
| 
 | ||||
|     private static final String CONTEXT_HOLDER_ATTRIBUTE = "CONTEXT_HOLDER_ATTRIBUTE"; | ||||
|     private static final String OAUTH_TOKEN_URI_PATH = "oauth/token"; // TODO to config properties? | ||||
|     private static final String OAUTH_REVOKE_TOKEN_URI_PATH = "/oauth/revoke-token"; // TODO to config properties? | ||||
|     private static final String CURRENT_USER_URI_PATH = "/user/me"; // TODO to config properties? | ||||
| 
 | ||||
|     private final String guiClientId; | ||||
|     private final String guiClientSecret; | ||||
|     private final WebserviceURIBuilderSupplier webserviceURIBuilderSupplier; | ||||
|     private final WebserviceURIService webserviceURIService; | ||||
|     private final ClientHttpRequestFactory clientHttpRequestFactory; | ||||
| 
 | ||||
|     @Autowired | ||||
|     public OAuth2AuthorizationContextHolder( | ||||
|             @Value("${sebserver.gui.webservice.clientId}") final String guiClientId, | ||||
|             @Value("${sebserver.gui.webservice.clientSecret}") final String guiClientSecret, | ||||
|             final WebserviceURIBuilderSupplier webserviceURIBuilderSupplier) { | ||||
|             final WebserviceURIService webserviceURIService, | ||||
|             final ClientHttpRequestFactory clientHttpRequestFactory) { | ||||
| 
 | ||||
|         this.guiClientId = guiClientId; | ||||
|         this.guiClientSecret = guiClientSecret; | ||||
|         this.webserviceURIBuilderSupplier = webserviceURIBuilderSupplier; | ||||
|         this.webserviceURIService = webserviceURIService; | ||||
|         this.clientHttpRequestFactory = clientHttpRequestFactory; | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|  | @ -85,7 +88,8 @@ public class OAuth2AuthorizationContextHolder implements AuthorizationContextHol | |||
|             context = new OAuth2AuthorizationContext( | ||||
|                     this.guiClientId, | ||||
|                     this.guiClientSecret, | ||||
|                     this.webserviceURIBuilderSupplier); | ||||
|                     this.webserviceURIService, | ||||
|                     this.clientHttpRequestFactory); | ||||
|             session.setAttribute(CONTEXT_HOLDER_ATTRIBUTE, context); | ||||
|         } | ||||
| 
 | ||||
|  | @ -132,7 +136,7 @@ public class OAuth2AuthorizationContextHolder implements AuthorizationContextHol | |||
| 
 | ||||
|         private static final String GRANT_TYPE = "password"; | ||||
|         private static final List<String> SCOPES = Collections.unmodifiableList( | ||||
|                 Arrays.asList("web-service-api-read", "web-service-api-write")); | ||||
|                 Arrays.asList("read", "write")); | ||||
| 
 | ||||
|         private boolean valid = true; | ||||
| 
 | ||||
|  | @ -141,34 +145,26 @@ public class OAuth2AuthorizationContextHolder implements AuthorizationContextHol | |||
|         private final String revokeTokenURI; | ||||
|         private final String currentUserURI; | ||||
| 
 | ||||
|         private UserInfo loggedInUser = null; | ||||
|         private Result<UserInfo> loggedInUser = null; | ||||
| 
 | ||||
|         OAuth2AuthorizationContext( | ||||
|                 final String guiClientId, | ||||
|                 final String guiClientSecret, | ||||
|                 final WebserviceURIBuilderSupplier webserviceURIBuilderSupplier) { | ||||
|                 final WebserviceURIService webserviceURIService, | ||||
|                 final ClientHttpRequestFactory clientHttpRequestFactory) { | ||||
| 
 | ||||
|             this.resource = new ResourceOwnerPasswordResourceDetails(); | ||||
|             this.resource.setAccessTokenUri( | ||||
|                     webserviceURIBuilderSupplier | ||||
|                             .getBuilder() | ||||
|                             .path(OAUTH_TOKEN_URI_PATH) | ||||
|                             .toUriString() /* restCallBuilder.withPath(OAUTH_TOKEN_URI_PATH) */); | ||||
|             this.resource.setAccessTokenUri(webserviceURIService.getOAuthTokenURI()); | ||||
|             this.resource.setClientId(guiClientId); | ||||
|             this.resource.setClientSecret(guiClientSecret); | ||||
|             this.resource.setGrantType(GRANT_TYPE); | ||||
|             this.resource.setScope(SCOPES); | ||||
| 
 | ||||
|             this.restTemplate = new DisposableOAuth2RestTemplate(this.resource); | ||||
|             this.restTemplate.setRequestFactory(clientHttpRequestFactory); | ||||
| 
 | ||||
|             this.revokeTokenURI = webserviceURIBuilderSupplier | ||||
|                     .getBuilder() | ||||
|                     .path(OAUTH_REVOKE_TOKEN_URI_PATH) | ||||
|                     .toUriString(); //restCallBuilder.withPath(OAUTH_REVOKE_TOKEN_URI_PATH); | ||||
|             this.currentUserURI = webserviceURIBuilderSupplier | ||||
|                     .getBuilder() | ||||
|                     .path(CURRENT_USER_URI_PATH) | ||||
|                     .toUriString(); //restCallBuilder.withPath(CURRENT_USER_URI_PATH); | ||||
|             this.revokeTokenURI = webserviceURIService.getOAuthRevokeTokenURI(); | ||||
|             this.currentUserURI = webserviceURIService.getCurrentUserRequestURI(); | ||||
|         } | ||||
| 
 | ||||
|         @Override | ||||
|  | @ -227,7 +223,7 @@ public class OAuth2AuthorizationContextHolder implements AuthorizationContextHol | |||
|         } | ||||
| 
 | ||||
|         @Override | ||||
|         public UserInfo getLoggedInUser() { | ||||
|         public Result<UserInfo> getLoggedInUser() { | ||||
|             if (this.loggedInUser != null) { | ||||
|                 return this.loggedInUser; | ||||
|             } | ||||
|  | @ -237,18 +233,27 @@ public class OAuth2AuthorizationContextHolder implements AuthorizationContextHol | |||
|             try { | ||||
|                 if (isValid() && isLoggedIn()) { | ||||
|                     final ResponseEntity<UserInfo> response = | ||||
|                             this.restTemplate.getForEntity(this.currentUserURI, UserInfo.class); | ||||
|                     this.loggedInUser = response.getBody(); | ||||
|                     return this.loggedInUser; | ||||
|                             this.restTemplate | ||||
|                                     .getForEntity(this.currentUserURI, UserInfo.class); | ||||
|                     if (response.getStatusCode() == HttpStatus.OK) { | ||||
|                         this.loggedInUser = Result.of(response.getBody()); | ||||
|                         return this.loggedInUser; | ||||
|                     } else { | ||||
|                         log.error("Unexpected error response: {}", response); | ||||
|                         return Result.ofError(new IllegalStateException( | ||||
|                                 "Http Request responded with status: " + response.getStatusCode())); | ||||
|                     } | ||||
|                 } else { | ||||
|                     throw new IllegalStateException("Logged in User requested on invalid or not logged in "); | ||||
|                     return Result.ofError( | ||||
|                             new IllegalStateException("Logged in User requested on invalid or not logged in ")); | ||||
|                 } | ||||
|             } catch (final AccessDeniedException | OAuth2AccessDeniedException ade) { | ||||
|                 log.error("Acccess denied while trying to request logged in User from API", ade); | ||||
|                 throw ade; | ||||
|                 return Result.ofError(ade); | ||||
|             } catch (final Exception e) { | ||||
|                 log.error("Unexpected error while trying to request logged in User from API", e); | ||||
|                 throw new RuntimeException("Unexpected error while trying to request logged in User from API", e); | ||||
|                 return Result.ofError( | ||||
|                         new RuntimeException("Unexpected error while trying to request logged in User from API", e)); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|  | @ -258,8 +263,9 @@ public class OAuth2AuthorizationContextHolder implements AuthorizationContextHol | |||
|                 return false; | ||||
|             } | ||||
| 
 | ||||
|             return getLoggedInUser().roles | ||||
|                     .contains(role.name()); | ||||
|             return getLoggedInUser() | ||||
|                     .getOrThrow().roles | ||||
|                             .contains(role.name()); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  |  | |||
|  | @ -12,6 +12,7 @@ import org.springframework.web.client.RestTemplate; | |||
| 
 | ||||
| import ch.ethz.seb.sebserver.gbl.model.user.UserInfo; | ||||
| import ch.ethz.seb.sebserver.gbl.model.user.UserRole; | ||||
| import ch.ethz.seb.sebserver.gbl.util.Result; | ||||
| 
 | ||||
| public interface SEBServerAuthorizationContext { | ||||
| 
 | ||||
|  | @ -23,7 +24,7 @@ public interface SEBServerAuthorizationContext { | |||
| 
 | ||||
|     boolean logout(); | ||||
| 
 | ||||
|     UserInfo getLoggedInUser(); | ||||
|     Result<UserInfo> getLoggedInUser(); | ||||
| 
 | ||||
|     public boolean hasRole(UserRole role); | ||||
| 
 | ||||
|  |  | |||
|  | @ -1,40 +0,0 @@ | |||
| /* | ||||
|  * Copyright (c) 2019 ETH Zürich, Educational Development and Technology (LET) | ||||
|  * | ||||
|  * This Source Code Form is subject to the terms of the Mozilla Public | ||||
|  * License, v. 2.0. If a copy of the MPL was not distributed with this | ||||
|  * file, You can obtain one at http://mozilla.org/MPL/2.0/. | ||||
|  */ | ||||
| 
 | ||||
| package ch.ethz.seb.sebserver.gui.service.remote.webservice.auth; | ||||
| 
 | ||||
| import org.springframework.beans.factory.annotation.Value; | ||||
| import org.springframework.context.annotation.Lazy; | ||||
| import org.springframework.stereotype.Component; | ||||
| import org.springframework.web.util.UriComponentsBuilder; | ||||
| 
 | ||||
| import ch.ethz.seb.sebserver.gbl.profile.GuiProfile; | ||||
| 
 | ||||
| @Lazy | ||||
| @Component | ||||
| @GuiProfile | ||||
| public class WebserviceURIBuilderSupplier { | ||||
| 
 | ||||
|     private final UriComponentsBuilder webserviceURIBuilder; | ||||
| 
 | ||||
|     public WebserviceURIBuilderSupplier( | ||||
|             @Value("${sebserver.gui.webservice.protocol}") final String webserviceProtocol, | ||||
|             @Value("${sebserver.gui.webservice.address}") final String webserviceServerAdress, | ||||
|             @Value("${sebserver.gui.webservice.portol}") final String webserviceServerPort, | ||||
|             @Value("${sebserver.gui.webservice.apipath}") final String webserviceAPIPath) { | ||||
| 
 | ||||
|         this.webserviceURIBuilder = UriComponentsBuilder | ||||
|                 .fromHttpUrl(webserviceProtocol + "://" + webserviceServerAdress) | ||||
|                 .port(webserviceServerPort) | ||||
|                 .path(webserviceAPIPath); | ||||
|     } | ||||
| 
 | ||||
|     public UriComponentsBuilder getBuilder() { | ||||
|         return this.webserviceURIBuilder.cloneBuilder(); | ||||
|     } | ||||
| } | ||||
|  | @ -0,0 +1,63 @@ | |||
| /* | ||||
|  * Copyright (c) 2019 ETH Zürich, Educational Development and Technology (LET) | ||||
|  * | ||||
|  * This Source Code Form is subject to the terms of the Mozilla Public | ||||
|  * License, v. 2.0. If a copy of the MPL was not distributed with this | ||||
|  * file, You can obtain one at http://mozilla.org/MPL/2.0/. | ||||
|  */ | ||||
| 
 | ||||
| package ch.ethz.seb.sebserver.gui.service.remote.webservice.auth; | ||||
| 
 | ||||
| import org.springframework.beans.factory.annotation.Value; | ||||
| import org.springframework.stereotype.Component; | ||||
| import org.springframework.web.util.UriComponentsBuilder; | ||||
| 
 | ||||
| import ch.ethz.seb.sebserver.gbl.api.SEBServerRestEndpoints; | ||||
| import ch.ethz.seb.sebserver.gbl.profile.GuiProfile; | ||||
| 
 | ||||
| @Component | ||||
| @GuiProfile | ||||
| public class WebserviceURIService { | ||||
| 
 | ||||
|     private static final String OAUTH_TOKEN_URI_PATH = "oauth/token"; // TODO to config properties? | ||||
|     private static final String OAUTH_REVOKE_TOKEN_URI_PATH = "/oauth/revoke-token"; // TODO to config properties? | ||||
|     private static final String CURRENT_USER_URI_PATH = SEBServerRestEndpoints.ENDPOINT_USER_ACCOUNT + "/me"; | ||||
| 
 | ||||
|     private final String webserviceServerAddress; | ||||
|     private final UriComponentsBuilder webserviceURIBuilder; | ||||
| 
 | ||||
|     public WebserviceURIService( | ||||
|             @Value("${sebserver.gui.webservice.protocol}") final String webserviceProtocol, | ||||
|             @Value("${sebserver.gui.webservice.address}") final String webserviceServerAdress, | ||||
|             @Value("${sebserver.gui.webservice.port}") final String webserviceServerPort, | ||||
|             @Value("${sebserver.gui.webservice.apipath}") final String webserviceAPIPath) { | ||||
| 
 | ||||
|         this.webserviceServerAddress = webserviceProtocol + "://" + webserviceServerAdress + ":" + webserviceServerPort; | ||||
|         this.webserviceURIBuilder = UriComponentsBuilder | ||||
|                 .fromHttpUrl(webserviceProtocol + "://" + webserviceServerAdress) | ||||
|                 .port(webserviceServerPort) | ||||
|                 .path(webserviceAPIPath); | ||||
|     } | ||||
| 
 | ||||
|     public UriComponentsBuilder getBuilder() { | ||||
|         return this.webserviceURIBuilder.cloneBuilder(); | ||||
|     } | ||||
| 
 | ||||
|     public String getOAuthTokenURI() { | ||||
|         return UriComponentsBuilder.fromHttpUrl(this.webserviceServerAddress) | ||||
|                 .path(OAUTH_TOKEN_URI_PATH) | ||||
|                 .toUriString(); | ||||
|     } | ||||
| 
 | ||||
|     public String getOAuthRevokeTokenURI() { | ||||
|         return UriComponentsBuilder.fromHttpUrl(this.webserviceServerAddress) | ||||
|                 .path(OAUTH_REVOKE_TOKEN_URI_PATH) | ||||
|                 .toUriString(); | ||||
|     } | ||||
| 
 | ||||
|     public String getCurrentUserRequestURI() { | ||||
|         return getBuilder() | ||||
|                 .path(CURRENT_USER_URI_PATH) | ||||
|                 .toUriString(); | ||||
|     } | ||||
| } | ||||
|  | @ -0,0 +1,42 @@ | |||
| /* | ||||
|  * 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.widget; | ||||
| 
 | ||||
| import org.eclipse.rap.rwt.RWT; | ||||
| import org.eclipse.swt.graphics.Rectangle; | ||||
| import org.eclipse.swt.layout.GridLayout; | ||||
| import org.eclipse.swt.widgets.MessageBox; | ||||
| import org.eclipse.swt.widgets.Shell; | ||||
| 
 | ||||
| public class Message extends MessageBox { | ||||
| 
 | ||||
|     private static final long serialVersionUID = 6973272221493264432L; | ||||
| 
 | ||||
|     public Message(final Shell parent, final String title, final String message, final int type) { | ||||
|         super(parent, type); | ||||
|         super.setText(title); | ||||
|         super.setMessage(message); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     protected void prepareOpen() { | ||||
|         super.prepareOpen(); | ||||
|         final GridLayout layout = (GridLayout) super.shell.getLayout(); | ||||
|         layout.marginTop = 10; | ||||
|         layout.marginBottom = 10; | ||||
|         super.shell.setData(RWT.CUSTOM_VARIANT, "message"); | ||||
|         final Rectangle bounds = super.shell.getBounds(); | ||||
|         if (bounds.width < 400) { | ||||
|             bounds.x = bounds.x - (400 - bounds.width) / 2; | ||||
|             bounds.width = 400; | ||||
|             super.shell.setBounds(bounds); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
| } | ||||
|  | @ -0,0 +1,56 @@ | |||
| /* | ||||
|  * 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.widget; | ||||
| 
 | ||||
| import java.util.List; | ||||
| import java.util.stream.Collectors; | ||||
| 
 | ||||
| import org.eclipse.swt.SWT; | ||||
| import org.eclipse.swt.widgets.Combo; | ||||
| import org.eclipse.swt.widgets.Composite; | ||||
| 
 | ||||
| import ch.ethz.seb.sebserver.gbl.util.Tuple; | ||||
| 
 | ||||
| public class SingleSelection extends Combo { | ||||
| 
 | ||||
|     private static final long serialVersionUID = 6522063655406404279L; | ||||
| 
 | ||||
|     final List<String> valueMapping; | ||||
|     final List<String> keyMapping; | ||||
| 
 | ||||
|     public SingleSelection(final Composite parent, final List<Tuple<String>> mapping) { | ||||
|         super(parent, SWT.READ_ONLY); | ||||
|         this.valueMapping = mapping.stream() | ||||
|                 .map(t -> t._2) | ||||
|                 .collect(Collectors.toList()); | ||||
|         this.keyMapping = mapping.stream() | ||||
|                 .map(t -> t._1) | ||||
|                 .collect(Collectors.toList()); | ||||
|         super.setItems(this.valueMapping.toArray(new String[mapping.size()])); | ||||
|     } | ||||
| 
 | ||||
|     public void select(final String key) { | ||||
|         final int selectionindex = this.keyMapping.indexOf(key); | ||||
|         if (selectionindex < 0) { | ||||
|             return; | ||||
|         } | ||||
| 
 | ||||
|         super.select(selectionindex); | ||||
|     } | ||||
| 
 | ||||
|     public String getSelectionValue() { | ||||
|         final int selectionindex = super.getSelectionIndex(); | ||||
|         if (selectionindex < 0) { | ||||
|             return null; | ||||
|         } | ||||
| 
 | ||||
|         return this.keyMapping.get(selectionindex); | ||||
|     } | ||||
| 
 | ||||
| } | ||||
|  | @ -0,0 +1,470 @@ | |||
| /* | ||||
|  * 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.widget; | ||||
| 
 | ||||
| import static ch.ethz.seb.sebserver.gui.service.i18n.PolyglotPageService.POLYGLOT_TREE_ITEM_TEXT_DATA_KEY; | ||||
| import static ch.ethz.seb.sebserver.gui.service.i18n.PolyglotPageService.POLYGLOT_WIDGET_FUNCTION_KEY; | ||||
| 
 | ||||
| import java.io.InputStream; | ||||
| import java.util.Iterator; | ||||
| import java.util.List; | ||||
| import java.util.Locale; | ||||
| import java.util.function.Consumer; | ||||
| 
 | ||||
| import org.eclipse.rap.rwt.RWT; | ||||
| import org.eclipse.swt.SWT; | ||||
| import org.eclipse.swt.graphics.Device; | ||||
| import org.eclipse.swt.graphics.Image; | ||||
| import org.eclipse.swt.graphics.ImageData; | ||||
| import org.eclipse.swt.layout.GridData; | ||||
| import org.eclipse.swt.widgets.Button; | ||||
| import org.eclipse.swt.widgets.Combo; | ||||
| import org.eclipse.swt.widgets.Composite; | ||||
| import org.eclipse.swt.widgets.Label; | ||||
| import org.eclipse.swt.widgets.Listener; | ||||
| import org.eclipse.swt.widgets.Table; | ||||
| import org.eclipse.swt.widgets.TableColumn; | ||||
| import org.eclipse.swt.widgets.TableItem; | ||||
| import org.eclipse.swt.widgets.Text; | ||||
| import org.eclipse.swt.widgets.Tree; | ||||
| import org.eclipse.swt.widgets.TreeItem; | ||||
| import org.slf4j.Logger; | ||||
| import org.slf4j.LoggerFactory; | ||||
| 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.Tuple; | ||||
| import ch.ethz.seb.sebserver.gui.service.i18n.I18nSupport; | ||||
| import ch.ethz.seb.sebserver.gui.service.i18n.LocTextKey; | ||||
| import ch.ethz.seb.sebserver.gui.service.i18n.PolyglotPageService; | ||||
| import ch.ethz.seb.sebserver.gui.service.page.PageContext; | ||||
| 
 | ||||
| @Lazy | ||||
| @Service | ||||
| @GuiProfile | ||||
| public class WidgetFactory { | ||||
| 
 | ||||
|     private static final Logger log = LoggerFactory.getLogger(WidgetFactory.class); | ||||
| 
 | ||||
|     public enum IconButtonType { | ||||
|         MAXIMIZE("maximize.png"), | ||||
|         MINIMIZE("minimize.png"), | ||||
|         SAVE_ACTION("saveAction.png"), | ||||
|         NEW_ACTION("newAction.png"), | ||||
|         DELETE_ACTION("deleteAction.png"), | ||||
|         ; | ||||
| 
 | ||||
|         private String fileName; | ||||
|         private ImageData image = null; | ||||
| 
 | ||||
|         private IconButtonType(final String fileName) { | ||||
|             this.fileName = fileName; | ||||
|         } | ||||
| 
 | ||||
|         public Image getImage(final Device device) { | ||||
|             if (this.image == null) { | ||||
|                 try { | ||||
|                     final InputStream resourceAsStream = | ||||
|                             WidgetFactory.class.getResourceAsStream("/static/images/" + this.fileName); | ||||
|                     this.image = new ImageData(resourceAsStream); | ||||
|                 } catch (final Exception e) { | ||||
|                     log.error("Failed to load resource image: {}", this.fileName, e); | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|             return new Image(device, this.image); | ||||
|         } | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
|     private final PolyglotPageService polyglotPageService; | ||||
|     private final I18nSupport i18nSupport; | ||||
| 
 | ||||
|     public WidgetFactory(final PolyglotPageService polyglotPageService) { | ||||
|         this.polyglotPageService = polyglotPageService; | ||||
|         this.i18nSupport = polyglotPageService.getI18nSupport(); | ||||
|     } | ||||
| 
 | ||||
|     public Button buttonLocalized(final Composite parent, final String locTextKey) { | ||||
|         final Button button = new Button(parent, SWT.NONE); | ||||
|         this.injectI18n(button, new LocTextKey(locTextKey)); | ||||
|         return button; | ||||
|     } | ||||
| 
 | ||||
|     public Button buttonLocalized(final Composite parent, final LocTextKey locTextKey) { | ||||
|         final Button button = new Button(parent, SWT.NONE); | ||||
|         this.injectI18n(button, locTextKey); | ||||
|         return button; | ||||
|     } | ||||
| 
 | ||||
|     public Button buttonLocalized(final Composite parent, final String style, final String locTextKey) { | ||||
|         final Button button = new Button(parent, SWT.NONE); | ||||
|         this.injectI18n(button, new LocTextKey(locTextKey)); | ||||
|         button.setData(RWT.CUSTOM_VARIANT, style); | ||||
|         return button; | ||||
|     } | ||||
| 
 | ||||
|     public Label label(final Composite parent, final String text) { | ||||
|         final Label label = new Label(parent, SWT.NONE); | ||||
|         label.setText(text); | ||||
|         return label; | ||||
|     } | ||||
| 
 | ||||
|     public Label labelLocalized(final Composite parent, final String locTextKey) { | ||||
|         final Label label = new Label(parent, SWT.NONE); | ||||
|         this.injectI18n(label, new LocTextKey(locTextKey)); | ||||
|         return label; | ||||
|     } | ||||
| 
 | ||||
|     public Label labelLocalized(final Composite parent, final LocTextKey locTextKey) { | ||||
|         final Label label = new Label(parent, SWT.NONE); | ||||
|         this.injectI18n(label, locTextKey); | ||||
|         return label; | ||||
|     } | ||||
| 
 | ||||
|     public Label labelLocalized(final Composite parent, final String style, final LocTextKey locTextKey) { | ||||
|         final Label label = new Label(parent, SWT.NONE); | ||||
|         this.injectI18n(label, locTextKey); | ||||
|         label.setData(RWT.CUSTOM_VARIANT, style); | ||||
|         return label; | ||||
|     } | ||||
| 
 | ||||
|     public Label labelLocalized( | ||||
|             final Composite parent, | ||||
|             final LocTextKey locTextKey, | ||||
|             final LocTextKey locToolTextKey) { | ||||
| 
 | ||||
|         final Label label = new Label(parent, SWT.NONE); | ||||
|         this.injectI18n(label, locTextKey, locToolTextKey); | ||||
|         return label; | ||||
|     } | ||||
| 
 | ||||
|     public Label labelLocalized( | ||||
|             final Composite parent, | ||||
|             final String style, | ||||
|             final LocTextKey locTextKey, | ||||
|             final LocTextKey locToolTextKey) { | ||||
| 
 | ||||
|         final Label label = new Label(parent, SWT.NONE); | ||||
|         this.injectI18n(label, locTextKey, locToolTextKey); | ||||
|         label.setData(RWT.CUSTOM_VARIANT, style); | ||||
|         return label; | ||||
|     } | ||||
| 
 | ||||
|     public Tree treeLocalized(final Composite parent, final int style) { | ||||
|         final Tree tree = new Tree(parent, SWT.SINGLE | SWT.FULL_SELECTION); | ||||
|         this.injectI18n(tree); | ||||
|         return tree; | ||||
|     } | ||||
| 
 | ||||
|     public TreeItem treeItemLocalized(final Tree parent, final String locTextKey) { | ||||
|         final TreeItem item = new TreeItem(parent, SWT.NONE); | ||||
|         this.injectI18n(item, new LocTextKey(locTextKey)); | ||||
|         return item; | ||||
|     } | ||||
| 
 | ||||
|     public TreeItem treeItemLocalized(final Tree parent, final LocTextKey locTextKey) { | ||||
|         final TreeItem item = new TreeItem(parent, SWT.NONE); | ||||
|         this.injectI18n(item, locTextKey); | ||||
|         return item; | ||||
|     } | ||||
| 
 | ||||
|     public TreeItem treeItemLocalized(final TreeItem parent, final String locTextKey) { | ||||
|         final TreeItem item = new TreeItem(parent, SWT.NONE); | ||||
|         this.injectI18n(item, new LocTextKey(locTextKey)); | ||||
|         return item; | ||||
|     } | ||||
| 
 | ||||
|     public TreeItem treeItemLocalized(final TreeItem parent, final LocTextKey locTextKey) { | ||||
|         final TreeItem item = new TreeItem(parent, SWT.NONE); | ||||
|         this.injectI18n(item, locTextKey); | ||||
|         return item; | ||||
|     } | ||||
| 
 | ||||
|     public Table tableLocalized(final Composite parent) { | ||||
|         final Table table = new Table(parent, SWT.NONE); | ||||
|         this.injectI18n(table); | ||||
|         return table; | ||||
|     } | ||||
| 
 | ||||
|     public TableColumn tableColumnLocalized(final Table table, final String locTextKey) { | ||||
|         final TableColumn tableColumn = new TableColumn(table, SWT.NONE); | ||||
|         this.injectI18n(tableColumn, new LocTextKey(locTextKey)); | ||||
|         return tableColumn; | ||||
|     } | ||||
| 
 | ||||
|     public Label labelSeparator(final Composite parent) { | ||||
|         final Label label = new Label(parent, SWT.SEPARATOR); | ||||
|         return label; | ||||
|     } | ||||
| 
 | ||||
|     public Label imageButton( | ||||
|             final IconButtonType type, | ||||
|             final Composite parent, | ||||
|             final LocTextKey toolTip, | ||||
|             final Listener listener) { | ||||
| 
 | ||||
|         final Label imageButton = labelLocalized(parent, (LocTextKey) null, toolTip); | ||||
|         imageButton.setData(RWT.CUSTOM_VARIANT, "imageButton"); | ||||
|         imageButton.setImage(type.getImage(parent.getDisplay())); | ||||
|         if (listener != null) { | ||||
|             imageButton.addListener(SWT.MouseDown, listener); | ||||
|         } | ||||
|         return imageButton; | ||||
|     } | ||||
| 
 | ||||
|     public Label formLabelLocalized(final Composite parent, final String locTextKey) { | ||||
|         final Label label = labelLocalized(parent, locTextKey); | ||||
|         final GridData gridData = new GridData(SWT.RIGHT, SWT.CENTER, true, false); | ||||
|         label.setLayoutData(gridData); | ||||
|         return label; | ||||
|     } | ||||
| 
 | ||||
|     public Label formValueLabel(final Composite parent, final String value, final int span) { | ||||
|         final Label label = new Label(parent, SWT.NONE); | ||||
|         label.setText(value); | ||||
|         final GridData gridData = new GridData(SWT.LEFT, SWT.CENTER, true, false, span, 1); | ||||
|         label.setLayoutData(gridData); | ||||
|         return label; | ||||
|     } | ||||
| 
 | ||||
|     public Text formTextInput(final Composite parent, final String value) { | ||||
|         return formTextInput(parent, value, 1, 1); | ||||
|     } | ||||
| 
 | ||||
|     public Text formTextInput(final Composite parent, final String value, final int hspan, final int vspan) { | ||||
|         final Text textInput = new Text(parent, SWT.LEFT | SWT.BORDER); | ||||
|         final GridData gridData = new GridData(SWT.FILL, SWT.FILL, true, false, hspan, vspan); | ||||
|         gridData.heightHint = 15; | ||||
|         textInput.setLayoutData(gridData); | ||||
|         textInput.setText(value); | ||||
|         return textInput; | ||||
|     } | ||||
| 
 | ||||
|     public Combo formSingleSelectionLocalized( | ||||
|             final Composite parent, | ||||
|             final String selection, | ||||
|             final List<Tuple<String>> items) { | ||||
| 
 | ||||
|         return formSingleSelectionLocalized(parent, selection, items, 1, 1); | ||||
|     } | ||||
| 
 | ||||
|     public Combo formSingleSelectionLocalized( | ||||
|             final Composite parent, | ||||
|             final String selection, | ||||
|             final List<Tuple<String>> items, | ||||
|             final int hspan, final int vspan) { | ||||
| 
 | ||||
|         final SingleSelection combo = singleSelectionLocalized(parent, items); | ||||
|         final GridData gridData = new GridData(SWT.FILL, SWT.FILL, true, false, hspan, vspan); | ||||
|         gridData.heightHint = 25; | ||||
|         combo.setLayoutData(gridData); | ||||
|         combo.select(selection); | ||||
|         return combo; | ||||
|     } | ||||
| 
 | ||||
|     public void formEmpty(final Composite parent) { | ||||
|         formEmpty(parent, 1, 1); | ||||
|     } | ||||
| 
 | ||||
|     public void formEmpty(final Composite parent, final int hspan, final int vspan) { | ||||
|         final Label empty = new Label(parent, SWT.LEFT); | ||||
|         empty.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false, hspan, vspan)); | ||||
|         empty.setText(""); | ||||
|     } | ||||
| 
 | ||||
|     public SingleSelection singleSelectionLocalized( | ||||
|             final Composite parent, | ||||
|             final List<Tuple<String>> items) { | ||||
| 
 | ||||
|         final SingleSelection combo = new SingleSelection(parent, items); | ||||
|         this.injectI18n(combo, combo.valueMapping); | ||||
|         return combo; | ||||
|     } | ||||
| 
 | ||||
|     public void injectI18n(final Label label, final LocTextKey locTextKey) { | ||||
|         injectI18n(label, locTextKey, null); | ||||
|     } | ||||
| 
 | ||||
|     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); | ||||
|     } | ||||
| 
 | ||||
|     public void injectI18n(final Button button, final LocTextKey locTextKey) { | ||||
|         injectI18n(button, locTextKey, null); | ||||
|     } | ||||
| 
 | ||||
|     public void injectI18n(final Button button, final LocTextKey locTextKey, final LocTextKey locToolTipKey) { | ||||
|         final Consumer<Button> buttonFunction = buttonFunction(locTextKey, locToolTipKey, this.i18nSupport); | ||||
|         button.setData(POLYGLOT_WIDGET_FUNCTION_KEY, buttonFunction); | ||||
|         buttonFunction.accept(button); | ||||
|     } | ||||
| 
 | ||||
|     public void injectI18n(final Tree tree) { | ||||
|         tree.setData(POLYGLOT_WIDGET_FUNCTION_KEY, treeFunction(this.i18nSupport)); | ||||
|     } | ||||
| 
 | ||||
|     public void injectI18n(final TreeItem treeItem, final LocTextKey locTextKey) { | ||||
|         treeItem.setData(POLYGLOT_TREE_ITEM_TEXT_DATA_KEY, locTextKey); | ||||
|         treeItem.setText(this.i18nSupport.getText(locTextKey)); | ||||
|     } | ||||
| 
 | ||||
|     public void injectI18n(final Table table) { | ||||
|         table.setData(POLYGLOT_WIDGET_FUNCTION_KEY, tableFunction(this.i18nSupport)); | ||||
|     } | ||||
| 
 | ||||
|     public void injectI18n(final TableColumn tableColumn, final LocTextKey locTextKey) { | ||||
|         tableColumn.setData(POLYGLOT_TREE_ITEM_TEXT_DATA_KEY, locTextKey); | ||||
|         tableColumn.setText(this.i18nSupport.getText(locTextKey)); | ||||
|     } | ||||
| 
 | ||||
|     public void injectI18n(final TableItem tableItem, final LocTextKey... locTextKey) { | ||||
|         if (locTextKey == null) { | ||||
|             return; | ||||
|         } | ||||
| 
 | ||||
|         tableItem.setData(POLYGLOT_TREE_ITEM_TEXT_DATA_KEY, locTextKey); | ||||
|         for (int i = 0; i < locTextKey.length; i++) { | ||||
|             tableItem.setText(i, this.i18nSupport.getText(locTextKey[i])); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     public void injectI18n(final Combo combo, final List<String> items) { | ||||
|         final Consumer<Combo> comboFunction = comboFunction(items, this.i18nSupport); | ||||
|         combo.setData(POLYGLOT_WIDGET_FUNCTION_KEY, comboFunction); | ||||
|         comboFunction.accept(combo); | ||||
|     } | ||||
| 
 | ||||
|     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, | ||||
|                     langSelectionLabelFunction(locale, this.i18nSupport)); | ||||
|             languageSelection.setData(RWT.CUSTOM_VARIANT, "header"); | ||||
|             languageSelection.setText("|  " + locale.getLanguage().toUpperCase()); | ||||
|             //languageSelection.updateLocale(this.i18nSupport); | ||||
|             languageSelection.addListener(SWT.MouseDown, event -> { | ||||
|                 this.polyglotPageService.setPageLocale(composerCtx.getRoot(), locale); | ||||
|             }); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     private static Consumer<Tree> treeFunction(final I18nSupport i18nSupport) { | ||||
|         return tree -> updateLocale(tree.getItems(), i18nSupport); | ||||
|     } | ||||
| 
 | ||||
|     private static Consumer<Table> tableFunction(final I18nSupport i18nSupport) { | ||||
|         return table -> { | ||||
|             updateLocale(table.getColumns(), i18nSupport); | ||||
|             updateLocale(table.getItems(), i18nSupport); | ||||
|         }; | ||||
|     } | ||||
| 
 | ||||
|     private static final Consumer<Label> langSelectionLabelFunction( | ||||
|             final Locale locale, | ||||
|             final I18nSupport i18nSupport) { | ||||
| 
 | ||||
|         return label -> label.setVisible( | ||||
|                 !i18nSupport.getCurrentLocale() | ||||
|                         .getLanguage() | ||||
|                         .equals(locale.getLanguage())); | ||||
|     } | ||||
| 
 | ||||
|     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(i18nSupport.getText(locToolTipKey)); | ||||
|             } | ||||
|         }; | ||||
|     } | ||||
| 
 | ||||
|     private static final Consumer<Combo> comboFunction( | ||||
|             final List<String> items, | ||||
|             final I18nSupport i18nSupport) { | ||||
| 
 | ||||
|         return combo -> { | ||||
|             int i = 0; | ||||
|             final Iterator<String> iterator = items.iterator(); | ||||
|             while (iterator.hasNext()) { | ||||
|                 combo.setItem(i, i18nSupport.getText(iterator.next())); | ||||
|                 i++; | ||||
|             } | ||||
|         }; | ||||
|     } | ||||
| 
 | ||||
|     private static final Consumer<Button> buttonFunction( | ||||
|             final LocTextKey locTextKey, | ||||
|             final LocTextKey locToolTipKey, | ||||
|             final I18nSupport i18nSupport) { | ||||
| 
 | ||||
|         return button -> { | ||||
|             if (locTextKey != null) { | ||||
|                 button.setText(i18nSupport.getText(locTextKey)); | ||||
|             } | ||||
|             if (locToolTipKey != null) { | ||||
|                 button.setToolTipText(i18nSupport.getText(locToolTipKey)); | ||||
|             } | ||||
|         }; | ||||
|     } | ||||
| 
 | ||||
|     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_TREE_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_TREE_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_TREE_ITEM_TEXT_DATA_KEY); | ||||
|             if (locTextKey != null) { | ||||
|                 childItem.setText(i18nSupport.getText(locTextKey)); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
| } | ||||
|  | @ -43,19 +43,19 @@ public interface BulkActionSupportDAO<T extends Entity> { | |||
|                 return (this instanceof ActivatableEntityDAO) | ||||
|                         ? ((ActivatableEntityDAO<?, ?>) this).setActive(all, true) | ||||
|                                 .map(BulkActionSupportDAO::transformResult) | ||||
|                                 .getOrHandleError(error -> handleBulkActionError(error, all)) | ||||
|                                 .get(error -> handleBulkActionError(error, all)) | ||||
|                         : Collections.emptyList(); | ||||
|             case DEACTIVATE: | ||||
|                 return (this instanceof ActivatableEntityDAO) | ||||
|                         ? ((ActivatableEntityDAO<?, ?>) this).setActive(all, false) | ||||
|                                 .map(BulkActionSupportDAO::transformResult) | ||||
|                                 .getOrHandleError(error -> handleBulkActionError(error, all)) | ||||
|                                 .get(error -> handleBulkActionError(error, all)) | ||||
|                         : Collections.emptyList(); | ||||
|             case HARD_DELETE: | ||||
|                 return (this instanceof EntityDAO) | ||||
|                         ? ((EntityDAO<?, ?>) this).delete(all) | ||||
|                                 .map(BulkActionSupportDAO::transformResult) | ||||
|                                 .getOrHandleError(error -> handleBulkActionError(error, all)) | ||||
|                                 .get(error -> handleBulkActionError(error, all)) | ||||
|                         : Collections.emptyList(); | ||||
|         } | ||||
| 
 | ||||
|  |  | |||
|  | @ -19,7 +19,7 @@ import org.springframework.transaction.annotation.Transactional; | |||
| 
 | ||||
| import ch.ethz.seb.sebserver.gbl.model.Entity; | ||||
| import ch.ethz.seb.sebserver.gbl.model.EntityKey; | ||||
| import ch.ethz.seb.sebserver.gbl.model.EntityKeyAndName; | ||||
| import ch.ethz.seb.sebserver.gbl.model.EntityName; | ||||
| import ch.ethz.seb.sebserver.gbl.model.EntityType; | ||||
| import ch.ethz.seb.sebserver.gbl.model.ModelIdAware; | ||||
| import ch.ethz.seb.sebserver.gbl.util.Result; | ||||
|  | @ -66,12 +66,12 @@ public interface EntityDAO<T extends Entity, M extends ModelIdAware> { | |||
|     Result<Collection<T>> loadEntities(Collection<EntityKey> keys); | ||||
| 
 | ||||
|     @Transactional(readOnly = true) | ||||
|     default Result<Collection<EntityKeyAndName>> loadEntityNames(final Collection<EntityKey> keys) { | ||||
|     default Result<Collection<EntityName>> loadEntityNames(final Collection<EntityKey> keys) { | ||||
|         return Result.tryCatch(() -> { | ||||
|             return loadEntities(keys) | ||||
|                     .getOrThrow() | ||||
|                     .stream() | ||||
|                     .map(entity -> new EntityKeyAndName( | ||||
|                     .map(entity -> new EntityName( | ||||
|                             entity.entityType(), | ||||
|                             entity.getModelId(), | ||||
|                             entity.getName())) | ||||
|  |  | |||
|  | @ -11,7 +11,7 @@ package ch.ethz.seb.sebserver.webservice.servicelayer.dao; | |||
| import org.joda.time.DateTime; | ||||
| import org.springframework.util.MultiValueMap; | ||||
| 
 | ||||
| import ch.ethz.seb.sebserver.gbl.POSTMapper; | ||||
| import ch.ethz.seb.sebserver.gbl.api.POSTMapper; | ||||
| import ch.ethz.seb.sebserver.gbl.model.exam.Exam; | ||||
| import ch.ethz.seb.sebserver.gbl.model.exam.Indicator; | ||||
| import ch.ethz.seb.sebserver.gbl.model.institution.LmsSetup; | ||||
|  |  | |||
|  | @ -32,8 +32,8 @@ import org.springframework.stereotype.Component; | |||
| import org.springframework.transaction.annotation.Transactional; | ||||
| 
 | ||||
| import ch.ethz.seb.sebserver.WebSecurityConfig; | ||||
| import ch.ethz.seb.sebserver.gbl.model.APIMessage.APIMessageException; | ||||
| import ch.ethz.seb.sebserver.gbl.model.APIMessage.ErrorMessage; | ||||
| import ch.ethz.seb.sebserver.gbl.api.APIMessage.APIMessageException; | ||||
| import ch.ethz.seb.sebserver.gbl.api.APIMessage.ErrorMessage; | ||||
| import ch.ethz.seb.sebserver.gbl.model.EntityKey; | ||||
| import ch.ethz.seb.sebserver.gbl.model.EntityType; | ||||
| import ch.ethz.seb.sebserver.gbl.model.user.UserInfo; | ||||
|  |  | |||
|  | @ -22,6 +22,7 @@ import org.springframework.beans.factory.annotation.Value; | |||
| import org.springframework.context.annotation.Bean; | ||||
| import org.springframework.context.annotation.Configuration; | ||||
| import org.springframework.core.annotation.Order; | ||||
| import org.springframework.http.HttpStatus; | ||||
| import org.springframework.http.MediaType; | ||||
| import org.springframework.security.authentication.AuthenticationManager; | ||||
| import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; | ||||
|  | @ -219,6 +220,7 @@ public class ClientSessionWebSecurityConfig extends WebSecurityConfigurerAdapter | |||
|             log.warn("Unauthorized Request: {} : Redirect to login after unauthorized request", | ||||
|                     request.getRequestURI()); | ||||
| 
 | ||||
|             response.setStatus(HttpStatus.UNAUTHORIZED.value()); | ||||
|             response.sendRedirect(this.redirect); | ||||
|         } | ||||
|     } | ||||
|  |  | |||
|  | @ -31,8 +31,8 @@ public class WebServiceUserDetails implements UserDetailsService { | |||
|     @Override | ||||
|     public UserDetails loadUserByUsername(final String username) throws UsernameNotFoundException { | ||||
|         return this.userDAO.sebServerUserByUsername(username) | ||||
|                 .getOrHandleError(t -> { | ||||
|                     throw new UsernameNotFoundException("No User with name: " + username + " found", t); | ||||
|                 .get(error -> { | ||||
|                     throw new UsernameNotFoundException("No User with name: " + username + " found", error); | ||||
|                 }); | ||||
|     } | ||||
| 
 | ||||
|  |  | |||
|  | @ -10,6 +10,7 @@ package ch.ethz.seb.sebserver.webservice.weblayer.api; | |||
| 
 | ||||
| import java.util.Arrays; | ||||
| import java.util.Collection; | ||||
| import java.util.List; | ||||
| import java.util.stream.Collectors; | ||||
| 
 | ||||
| import org.slf4j.Logger; | ||||
|  | @ -26,8 +27,8 @@ import org.springframework.web.bind.annotation.ExceptionHandler; | |||
| import org.springframework.web.context.request.WebRequest; | ||||
| import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler; | ||||
| 
 | ||||
| import ch.ethz.seb.sebserver.gbl.model.APIMessage; | ||||
| import ch.ethz.seb.sebserver.gbl.model.APIMessage.APIMessageException; | ||||
| import ch.ethz.seb.sebserver.gbl.api.APIMessage; | ||||
| import ch.ethz.seb.sebserver.gbl.api.APIMessage.APIMessageException; | ||||
| import ch.ethz.seb.sebserver.webservice.servicelayer.authorization.PermissionDeniedException; | ||||
| import ch.ethz.seb.sebserver.webservice.servicelayer.dao.ResourceNotFoundException; | ||||
| import ch.ethz.seb.sebserver.webservice.servicelayer.validation.BeanValidationException; | ||||
|  | @ -47,7 +48,8 @@ public class APIExceptionHandler extends ResponseEntityExceptionHandler { | |||
|             final WebRequest request) { | ||||
| 
 | ||||
|         log.error("Unexpected generic error catched at the API endpoint: ", ex); | ||||
|         return new ResponseEntity<>(APIMessage.ErrorMessage.GENERIC.of(ex.getMessage()), status); | ||||
|         final List<APIMessage> errors = Arrays.asList(APIMessage.ErrorMessage.GENERIC.of(ex.getMessage())); | ||||
|         return new ResponseEntity<>(errors, status); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|  |  | |||
|  | @ -28,11 +28,11 @@ import org.springframework.web.bind.annotation.RequestMethod; | |||
| import org.springframework.web.bind.annotation.RequestParam; | ||||
| 
 | ||||
| import ch.ethz.seb.sebserver.gbl.Constants; | ||||
| import ch.ethz.seb.sebserver.gbl.POSTMapper; | ||||
| import ch.ethz.seb.sebserver.gbl.api.POSTMapper; | ||||
| import ch.ethz.seb.sebserver.gbl.model.Domain; | ||||
| import ch.ethz.seb.sebserver.gbl.model.Entity; | ||||
| import ch.ethz.seb.sebserver.gbl.model.EntityKey; | ||||
| import ch.ethz.seb.sebserver.gbl.model.EntityKeyAndName; | ||||
| import ch.ethz.seb.sebserver.gbl.model.EntityName; | ||||
| import ch.ethz.seb.sebserver.gbl.model.EntityProcessingReport; | ||||
| import ch.ethz.seb.sebserver.gbl.model.EntityType; | ||||
| import ch.ethz.seb.sebserver.gbl.model.Page; | ||||
|  | @ -124,7 +124,7 @@ public abstract class EntityController<T extends GrantEntity, M extends GrantEnt | |||
|             method = RequestMethod.GET, | ||||
|             consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE, | ||||
|             produces = MediaType.APPLICATION_JSON_VALUE) | ||||
|     public Collection<EntityKeyAndName> getNames( | ||||
|     public Collection<EntityName> getNames( | ||||
|             @RequestParam( | ||||
|                     name = Entity.FILTER_ATTR_INSTITUTION, | ||||
|                     required = true, | ||||
|  |  | |||
|  | @ -30,8 +30,9 @@ import org.springframework.web.bind.annotation.RequestMethod; | |||
| import org.springframework.web.bind.annotation.RequestParam; | ||||
| import org.springframework.web.bind.annotation.RestController; | ||||
| 
 | ||||
| import ch.ethz.seb.sebserver.gbl.POSTMapper; | ||||
| import ch.ethz.seb.sebserver.gbl.model.Domain.LMS_SETUP; | ||||
| import ch.ethz.seb.sebserver.gbl.api.POSTMapper; | ||||
| import ch.ethz.seb.sebserver.gbl.api.SEBServerRestEndpoints; | ||||
| import ch.ethz.seb.sebserver.gbl.model.Entity; | ||||
| import ch.ethz.seb.sebserver.gbl.model.EntityKey; | ||||
| import ch.ethz.seb.sebserver.gbl.model.EntityType; | ||||
|  | @ -58,7 +59,7 @@ import ch.ethz.seb.sebserver.webservice.servicelayer.validation.BeanValidationSe | |||
| 
 | ||||
| @WebServiceProfile | ||||
| @RestController | ||||
| @RequestMapping("/${sebserver.webservice.api.admin.endpoint}" + RestAPI.ENDPOINT_EXAM_ADMINISTRATION) | ||||
| @RequestMapping("/${sebserver.webservice.api.admin.endpoint}" + SEBServerRestEndpoints.ENDPOINT_EXAM_ADMINISTRATION) | ||||
| public class ExamAdministrationController extends ActivatableEntityController<Exam, Exam> { | ||||
| 
 | ||||
|     private final ExamDAO examDAO; | ||||
|  |  | |||
|  | @ -13,7 +13,8 @@ import org.springframework.web.bind.annotation.RequestMapping; | |||
| import org.springframework.web.bind.annotation.RequestMethod; | ||||
| import org.springframework.web.bind.annotation.RestController; | ||||
| 
 | ||||
| import ch.ethz.seb.sebserver.gbl.POSTMapper; | ||||
| import ch.ethz.seb.sebserver.gbl.api.POSTMapper; | ||||
| import ch.ethz.seb.sebserver.gbl.api.SEBServerRestEndpoints; | ||||
| import ch.ethz.seb.sebserver.gbl.model.institution.Institution; | ||||
| import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile; | ||||
| import ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.InstitutionRecordDynamicSqlSupport; | ||||
|  | @ -27,7 +28,7 @@ import ch.ethz.seb.sebserver.webservice.servicelayer.validation.BeanValidationSe | |||
| 
 | ||||
| @WebServiceProfile | ||||
| @RestController | ||||
| @RequestMapping("/${sebserver.webservice.api.admin.endpoint}" + RestAPI.ENDPOINT_INSTITUTION) | ||||
| @RequestMapping("/${sebserver.webservice.api.admin.endpoint}" + SEBServerRestEndpoints.ENDPOINT_INSTITUTION) | ||||
| public class InstitutionController extends ActivatableEntityController<Institution, Institution> { | ||||
| 
 | ||||
|     private final InstitutionDAO institutionDAO; | ||||
|  |  | |||
|  | @ -21,7 +21,8 @@ import org.springframework.web.bind.annotation.RequestMapping; | |||
| import org.springframework.web.bind.annotation.RequestMethod; | ||||
| import org.springframework.web.bind.annotation.RestController; | ||||
| 
 | ||||
| import ch.ethz.seb.sebserver.gbl.POSTMapper; | ||||
| import ch.ethz.seb.sebserver.gbl.api.POSTMapper; | ||||
| import ch.ethz.seb.sebserver.gbl.api.SEBServerRestEndpoints; | ||||
| import ch.ethz.seb.sebserver.gbl.model.EntityType; | ||||
| import ch.ethz.seb.sebserver.gbl.model.institution.LmsSetup; | ||||
| import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile; | ||||
|  | @ -37,7 +38,7 @@ import ch.ethz.seb.sebserver.webservice.servicelayer.validation.BeanValidationSe | |||
| 
 | ||||
| @WebServiceProfile | ||||
| @RestController | ||||
| @RequestMapping("/${sebserver.webservice.api.admin.endpoint}" + RestAPI.ENDPOINT_LMS_SETUP) | ||||
| @RequestMapping("/${sebserver.webservice.api.admin.endpoint}" + SEBServerRestEndpoints.ENDPOINT_LMS_SETUP) | ||||
| public class LmsSetupController extends ActivatableEntityController<LmsSetup, LmsSetup> { | ||||
| 
 | ||||
|     private final LmsAPIService lmsAPIService; | ||||
|  |  | |||
|  | @ -15,6 +15,7 @@ import org.springframework.web.bind.annotation.RequestParam; | |||
| import org.springframework.web.bind.annotation.RestController; | ||||
| 
 | ||||
| import ch.ethz.seb.sebserver.gbl.model.Domain.LMS_SETUP; | ||||
| import ch.ethz.seb.sebserver.gbl.api.SEBServerRestEndpoints; | ||||
| import ch.ethz.seb.sebserver.gbl.model.EntityType; | ||||
| import ch.ethz.seb.sebserver.gbl.model.Page; | ||||
| import ch.ethz.seb.sebserver.gbl.model.exam.QuizData; | ||||
|  | @ -27,7 +28,7 @@ import ch.ethz.seb.sebserver.webservice.servicelayer.lms.LmsAPITemplate; | |||
| 
 | ||||
| @WebServiceProfile | ||||
| @RestController | ||||
| @RequestMapping("/${sebserver.webservice.api.admin.endpoint}" + RestAPI.ENDPOINT_QUIZ_IMPORT) | ||||
| @RequestMapping("/${sebserver.webservice.api.admin.endpoint}" + SEBServerRestEndpoints.ENDPOINT_QUIZ_IMPORT) | ||||
| public class QuizImportController { | ||||
| 
 | ||||
|     private final int defaultPageSize; | ||||
|  |  | |||
|  | @ -14,7 +14,8 @@ import org.springframework.web.bind.annotation.RequestMapping; | |||
| import org.springframework.web.bind.annotation.RequestMethod; | ||||
| import org.springframework.web.bind.annotation.RestController; | ||||
| 
 | ||||
| import ch.ethz.seb.sebserver.gbl.POSTMapper; | ||||
| import ch.ethz.seb.sebserver.gbl.api.POSTMapper; | ||||
| import ch.ethz.seb.sebserver.gbl.api.SEBServerRestEndpoints; | ||||
| import ch.ethz.seb.sebserver.gbl.model.user.UserInfo; | ||||
| import ch.ethz.seb.sebserver.gbl.model.user.UserMod; | ||||
| import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile; | ||||
|  | @ -30,7 +31,7 @@ import ch.ethz.seb.sebserver.webservice.weblayer.oauth.RevokeTokenEndpoint; | |||
| 
 | ||||
| @WebServiceProfile | ||||
| @RestController | ||||
| @RequestMapping("${sebserver.webservice.api.admin.endpoint}" + RestAPI.ENDPOINT_USER_ACCOUNT) | ||||
| @RequestMapping("${sebserver.webservice.api.admin.endpoint}" + SEBServerRestEndpoints.ENDPOINT_USER_ACCOUNT) | ||||
| public class UserAccountController extends ActivatableEntityController<UserInfo, UserMod> { | ||||
| 
 | ||||
|     private final ApplicationEventPublisher applicationEventPublisher; | ||||
|  |  | |||
|  | @ -17,6 +17,7 @@ import org.springframework.web.bind.annotation.RequestMethod; | |||
| import org.springframework.web.bind.annotation.RequestParam; | ||||
| import org.springframework.web.bind.annotation.RestController; | ||||
| 
 | ||||
| import ch.ethz.seb.sebserver.gbl.api.SEBServerRestEndpoints; | ||||
| import ch.ethz.seb.sebserver.gbl.model.EntityType; | ||||
| import ch.ethz.seb.sebserver.gbl.model.Page; | ||||
| import ch.ethz.seb.sebserver.gbl.model.user.UserActivityLog; | ||||
|  | @ -32,7 +33,7 @@ import ch.ethz.seb.sebserver.webservice.servicelayer.dao.UserActivityLogDAO; | |||
| 
 | ||||
| @WebServiceProfile | ||||
| @RestController | ||||
| @RequestMapping("/${sebserver.webservice.api.admin.endpoint}" + RestAPI.ENDPOINT_USER_ACTIVITY_LOG) | ||||
| @RequestMapping("/${sebserver.webservice.api.admin.endpoint}" + SEBServerRestEndpoints.ENDPOINT_USER_ACTIVITY_LOG) | ||||
| public class UserActivityLogController { | ||||
| 
 | ||||
|     private final UserActivityLogDAO userActivityLogDAO; | ||||
|  |  | |||
|  | @ -1,2 +1,4 @@ | |||
| spring.application.name=SEB Server | ||||
| spring.profiles.active=dev | ||||
| 
 | ||||
| sebserver.version=1.0 beta | ||||
							
								
								
									
										0
									
								
								src/main/resources/messages-de.properties
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								src/main/resources/messages-de.properties
									
										
									
									
									
										Normal file
									
								
							
							
								
								
									
										31
									
								
								src/main/resources/messages.properties
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								src/main/resources/messages.properties
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,31 @@ | |||
| ################################ | ||||
| # Overall | ||||
| ################################ | ||||
| 
 | ||||
| sebserver.overall.version=SEB Server Version : {0} | ||||
| sebserver.overall.imprint=Imprint | ||||
| sebserver.overall.about=About | ||||
| 
 | ||||
| 
 | ||||
| ################################ | ||||
| # Login Page | ||||
| ################################ | ||||
| 
 | ||||
| sebserver.login.username=User Name | ||||
| sebserver.login.pwd=Password | ||||
| sebserver.login.login=Sign In | ||||
| sebserver.login.failed.title=Login Failed | ||||
| sebserver.login.failed.message=Access Denied: Wrong username or password | ||||
| 
 | ||||
| ################################ | ||||
| # Main Page | ||||
| ################################ | ||||
| 
 | ||||
| sebserver.logout=Logout | ||||
| sebserver.mainpage.maximize.tooltip=Maximize | ||||
| sebserver.mainpage.minimize.tooltip=Minimize | ||||
| sebserver.activitiespane.title=Activities | ||||
| sebserver.actionpane.title=Actions | ||||
| sebserver.activities.inst=Institution | ||||
| 
 | ||||
| sebserver.error.unexpected=Unexpected Error | ||||
|  | @ -84,7 +84,7 @@ Composite.content { | |||
|     margin: 0 0 0 0; | ||||
| } | ||||
| 
 | ||||
| Composite.selectionPane { | ||||
| Composite.actionPane { | ||||
|     background-color: #D3D9DB; | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -14,7 +14,7 @@ import java.io.IOException; | |||
| 
 | ||||
| import org.junit.Test; | ||||
| 
 | ||||
| import ch.ethz.seb.sebserver.gbl.JSONMapper; | ||||
| import ch.ethz.seb.sebserver.gbl.api.JSONMapper; | ||||
| import ch.ethz.seb.sebserver.gbl.model.EntityType; | ||||
| import ch.ethz.seb.sebserver.webservice.servicelayer.dao.UserActivityLogDAO.ActivityType; | ||||
| 
 | ||||
|  |  | |||
|  | @ -75,8 +75,8 @@ public class ResultTest { | |||
|         final Result<String> resultOf = Result.of("ONE"); | ||||
|         final Result<String> resultOfError = Result.ofError(new RuntimeException("Some Error")); | ||||
| 
 | ||||
|         assertEquals("ONE", resultOf.getOrElse("TWO")); | ||||
|         assertEquals("TWO", resultOfError.getOrElse("TWO")); | ||||
|         assertEquals("ONE", resultOf.getOr("TWO")); | ||||
|         assertEquals("TWO", resultOfError.getOr("TWO")); | ||||
| 
 | ||||
|         assertEquals("ONE", resultOf.getOrElse(() -> "TWO")); | ||||
|         assertEquals("TWO", resultOfError.getOrElse(() -> "TWO")); | ||||
|  | @ -87,8 +87,8 @@ public class ResultTest { | |||
|         final Result<String> resultOf = Result.of("ONE"); | ||||
|         final Result<String> resultOfError = Result.ofError(new RuntimeException("Some Error")); | ||||
| 
 | ||||
|         assertEquals("ONE", resultOf.getOrHandleError(t -> t.getMessage())); | ||||
|         assertEquals("Some Error", resultOfError.getOrHandleError(t -> t.getMessage())); | ||||
|         assertEquals("ONE", resultOf.get(t -> t.getMessage())); | ||||
|         assertEquals("Some Error", resultOfError.get(t -> t.getMessage())); | ||||
| 
 | ||||
|         assertEquals("ONE", resultOf.getOrThrowRuntime("Should not be thrown")); | ||||
|         try { | ||||
|  |  | |||
|  | @ -41,7 +41,7 @@ import org.springframework.web.context.WebApplicationContext; | |||
| import com.fasterxml.jackson.core.type.TypeReference; | ||||
| 
 | ||||
| import ch.ethz.seb.sebserver.SEBServer; | ||||
| import ch.ethz.seb.sebserver.gbl.JSONMapper; | ||||
| import ch.ethz.seb.sebserver.gbl.api.JSONMapper; | ||||
| 
 | ||||
| @RunWith(SpringRunner.class) | ||||
| @SpringBootTest( | ||||
|  |  | |||
|  | @ -19,11 +19,11 @@ import org.springframework.test.context.jdbc.Sql; | |||
| 
 | ||||
| import com.fasterxml.jackson.core.type.TypeReference; | ||||
| 
 | ||||
| import ch.ethz.seb.sebserver.gbl.model.APIMessage; | ||||
| import ch.ethz.seb.sebserver.gbl.api.APIMessage; | ||||
| import ch.ethz.seb.sebserver.gbl.api.SEBServerRestEndpoints; | ||||
| import ch.ethz.seb.sebserver.gbl.model.EntityProcessingReport; | ||||
| import ch.ethz.seb.sebserver.gbl.model.Page; | ||||
| import ch.ethz.seb.sebserver.gbl.model.institution.Institution; | ||||
| import ch.ethz.seb.sebserver.webservice.weblayer.api.RestAPI; | ||||
| 
 | ||||
| @Sql(scripts = { "classpath:schema-test.sql", "classpath:data-test.sql" }) | ||||
| public class InstitutionAPITest extends AdministrationAPIIntegrationTester { | ||||
|  | @ -32,7 +32,7 @@ public class InstitutionAPITest extends AdministrationAPIIntegrationTester { | |||
|     public void getInstitutions() throws Exception { | ||||
|         Page<Institution> institutions = new RestAPITestHelper() | ||||
|                 .withAccessToken(getSebAdminAccess()) | ||||
|                 .withPath(RestAPI.ENDPOINT_INSTITUTION) | ||||
|                 .withPath(SEBServerRestEndpoints.ENDPOINT_INSTITUTION) | ||||
|                 .withExpectedStatus(HttpStatus.OK) | ||||
|                 .getAsObject(new TypeReference<Page<Institution>>() { | ||||
|                 }); | ||||
|  | @ -44,7 +44,7 @@ public class InstitutionAPITest extends AdministrationAPIIntegrationTester { | |||
| 
 | ||||
|         institutions = new RestAPITestHelper() | ||||
|                 .withAccessToken(getSebAdminAccess()) | ||||
|                 .withPath(RestAPI.ENDPOINT_INSTITUTION) | ||||
|                 .withPath(SEBServerRestEndpoints.ENDPOINT_INSTITUTION) | ||||
|                 .withAttribute("active", "true") | ||||
|                 .withExpectedStatus(HttpStatus.OK) | ||||
|                 .getAsObject(new TypeReference<Page<Institution>>() { | ||||
|  | @ -57,7 +57,7 @@ public class InstitutionAPITest extends AdministrationAPIIntegrationTester { | |||
| 
 | ||||
|         institutions = new RestAPITestHelper() | ||||
|                 .withAccessToken(getSebAdminAccess()) | ||||
|                 .withPath(RestAPI.ENDPOINT_INSTITUTION) | ||||
|                 .withPath(SEBServerRestEndpoints.ENDPOINT_INSTITUTION) | ||||
|                 .withAttribute("active", "false") | ||||
|                 .withExpectedStatus(HttpStatus.OK) | ||||
|                 .getAsObject(new TypeReference<Page<Institution>>() { | ||||
|  | @ -70,7 +70,7 @@ public class InstitutionAPITest extends AdministrationAPIIntegrationTester { | |||
|         // institutional admin sees only his institution | ||||
|         institutions = new RestAPITestHelper() | ||||
|                 .withAccessToken(getAdminInstitution1Access()) | ||||
|                 .withPath(RestAPI.ENDPOINT_INSTITUTION) | ||||
|                 .withPath(SEBServerRestEndpoints.ENDPOINT_INSTITUTION) | ||||
|                 .withExpectedStatus(HttpStatus.OK) | ||||
|                 .getAsObject(new TypeReference<Page<Institution>>() { | ||||
|                 }); | ||||
|  | @ -82,7 +82,7 @@ public class InstitutionAPITest extends AdministrationAPIIntegrationTester { | |||
|         // Institutional admin tries to get data from other institution | ||||
|         final APIMessage errorMessage = new RestAPITestHelper() | ||||
|                 .withAccessToken(getAdminInstitution1Access()) | ||||
|                 .withPath(RestAPI.ENDPOINT_INSTITUTION) | ||||
|                 .withPath(SEBServerRestEndpoints.ENDPOINT_INSTITUTION) | ||||
|                 .withAttribute("institutionId", "2") // try to hack | ||||
|                 .withExpectedStatus(HttpStatus.FORBIDDEN) | ||||
|                 .getAsObject(new TypeReference<APIMessage>() { | ||||
|  | @ -96,7 +96,7 @@ public class InstitutionAPITest extends AdministrationAPIIntegrationTester { | |||
|     public void getInstitutionById() throws Exception { | ||||
|         Institution institution = new RestAPITestHelper() | ||||
|                 .withAccessToken(getSebAdminAccess()) | ||||
|                 .withPath(RestAPI.ENDPOINT_INSTITUTION + "/1") | ||||
|                 .withPath(SEBServerRestEndpoints.ENDPOINT_INSTITUTION + "/1") | ||||
|                 .withExpectedStatus(HttpStatus.OK) | ||||
|                 .getAsObject(new TypeReference<Institution>() { | ||||
|                 }); | ||||
|  | @ -107,7 +107,7 @@ public class InstitutionAPITest extends AdministrationAPIIntegrationTester { | |||
|         // a seb-admin is also able to get an institution that is not the one he self belongs to | ||||
|         institution = new RestAPITestHelper() | ||||
|                 .withAccessToken(getSebAdminAccess()) | ||||
|                 .withPath(RestAPI.ENDPOINT_INSTITUTION + "/2") | ||||
|                 .withPath(SEBServerRestEndpoints.ENDPOINT_INSTITUTION + "/2") | ||||
|                 .withExpectedStatus(HttpStatus.OK) | ||||
|                 .getAsObject(new TypeReference<Institution>() { | ||||
|                 }); | ||||
|  | @ -118,7 +118,7 @@ public class InstitutionAPITest extends AdministrationAPIIntegrationTester { | |||
|         // but a institutional-admin is not able to get an institution that is not the one he self belongs to | ||||
|         new RestAPITestHelper() | ||||
|                 .withAccessToken(getAdminInstitution1Access()) | ||||
|                 .withPath(RestAPI.ENDPOINT_INSTITUTION + "/2") | ||||
|                 .withPath(SEBServerRestEndpoints.ENDPOINT_INSTITUTION + "/2") | ||||
|                 .withExpectedStatus(HttpStatus.FORBIDDEN) | ||||
|                 .getAsString(); | ||||
|     } | ||||
|  | @ -128,7 +128,7 @@ public class InstitutionAPITest extends AdministrationAPIIntegrationTester { | |||
|         // create new institution with seb-admin | ||||
|         Institution institution = new RestAPITestHelper() | ||||
|                 .withAccessToken(getSebAdminAccess()) | ||||
|                 .withPath(RestAPI.ENDPOINT_INSTITUTION) | ||||
|                 .withPath(SEBServerRestEndpoints.ENDPOINT_INSTITUTION) | ||||
|                 .withMethod(HttpMethod.POST) | ||||
|                 .withAttribute("name", "new institution") | ||||
|                 .withAttribute("urlSuffix", "new_inst") | ||||
|  | @ -144,7 +144,7 @@ public class InstitutionAPITest extends AdministrationAPIIntegrationTester { | |||
|         // an institutional admin should not be allowed to create a new institution | ||||
|         APIMessage errorMessage = new RestAPITestHelper() | ||||
|                 .withAccessToken(getAdminInstitution1Access()) | ||||
|                 .withPath(RestAPI.ENDPOINT_INSTITUTION) | ||||
|                 .withPath(SEBServerRestEndpoints.ENDPOINT_INSTITUTION) | ||||
|                 .withMethod(HttpMethod.POST) | ||||
|                 .withAttribute("name", "new institution") | ||||
|                 .withAttribute("urlSuffix", "new_inst") | ||||
|  | @ -156,7 +156,7 @@ public class InstitutionAPITest extends AdministrationAPIIntegrationTester { | |||
|         // and name for institution must be unique | ||||
|         errorMessage = new RestAPITestHelper() | ||||
|                 .withAccessToken(getSebAdminAccess()) | ||||
|                 .withPath(RestAPI.ENDPOINT_INSTITUTION) | ||||
|                 .withPath(SEBServerRestEndpoints.ENDPOINT_INSTITUTION) | ||||
|                 .withMethod(HttpMethod.POST) | ||||
|                 .withAttribute("name", "new institution") | ||||
|                 .withAttribute("urlSuffix", "new_inst") | ||||
|  | @ -171,7 +171,7 @@ public class InstitutionAPITest extends AdministrationAPIIntegrationTester { | |||
|         // and predefined id should be ignored | ||||
|         institution = new RestAPITestHelper() | ||||
|                 .withAccessToken(getSebAdminAccess()) | ||||
|                 .withPath(RestAPI.ENDPOINT_INSTITUTION) | ||||
|                 .withPath(SEBServerRestEndpoints.ENDPOINT_INSTITUTION) | ||||
|                 .withMethod(HttpMethod.POST) | ||||
|                 .withAttribute("id", "123") | ||||
|                 .withAttribute("name", "newer institution") | ||||
|  | @ -191,7 +191,7 @@ public class InstitutionAPITest extends AdministrationAPIIntegrationTester { | |||
|         final String sebAdminAccess = getSebAdminAccess(); | ||||
|         Institution institution = new RestAPITestHelper() | ||||
|                 .withAccessToken(sebAdminAccess) | ||||
|                 .withPath(RestAPI.ENDPOINT_INSTITUTION) | ||||
|                 .withPath(SEBServerRestEndpoints.ENDPOINT_INSTITUTION) | ||||
|                 .withMethod(HttpMethod.POST) | ||||
|                 .withAttribute("name", "testInstitution") | ||||
|                 .withExpectedStatus(HttpStatus.OK) | ||||
|  | @ -205,7 +205,7 @@ public class InstitutionAPITest extends AdministrationAPIIntegrationTester { | |||
|         // get | ||||
|         institution = new RestAPITestHelper() | ||||
|                 .withAccessToken(sebAdminAccess) | ||||
|                 .withPath(RestAPI.ENDPOINT_INSTITUTION).withPath("/").withPath(String.valueOf(institution.id)) | ||||
|                 .withPath(SEBServerRestEndpoints.ENDPOINT_INSTITUTION).withPath("/").withPath(String.valueOf(institution.id)) | ||||
|                 .withMethod(HttpMethod.GET) | ||||
|                 .withExpectedStatus(HttpStatus.OK) | ||||
|                 .getAsObject(new TypeReference<Institution>() { | ||||
|  | @ -219,7 +219,7 @@ public class InstitutionAPITest extends AdministrationAPIIntegrationTester { | |||
|         // modify | ||||
|         institution = new RestAPITestHelper() | ||||
|                 .withAccessToken(sebAdminAccess) | ||||
|                 .withPath(RestAPI.ENDPOINT_INSTITUTION).withPath("/").withPath(String.valueOf(institution.id)) | ||||
|                 .withPath(SEBServerRestEndpoints.ENDPOINT_INSTITUTION).withPath("/").withPath(String.valueOf(institution.id)) | ||||
|                 .withMethod(HttpMethod.PUT) | ||||
|                 .withBodyJson(new Institution(null, "testInstitution", "testSuffix", null, null)) | ||||
|                 .withExpectedStatus(HttpStatus.OK) | ||||
|  | @ -234,7 +234,7 @@ public class InstitutionAPITest extends AdministrationAPIIntegrationTester { | |||
|         // activate | ||||
|         EntityProcessingReport report = new RestAPITestHelper() | ||||
|                 .withAccessToken(sebAdminAccess) | ||||
|                 .withPath(RestAPI.ENDPOINT_INSTITUTION) | ||||
|                 .withPath(SEBServerRestEndpoints.ENDPOINT_INSTITUTION) | ||||
|                 .withPath("/").withPath(String.valueOf(institution.id)).withPath("/active") | ||||
|                 .withMethod(HttpMethod.POST) | ||||
|                 .withExpectedStatus(HttpStatus.OK) | ||||
|  | @ -250,7 +250,7 @@ public class InstitutionAPITest extends AdministrationAPIIntegrationTester { | |||
|         // get | ||||
|         institution = new RestAPITestHelper() | ||||
|                 .withAccessToken(sebAdminAccess) | ||||
|                 .withPath(RestAPI.ENDPOINT_INSTITUTION).withPath("/").withPath(String.valueOf(institution.id)) | ||||
|                 .withPath(SEBServerRestEndpoints.ENDPOINT_INSTITUTION).withPath("/").withPath(String.valueOf(institution.id)) | ||||
|                 .withMethod(HttpMethod.GET) | ||||
|                 .withExpectedStatus(HttpStatus.OK) | ||||
|                 .getAsObject(new TypeReference<Institution>() { | ||||
|  | @ -262,7 +262,7 @@ public class InstitutionAPITest extends AdministrationAPIIntegrationTester { | |||
|         // deactivate | ||||
|         report = new RestAPITestHelper() | ||||
|                 .withAccessToken(sebAdminAccess) | ||||
|                 .withPath(RestAPI.ENDPOINT_INSTITUTION) | ||||
|                 .withPath(SEBServerRestEndpoints.ENDPOINT_INSTITUTION) | ||||
|                 .withPath("/").withPath(String.valueOf(institution.id)).withPath("/inactive") | ||||
|                 .withMethod(HttpMethod.POST) | ||||
|                 .withExpectedStatus(HttpStatus.OK) | ||||
|  | @ -278,7 +278,7 @@ public class InstitutionAPITest extends AdministrationAPIIntegrationTester { | |||
|         // get | ||||
|         institution = new RestAPITestHelper() | ||||
|                 .withAccessToken(sebAdminAccess) | ||||
|                 .withPath(RestAPI.ENDPOINT_INSTITUTION).withPath("/").withPath(String.valueOf(institution.id)) | ||||
|                 .withPath(SEBServerRestEndpoints.ENDPOINT_INSTITUTION).withPath("/").withPath(String.valueOf(institution.id)) | ||||
|                 .withMethod(HttpMethod.GET) | ||||
|                 .withExpectedStatus(HttpStatus.OK) | ||||
|                 .getAsObject(new TypeReference<Institution>() { | ||||
|  | @ -290,7 +290,7 @@ public class InstitutionAPITest extends AdministrationAPIIntegrationTester { | |||
|         // delete | ||||
|         report = new RestAPITestHelper() | ||||
|                 .withAccessToken(sebAdminAccess) | ||||
|                 .withPath(RestAPI.ENDPOINT_INSTITUTION) | ||||
|                 .withPath(SEBServerRestEndpoints.ENDPOINT_INSTITUTION) | ||||
|                 .withPath("/").withPath(String.valueOf(institution.id)) | ||||
|                 .withMethod(HttpMethod.DELETE) | ||||
|                 .withExpectedStatus(HttpStatus.OK) | ||||
|  | @ -307,7 +307,7 @@ public class InstitutionAPITest extends AdministrationAPIIntegrationTester { | |||
|         // get | ||||
|         final APIMessage error = new RestAPITestHelper() | ||||
|                 .withAccessToken(sebAdminAccess) | ||||
|                 .withPath(RestAPI.ENDPOINT_INSTITUTION).withPath("/").withPath(String.valueOf(institution.id)) | ||||
|                 .withPath(SEBServerRestEndpoints.ENDPOINT_INSTITUTION).withPath("/").withPath(String.valueOf(institution.id)) | ||||
|                 .withMethod(HttpMethod.GET) | ||||
|                 .withExpectedStatus(HttpStatus.NOT_FOUND) | ||||
|                 .getAsObject(new TypeReference<APIMessage>() { | ||||
|  |  | |||
|  | @ -31,11 +31,12 @@ import org.springframework.test.context.jdbc.Sql; | |||
| import com.fasterxml.jackson.core.type.TypeReference; | ||||
| 
 | ||||
| import ch.ethz.seb.sebserver.gbl.Constants; | ||||
| import ch.ethz.seb.sebserver.gbl.model.APIMessage; | ||||
| import ch.ethz.seb.sebserver.gbl.api.APIMessage; | ||||
| import ch.ethz.seb.sebserver.gbl.api.SEBServerRestEndpoints; | ||||
| import ch.ethz.seb.sebserver.gbl.model.Domain; | ||||
| import ch.ethz.seb.sebserver.gbl.model.Entity; | ||||
| import ch.ethz.seb.sebserver.gbl.model.EntityKey; | ||||
| import ch.ethz.seb.sebserver.gbl.model.EntityKeyAndName; | ||||
| import ch.ethz.seb.sebserver.gbl.model.EntityName; | ||||
| import ch.ethz.seb.sebserver.gbl.model.EntityProcessingReport; | ||||
| import ch.ethz.seb.sebserver.gbl.model.Page; | ||||
| import ch.ethz.seb.sebserver.gbl.model.user.UserActivityLog; | ||||
|  | @ -43,7 +44,6 @@ 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.webservice.servicelayer.dao.UserActivityLogDAO.ActivityType; | ||||
| import ch.ethz.seb.sebserver.webservice.weblayer.api.RestAPI; | ||||
| 
 | ||||
| @Sql(scripts = { "classpath:schema-test.sql", "classpath:data-test.sql" }) | ||||
| public class UserAPITest extends AdministrationAPIIntegrationTester { | ||||
|  | @ -52,7 +52,7 @@ public class UserAPITest extends AdministrationAPIIntegrationTester { | |||
|     public void getMyUserInfo() throws Exception { | ||||
|         String contentAsString = new RestAPITestHelper() | ||||
|                 .withAccessToken(getSebAdminAccess()) | ||||
|                 .withPath(RestAPI.ENDPOINT_USER_ACCOUNT + "/me") | ||||
|                 .withPath(SEBServerRestEndpoints.ENDPOINT_USER_ACCOUNT + "/me") | ||||
|                 .withExpectedStatus(HttpStatus.OK) | ||||
|                 .getAsString(); | ||||
| 
 | ||||
|  | @ -70,7 +70,7 @@ public class UserAPITest extends AdministrationAPIIntegrationTester { | |||
| 
 | ||||
|         contentAsString = new RestAPITestHelper() | ||||
|                 .withAccessToken(getAdminInstitution1Access()) | ||||
|                 .withPath(RestAPI.ENDPOINT_USER_ACCOUNT + "/me") | ||||
|                 .withPath(SEBServerRestEndpoints.ENDPOINT_USER_ACCOUNT + "/me") | ||||
|                 .withExpectedStatus(HttpStatus.OK) | ||||
|                 .getAsString(); | ||||
| 
 | ||||
|  | @ -90,7 +90,7 @@ public class UserAPITest extends AdministrationAPIIntegrationTester { | |||
|     @Test | ||||
|     public void getUserInfoWithUUID() throws Exception { | ||||
|         final String sebAdminAccessToken = getSebAdminAccess(); | ||||
|         String contentAsString = this.mockMvc.perform(get(this.endpoint + RestAPI.ENDPOINT_USER_ACCOUNT + "/user2") | ||||
|         String contentAsString = this.mockMvc.perform(get(this.endpoint + SEBServerRestEndpoints.ENDPOINT_USER_ACCOUNT + "/user2") | ||||
|                 .contentType(MediaType.APPLICATION_FORM_URLENCODED) | ||||
|                 .header("Authorization", "Bearer " + sebAdminAccessToken)) | ||||
|                 .andExpect(status().isOk()) | ||||
|  | @ -109,7 +109,7 @@ public class UserAPITest extends AdministrationAPIIntegrationTester { | |||
|                 contentAsString); | ||||
| 
 | ||||
|         final String adminInstitution2AccessToken = getAdminInstitution2Access(); | ||||
|         contentAsString = this.mockMvc.perform(get(this.endpoint + RestAPI.ENDPOINT_USER_ACCOUNT + "/user1") | ||||
|         contentAsString = this.mockMvc.perform(get(this.endpoint + SEBServerRestEndpoints.ENDPOINT_USER_ACCOUNT + "/user1") | ||||
|                 .contentType(MediaType.APPLICATION_FORM_URLENCODED) | ||||
|                 .header("Authorization", "Bearer " + adminInstitution2AccessToken)) | ||||
|                 .andExpect(status().isForbidden()) | ||||
|  | @ -127,7 +127,7 @@ public class UserAPITest extends AdministrationAPIIntegrationTester { | |||
|     public void institutionalAdminNotAllowedToSeeUsersOfOtherInstitution() throws Exception { | ||||
|         new RestAPITestHelper() | ||||
|                 .withAccessToken(getAdminInstitution1Access()) | ||||
|                 .withPath(RestAPI.ENDPOINT_USER_ACCOUNT + "?institutionId=2") | ||||
|                 .withPath(SEBServerRestEndpoints.ENDPOINT_USER_ACCOUNT + "?institutionId=2") | ||||
|                 .withExpectedStatus(HttpStatus.FORBIDDEN) | ||||
|                 .getAsString(); | ||||
|     } | ||||
|  | @ -136,7 +136,7 @@ public class UserAPITest extends AdministrationAPIIntegrationTester { | |||
|     public void getAllUserInfoNoFilter() throws Exception { | ||||
|         Page<UserInfo> userInfos = new RestAPITestHelper() | ||||
|                 .withAccessToken(getSebAdminAccess()) | ||||
|                 .withPath(RestAPI.ENDPOINT_USER_ACCOUNT) | ||||
|                 .withPath(SEBServerRestEndpoints.ENDPOINT_USER_ACCOUNT) | ||||
|                 .withExpectedStatus(HttpStatus.OK) | ||||
|                 .getAsObject(new TypeReference<Page<UserInfo>>() { | ||||
|                 }); | ||||
|  | @ -150,7 +150,7 @@ public class UserAPITest extends AdministrationAPIIntegrationTester { | |||
| 
 | ||||
|         userInfos = new RestAPITestHelper() | ||||
|                 .withAccessToken(getAdminInstitution2Access()) | ||||
|                 .withPath(RestAPI.ENDPOINT_USER_ACCOUNT) | ||||
|                 .withPath(SEBServerRestEndpoints.ENDPOINT_USER_ACCOUNT) | ||||
|                 .withAttribute("institutionId", "2") | ||||
|                 .withExpectedStatus(HttpStatus.OK) | ||||
|                 .getAsObject(new TypeReference<Page<UserInfo>>() { | ||||
|  | @ -167,7 +167,7 @@ public class UserAPITest extends AdministrationAPIIntegrationTester { | |||
|         //.. and without inactive, if active flag is set to true | ||||
|         userInfos = new RestAPITestHelper() | ||||
|                 .withAccessToken(getAdminInstitution2Access()) | ||||
|                 .withPath(RestAPI.ENDPOINT_USER_ACCOUNT) | ||||
|                 .withPath(SEBServerRestEndpoints.ENDPOINT_USER_ACCOUNT) | ||||
|                 .withAttribute(Entity.FILTER_ATTR_INSTITUTION, "2") | ||||
|                 .withAttribute(Entity.FILTER_ATTR_ACTIVE, "true") | ||||
|                 .withExpectedStatus(HttpStatus.OK) | ||||
|  | @ -183,7 +183,7 @@ public class UserAPITest extends AdministrationAPIIntegrationTester { | |||
|         //.. and only inactive, if active flag is set to false | ||||
|         userInfos = new RestAPITestHelper() | ||||
|                 .withAccessToken(getAdminInstitution2Access()) | ||||
|                 .withPath(RestAPI.ENDPOINT_USER_ACCOUNT) | ||||
|                 .withPath(SEBServerRestEndpoints.ENDPOINT_USER_ACCOUNT) | ||||
|                 .withAttribute(Entity.FILTER_ATTR_INSTITUTION, "2") | ||||
|                 .withAttribute(Entity.FILTER_ATTR_ACTIVE, "false") | ||||
|                 .withExpectedStatus(HttpStatus.OK) | ||||
|  | @ -203,7 +203,7 @@ public class UserAPITest extends AdministrationAPIIntegrationTester { | |||
| 
 | ||||
|         final String token = getSebAdminAccess(); | ||||
|         final Page<UserInfo> userInfos = this.jsonMapper.readValue( | ||||
|                 this.mockMvc.perform(get(this.endpoint + RestAPI.ENDPOINT_USER_ACCOUNT) | ||||
|                 this.mockMvc.perform(get(this.endpoint + SEBServerRestEndpoints.ENDPOINT_USER_ACCOUNT) | ||||
|                         .contentType(MediaType.APPLICATION_FORM_URLENCODED) | ||||
|                         .header("Authorization", "Bearer " + token)) | ||||
|                         .andExpect(status().isOk()) | ||||
|  | @ -225,7 +225,7 @@ public class UserAPITest extends AdministrationAPIIntegrationTester { | |||
| 
 | ||||
|         final String token = getSebAdminAccess(); | ||||
|         final Page<UserInfo> userInfos = this.jsonMapper.readValue( | ||||
|                 this.mockMvc.perform(get(this.endpoint + RestAPI.ENDPOINT_USER_ACCOUNT + "?institutionId=2") | ||||
|                 this.mockMvc.perform(get(this.endpoint + SEBServerRestEndpoints.ENDPOINT_USER_ACCOUNT + "?institutionId=2") | ||||
|                         .contentType(MediaType.APPLICATION_FORM_URLENCODED) | ||||
|                         .header("Authorization", "Bearer " + token)) | ||||
|                         .andExpect(status().isOk()) | ||||
|  | @ -244,7 +244,7 @@ public class UserAPITest extends AdministrationAPIIntegrationTester { | |||
|     public void getPageNoFilterNoPageAttributesDescendingOrder() throws Exception { | ||||
|         final String token = getSebAdminAccess(); | ||||
|         final Page<UserInfo> userInfos = this.jsonMapper.readValue( | ||||
|                 this.mockMvc.perform(get(this.endpoint + RestAPI.ENDPOINT_USER_ACCOUNT + "?sort=-") | ||||
|                 this.mockMvc.perform(get(this.endpoint + SEBServerRestEndpoints.ENDPOINT_USER_ACCOUNT + "?sort=-") | ||||
|                         .contentType(MediaType.APPLICATION_FORM_URLENCODED) | ||||
|                         .header("Authorization", "Bearer " + token)) | ||||
|                         .andExpect(status().isOk()) | ||||
|  | @ -266,7 +266,7 @@ public class UserAPITest extends AdministrationAPIIntegrationTester { | |||
|         // first page default sort order | ||||
|         Page<UserInfo> userInfos = this.jsonMapper.readValue( | ||||
|                 this.mockMvc | ||||
|                         .perform(get(this.endpoint + RestAPI.ENDPOINT_USER_ACCOUNT | ||||
|                         .perform(get(this.endpoint + SEBServerRestEndpoints.ENDPOINT_USER_ACCOUNT | ||||
|                                 + "?page_number=1&page_size=3&institutionId=2") | ||||
|                                         .contentType(MediaType.APPLICATION_FORM_URLENCODED) | ||||
|                                         .header("Authorization", "Bearer " + token)) | ||||
|  | @ -284,7 +284,7 @@ public class UserAPITest extends AdministrationAPIIntegrationTester { | |||
|         // second page default sort order | ||||
|         userInfos = this.jsonMapper.readValue( | ||||
|                 this.mockMvc | ||||
|                         .perform(get(this.endpoint + RestAPI.ENDPOINT_USER_ACCOUNT | ||||
|                         .perform(get(this.endpoint + SEBServerRestEndpoints.ENDPOINT_USER_ACCOUNT | ||||
|                                 + "?page_number=2&page_size=3&institutionId=2") | ||||
|                                         .contentType(MediaType.APPLICATION_FORM_URLENCODED) | ||||
|                                         .header("Authorization", "Bearer " + token)) | ||||
|  | @ -303,7 +303,7 @@ public class UserAPITest extends AdministrationAPIIntegrationTester { | |||
|         // invalid page number should refer to last page | ||||
|         userInfos = this.jsonMapper.readValue( | ||||
|                 this.mockMvc | ||||
|                         .perform(get(this.endpoint + RestAPI.ENDPOINT_USER_ACCOUNT | ||||
|                         .perform(get(this.endpoint + SEBServerRestEndpoints.ENDPOINT_USER_ACCOUNT | ||||
|                                 + "?page_number=3&page_size=3&institutionId=2") | ||||
|                                         .contentType(MediaType.APPLICATION_FORM_URLENCODED) | ||||
|                                         .header("Authorization", "Bearer " + token)) | ||||
|  | @ -322,7 +322,7 @@ public class UserAPITest extends AdministrationAPIIntegrationTester { | |||
|         // first page descending sort order | ||||
|         userInfos = this.jsonMapper.readValue( | ||||
|                 this.mockMvc | ||||
|                         .perform(get(this.endpoint + RestAPI.ENDPOINT_USER_ACCOUNT | ||||
|                         .perform(get(this.endpoint + SEBServerRestEndpoints.ENDPOINT_USER_ACCOUNT | ||||
|                                 + "?page_number=1&page_size=3&sort=-&institutionId=2") | ||||
|                                         .contentType(MediaType.APPLICATION_FORM_URLENCODED) | ||||
|                                         .header("Authorization", "Bearer " + token)) | ||||
|  | @ -350,7 +350,7 @@ public class UserAPITest extends AdministrationAPIIntegrationTester { | |||
|     public void getAllUserInfo() throws Exception { | ||||
|         final String token = getSebAdminAccess(); | ||||
|         final Page<UserInfo> userInfos = this.jsonMapper.readValue( | ||||
|                 this.mockMvc.perform(get(this.endpoint + RestAPI.ENDPOINT_USER_ACCOUNT) | ||||
|                 this.mockMvc.perform(get(this.endpoint + SEBServerRestEndpoints.ENDPOINT_USER_ACCOUNT) | ||||
|                         .contentType(MediaType.APPLICATION_FORM_URLENCODED) | ||||
|                         .header("Authorization", "Bearer " + token)) | ||||
|                         .andExpect(status().isOk()) | ||||
|  | @ -366,7 +366,7 @@ public class UserAPITest extends AdministrationAPIIntegrationTester { | |||
|     public void getAllUserInfoWithOnlyActive() throws Exception { | ||||
|         final String token = getSebAdminAccess(); | ||||
|         final Page<UserInfo> userInfos = this.jsonMapper.readValue( | ||||
|                 this.mockMvc.perform(get(this.endpoint + RestAPI.ENDPOINT_USER_ACCOUNT + "?active=true&institutionId=2") | ||||
|                 this.mockMvc.perform(get(this.endpoint + SEBServerRestEndpoints.ENDPOINT_USER_ACCOUNT + "?active=true&institutionId=2") | ||||
|                         .contentType(MediaType.APPLICATION_FORM_URLENCODED) | ||||
|                         .header("Authorization", "Bearer " + token)) | ||||
|                         .andExpect(status().isOk()) | ||||
|  | @ -385,7 +385,7 @@ public class UserAPITest extends AdministrationAPIIntegrationTester { | |||
|         // expecting none for SEBAdmins institution | ||||
|         final String token = getSebAdminAccess(); | ||||
|         Page<UserInfo> userInfos = this.jsonMapper.readValue( | ||||
|                 this.mockMvc.perform(get(this.endpoint + RestAPI.ENDPOINT_USER_ACCOUNT + "?active=false") | ||||
|                 this.mockMvc.perform(get(this.endpoint + SEBServerRestEndpoints.ENDPOINT_USER_ACCOUNT + "?active=false") | ||||
|                         .contentType(MediaType.APPLICATION_FORM_URLENCODED) | ||||
|                         .header("Authorization", "Bearer " + token)) | ||||
|                         .andExpect(status().isOk()) | ||||
|  | @ -399,7 +399,7 @@ public class UserAPITest extends AdministrationAPIIntegrationTester { | |||
|         // expecting one for institution 2 | ||||
|         userInfos = this.jsonMapper.readValue( | ||||
|                 this.mockMvc | ||||
|                         .perform(get(this.endpoint + RestAPI.ENDPOINT_USER_ACCOUNT + "?active=false&institutionId=2") | ||||
|                         .perform(get(this.endpoint + SEBServerRestEndpoints.ENDPOINT_USER_ACCOUNT + "?active=false&institutionId=2") | ||||
|                                 .contentType(MediaType.APPLICATION_FORM_URLENCODED) | ||||
|                                 .header("Authorization", "Bearer " + token)) | ||||
|                         .andExpect(status().isOk()) | ||||
|  | @ -416,7 +416,7 @@ public class UserAPITest extends AdministrationAPIIntegrationTester { | |||
|     public void getAllUserInfoWithSearchUsernameLike() throws Exception { | ||||
|         final String token = getSebAdminAccess(); | ||||
|         final Page<UserInfo> userInfos = this.jsonMapper.readValue( | ||||
|                 this.mockMvc.perform(get(this.endpoint + RestAPI.ENDPOINT_USER_ACCOUNT + "?username=exam") | ||||
|                 this.mockMvc.perform(get(this.endpoint + SEBServerRestEndpoints.ENDPOINT_USER_ACCOUNT + "?username=exam") | ||||
|                         .contentType(MediaType.APPLICATION_FORM_URLENCODED) | ||||
|                         .header("Authorization", "Bearer " + token)) | ||||
|                         .andExpect(status().isOk()) | ||||
|  | @ -432,7 +432,7 @@ public class UserAPITest extends AdministrationAPIIntegrationTester { | |||
|     @Test | ||||
|     public void testOwnerGet() throws Exception { | ||||
|         final String examAdminToken1 = getExamAdmin1(); | ||||
|         this.mockMvc.perform(get(this.endpoint + RestAPI.ENDPOINT_USER_ACCOUNT + "/me") | ||||
|         this.mockMvc.perform(get(this.endpoint + SEBServerRestEndpoints.ENDPOINT_USER_ACCOUNT + "/me") | ||||
|                 .header("Authorization", "Bearer " + examAdminToken1)) | ||||
|                 .andExpect(status().isOk()) | ||||
|                 .andReturn().getResponse().getContentAsString(); | ||||
|  | @ -442,7 +442,7 @@ public class UserAPITest extends AdministrationAPIIntegrationTester { | |||
|     public void createUserTest() throws Exception { | ||||
|         final String token = getSebAdminAccess(); | ||||
|         final UserInfo createdUser = this.jsonMapper.readValue( | ||||
|                 this.mockMvc.perform(post(this.endpoint + RestAPI.ENDPOINT_USER_ACCOUNT) | ||||
|                 this.mockMvc.perform(post(this.endpoint + SEBServerRestEndpoints.ENDPOINT_USER_ACCOUNT) | ||||
|                         .header("Authorization", "Bearer " + token) | ||||
|                         .contentType(MediaType.APPLICATION_FORM_URLENCODED) | ||||
|                         .param(Domain.USER.ATTR_NAME, "NewTestUser") | ||||
|  | @ -461,7 +461,7 @@ public class UserAPITest extends AdministrationAPIIntegrationTester { | |||
| 
 | ||||
|         // get newly created user and check equality | ||||
|         final UserInfo createdUserGet = this.jsonMapper.readValue( | ||||
|                 this.mockMvc.perform(get(this.endpoint + RestAPI.ENDPOINT_USER_ACCOUNT + "/" + createdUser.uuid) | ||||
|                 this.mockMvc.perform(get(this.endpoint + SEBServerRestEndpoints.ENDPOINT_USER_ACCOUNT + "/" + createdUser.uuid) | ||||
|                         .contentType(MediaType.APPLICATION_FORM_URLENCODED) | ||||
|                         .header("Authorization", "Bearer " + token)) | ||||
|                         .andExpect(status().isOk()) | ||||
|  | @ -477,7 +477,7 @@ public class UserAPITest extends AdministrationAPIIntegrationTester { | |||
|         final Page<UserActivityLog> logs = this.jsonMapper.readValue( | ||||
|                 this.mockMvc | ||||
|                         .perform( | ||||
|                                 get(this.endpoint + RestAPI.ENDPOINT_USER_ACTIVITY_LOG | ||||
|                                 get(this.endpoint + SEBServerRestEndpoints.ENDPOINT_USER_ACTIVITY_LOG | ||||
|                                         + "?user=user1&activity_types=CREATE") | ||||
|                                                 .header("Authorization", "Bearer " + token)) | ||||
|                         .andExpect(status().isOk()) | ||||
|  | @ -557,7 +557,7 @@ public class UserAPITest extends AdministrationAPIIntegrationTester { | |||
|     public void modifyUserWithPUTMethod() throws Exception { | ||||
|         final String token = getSebAdminAccess(); | ||||
|         final UserInfo user = this.jsonMapper.readValue( | ||||
|                 this.mockMvc.perform(get(this.endpoint + RestAPI.ENDPOINT_USER_ACCOUNT + "/user7") | ||||
|                 this.mockMvc.perform(get(this.endpoint + SEBServerRestEndpoints.ENDPOINT_USER_ACCOUNT + "/user7") | ||||
|                         .contentType(MediaType.APPLICATION_FORM_URLENCODED) | ||||
|                         .header("Authorization", "Bearer " + token)) | ||||
|                         .andExpect(status().isOk()) | ||||
|  | @ -586,7 +586,7 @@ public class UserAPITest extends AdministrationAPIIntegrationTester { | |||
|         final String modifyUserJson = this.jsonMapper.writeValueAsString(modifyUser); | ||||
| 
 | ||||
|         UserInfo modifiedUserResult = this.jsonMapper.readValue( | ||||
|                 this.mockMvc.perform(put(this.endpoint + RestAPI.ENDPOINT_USER_ACCOUNT + "/" + user.getUuid()) | ||||
|                 this.mockMvc.perform(put(this.endpoint + SEBServerRestEndpoints.ENDPOINT_USER_ACCOUNT + "/" + user.getUuid()) | ||||
|                         .header("Authorization", "Bearer " + token) | ||||
|                         .contentType(MediaType.APPLICATION_JSON_UTF8) | ||||
|                         .content(modifyUserJson)) | ||||
|  | @ -604,7 +604,7 @@ public class UserAPITest extends AdministrationAPIIntegrationTester { | |||
| 
 | ||||
|         // double check by getting the user by UUID | ||||
|         modifiedUserResult = this.jsonMapper.readValue( | ||||
|                 this.mockMvc.perform(get(this.endpoint + RestAPI.ENDPOINT_USER_ACCOUNT + "/" + modifiedUserResult.uuid) | ||||
|                 this.mockMvc.perform(get(this.endpoint + SEBServerRestEndpoints.ENDPOINT_USER_ACCOUNT + "/" + modifiedUserResult.uuid) | ||||
|                         .contentType(MediaType.APPLICATION_FORM_URLENCODED) | ||||
|                         .header("Authorization", "Bearer " + token)) | ||||
|                         .andExpect(status().isOk()) | ||||
|  | @ -657,7 +657,7 @@ public class UserAPITest extends AdministrationAPIIntegrationTester { | |||
|     public void testOwnerModifyPossibleForExamAdmin() throws Exception { | ||||
|         final String examAdminToken1 = getExamAdmin1(); | ||||
|         final UserInfo examAdmin = this.jsonMapper.readValue( | ||||
|                 this.mockMvc.perform(get(this.endpoint + RestAPI.ENDPOINT_USER_ACCOUNT + "/me") | ||||
|                 this.mockMvc.perform(get(this.endpoint + SEBServerRestEndpoints.ENDPOINT_USER_ACCOUNT + "/me") | ||||
|                         .header("Authorization", "Bearer " + examAdminToken1)) | ||||
|                         .andExpect(status().isOk()) | ||||
|                         .andReturn().getResponse().getContentAsString(), | ||||
|  | @ -667,7 +667,7 @@ public class UserAPITest extends AdministrationAPIIntegrationTester { | |||
|         final UserMod modifiedUser = new UserMod(examAdmin, null, null); | ||||
|         final String modifiedUserJson = this.jsonMapper.writeValueAsString(modifiedUser); | ||||
| 
 | ||||
|         this.mockMvc.perform(put(this.endpoint + RestAPI.ENDPOINT_USER_ACCOUNT + "/" + modifiedUser.uuid) | ||||
|         this.mockMvc.perform(put(this.endpoint + SEBServerRestEndpoints.ENDPOINT_USER_ACCOUNT + "/" + modifiedUser.uuid) | ||||
|                 .header("Authorization", "Bearer " + examAdminToken1) | ||||
|                 .contentType(MediaType.APPLICATION_JSON_UTF8) | ||||
|                 .content(modifiedUserJson)) | ||||
|  | @ -679,7 +679,7 @@ public class UserAPITest extends AdministrationAPIIntegrationTester { | |||
|     public void institutionalAdminTryToCreateOrModifyUserForOtherInstituionNotPossible() throws Exception { | ||||
| 
 | ||||
|         final String token = getAdminInstitution1Access(); | ||||
|         this.mockMvc.perform(post(this.endpoint + RestAPI.ENDPOINT_USER_ACCOUNT) | ||||
|         this.mockMvc.perform(post(this.endpoint + SEBServerRestEndpoints.ENDPOINT_USER_ACCOUNT) | ||||
|                 .header("Authorization", "Bearer " + token) | ||||
|                 .contentType(MediaType.APPLICATION_FORM_URLENCODED) | ||||
|                 .param(Domain.USER.ATTR_INSTITUTION_ID, "2") | ||||
|  | @ -695,7 +695,7 @@ public class UserAPITest extends AdministrationAPIIntegrationTester { | |||
|                 new HashSet<>(Arrays.asList(UserRole.EXAM_ADMIN.name()))); | ||||
|         final UserMod newUser = new UserMod(userInfo, "12345678", "12345678"); | ||||
|         final String newUserJson = this.jsonMapper.writeValueAsString(newUser); | ||||
|         this.mockMvc.perform(put(this.endpoint + RestAPI.ENDPOINT_USER_ACCOUNT + "/NewTestUser") | ||||
|         this.mockMvc.perform(put(this.endpoint + SEBServerRestEndpoints.ENDPOINT_USER_ACCOUNT + "/NewTestUser") | ||||
|                 .header("Authorization", "Bearer " + token) | ||||
|                 .contentType(MediaType.APPLICATION_JSON_UTF8) | ||||
|                 .content(newUserJson)) | ||||
|  | @ -707,7 +707,7 @@ public class UserAPITest extends AdministrationAPIIntegrationTester { | |||
|     public void unauthorizedAdminTryToCreateUserNotPossible() throws Exception { | ||||
| 
 | ||||
|         final String token = getExamAdmin1(); | ||||
|         this.mockMvc.perform(post(this.endpoint + RestAPI.ENDPOINT_USER_ACCOUNT) | ||||
|         this.mockMvc.perform(post(this.endpoint + SEBServerRestEndpoints.ENDPOINT_USER_ACCOUNT) | ||||
|                 .header("Authorization", "Bearer " + token) | ||||
|                 .contentType(MediaType.APPLICATION_FORM_URLENCODED) | ||||
|                 .param(Domain.USER.ATTR_INSTITUTION_ID, "2") | ||||
|  | @ -723,7 +723,7 @@ public class UserAPITest extends AdministrationAPIIntegrationTester { | |||
|                 new HashSet<>(Arrays.asList(UserRole.EXAM_ADMIN.name()))); | ||||
|         final UserMod newUser = new UserMod(userInfo, "12345678", "12345678"); | ||||
|         final String newUserJson = this.jsonMapper.writeValueAsString(newUser); | ||||
|         this.mockMvc.perform(put(this.endpoint + RestAPI.ENDPOINT_USER_ACCOUNT + "/NewTestUser") | ||||
|         this.mockMvc.perform(put(this.endpoint + SEBServerRestEndpoints.ENDPOINT_USER_ACCOUNT + "/NewTestUser") | ||||
|                 .header("Authorization", "Bearer " + token) | ||||
|                 .contentType(MediaType.APPLICATION_JSON_UTF8) | ||||
|                 .content(newUserJson)) | ||||
|  | @ -739,7 +739,7 @@ public class UserAPITest extends AdministrationAPIIntegrationTester { | |||
|         // a SEB Server Admin now changes the password of ExamAdmin1 | ||||
|         final String sebAdminToken = getSebAdminAccess(); | ||||
|         final UserInfo examAdmin1 = this.jsonMapper.readValue( | ||||
|                 this.mockMvc.perform(get(this.endpoint + RestAPI.ENDPOINT_USER_ACCOUNT + "/user4") | ||||
|                 this.mockMvc.perform(get(this.endpoint + SEBServerRestEndpoints.ENDPOINT_USER_ACCOUNT + "/user4") | ||||
|                         .contentType(MediaType.APPLICATION_FORM_URLENCODED) | ||||
|                         .header("Authorization", "Bearer " + sebAdminToken)) | ||||
|                         .andExpect(status().isOk()) | ||||
|  | @ -753,7 +753,7 @@ public class UserAPITest extends AdministrationAPIIntegrationTester { | |||
|                 "newPassword"); | ||||
|         final String modifiedUserJson = this.jsonMapper.writeValueAsString(modifiedUser); | ||||
| 
 | ||||
|         this.mockMvc.perform(put(this.endpoint + RestAPI.ENDPOINT_USER_ACCOUNT + "/" + modifiedUser.uuid) | ||||
|         this.mockMvc.perform(put(this.endpoint + SEBServerRestEndpoints.ENDPOINT_USER_ACCOUNT + "/" + modifiedUser.uuid) | ||||
|                 .header("Authorization", "Bearer " + sebAdminToken) | ||||
|                 .contentType(MediaType.APPLICATION_JSON_UTF8) | ||||
|                 .content(modifiedUserJson)) | ||||
|  | @ -769,14 +769,14 @@ public class UserAPITest extends AdministrationAPIIntegrationTester { | |||
|         } | ||||
| 
 | ||||
|         // it should also not be possible to use an old token again after password change | ||||
|         this.mockMvc.perform(get(this.endpoint + RestAPI.ENDPOINT_USER_ACCOUNT + "/me") | ||||
|         this.mockMvc.perform(get(this.endpoint + SEBServerRestEndpoints.ENDPOINT_USER_ACCOUNT + "/me") | ||||
|                 .header("Authorization", "Bearer " + examAdminToken1)) | ||||
|                 .andExpect(status().isUnauthorized()) | ||||
|                 .andReturn().getResponse().getContentAsString(); | ||||
| 
 | ||||
|         // but it should be possible to get a new access token and request again | ||||
|         final String examAdminToken2 = obtainAccessToken("examAdmin1", "newPassword"); | ||||
|         this.mockMvc.perform(get(this.endpoint + RestAPI.ENDPOINT_USER_ACCOUNT + "/me") | ||||
|         this.mockMvc.perform(get(this.endpoint + SEBServerRestEndpoints.ENDPOINT_USER_ACCOUNT + "/me") | ||||
|                 .header("Authorization", "Bearer " + examAdminToken2)) | ||||
|                 .andExpect(status().isOk()) | ||||
|                 .andReturn().getResponse().getContentAsString(); | ||||
|  | @ -786,7 +786,7 @@ public class UserAPITest extends AdministrationAPIIntegrationTester { | |||
|     public void modifyUserPasswordInvalidPasswords() throws Exception { | ||||
|         final String sebAdminToken = getSebAdminAccess(); | ||||
|         final UserInfo examAdmin1 = this.jsonMapper.readValue( | ||||
|                 this.mockMvc.perform(get(this.endpoint + RestAPI.ENDPOINT_USER_ACCOUNT + "/user4") | ||||
|                 this.mockMvc.perform(get(this.endpoint + SEBServerRestEndpoints.ENDPOINT_USER_ACCOUNT + "/user4") | ||||
|                         .contentType(MediaType.APPLICATION_FORM_URLENCODED) | ||||
|                         .header("Authorization", "Bearer " + sebAdminToken)) | ||||
|                         .andExpect(status().isOk()) | ||||
|  | @ -802,7 +802,7 @@ public class UserAPITest extends AdministrationAPIIntegrationTester { | |||
|         String modifiedUserJson = this.jsonMapper.writeValueAsString(modifiedUser); | ||||
| 
 | ||||
|         List<APIMessage> messages = this.jsonMapper.readValue( | ||||
|                 this.mockMvc.perform(put(this.endpoint + RestAPI.ENDPOINT_USER_ACCOUNT + "/" + modifiedUser.uuid) | ||||
|                 this.mockMvc.perform(put(this.endpoint + SEBServerRestEndpoints.ENDPOINT_USER_ACCOUNT + "/" + modifiedUser.uuid) | ||||
|                         .header("Authorization", "Bearer " + sebAdminToken) | ||||
|                         .contentType(MediaType.APPLICATION_JSON_UTF8) | ||||
|                         .content(modifiedUserJson)) | ||||
|  | @ -824,7 +824,7 @@ public class UserAPITest extends AdministrationAPIIntegrationTester { | |||
|         modifiedUserJson = this.jsonMapper.writeValueAsString(modifiedUser); | ||||
| 
 | ||||
|         messages = this.jsonMapper.readValue( | ||||
|                 this.mockMvc.perform(put(this.endpoint + RestAPI.ENDPOINT_USER_ACCOUNT + "/" + modifiedUser.uuid) | ||||
|                 this.mockMvc.perform(put(this.endpoint + SEBServerRestEndpoints.ENDPOINT_USER_ACCOUNT + "/" + modifiedUser.uuid) | ||||
|                         .header("Authorization", "Bearer " + sebAdminToken) | ||||
|                         .contentType(MediaType.APPLICATION_JSON_UTF8) | ||||
|                         .content(modifiedUserJson)) | ||||
|  | @ -843,7 +843,7 @@ public class UserAPITest extends AdministrationAPIIntegrationTester { | |||
|         final String timeNow = DateTime.now(DateTimeZone.UTC).toString(Constants.DATE_TIME_PATTERN_UTC_NO_MILLIS); | ||||
|         // only a SEB Administrator or an Institutional administrator should be able to deactivate a user-account | ||||
|         final String examAdminToken = getExamAdmin1(); | ||||
|         this.mockMvc.perform(post(this.endpoint + RestAPI.ENDPOINT_USER_ACCOUNT + "/user4/inactive") | ||||
|         this.mockMvc.perform(post(this.endpoint + SEBServerRestEndpoints.ENDPOINT_USER_ACCOUNT + "/user4/inactive") | ||||
|                 .contentType(MediaType.APPLICATION_FORM_URLENCODED) | ||||
|                 .header("Authorization", "Bearer " + examAdminToken)) | ||||
|                 .andExpect(status().isForbidden()); | ||||
|  | @ -851,7 +851,7 @@ public class UserAPITest extends AdministrationAPIIntegrationTester { | |||
|         // With SEB Administrator it should work | ||||
|         final String sebAdminToken = getSebAdminAccess(); | ||||
|         final EntityProcessingReport report = this.jsonMapper.readValue( | ||||
|                 this.mockMvc.perform(post(this.endpoint + RestAPI.ENDPOINT_USER_ACCOUNT + "/user4/inactive") | ||||
|                 this.mockMvc.perform(post(this.endpoint + SEBServerRestEndpoints.ENDPOINT_USER_ACCOUNT + "/user4/inactive") | ||||
|                         .contentType(MediaType.APPLICATION_FORM_URLENCODED) | ||||
|                         .header("Authorization", "Bearer " + sebAdminToken)) | ||||
|                         .andExpect(status().isOk()) | ||||
|  | @ -868,7 +868,7 @@ public class UserAPITest extends AdministrationAPIIntegrationTester { | |||
|         // get user and check activity | ||||
|         final EntityKey key = report.source.iterator().next(); | ||||
|         final UserInfo user = this.jsonMapper.readValue( | ||||
|                 this.mockMvc.perform(get(this.endpoint + RestAPI.ENDPOINT_USER_ACCOUNT + "/" + key.modelId) | ||||
|                 this.mockMvc.perform(get(this.endpoint + SEBServerRestEndpoints.ENDPOINT_USER_ACCOUNT + "/" + key.modelId) | ||||
|                         .contentType(MediaType.APPLICATION_FORM_URLENCODED) | ||||
|                         .header("Authorization", "Bearer " + sebAdminToken)) | ||||
|                         .andExpect(status().isOk()) | ||||
|  | @ -883,7 +883,7 @@ public class UserAPITest extends AdministrationAPIIntegrationTester { | |||
|         final Page<UserActivityLog> userLogs = this.jsonMapper.readValue( | ||||
|                 this.mockMvc | ||||
|                         .perform( | ||||
|                                 get(this.endpoint + RestAPI.ENDPOINT_USER_ACTIVITY_LOG + "/?user=user1&from=" + timeNow) | ||||
|                                 get(this.endpoint + SEBServerRestEndpoints.ENDPOINT_USER_ACTIVITY_LOG + "/?user=user1&from=" + timeNow) | ||||
|                                         .contentType(MediaType.APPLICATION_FORM_URLENCODED) | ||||
|                                         .header("Authorization", "Bearer " + sebAdminToken)) | ||||
|                         .andExpect(status().isOk()) | ||||
|  | @ -903,7 +903,7 @@ public class UserAPITest extends AdministrationAPIIntegrationTester { | |||
|         final String timeNow = DateTime.now(DateTimeZone.UTC).toString(Constants.DATE_TIME_PATTERN_UTC_NO_MILLIS); | ||||
|         // only a SEB Administrator or an Institutional administrator should be able to deactivate a user-account | ||||
|         final String examAdminToken = getExamAdmin1(); | ||||
|         this.mockMvc.perform(post(this.endpoint + RestAPI.ENDPOINT_USER_ACCOUNT + "/user6/active") | ||||
|         this.mockMvc.perform(post(this.endpoint + SEBServerRestEndpoints.ENDPOINT_USER_ACCOUNT + "/user6/active") | ||||
|                 .contentType(MediaType.APPLICATION_FORM_URLENCODED) | ||||
|                 .header("Authorization", "Bearer " + examAdminToken)) | ||||
|                 .andExpect(status().isForbidden()); | ||||
|  | @ -911,7 +911,7 @@ public class UserAPITest extends AdministrationAPIIntegrationTester { | |||
|         // With SEB Administrator it should work | ||||
|         final String sebAdminToken = getSebAdminAccess(); | ||||
|         final EntityProcessingReport report = this.jsonMapper.readValue( | ||||
|                 this.mockMvc.perform(post(this.endpoint + RestAPI.ENDPOINT_USER_ACCOUNT + "/user6/active") | ||||
|                 this.mockMvc.perform(post(this.endpoint + SEBServerRestEndpoints.ENDPOINT_USER_ACCOUNT + "/user6/active") | ||||
|                         .contentType(MediaType.APPLICATION_FORM_URLENCODED) | ||||
|                         .header("Authorization", "Bearer " + sebAdminToken)) | ||||
|                         .andExpect(status().isOk()) | ||||
|  | @ -928,7 +928,7 @@ public class UserAPITest extends AdministrationAPIIntegrationTester { | |||
|         // get user and check activity | ||||
|         final EntityKey key = report.source.iterator().next(); | ||||
|         final UserInfo user = this.jsonMapper.readValue( | ||||
|                 this.mockMvc.perform(get(this.endpoint + RestAPI.ENDPOINT_USER_ACCOUNT + "/" + key.modelId) | ||||
|                 this.mockMvc.perform(get(this.endpoint + SEBServerRestEndpoints.ENDPOINT_USER_ACCOUNT + "/" + key.modelId) | ||||
|                         .contentType(MediaType.APPLICATION_FORM_URLENCODED) | ||||
|                         .header("Authorization", "Bearer " + sebAdminToken)) | ||||
|                         .andExpect(status().isOk()) | ||||
|  | @ -942,7 +942,7 @@ public class UserAPITest extends AdministrationAPIIntegrationTester { | |||
|         // check also user activity log | ||||
|         final Page<UserActivityLog> userLogs = this.jsonMapper.readValue( | ||||
|                 this.mockMvc | ||||
|                         .perform(get(this.endpoint + RestAPI.ENDPOINT_USER_ACTIVITY_LOG + "?user=user1&from=" + timeNow) | ||||
|                         .perform(get(this.endpoint + SEBServerRestEndpoints.ENDPOINT_USER_ACTIVITY_LOG + "?user=user1&from=" + timeNow) | ||||
|                                 .header("Authorization", "Bearer " + sebAdminToken)) | ||||
|                         .andExpect(status().isOk()) | ||||
|                         .andReturn().getResponse().getContentAsString(), | ||||
|  | @ -962,7 +962,7 @@ public class UserAPITest extends AdministrationAPIIntegrationTester { | |||
| 
 | ||||
|         // all active for the own institution | ||||
|         Page<UserInfo> usersPage = this.jsonMapper.readValue( | ||||
|                 this.mockMvc.perform(get(this.endpoint + RestAPI.ENDPOINT_USER_ACCOUNT + "/all/active") | ||||
|                 this.mockMvc.perform(get(this.endpoint + SEBServerRestEndpoints.ENDPOINT_USER_ACCOUNT + "/all/active") | ||||
|                         .contentType(MediaType.APPLICATION_FORM_URLENCODED) | ||||
|                         .header("Authorization", "Bearer " + sebAdminToken)) | ||||
|                         .andExpect(status().isOk()) | ||||
|  | @ -976,7 +976,7 @@ public class UserAPITest extends AdministrationAPIIntegrationTester { | |||
| 
 | ||||
|         // all inactive of the own institution | ||||
|         usersPage = this.jsonMapper.readValue( | ||||
|                 this.mockMvc.perform(get(this.endpoint + RestAPI.ENDPOINT_USER_ACCOUNT + "/all/inactive") | ||||
|                 this.mockMvc.perform(get(this.endpoint + SEBServerRestEndpoints.ENDPOINT_USER_ACCOUNT + "/all/inactive") | ||||
|                         .contentType(MediaType.APPLICATION_FORM_URLENCODED) | ||||
|                         .header("Authorization", "Bearer " + sebAdminToken)) | ||||
|                         .andExpect(status().isOk()) | ||||
|  | @ -990,7 +990,7 @@ public class UserAPITest extends AdministrationAPIIntegrationTester { | |||
| 
 | ||||
|         // all active of institution 2 | ||||
|         usersPage = this.jsonMapper.readValue( | ||||
|                 this.mockMvc.perform(get(this.endpoint + RestAPI.ENDPOINT_USER_ACCOUNT + "/all/active?institutionId=2") | ||||
|                 this.mockMvc.perform(get(this.endpoint + SEBServerRestEndpoints.ENDPOINT_USER_ACCOUNT + "/all/active?institutionId=2") | ||||
|                         .contentType(MediaType.APPLICATION_FORM_URLENCODED) | ||||
|                         .header("Authorization", "Bearer " + sebAdminToken)) | ||||
|                         .andExpect(status().isOk()) | ||||
|  | @ -1005,7 +1005,7 @@ public class UserAPITest extends AdministrationAPIIntegrationTester { | |||
|         // all inactive of institution 2 | ||||
|         usersPage = this.jsonMapper.readValue( | ||||
|                 this.mockMvc | ||||
|                         .perform(get(this.endpoint + RestAPI.ENDPOINT_USER_ACCOUNT + "/all/inactive?institutionId=2") | ||||
|                         .perform(get(this.endpoint + SEBServerRestEndpoints.ENDPOINT_USER_ACCOUNT + "/all/inactive?institutionId=2") | ||||
|                                 .contentType(MediaType.APPLICATION_FORM_URLENCODED) | ||||
|                                 .header("Authorization", "Bearer " + sebAdminToken)) | ||||
|                         .andExpect(status().isOk()) | ||||
|  | @ -1026,7 +1026,7 @@ public class UserAPITest extends AdministrationAPIIntegrationTester { | |||
|         Collection<UserInfo> users = this.jsonMapper.readValue( | ||||
|                 this.mockMvc | ||||
|                         .perform( | ||||
|                                 get(this.endpoint + RestAPI.ENDPOINT_USER_ACCOUNT + "/list?ids=user1,user2,user6,user7") | ||||
|                                 get(this.endpoint + SEBServerRestEndpoints.ENDPOINT_USER_ACCOUNT + "/list?ids=user1,user2,user6,user7") | ||||
|                                         .contentType(MediaType.APPLICATION_FORM_URLENCODED) | ||||
|                                         .header("Authorization", "Bearer " + sebAdminToken)) | ||||
|                         .andExpect(status().isOk()) | ||||
|  | @ -1043,7 +1043,7 @@ public class UserAPITest extends AdministrationAPIIntegrationTester { | |||
|         users = this.jsonMapper.readValue( | ||||
|                 this.mockMvc | ||||
|                         .perform( | ||||
|                                 get(this.endpoint + RestAPI.ENDPOINT_USER_ACCOUNT + "/list?ids=user1,user2,user6,user7") | ||||
|                                 get(this.endpoint + SEBServerRestEndpoints.ENDPOINT_USER_ACCOUNT + "/list?ids=user1,user2,user6,user7") | ||||
|                                         .contentType(MediaType.APPLICATION_FORM_URLENCODED) | ||||
|                                         .header("Authorization", "Bearer " + instAdminToken)) | ||||
|                         .andExpect(status().isOk()) | ||||
|  | @ -1061,14 +1061,14 @@ public class UserAPITest extends AdministrationAPIIntegrationTester { | |||
|         final String sebAdminToken = getSebAdminAccess(); | ||||
| 
 | ||||
|         // for SEB Admin | ||||
|         Collection<EntityKeyAndName> names = this.jsonMapper.readValue( | ||||
|         Collection<EntityName> names = this.jsonMapper.readValue( | ||||
|                 this.mockMvc | ||||
|                         .perform(get(this.endpoint + RestAPI.ENDPOINT_USER_ACCOUNT + "/names") | ||||
|                         .perform(get(this.endpoint + SEBServerRestEndpoints.ENDPOINT_USER_ACCOUNT + "/names") | ||||
|                                 .contentType(MediaType.APPLICATION_FORM_URLENCODED) | ||||
|                                 .header("Authorization", "Bearer " + sebAdminToken)) | ||||
|                         .andExpect(status().isOk()) | ||||
|                         .andReturn().getResponse().getContentAsString(), | ||||
|                 new TypeReference<Collection<EntityKeyAndName>>() { | ||||
|                 new TypeReference<Collection<EntityName>>() { | ||||
|                 }); | ||||
| 
 | ||||
|         assertNotNull(names); | ||||
|  | @ -1081,12 +1081,12 @@ public class UserAPITest extends AdministrationAPIIntegrationTester { | |||
|         final String instAdminToken = getAdminInstitution2Access(); | ||||
|         names = this.jsonMapper.readValue( | ||||
|                 this.mockMvc | ||||
|                         .perform(get(this.endpoint + RestAPI.ENDPOINT_USER_ACCOUNT + "/names") | ||||
|                         .perform(get(this.endpoint + SEBServerRestEndpoints.ENDPOINT_USER_ACCOUNT + "/names") | ||||
|                                 .contentType(MediaType.APPLICATION_FORM_URLENCODED) | ||||
|                                 .header("Authorization", "Bearer " + instAdminToken)) | ||||
|                         .andExpect(status().isOk()) | ||||
|                         .andReturn().getResponse().getContentAsString(), | ||||
|                 new TypeReference<Collection<EntityKeyAndName>>() { | ||||
|                 new TypeReference<Collection<EntityName>>() { | ||||
|                 }); | ||||
| 
 | ||||
|         assertNotNull(names); | ||||
|  | @ -1099,12 +1099,12 @@ public class UserAPITest extends AdministrationAPIIntegrationTester { | |||
|         // for an institutional admin 2 only active | ||||
|         names = this.jsonMapper.readValue( | ||||
|                 this.mockMvc | ||||
|                         .perform(get(this.endpoint + RestAPI.ENDPOINT_USER_ACCOUNT + "/names?active=true") | ||||
|                         .perform(get(this.endpoint + SEBServerRestEndpoints.ENDPOINT_USER_ACCOUNT + "/names?active=true") | ||||
|                                 .contentType(MediaType.APPLICATION_FORM_URLENCODED) | ||||
|                                 .header("Authorization", "Bearer " + instAdminToken)) | ||||
|                         .andExpect(status().isOk()) | ||||
|                         .andReturn().getResponse().getContentAsString(), | ||||
|                 new TypeReference<Collection<EntityKeyAndName>>() { | ||||
|                 new TypeReference<Collection<EntityName>>() { | ||||
|                 }); | ||||
| 
 | ||||
|         assertNotNull(names); | ||||
|  |  | |||
|  | @ -19,9 +19,9 @@ import org.springframework.test.context.jdbc.Sql; | |||
| import com.fasterxml.jackson.core.type.TypeReference; | ||||
| 
 | ||||
| import ch.ethz.seb.sebserver.gbl.Constants; | ||||
| import ch.ethz.seb.sebserver.gbl.api.SEBServerRestEndpoints; | ||||
| import ch.ethz.seb.sebserver.gbl.model.Page; | ||||
| import ch.ethz.seb.sebserver.gbl.model.user.UserActivityLog; | ||||
| import ch.ethz.seb.sebserver.webservice.weblayer.api.RestAPI; | ||||
| 
 | ||||
| @Sql(scripts = { "classpath:schema-test.sql", "classpath:data-test.sql" }) | ||||
| public class UserActivityLogAPITest extends AdministrationAPIIntegrationTester { | ||||
|  | @ -30,7 +30,7 @@ public class UserActivityLogAPITest extends AdministrationAPIIntegrationTester { | |||
|     public void getAllAsSEBAdmin() throws Exception { | ||||
|         final String token = getSebAdminAccess(); | ||||
|         final Page<UserActivityLog> logs = this.jsonMapper.readValue( | ||||
|                 this.mockMvc.perform(get(this.endpoint + RestAPI.ENDPOINT_USER_ACTIVITY_LOG) | ||||
|                 this.mockMvc.perform(get(this.endpoint + SEBServerRestEndpoints.ENDPOINT_USER_ACTIVITY_LOG) | ||||
|                         .header("Authorization", "Bearer " + token)) | ||||
|                         .andExpect(status().isOk()) | ||||
|                         .andReturn().getResponse().getContentAsString(), | ||||
|  | @ -47,7 +47,7 @@ public class UserActivityLogAPITest extends AdministrationAPIIntegrationTester { | |||
|         // for a user in another institution, the institution has to be defined | ||||
|         Page<UserActivityLog> logs = this.jsonMapper.readValue( | ||||
|                 this.mockMvc | ||||
|                         .perform(get(this.endpoint + RestAPI.ENDPOINT_USER_ACTIVITY_LOG + "?user=user4&institutionId=2") | ||||
|                         .perform(get(this.endpoint + SEBServerRestEndpoints.ENDPOINT_USER_ACTIVITY_LOG + "?user=user4&institutionId=2") | ||||
|                                 .header("Authorization", "Bearer " + token)) | ||||
|                         .andExpect(status().isOk()) | ||||
|                         .andReturn().getResponse().getContentAsString(), | ||||
|  | @ -59,7 +59,7 @@ public class UserActivityLogAPITest extends AdministrationAPIIntegrationTester { | |||
| 
 | ||||
|         // for a user in the same institution no institution is needed | ||||
|         logs = this.jsonMapper.readValue( | ||||
|                 this.mockMvc.perform(get(this.endpoint + RestAPI.ENDPOINT_USER_ACTIVITY_LOG + "?user=user2") | ||||
|                 this.mockMvc.perform(get(this.endpoint + SEBServerRestEndpoints.ENDPOINT_USER_ACTIVITY_LOG + "?user=user2") | ||||
|                         .header("Authorization", "Bearer " + token)) | ||||
|                         .andExpect(status().isOk()) | ||||
|                         .andReturn().getResponse().getContentAsString(), | ||||
|  | @ -82,7 +82,7 @@ public class UserActivityLogAPITest extends AdministrationAPIIntegrationTester { | |||
|         final String token = getSebAdminAccess(); | ||||
|         Page<UserActivityLog> logs = this.jsonMapper.readValue( | ||||
|                 this.mockMvc.perform( | ||||
|                         get(this.endpoint + RestAPI.ENDPOINT_USER_ACTIVITY_LOG + "?institutionId=2&from=" + sec2) | ||||
|                         get(this.endpoint + SEBServerRestEndpoints.ENDPOINT_USER_ACTIVITY_LOG + "?institutionId=2&from=" + sec2) | ||||
|                                 .header("Authorization", "Bearer " + token)) | ||||
|                         .andExpect(status().isOk()) | ||||
|                         .andReturn().getResponse().getContentAsString(), | ||||
|  | @ -94,7 +94,7 @@ public class UserActivityLogAPITest extends AdministrationAPIIntegrationTester { | |||
| 
 | ||||
|         logs = this.jsonMapper.readValue( | ||||
|                 this.mockMvc | ||||
|                         .perform(get(this.endpoint + RestAPI.ENDPOINT_USER_ACTIVITY_LOG + "?institutionId=2&from=" | ||||
|                         .perform(get(this.endpoint + SEBServerRestEndpoints.ENDPOINT_USER_ACTIVITY_LOG + "?institutionId=2&from=" | ||||
|                                 + sec2 + "&to=" + sec4) | ||||
|                                         .header("Authorization", "Bearer " + token)) | ||||
|                         .andExpect(status().isOk()) | ||||
|  | @ -108,7 +108,7 @@ public class UserActivityLogAPITest extends AdministrationAPIIntegrationTester { | |||
|         logs = this.jsonMapper.readValue( | ||||
|                 this.mockMvc | ||||
|                         .perform( | ||||
|                                 get(this.endpoint + RestAPI.ENDPOINT_USER_ACTIVITY_LOG + "?institutionId=2&from=" + sec2 | ||||
|                                 get(this.endpoint + SEBServerRestEndpoints.ENDPOINT_USER_ACTIVITY_LOG + "?institutionId=2&from=" + sec2 | ||||
|                                         + "&to=" + sec5) | ||||
|                                                 .header("Authorization", "Bearer " + token)) | ||||
|                         .andExpect(status().isOk()) | ||||
|  | @ -122,7 +122,7 @@ public class UserActivityLogAPITest extends AdministrationAPIIntegrationTester { | |||
|         logs = this.jsonMapper.readValue( | ||||
|                 this.mockMvc | ||||
|                         .perform( | ||||
|                                 get(this.endpoint + RestAPI.ENDPOINT_USER_ACTIVITY_LOG + "?institutionId=2&from=" + sec2 | ||||
|                                 get(this.endpoint + SEBServerRestEndpoints.ENDPOINT_USER_ACTIVITY_LOG + "?institutionId=2&from=" + sec2 | ||||
|                                         + "&to=" + sec6) | ||||
|                                                 .header("Authorization", "Bearer " + token)) | ||||
|                         .andExpect(status().isOk()) | ||||
|  | @ -138,7 +138,7 @@ public class UserActivityLogAPITest extends AdministrationAPIIntegrationTester { | |||
|     public void getAllAsSEBAdminForActivityType() throws Exception { | ||||
|         final String token = getSebAdminAccess(); | ||||
|         Page<UserActivityLog> logs = this.jsonMapper.readValue( | ||||
|                 this.mockMvc.perform(get(this.endpoint + RestAPI.ENDPOINT_USER_ACTIVITY_LOG + "?activity_types=CREATE") | ||||
|                 this.mockMvc.perform(get(this.endpoint + SEBServerRestEndpoints.ENDPOINT_USER_ACTIVITY_LOG + "?activity_types=CREATE") | ||||
|                         .header("Authorization", "Bearer " + token)) | ||||
|                         .andExpect(status().isOk()) | ||||
|                         .andReturn().getResponse().getContentAsString(), | ||||
|  | @ -151,7 +151,7 @@ public class UserActivityLogAPITest extends AdministrationAPIIntegrationTester { | |||
|         logs = this.jsonMapper.readValue( | ||||
|                 this.mockMvc | ||||
|                         .perform( | ||||
|                                 get(this.endpoint + RestAPI.ENDPOINT_USER_ACTIVITY_LOG | ||||
|                                 get(this.endpoint + SEBServerRestEndpoints.ENDPOINT_USER_ACTIVITY_LOG | ||||
|                                         + "?activity_types=CREATE,MODIFY") | ||||
|                                                 .header("Authorization", "Bearer " + token)) | ||||
|                         .andExpect(status().isOk()) | ||||
|  | @ -166,7 +166,7 @@ public class UserActivityLogAPITest extends AdministrationAPIIntegrationTester { | |||
|         logs = this.jsonMapper.readValue( | ||||
|                 this.mockMvc | ||||
|                         .perform( | ||||
|                                 get(this.endpoint + RestAPI.ENDPOINT_USER_ACTIVITY_LOG | ||||
|                                 get(this.endpoint + SEBServerRestEndpoints.ENDPOINT_USER_ACTIVITY_LOG | ||||
|                                         + "?institutionId=2&activity_types=CREATE,MODIFY") | ||||
|                                                 .header("Authorization", "Bearer " + token)) | ||||
|                         .andExpect(status().isOk()) | ||||
|  | @ -183,7 +183,7 @@ public class UserActivityLogAPITest extends AdministrationAPIIntegrationTester { | |||
|         final String token = getSebAdminAccess(); | ||||
|         Page<UserActivityLog> logs = this.jsonMapper.readValue( | ||||
|                 this.mockMvc | ||||
|                         .perform(get(this.endpoint + RestAPI.ENDPOINT_USER_ACTIVITY_LOG + "?entity_types=INSTITUTION") | ||||
|                         .perform(get(this.endpoint + SEBServerRestEndpoints.ENDPOINT_USER_ACTIVITY_LOG + "?entity_types=INSTITUTION") | ||||
|                                 .header("Authorization", "Bearer " + token)) | ||||
|                         .andExpect(status().isOk()) | ||||
|                         .andReturn().getResponse().getContentAsString(), | ||||
|  | @ -196,7 +196,7 @@ public class UserActivityLogAPITest extends AdministrationAPIIntegrationTester { | |||
|         logs = this.jsonMapper.readValue( | ||||
|                 this.mockMvc | ||||
|                         .perform( | ||||
|                                 get(this.endpoint + RestAPI.ENDPOINT_USER_ACTIVITY_LOG | ||||
|                                 get(this.endpoint + SEBServerRestEndpoints.ENDPOINT_USER_ACTIVITY_LOG | ||||
|                                         + "?entity_types=INSTITUTION,EXAM") | ||||
|                                                 .header("Authorization", "Bearer " + token)) | ||||
|                         .andExpect(status().isOk()) | ||||
|  | @ -210,7 +210,7 @@ public class UserActivityLogAPITest extends AdministrationAPIIntegrationTester { | |||
|         logs = this.jsonMapper.readValue( | ||||
|                 this.mockMvc | ||||
|                         .perform( | ||||
|                                 get(this.endpoint + RestAPI.ENDPOINT_USER_ACTIVITY_LOG | ||||
|                                 get(this.endpoint + SEBServerRestEndpoints.ENDPOINT_USER_ACTIVITY_LOG | ||||
|                                         + "?entity_types=INSTITUTION,EXAM&institutionId=2") | ||||
|                                                 .header("Authorization", "Bearer " + token)) | ||||
|                         .andExpect(status().isOk()) | ||||
|  | @ -226,7 +226,7 @@ public class UserActivityLogAPITest extends AdministrationAPIIntegrationTester { | |||
|     public void getAllAsInstitutionalAdmin() throws Exception { | ||||
|         final String token = getAdminInstitution1Access(); | ||||
|         final Page<UserActivityLog> logs = this.jsonMapper.readValue( | ||||
|                 this.mockMvc.perform(get(this.endpoint + RestAPI.ENDPOINT_USER_ACTIVITY_LOG) | ||||
|                 this.mockMvc.perform(get(this.endpoint + SEBServerRestEndpoints.ENDPOINT_USER_ACTIVITY_LOG) | ||||
|                         .header("Authorization", "Bearer " + token)) | ||||
|                         .andExpect(status().isOk()) | ||||
|                         .andReturn().getResponse().getContentAsString(), | ||||
|  | @ -242,18 +242,18 @@ public class UserActivityLogAPITest extends AdministrationAPIIntegrationTester { | |||
|         String token = getExamAdmin1(); | ||||
| 
 | ||||
|         // no privilege at all | ||||
|         this.mockMvc.perform(get(this.endpoint + RestAPI.ENDPOINT_USER_ACTIVITY_LOG) | ||||
|         this.mockMvc.perform(get(this.endpoint + SEBServerRestEndpoints.ENDPOINT_USER_ACTIVITY_LOG) | ||||
|                 .header("Authorization", "Bearer " + token)) | ||||
|                 .andExpect(status().isForbidden()); | ||||
|         // no privilege at all | ||||
|         this.mockMvc.perform(get(this.endpoint + RestAPI.ENDPOINT_USER_ACTIVITY_LOG + "?user=user4") | ||||
|         this.mockMvc.perform(get(this.endpoint + SEBServerRestEndpoints.ENDPOINT_USER_ACTIVITY_LOG + "?user=user4") | ||||
|                 .header("Authorization", "Bearer " + token)) | ||||
|                 .andExpect(status().isForbidden()); | ||||
| 
 | ||||
|         // no privilege to query logs of users of other institution | ||||
|         token = getAdminInstitution1Access(); | ||||
|         final Page<UserActivityLog> logs = this.jsonMapper.readValue( | ||||
|                 this.mockMvc.perform(get(this.endpoint + RestAPI.ENDPOINT_USER_ACTIVITY_LOG + "?user=user4") | ||||
|                 this.mockMvc.perform(get(this.endpoint + SEBServerRestEndpoints.ENDPOINT_USER_ACTIVITY_LOG + "?user=user4") | ||||
|                         .header("Authorization", "Bearer " + token)) | ||||
|                         .andExpect(status().isOk()) | ||||
|                         .andReturn().getResponse().getContentAsString(), | ||||
|  |  | |||
|  | @ -31,7 +31,7 @@ import org.springframework.util.MultiValueMap; | |||
| import org.springframework.web.context.WebApplicationContext; | ||||
| 
 | ||||
| import ch.ethz.seb.sebserver.SEBServer; | ||||
| import ch.ethz.seb.sebserver.gbl.JSONMapper; | ||||
| import ch.ethz.seb.sebserver.gbl.api.JSONMapper; | ||||
| 
 | ||||
| @RunWith(SpringRunner.class) | ||||
| @SpringBootTest( | ||||
|  |  | |||
		Loading…
	
	Add table
		
		Reference in a new issue
	
	 anhefti
						anhefti