#SEBSERV-27 user account list (with filter)
							
								
								
									
										2
									
								
								pom.xml
									
										
									
									
									
								
							
							
						
						|  | @ -58,7 +58,7 @@ | |||
|     <profile> | ||||
|       <id>Java 11</id> | ||||
|       <activation> | ||||
|         <activeByDefault>true</activeByDefault> | ||||
|         <activeByDefault>false</activeByDefault> | ||||
|       </activation> | ||||
|       <properties> | ||||
|         <java.version>11</java.version> | ||||
|  |  | |||
|  | @ -6,7 +6,7 @@ | |||
|  * file, You can obtain one at http://mozilla.org/MPL/2.0/. | ||||
|  */ | ||||
| 
 | ||||
| package ch.ethz.seb.sebserver.gui.service.page.form; | ||||
| package ch.ethz.seb.sebserver.gui.service.form; | ||||
| 
 | ||||
| import java.util.ArrayList; | ||||
| import java.util.HashSet; | ||||
|  | @ -6,7 +6,7 @@ | |||
|  * file, You can obtain one at http://mozilla.org/MPL/2.0/. | ||||
|  */ | ||||
| 
 | ||||
| package ch.ethz.seb.sebserver.gui.service.page.form; | ||||
| package ch.ethz.seb.sebserver.gui.service.form; | ||||
| 
 | ||||
| import java.util.List; | ||||
| import java.util.function.Consumer; | ||||
|  | @ -6,7 +6,7 @@ | |||
|  * file, You can obtain one at http://mozilla.org/MPL/2.0/. | ||||
|  */ | ||||
| 
 | ||||
| package ch.ethz.seb.sebserver.gui.service.page.form; | ||||
| package ch.ethz.seb.sebserver.gui.service.form; | ||||
| 
 | ||||
| import java.util.function.Consumer; | ||||
| import java.util.function.Function; | ||||
|  | @ -15,14 +15,14 @@ import org.slf4j.Logger; | |||
| import org.slf4j.LoggerFactory; | ||||
| 
 | ||||
| import ch.ethz.seb.sebserver.gbl.util.Result; | ||||
| import ch.ethz.seb.sebserver.gui.service.form.Form.FormFieldAccessor; | ||||
| import ch.ethz.seb.sebserver.gui.service.i18n.I18nSupport; | ||||
| import ch.ethz.seb.sebserver.gui.service.i18n.LocTextKey; | ||||
| import ch.ethz.seb.sebserver.gui.service.page.FieldValidationError; | ||||
| import ch.ethz.seb.sebserver.gui.service.page.PageContext; | ||||
| import ch.ethz.seb.sebserver.gui.service.page.action.Action; | ||||
| import ch.ethz.seb.sebserver.gui.service.page.action.ActionDefinition; | ||||
| import ch.ethz.seb.sebserver.gui.service.page.event.ActionEvent; | ||||
| import ch.ethz.seb.sebserver.gui.service.page.form.Form.FormFieldAccessor; | ||||
| import ch.ethz.seb.sebserver.gui.service.page.validation.FieldValidationError; | ||||
| import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestCall; | ||||
| import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestCallError; | ||||
| 
 | ||||
|  | @ -6,7 +6,7 @@ | |||
|  * file, You can obtain one at http://mozilla.org/MPL/2.0/. | ||||
|  */ | ||||
| 
 | ||||
| package ch.ethz.seb.sebserver.gui.service.page.form; | ||||
| package ch.ethz.seb.sebserver.gui.service.form; | ||||
| 
 | ||||
| import org.springframework.context.annotation.Lazy; | ||||
| import org.springframework.stereotype.Component; | ||||
|  | @ -15,6 +15,9 @@ import org.joda.time.DateTime; | |||
| 
 | ||||
| public interface I18nSupport { | ||||
| 
 | ||||
|     /** Get all supported languages as a collection of Locale | ||||
|      * | ||||
|      * @return all supported languages as a collection of Locale */ | ||||
|     Collection<Locale> supportedLanguages(); | ||||
| 
 | ||||
|     /** Get the current Locale either form a user if this is called from a logged in user context or the | ||||
|  |  | |||
|  | @ -53,11 +53,11 @@ public class I18nSupportImpl implements I18nSupport { | |||
|                 .withZoneUTC(); | ||||
|     } | ||||
| 
 | ||||
|     private static final Collection<Locale> SUPPORTED = Arrays.asList(Locale.ENGLISH, Locale.GERMANY); | ||||
|     private static final Collection<Locale> SUPPORTED_LANGUAGES = Arrays.asList(Locale.ENGLISH, Locale.GERMAN); | ||||
| 
 | ||||
|     @Override | ||||
|     public Collection<Locale> supportedLanguages() { | ||||
|         return SUPPORTED; | ||||
|         return SUPPORTED_LANGUAGES; | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|  |  | |||
|  | @ -6,7 +6,7 @@ | |||
|  * file, You can obtain one at http://mozilla.org/MPL/2.0/. | ||||
|  */ | ||||
| 
 | ||||
| package ch.ethz.seb.sebserver.gui.service.page.validation; | ||||
| package ch.ethz.seb.sebserver.gui.service.page; | ||||
| 
 | ||||
| import ch.ethz.seb.sebserver.gbl.api.APIMessage; | ||||
| 
 | ||||
|  | @ -8,88 +8,48 @@ | |||
| 
 | ||||
| package ch.ethz.seb.sebserver.gui.service.page.action; | ||||
| 
 | ||||
| import ch.ethz.seb.sebserver.gui.service.widget.WidgetFactory.IconButtonType; | ||||
| import ch.ethz.seb.sebserver.gui.service.widget.WidgetFactory.ImageIcon; | ||||
| 
 | ||||
| public enum ActionDefinition { | ||||
| 
 | ||||
|     INSTITUTION_NEW( | ||||
|             "sebserver.institution.action.new", | ||||
|             IconButtonType.NEW_ACTION), | ||||
|             ImageIcon.NEW), | ||||
| 
 | ||||
|     INSTITUTION_VIEW( | ||||
|             "sebserver.institution.action.view", | ||||
|             IconButtonType.VIEW_ACTION), | ||||
|             ImageIcon.SHOW), | ||||
| 
 | ||||
|     INSTITUTION_MODIFY( | ||||
|             "sebserver.institution.action.modify", | ||||
|             IconButtonType.MODIFY_ACTION), | ||||
|             ImageIcon.EDIT), | ||||
| 
 | ||||
|     INSTITUTION_CANCEL_MODIFY( | ||||
|             "sebserver.overall.action.modify.cancel", | ||||
|             IconButtonType.CANCEL_ACTION), | ||||
|             ImageIcon.CANCEL), | ||||
| 
 | ||||
|     INSTITUTION_SAVE( | ||||
|             "sebserver.institution.action.save", | ||||
|             IconButtonType.SAVE_ACTION), | ||||
|             ImageIcon.SAVE), | ||||
| 
 | ||||
|     INSTITUTION_ACTIVATE( | ||||
|             "sebserver.institution.action.activate", | ||||
|             IconButtonType.ACTIVATE_ACTION), | ||||
|             ImageIcon.INACTIVE), | ||||
| 
 | ||||
|     INSTITUTION_DEACTIVATE( | ||||
|             "sebserver.institution.action.deactivate", | ||||
|             IconButtonType.DEACTIVATE_ACTION), | ||||
|             ImageIcon.ACTIVE), | ||||
| 
 | ||||
|     INSTITUTION_DELETE( | ||||
|             "sebserver.institution.action.modify", | ||||
|             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), | ||||
|             ImageIcon.DELETE), | ||||
| 
 | ||||
|     ; | ||||
| 
 | ||||
|     public final String name; | ||||
|     public final IconButtonType icon; | ||||
|     public final ImageIcon icon; | ||||
| 
 | ||||
|     private ActionDefinition(final String name, final IconButtonType icon) { | ||||
|     private ActionDefinition(final String name, final ImageIcon icon) { | ||||
|         this.name = name; | ||||
|         this.icon = icon; | ||||
|     } | ||||
|  |  | |||
|  | @ -8,8 +8,6 @@ | |||
| 
 | ||||
| package ch.ethz.seb.sebserver.gui.service.page.action; | ||||
| 
 | ||||
| import static ch.ethz.seb.sebserver.gui.service.page.activity.ActivitySelection.Activity.INSTITUTION_NODE; | ||||
| 
 | ||||
| import java.util.Collection; | ||||
| import java.util.function.Function; | ||||
| 
 | ||||
|  | @ -22,10 +20,12 @@ 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.PageMessageException; | ||||
| import ch.ethz.seb.sebserver.gui.service.page.activity.ActivitySelection; | ||||
| import ch.ethz.seb.sebserver.gui.service.page.activity.ActivitySelection.Activity; | ||||
| import ch.ethz.seb.sebserver.gui.service.page.event.ActivitySelectionEvent; | ||||
| import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.institution.ActivateInstitution; | ||||
| import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.institution.DeactivateInstitution; | ||||
| 
 | ||||
| /** Defines the action execution functions for all Institution action. */ | ||||
| public final class InstitutionActions { | ||||
| 
 | ||||
|     public static Function<Institution, Institution> postSaveAdapter(final PageContext pageContext) { | ||||
|  | @ -92,7 +92,7 @@ public final class InstitutionActions { | |||
| 
 | ||||
|     private static ActivitySelection goToInstitution(final PageContext pageContext, final String modelId, | ||||
|             final boolean edit) { | ||||
|         final ActivitySelection activitySelection = INSTITUTION_NODE | ||||
|         final ActivitySelection activitySelection = Activity.INSTITUTION_FORM | ||||
|                 .createSelection() | ||||
|                 .withEntity(new EntityKey(modelId, EntityType.INSTITUTION)) | ||||
|                 .withAttribute(AttributeKeys.READ_ONLY, String.valueOf(!edit)) | ||||
|  |  | |||
|  | @ -90,30 +90,31 @@ public class ActivitiesPane implements TemplateComposer { | |||
|         navigationGridData.horizontalIndent = 10; | ||||
|         navigation.setLayoutData(navigationGridData); | ||||
| 
 | ||||
|         // institution | ||||
|         // Institution | ||||
|         if (userInfo.hasRole(UserRole.SEB_SERVER_ADMIN)) { | ||||
|             // institutions (list) as root | ||||
|             final TreeItem institutions = this.widgetFactory.treeItemLocalized( | ||||
|                     navigation, | ||||
|                     Activity.INSTITUTION_ROOT.title); | ||||
|             injectActivitySelection(institutions, Activity.INSTITUTION_ROOT.createSelection()); | ||||
|                     Activity.INSTITUTION_LIST.title); | ||||
|             injectActivitySelection(institutions, Activity.INSTITUTION_LIST.createSelection()); | ||||
| 
 | ||||
|         } else { | ||||
|             // institution node as none root | ||||
|             final TreeItem institutions = this.widgetFactory.treeItemLocalized( | ||||
|                     navigation, | ||||
|                     Activity.INSTITUTION_NODE.title); | ||||
|                     Activity.INSTITUTION_FORM.title); | ||||
|             injectActivitySelection( | ||||
|                     institutions, | ||||
|                     Activity.INSTITUTION_NODE.createSelection() | ||||
|                     Activity.INSTITUTION_FORM.createSelection() | ||||
|                             .withEntity(new EntityKey(userInfo.institutionId, EntityType.INSTITUTION)) | ||||
|                             .withAttribute(AttributeKeys.READ_ONLY, "true")); | ||||
|         } | ||||
| 
 | ||||
| //        final TreeItem user = this.widgetFactory.treeItemLocalized( | ||||
| //                navigation, | ||||
| //                "org.sebserver.activities.user"); | ||||
| //        ActivitySelection.set(user, Activity.USERS.createSelection()); | ||||
|         // User Account | ||||
|         final TreeItem userAccounts = this.widgetFactory.treeItemLocalized( | ||||
|                 navigation, | ||||
|                 Activity.USER_ACCOUNT_LIST.title); | ||||
|         injectActivitySelection(userAccounts, Activity.USER_ACCOUNT_LIST.createSelection()); | ||||
| // | ||||
| //        final TreeItem configs = this.widgetFactory.treeItemLocalized( | ||||
| //                navigation, | ||||
|  |  | |||
|  | @ -22,6 +22,7 @@ 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.content.InstitutionForm; | ||||
| import ch.ethz.seb.sebserver.gui.service.page.content.InstitutionList; | ||||
| import ch.ethz.seb.sebserver.gui.service.page.content.UserAccountList; | ||||
| import ch.ethz.seb.sebserver.gui.service.page.impl.TODOTemplate; | ||||
| 
 | ||||
| public class ActivitySelection { | ||||
|  | @ -35,15 +36,25 @@ public class ActivitySelection { | |||
| 
 | ||||
|     public enum Activity { | ||||
|         NONE(TODOTemplate.class, TODOTemplate.class), | ||||
|         INSTITUTION_ROOT( | ||||
|         INSTITUTION_LIST( | ||||
|                 InstitutionList.class, | ||||
|                 ActionPane.class, | ||||
|                 new LocTextKey("sebserver.activities.inst")), | ||||
|         INSTITUTION_NODE( | ||||
|                 new LocTextKey("sebserver.activities.institution")), | ||||
|         INSTITUTION_FORM( | ||||
|                 InstitutionForm.class, | ||||
|                 ActionPane.class, | ||||
|                 new LocTextKey("sebserver.activities.inst")), | ||||
| // | ||||
|                 new LocTextKey("sebserver.activities.institution")), | ||||
| 
 | ||||
|         USER_ACCOUNT_LIST( | ||||
|                 UserAccountList.class, | ||||
|                 ActionPane.class, | ||||
|                 new LocTextKey("sebserver.activities.useraccount")), | ||||
| 
 | ||||
|         USER_ACCOUNT_FORM( | ||||
|                 UserAccountList.class, | ||||
|                 ActionPane.class, | ||||
|                 new LocTextKey("sebserver.activities.useraccount")), | ||||
| 
 | ||||
| //        USERS(UserAccountsForm.class, ActionPane.class), | ||||
| // | ||||
| //        EXAMS(ExamsListPage.class, ActionPane.class), | ||||
|  |  | |||
|  | @ -13,11 +13,7 @@ import java.util.UUID; | |||
| import java.util.function.Supplier; | ||||
| 
 | ||||
| import org.apache.commons.lang3.BooleanUtils; | ||||
| import org.eclipse.swt.SWT; | ||||
| import org.eclipse.swt.layout.GridData; | ||||
| import org.eclipse.swt.layout.GridLayout; | ||||
| import org.eclipse.swt.widgets.Composite; | ||||
| import org.eclipse.swt.widgets.Label; | ||||
| import org.slf4j.Logger; | ||||
| import org.slf4j.LoggerFactory; | ||||
| import org.springframework.context.annotation.Lazy; | ||||
|  | @ -31,15 +27,14 @@ import ch.ethz.seb.sebserver.gbl.model.Entity; | |||
| import ch.ethz.seb.sebserver.gbl.model.EntityKey; | ||||
| import ch.ethz.seb.sebserver.gbl.model.institution.Institution; | ||||
| import ch.ethz.seb.sebserver.gbl.profile.GuiProfile; | ||||
| import ch.ethz.seb.sebserver.gui.service.form.FormHandle; | ||||
| import ch.ethz.seb.sebserver.gui.service.form.PageFormService; | ||||
| import ch.ethz.seb.sebserver.gui.service.i18n.LocTextKey; | ||||
| import ch.ethz.seb.sebserver.gui.service.page.PageContext; | ||||
| import ch.ethz.seb.sebserver.gui.service.page.PageContext.AttributeKeys; | ||||
| import ch.ethz.seb.sebserver.gui.service.page.TemplateComposer; | ||||
| import ch.ethz.seb.sebserver.gui.service.page.action.ActionDefinition; | ||||
| import ch.ethz.seb.sebserver.gui.service.page.action.InstitutionActions; | ||||
| import ch.ethz.seb.sebserver.gui.service.page.event.ActionEventListener; | ||||
| import ch.ethz.seb.sebserver.gui.service.page.form.FormHandle; | ||||
| import ch.ethz.seb.sebserver.gui.service.page.form.PageFormService; | ||||
| import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestService; | ||||
| import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.institution.GetInstitution; | ||||
| import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.institution.GetInstitutionDependency; | ||||
|  | @ -107,29 +102,17 @@ public class InstitutionForm implements TemplateComposer { | |||
|             return; | ||||
|         } | ||||
| 
 | ||||
|         // page grid | ||||
|         final Composite content = new Composite(formContext.getParent(), SWT.NONE); | ||||
|         final GridLayout contentLayout = new GridLayout(); | ||||
|         contentLayout.marginLeft = 10; | ||||
|         content.setLayout(contentLayout); | ||||
|         content.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); | ||||
| 
 | ||||
|         // title (interactive9 | ||||
|         final Label pageTitle = widgetFactory.labelLocalizedTitle( | ||||
|                 content, new LocTextKey( | ||||
|                         "sebserver.institution.form.title", | ||||
|                         institution.name)); | ||||
| 
 | ||||
|         pageTitle.setLayoutData(new GridData(SWT.TOP, SWT.LEFT, true, false)); | ||||
|         ActionEventListener.injectListener( | ||||
|                 pageTitle, | ||||
|         // the default page layout with interactive title | ||||
|         final Composite content = widgetFactory.defaultPageLayout( | ||||
|                 formContext.getParent(), | ||||
|                 new LocTextKey("sebserver.institution.form.title", institution.name), | ||||
|                 ActionDefinition.INSTITUTION_SAVE, | ||||
|                 event -> { | ||||
|                 title -> event -> { | ||||
|                     final Entity entity = (Entity) event.source; | ||||
|                     widgetFactory.injectI18n(pageTitle, new LocTextKey( | ||||
|                     widgetFactory.injectI18n(title, new LocTextKey( | ||||
|                             "sebserver.institution.form.title", | ||||
|                             entity.getName())); | ||||
|                     content.layout(); | ||||
|                     title.getParent().layout(); | ||||
|                 }); | ||||
| 
 | ||||
|         // The Institution form | ||||
|  |  | |||
|  | @ -0,0 +1,42 @@ | |||
| /* | ||||
|  * 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.content; | ||||
| 
 | ||||
| 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.page.PageContext; | ||||
| import ch.ethz.seb.sebserver.gui.service.page.TemplateComposer; | ||||
| import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestService; | ||||
| import ch.ethz.seb.sebserver.gui.service.widget.WidgetFactory; | ||||
| 
 | ||||
| @Lazy | ||||
| @Component | ||||
| @GuiProfile | ||||
| public class UserAccountForm implements TemplateComposer { | ||||
| 
 | ||||
|     private final WidgetFactory widgetFactory; | ||||
|     private final RestService restService; | ||||
| 
 | ||||
|     protected UserAccountForm( | ||||
|             final WidgetFactory widgetFactory, | ||||
|             final RestService restService) { | ||||
| 
 | ||||
|         this.widgetFactory = widgetFactory; | ||||
|         this.restService = restService; | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void compose(final PageContext pageContext) { | ||||
|         // TODO Auto-generated method stub | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
| } | ||||
|  | @ -0,0 +1,98 @@ | |||
| /* | ||||
|  * 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.content; | ||||
| 
 | ||||
| import org.eclipse.swt.widgets.Composite; | ||||
| import org.springframework.beans.factory.annotation.Value; | ||||
| import org.springframework.context.annotation.Lazy; | ||||
| import org.springframework.stereotype.Component; | ||||
| 
 | ||||
| import ch.ethz.seb.sebserver.gbl.model.Domain; | ||||
| import ch.ethz.seb.sebserver.gbl.model.user.UserInfo; | ||||
| 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.TemplateComposer; | ||||
| import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestService; | ||||
| import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.useraccount.GetUserAccounts; | ||||
| import ch.ethz.seb.sebserver.gui.service.table.ColumnDefinition; | ||||
| import ch.ethz.seb.sebserver.gui.service.table.ColumnDefinition.TableFilterAttribute; | ||||
| import ch.ethz.seb.sebserver.gui.service.table.EntityTable; | ||||
| import ch.ethz.seb.sebserver.gui.service.table.TableFilter.CriteriaType; | ||||
| import ch.ethz.seb.sebserver.gui.service.widget.WidgetFactory; | ||||
| 
 | ||||
| @Lazy | ||||
| @Component | ||||
| @GuiProfile | ||||
| public class UserAccountList implements TemplateComposer { | ||||
| 
 | ||||
|     private final WidgetFactory widgetFactory; | ||||
|     private final RestService restService; | ||||
|     private final int pageSize; | ||||
| 
 | ||||
|     protected UserAccountList( | ||||
|             final WidgetFactory widgetFactory, | ||||
|             final RestService restService, | ||||
|             @Value("${sebserver.gui.list.page.size}") final Integer pageSize) { | ||||
| 
 | ||||
|         this.widgetFactory = widgetFactory; | ||||
|         this.restService = restService; | ||||
|         this.pageSize = (pageSize != null) ? pageSize : 20; | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void compose(final PageContext pageContext) { | ||||
|         final Composite content = this.widgetFactory.defaultPageLayout( | ||||
|                 pageContext.getParent(), | ||||
|                 new LocTextKey("sebserver.useraccount.list.title")); | ||||
| 
 | ||||
|         // table | ||||
|         final EntityTable<UserInfo> table = | ||||
|                 this.widgetFactory.entityTableBuilder(this.restService.getRestCall(GetUserAccounts.class)) | ||||
|                         .withPaging(this.pageSize) | ||||
|                         .withColumn(new ColumnDefinition<>( | ||||
|                                 Domain.USER.ATTR_NAME, | ||||
|                                 new LocTextKey("sebserver.useraccount.list.column.name"), | ||||
|                                 entity -> entity.name, | ||||
|                                 new TableFilterAttribute(CriteriaType.TEXT, Domain.USER.ATTR_NAME), | ||||
|                                 true)) | ||||
|                         .withColumn(new ColumnDefinition<>( | ||||
|                                 Domain.USER.ATTR_USERNAME, | ||||
|                                 new LocTextKey("sebserver.useraccount.list.column.username"), | ||||
|                                 entity -> entity.username, | ||||
|                                 new TableFilterAttribute(CriteriaType.TEXT, Domain.USER.ATTR_USERNAME), | ||||
|                                 true)) | ||||
|                         .withColumn(new ColumnDefinition<>( | ||||
|                                 Domain.USER.ATTR_EMAIL, | ||||
|                                 new LocTextKey("sebserver.useraccount.list.column.email"), | ||||
|                                 entity -> entity.email, | ||||
|                                 new TableFilterAttribute(CriteriaType.TEXT, Domain.USER.ATTR_EMAIL), | ||||
|                                 true)) | ||||
|                         .withColumn(new ColumnDefinition<>( | ||||
|                                 Domain.USER.ATTR_LOCALE, | ||||
|                                 new LocTextKey("sebserver.useraccount.list.column.language"), | ||||
|                                 this::getLocaleDisplayText, | ||||
|                                 new TableFilterAttribute(CriteriaType.COUNTRY_SELECTION, Domain.USER.ATTR_LOCALE), | ||||
|                                 true, true)) | ||||
|                         .withColumn(new ColumnDefinition<>( | ||||
|                                 Domain.USER.ATTR_ACTIVE, | ||||
|                                 new LocTextKey("sebserver.useraccount.list.column.active"), | ||||
|                                 entity -> entity.active, | ||||
|                                 true)) | ||||
|                         .compose(content); | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
|     private String getLocaleDisplayText(final UserInfo userInfo) { | ||||
|         return (userInfo.locale != null) | ||||
|                 ? userInfo.locale.getDisplayLanguage(this.widgetFactory.getI18nSupport().getCurrentLocale()) | ||||
|                 : null; | ||||
|     } | ||||
| 
 | ||||
| } | ||||
|  | @ -31,7 +31,7 @@ public class DefaultLoginPage implements PageDefinition { | |||
|     public PageContext applyPageContext(final PageContext pageContext) { | ||||
|         return pageContext.withAttribute( | ||||
|                 AttributeKeys.PAGE_TEMPLATE_COMPOSER_NAME, | ||||
|                 SEBLogin.class.getName()); | ||||
|                 LoginPage.class.getName()); | ||||
|     } | ||||
| 
 | ||||
| } | ||||
|  |  | |||
|  | @ -31,7 +31,7 @@ public class DefaultMainPage implements PageDefinition { | |||
|     public PageContext applyPageContext(final PageContext pageContext) { | ||||
|         return pageContext.withAttribute( | ||||
|                 AttributeKeys.PAGE_TEMPLATE_COMPOSER_NAME, | ||||
|                 SEBMainPage.class.getName()); | ||||
|                 MainPage.class.getName()); | ||||
|     } | ||||
| 
 | ||||
| } | ||||
|  |  | |||
|  | @ -34,15 +34,15 @@ import ch.ethz.seb.sebserver.gui.service.widget.WidgetFactory; | |||
| @Lazy | ||||
| @Component | ||||
| @GuiProfile | ||||
| public class SEBLogin implements TemplateComposer { | ||||
| public class LoginPage implements TemplateComposer { | ||||
| 
 | ||||
|     private static final Logger log = LoggerFactory.getLogger(SEBLogin.class); | ||||
|     private static final Logger log = LoggerFactory.getLogger(LoginPage.class); | ||||
| 
 | ||||
|     private final AuthorizationContextHolder authorizationContextHolder; | ||||
|     private final WidgetFactory widgetFactory; | ||||
|     private final I18nSupport i18nSupport; | ||||
| 
 | ||||
|     public SEBLogin( | ||||
|     public LoginPage( | ||||
|             final AuthorizationContextHolder authorizationContextHolder, | ||||
|             final WidgetFactory widgetFactory, | ||||
|             final I18nSupport i18nSupport) { | ||||
|  | @ -56,16 +56,6 @@ public class SEBLogin implements TemplateComposer { | |||
|     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("sebserver.logout"), | ||||
| //                    this.i18nSupport.getText("sebserver.logout.success.message"), | ||||
| //                    SWT.ICON_INFORMATION); | ||||
| //            logoutSuccess.open(null); | ||||
| //            pageContext = pageContext.removeAttribute(AttributeKeys.LGOUT_SUCCESS); | ||||
| //        } | ||||
| 
 | ||||
|         final Composite loginGroup = new Composite(parent, SWT.NONE); | ||||
|         final GridLayout rowLayout = new GridLayout(); | ||||
|         rowLayout.marginWidth = 20; | ||||
|  | @ -29,20 +29,20 @@ 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.page.event.PageEventListener; | ||||
| import ch.ethz.seb.sebserver.gui.service.widget.WidgetFactory; | ||||
| import ch.ethz.seb.sebserver.gui.service.widget.WidgetFactory.IconButtonType; | ||||
| import ch.ethz.seb.sebserver.gui.service.widget.WidgetFactory.ImageIcon; | ||||
| 
 | ||||
| @Lazy | ||||
| @Component | ||||
| @GuiProfile | ||||
| public class SEBMainPage implements TemplateComposer { | ||||
| public class MainPage implements TemplateComposer { | ||||
| 
 | ||||
|     static final Logger log = LoggerFactory.getLogger(SEBMainPage.class); | ||||
|     static final Logger log = LoggerFactory.getLogger(MainPage.class); | ||||
| 
 | ||||
|     public static final String ATTR_MAIN_PAGE_STATE = "MAIN_PAGE_STATE"; | ||||
| 
 | ||||
|     private static final int ACTIVITY_PANE_WEIGHT = 20; | ||||
|     private static final int ACTIVITY_PANE_WEIGHT = 15; | ||||
|     private static final int CONTENT_PANE_WEIGHT = 65; | ||||
|     private static final int ACTION_PANE_WEIGHT = 15; | ||||
|     private static final int ACTION_PANE_WEIGHT = 20; | ||||
|     private static final int[] DEFAULT_SASH_WEIGHTS = new int[] { | ||||
|             ACTIVITY_PANE_WEIGHT, | ||||
|             CONTENT_PANE_WEIGHT, | ||||
|  | @ -52,7 +52,7 @@ public class SEBMainPage implements TemplateComposer { | |||
| 
 | ||||
|     private final WidgetFactory widgetFactory; | ||||
| 
 | ||||
|     public SEBMainPage(final WidgetFactory widgetFactory) { | ||||
|     public MainPage(final WidgetFactory widgetFactory) { | ||||
|         this.widgetFactory = widgetFactory; | ||||
|     } | ||||
| 
 | ||||
|  | @ -84,7 +84,7 @@ public class SEBMainPage implements TemplateComposer { | |||
|         content.setLayout(contentOuterlayout); | ||||
| 
 | ||||
|         final Label toggleView = this.widgetFactory.imageButton( | ||||
|                 IconButtonType.MAXIMIZE, | ||||
|                 ImageIcon.MAXIMIZE, | ||||
|                 content, | ||||
|                 new LocTextKey("sebserver.mainpage.maximize.tooltip"), | ||||
|                 event -> { | ||||
|  | @ -92,7 +92,7 @@ public class SEBMainPage implements TemplateComposer { | |||
|                     if ((Boolean) ib.getData("fullScreen")) { | ||||
|                         mainSash.setWeights(DEFAULT_SASH_WEIGHTS); | ||||
|                         ib.setData("fullScreen", false); | ||||
|                         ib.setImage(WidgetFactory.IconButtonType.MAXIMIZE.getImage(ib.getDisplay())); | ||||
|                         ib.setImage(WidgetFactory.ImageIcon.MAXIMIZE.getImage(ib.getDisplay())); | ||||
|                         this.widgetFactory.injectI18n( | ||||
|                                 ib, | ||||
|                                 null, | ||||
|  | @ -100,7 +100,7 @@ public class SEBMainPage implements TemplateComposer { | |||
|                     } else { | ||||
|                         mainSash.setWeights(OPENED_SASH_WEIGHTS); | ||||
|                         ib.setData("fullScreen", true); | ||||
|                         ib.setImage(WidgetFactory.IconButtonType.MINIMIZE.getImage(ib.getDisplay())); | ||||
|                         ib.setImage(WidgetFactory.ImageIcon.MINIMIZE.getImage(ib.getDisplay())); | ||||
|                         this.widgetFactory.injectI18n( | ||||
|                                 ib, | ||||
|                                 null, | ||||
|  | @ -28,15 +28,15 @@ public final class MainPageState { | |||
|                     .getUISession() | ||||
|                     .getHttpSession(); | ||||
| 
 | ||||
|             MainPageState mainPageState = (MainPageState) httpSession.getAttribute(SEBMainPage.ATTR_MAIN_PAGE_STATE); | ||||
|             MainPageState mainPageState = (MainPageState) httpSession.getAttribute(MainPage.ATTR_MAIN_PAGE_STATE); | ||||
|             if (mainPageState == null) { | ||||
|                 mainPageState = new MainPageState(); | ||||
|                 httpSession.setAttribute(SEBMainPage.ATTR_MAIN_PAGE_STATE, mainPageState); | ||||
|                 httpSession.setAttribute(MainPage.ATTR_MAIN_PAGE_STATE, mainPageState); | ||||
|             } | ||||
| 
 | ||||
|             return mainPageState; | ||||
|         } catch (final Exception e) { | ||||
|             SEBMainPage.log.error("Unexpected error while trying to get MainPageState from user-session"); | ||||
|             MainPage.log.error("Unexpected error while trying to get MainPageState from user-session"); | ||||
|         } | ||||
| 
 | ||||
|         return null; | ||||
|  |  | |||
|  | @ -1,17 +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.api; | ||||
| 
 | ||||
| import org.springframework.util.MultiValueMap; | ||||
| 
 | ||||
| public interface FilterAttributeSupplier { | ||||
| 
 | ||||
|     MultiValueMap<String, String> getAttributes(); | ||||
| 
 | ||||
| } | ||||
|  | @ -171,6 +171,13 @@ public abstract class RestCall<T> { | |||
|             return this; | ||||
|         } | ||||
| 
 | ||||
|         public RestCallBuilder withQueryParams(final MultiValueMap<String, String> params) { | ||||
|             if (params != null) { | ||||
|                 this.queryParams.putAll(params); | ||||
|             } | ||||
|             return this; | ||||
|         } | ||||
| 
 | ||||
|         public RestCallBuilder withPaging(final int pageNumber, final int pageSize) { | ||||
|             this.queryParams.put(Page.ATTR_PAGE_NUMBER, Arrays.asList(String.valueOf(pageNumber))); | ||||
|             this.queryParams.put(Page.ATTR_PAGE_SIZE, Arrays.asList(String.valueOf(pageSize))); | ||||
|  | @ -184,13 +191,6 @@ public abstract class RestCall<T> { | |||
|             return this; | ||||
|         } | ||||
| 
 | ||||
|         public RestCallBuilder withFilterAttributes(final FilterAttributeSupplier filterAttributes) { | ||||
|             if (filterAttributes != null) { | ||||
|                 this.queryParams.putAll(filterAttributes.getAttributes()); | ||||
|             } | ||||
|             return this; | ||||
|         } | ||||
| 
 | ||||
|         public RestCallBuilder withFormBinding(final FormBinding formBinding) { | ||||
|             return withURIVariable(API.PARAM_MODEL_ID, formBinding.entityKey().modelId) | ||||
|                     .withBody(formBinding.getFormAsJson()); | ||||
|  |  | |||
|  | @ -9,9 +9,13 @@ | |||
| package ch.ethz.seb.sebserver.gui.service.remote.webservice.api; | ||||
| 
 | ||||
| import java.util.Collection; | ||||
| import java.util.Collections; | ||||
| import java.util.List; | ||||
| import java.util.Map; | ||||
| import java.util.stream.Collectors; | ||||
| 
 | ||||
| import org.slf4j.Logger; | ||||
| import org.slf4j.LoggerFactory; | ||||
| import org.springframework.context.annotation.Lazy; | ||||
| import org.springframework.stereotype.Service; | ||||
| import org.springframework.web.client.RestTemplate; | ||||
|  | @ -19,6 +23,8 @@ import org.springframework.web.util.UriComponentsBuilder; | |||
| 
 | ||||
| import ch.ethz.seb.sebserver.gbl.api.JSONMapper; | ||||
| import ch.ethz.seb.sebserver.gbl.profile.GuiProfile; | ||||
| import ch.ethz.seb.sebserver.gbl.util.Tuple; | ||||
| 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.remote.webservice.auth.WebserviceURIService; | ||||
| 
 | ||||
|  | @ -27,6 +33,8 @@ import ch.ethz.seb.sebserver.gui.service.remote.webservice.auth.WebserviceURISer | |||
| @GuiProfile | ||||
| public class RestService { | ||||
| 
 | ||||
|     private static final Logger log = LoggerFactory.getLogger(RestService.class); | ||||
| 
 | ||||
|     private final AuthorizationContextHolder authorizationContextHolder; | ||||
|     private final WebserviceURIService webserviceURIBuilderSupplier; | ||||
|     private final Map<String, RestCall<?>> calls; | ||||
|  | @ -47,22 +55,22 @@ public class RestService { | |||
|                         call -> call.init(this, jsonMapper))); | ||||
|     } | ||||
| 
 | ||||
|     public RestTemplate getWebserviceAPIRestTemplate() { | ||||
|     public final RestTemplate getWebserviceAPIRestTemplate() { | ||||
|         return this.authorizationContextHolder | ||||
|                 .getAuthorizationContext() | ||||
|                 .getRestTemplate(); | ||||
|     } | ||||
| 
 | ||||
|     public UriComponentsBuilder getWebserviceURIBuilder() { | ||||
|     public final UriComponentsBuilder getWebserviceURIBuilder() { | ||||
|         return this.webserviceURIBuilderSupplier.getURIBuilder(); | ||||
|     } | ||||
| 
 | ||||
|     @SuppressWarnings("unchecked") | ||||
|     public <T> RestCall<T> getRestCall(final Class<? extends RestCall<T>> type) { | ||||
|     public final <T> RestCall<T> getRestCall(final Class<? extends RestCall<T>> type) { | ||||
|         return (RestCall<T>) this.calls.get(type.getName()); | ||||
|     } | ||||
| 
 | ||||
|     public <T> RestCall<T>.RestCallBuilder getBuilder(final Class<? extends RestCall<T>> type) { | ||||
|     public final <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) { | ||||
|  | @ -72,4 +80,19 @@ public class RestService { | |||
|         return restCall.newBuilder(); | ||||
|     } | ||||
| 
 | ||||
|     public final List<Tuple<String>> getInstitutionSelection() { | ||||
|         try { | ||||
|             return getBuilder(GetInstitutionNames.class) | ||||
|                     .call() | ||||
|                     .map(list -> list | ||||
|                             .stream() | ||||
|                             .map(entityName -> new Tuple<>(entityName.modelId, entityName.name)) | ||||
|                             .collect(Collectors.toList())) | ||||
|                     .getOrThrow(); | ||||
|         } catch (final Exception e) { | ||||
|             log.error("Failed to get selection resource for Institution selection", e); | ||||
|             return Collections.emptyList(); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
| } | ||||
|  |  | |||
|  | @ -36,10 +36,4 @@ public class GetInstitutionNames extends RestCall<List<EntityName>> { | |||
|                 API.INSTITUTION_ENDPOINT + API.NAMES_PATH_SEGMENT); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public RestCall<List<EntityName>>.RestCallBuilder newBuilder() { | ||||
|         return super.newBuilder() | ||||
|                 .onlyActive(true); | ||||
|     } | ||||
| 
 | ||||
| } | ||||
|  |  | |||
|  | @ -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.remote.webservice.api.useraccount; | ||||
| 
 | ||||
| import org.springframework.context.annotation.Lazy; | ||||
| import org.springframework.http.HttpMethod; | ||||
| import org.springframework.http.MediaType; | ||||
| import org.springframework.stereotype.Component; | ||||
| 
 | ||||
| import com.fasterxml.jackson.core.type.TypeReference; | ||||
| 
 | ||||
| import ch.ethz.seb.sebserver.gbl.api.API; | ||||
| import ch.ethz.seb.sebserver.gbl.model.user.UserInfo; | ||||
| import ch.ethz.seb.sebserver.gbl.profile.GuiProfile; | ||||
| import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestCall; | ||||
| 
 | ||||
| @Lazy | ||||
| @Component | ||||
| @GuiProfile | ||||
| public class GetUserAccount extends RestCall<UserInfo> { | ||||
| 
 | ||||
|     protected GetUserAccount() { | ||||
|         super( | ||||
|                 new TypeReference<UserInfo>() { | ||||
|                 }, | ||||
|                 HttpMethod.GET, | ||||
|                 MediaType.APPLICATION_FORM_URLENCODED, | ||||
|                 API.USER_ACCOUNT_ENDPOINT + API.MODEL_ID_VAR_PATH_SEGMENT); | ||||
|     } | ||||
| 
 | ||||
| } | ||||
|  | @ -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.remote.webservice.api.useraccount; | ||||
| 
 | ||||
| import org.springframework.context.annotation.Lazy; | ||||
| import org.springframework.http.HttpMethod; | ||||
| import org.springframework.http.MediaType; | ||||
| import org.springframework.stereotype.Component; | ||||
| 
 | ||||
| import com.fasterxml.jackson.core.type.TypeReference; | ||||
| 
 | ||||
| import ch.ethz.seb.sebserver.gbl.api.API; | ||||
| import ch.ethz.seb.sebserver.gbl.model.Page; | ||||
| import ch.ethz.seb.sebserver.gbl.model.user.UserInfo; | ||||
| import ch.ethz.seb.sebserver.gbl.profile.GuiProfile; | ||||
| import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestCall; | ||||
| 
 | ||||
| @Lazy | ||||
| @Component | ||||
| @GuiProfile | ||||
| public class GetUserAccounts extends RestCall<Page<UserInfo>> { | ||||
| 
 | ||||
|     protected GetUserAccounts() { | ||||
|         super( | ||||
|                 new TypeReference<Page<UserInfo>>() { | ||||
|                 }, | ||||
|                 HttpMethod.GET, | ||||
|                 MediaType.APPLICATION_FORM_URLENCODED, | ||||
|                 API.USER_ACCOUNT_ENDPOINT); | ||||
|     } | ||||
| 
 | ||||
| } | ||||
|  | @ -8,10 +8,13 @@ | |||
| 
 | ||||
| package ch.ethz.seb.sebserver.gui.service.table; | ||||
| 
 | ||||
| import java.util.List; | ||||
| import java.util.function.Function; | ||||
| 
 | ||||
| import ch.ethz.seb.sebserver.gbl.model.Entity; | ||||
| import ch.ethz.seb.sebserver.gbl.util.Tuple; | ||||
| import ch.ethz.seb.sebserver.gui.service.i18n.LocTextKey; | ||||
| import ch.ethz.seb.sebserver.gui.service.table.TableFilter.CriteriaType; | ||||
| 
 | ||||
| public final class ColumnDefinition<ROW extends Entity> { | ||||
| 
 | ||||
|  | @ -20,8 +23,13 @@ public final class ColumnDefinition<ROW extends Entity> { | |||
|     final LocTextKey tooltip; | ||||
|     final int widthPercent; | ||||
|     final Function<ROW, Object> valueSupplier; | ||||
|     final ColumnFilterDefinition filter; | ||||
|     final boolean sortable; | ||||
|     final TableFilterAttribute filterAttribute; | ||||
|     final boolean localized; | ||||
| 
 | ||||
|     public ColumnDefinition(final String columnName, final LocTextKey displayName) { | ||||
|         this(columnName, displayName, null, -1); | ||||
|     } | ||||
| 
 | ||||
|     public ColumnDefinition( | ||||
|             final String columnName, | ||||
|  | @ -29,7 +37,7 @@ public final class ColumnDefinition<ROW extends Entity> { | |||
|             final LocTextKey tooltip, | ||||
|             final int widthPercent) { | ||||
| 
 | ||||
|         this(columnName, displayName, tooltip, widthPercent, null, null, false); | ||||
|         this(columnName, displayName, tooltip, widthPercent, null, null, false, false); | ||||
|     } | ||||
| 
 | ||||
|     public ColumnDefinition( | ||||
|  | @ -37,7 +45,7 @@ public final class ColumnDefinition<ROW extends Entity> { | |||
|             final LocTextKey displayName, | ||||
|             final int widthPercent) { | ||||
| 
 | ||||
|         this(columnName, displayName, null, widthPercent, null, null, false); | ||||
|         this(columnName, displayName, null, widthPercent, null, null, false, false); | ||||
|     } | ||||
| 
 | ||||
|     public ColumnDefinition( | ||||
|  | @ -46,7 +54,38 @@ public final class ColumnDefinition<ROW extends Entity> { | |||
|             final Function<ROW, Object> valueSupplier, | ||||
|             final boolean sortable) { | ||||
| 
 | ||||
|         this(columnName, displayName, null, -1, valueSupplier, null, sortable); | ||||
|         this(columnName, displayName, null, -1, valueSupplier, null, sortable, false); | ||||
|     } | ||||
| 
 | ||||
|     public ColumnDefinition( | ||||
|             final String columnName, | ||||
|             final LocTextKey displayName, | ||||
|             final Function<ROW, Object> valueSupplier, | ||||
|             final boolean sortable, | ||||
|             final boolean localized) { | ||||
| 
 | ||||
|         this(columnName, displayName, null, -1, valueSupplier, null, sortable, localized); | ||||
|     } | ||||
| 
 | ||||
|     public ColumnDefinition( | ||||
|             final String columnName, | ||||
|             final LocTextKey displayName, | ||||
|             final Function<ROW, Object> valueSupplier, | ||||
|             final TableFilterAttribute tableFilterAttribute, | ||||
|             final boolean sortable) { | ||||
| 
 | ||||
|         this(columnName, displayName, null, -1, valueSupplier, tableFilterAttribute, sortable, false); | ||||
|     } | ||||
| 
 | ||||
|     public ColumnDefinition( | ||||
|             final String columnName, | ||||
|             final LocTextKey displayName, | ||||
|             final Function<ROW, Object> valueSupplier, | ||||
|             final TableFilterAttribute tableFilterAttribute, | ||||
|             final boolean sortable, | ||||
|             final boolean localized) { | ||||
| 
 | ||||
|         this(columnName, displayName, null, -1, valueSupplier, tableFilterAttribute, sortable, localized); | ||||
|     } | ||||
| 
 | ||||
|     public ColumnDefinition( | ||||
|  | @ -55,15 +94,48 @@ public final class ColumnDefinition<ROW extends Entity> { | |||
|             final LocTextKey tooltip, | ||||
|             final int widthPercent, | ||||
|             final Function<ROW, Object> valueSupplier, | ||||
|             final ColumnFilterDefinition filter, | ||||
|             final boolean sortable) { | ||||
|             final TableFilterAttribute filterAttribute, | ||||
|             final boolean sortable, | ||||
|             final boolean localized) { | ||||
| 
 | ||||
|         this.columnName = columnName; | ||||
|         this.displayName = displayName; | ||||
|         this.tooltip = tooltip; | ||||
|         this.widthPercent = widthPercent; | ||||
|         this.valueSupplier = valueSupplier; | ||||
|         this.filter = filter; | ||||
|         this.filterAttribute = filterAttribute; | ||||
|         this.sortable = sortable; | ||||
|         this.localized = localized; | ||||
|     } | ||||
| 
 | ||||
|     public static final class TableFilterAttribute { | ||||
| 
 | ||||
|         public final CriteriaType type; | ||||
|         public final String columnName; | ||||
|         public final String initValue; | ||||
|         public final List<Tuple<String>> selectionResource; | ||||
| 
 | ||||
|         public TableFilterAttribute( | ||||
|                 final CriteriaType type, | ||||
|                 final String columnName) { | ||||
| 
 | ||||
|             this.type = type; | ||||
|             this.columnName = columnName; | ||||
|             this.initValue = ""; | ||||
|             this.selectionResource = null; | ||||
|         } | ||||
| 
 | ||||
|         public TableFilterAttribute( | ||||
|                 final CriteriaType type, | ||||
|                 final String columnName, | ||||
|                 final String initValue, | ||||
|                 final List<Tuple<String>> selectionResource) { | ||||
| 
 | ||||
|             this.type = type; | ||||
|             this.columnName = columnName; | ||||
|             this.initValue = initValue; | ||||
|             this.selectionResource = selectionResource; | ||||
|         } | ||||
| 
 | ||||
|     } | ||||
| } | ||||
|  |  | |||
|  | @ -1,13 +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.table; | ||||
| 
 | ||||
| public class ColumnFilterDefinition { | ||||
| 
 | ||||
| } | ||||
|  | @ -8,10 +8,14 @@ | |||
| 
 | ||||
| package ch.ethz.seb.sebserver.gui.service.table; | ||||
| 
 | ||||
| import static ch.ethz.seb.sebserver.gui.service.i18n.PolyglotPageService.POLYGLOT_WIDGET_FUNCTION_KEY; | ||||
| 
 | ||||
| import java.util.Arrays; | ||||
| import java.util.Collections; | ||||
| import java.util.List; | ||||
| import java.util.Objects; | ||||
| import java.util.Set; | ||||
| import java.util.function.Consumer; | ||||
| import java.util.stream.Collectors; | ||||
| 
 | ||||
| import org.eclipse.swt.SWT; | ||||
|  | @ -22,6 +26,7 @@ import org.eclipse.swt.widgets.Event; | |||
| import org.eclipse.swt.widgets.Table; | ||||
| import org.eclipse.swt.widgets.TableColumn; | ||||
| import org.eclipse.swt.widgets.TableItem; | ||||
| import org.eclipse.swt.widgets.Widget; | ||||
| import org.slf4j.Logger; | ||||
| import org.slf4j.LoggerFactory; | ||||
| 
 | ||||
|  | @ -31,21 +36,23 @@ import ch.ethz.seb.sebserver.gbl.model.Page; | |||
| import ch.ethz.seb.sebserver.gbl.util.Utils; | ||||
| import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestCall; | ||||
| import ch.ethz.seb.sebserver.gui.service.widget.WidgetFactory; | ||||
| import ch.ethz.seb.sebserver.gui.service.widget.WidgetFactory.ImageIcon; | ||||
| import ch.ethz.seb.sebserver.webservice.servicelayer.PaginationService.SortOrder; | ||||
| 
 | ||||
| public class EntityTable<ROW extends Entity> extends Composite { | ||||
| 
 | ||||
|     private static final Logger log = LoggerFactory.getLogger(EntityTable.class); | ||||
| 
 | ||||
|     private static final long serialVersionUID = -4931198225547108993L; | ||||
| 
 | ||||
|     public static final String TABLE_ROW_DATA = "TABLE_ROW_DATA"; | ||||
|     private static final Logger log = LoggerFactory.getLogger(EntityTable.class); | ||||
| 
 | ||||
|     private transient final WidgetFactory widgetFactory; | ||||
|     static final String COLUMN_DEFINITION = "COLUMN_DEFINITION"; | ||||
|     static final String TABLE_ROW_DATA = "TABLE_ROW_DATA"; | ||||
| 
 | ||||
|     private transient final RestCall<Page<ROW>> restCall; | ||||
|     private transient final List<ColumnDefinition<ROW>> columns; | ||||
|     private transient final List<TableRowAction> actions; | ||||
|     transient final WidgetFactory widgetFactory; | ||||
| 
 | ||||
|     transient final RestCall<Page<ROW>> restCall; | ||||
|     transient final List<ColumnDefinition<ROW>> columns; | ||||
|     transient final List<TableRowAction> actions; | ||||
| 
 | ||||
|     private transient final TableFilter<ROW> filter; | ||||
|     private transient final Table table; | ||||
|  | @ -65,8 +72,7 @@ public class EntityTable<ROW extends Entity> extends Composite { | |||
|             final WidgetFactory widgetFactory, | ||||
|             final List<ColumnDefinition<ROW>> columns, | ||||
|             final List<TableRowAction> actions, | ||||
|             final int pageSize, | ||||
|             final boolean withFilter) { | ||||
|             final int pageSize) { | ||||
| 
 | ||||
|         super(parent, type); | ||||
|         this.widgetFactory = widgetFactory; | ||||
|  | @ -75,22 +81,34 @@ public class EntityTable<ROW extends Entity> extends Composite { | |||
|         this.actions = Utils.immutableListOf(actions); | ||||
| 
 | ||||
|         super.setLayout(new GridLayout()); | ||||
|         GridData gridData = new GridData(SWT.FILL, SWT.TOP, true, false); | ||||
|         GridData gridData = new GridData(SWT.FILL, SWT.TOP, true, true); | ||||
| 
 | ||||
|         gridData.heightHint = (pageSize + 1) * 40; | ||||
|         super.setLayoutData(gridData); | ||||
| 
 | ||||
|         this.pageSize = pageSize; | ||||
|         this.filter = (withFilter) ? new TableFilter<>(this) : null; | ||||
|         this.filter = columns | ||||
|                 .stream() | ||||
|                 .map(column -> column.filterAttribute) | ||||
|                 .filter(Objects::nonNull) | ||||
|                 .findFirst() | ||||
|                 .isPresent() ? new TableFilter<>(this) : null; | ||||
| 
 | ||||
|         this.table = widgetFactory.tableLocalized(this); | ||||
|         this.table.setLayout(new GridLayout(columns.size(), true)); | ||||
|         final GridLayout gridLayout = new GridLayout(columns.size(), true); | ||||
|         this.table.setLayout(gridLayout); | ||||
|         gridData = new GridData(SWT.FILL, SWT.CENTER, true, false); | ||||
|         gridData.heightHint = (pageSize + 1) * 25; | ||||
|         gridData.heightHint = (pageSize + 1) * 27; | ||||
|         this.table.setLayoutData(gridData); | ||||
|         this.table.addListener(SWT.Resize, this::adaptColumnWidth); | ||||
|         @SuppressWarnings("unchecked") | ||||
|         final Consumer<Table> locFunction = (Consumer<Table>) this.table.getData(POLYGLOT_WIDGET_FUNCTION_KEY); | ||||
|         final Consumer<Table> newLocFunction = t -> { | ||||
|             updateValues(); | ||||
|             locFunction.accept(t); | ||||
|         }; | ||||
|         this.table.setData(POLYGLOT_WIDGET_FUNCTION_KEY, newLocFunction); | ||||
| 
 | ||||
|         //this.table.setLayoutData(new GridData(GridData.FILL_BOTH)); | ||||
|         this.table.setHeaderVisible(true); | ||||
|         this.table.setLinesVisible(true); | ||||
| 
 | ||||
|  | @ -128,7 +146,11 @@ public class EntityTable<ROW extends Entity> extends Composite { | |||
|     } | ||||
| 
 | ||||
|     public void applyFilter() { | ||||
|         // TODO remove all rows, set current page to 0, call rest to get entities and build rows and navigation again | ||||
|         updateTableRows( | ||||
|                 this.pageNumber, | ||||
|                 this.pageSize, | ||||
|                 this.sortColumn, | ||||
|                 this.sortOrder); | ||||
|     } | ||||
| 
 | ||||
|     public void applySort(final String columnName) { | ||||
|  | @ -173,6 +195,9 @@ public class EntityTable<ROW extends Entity> extends Composite { | |||
|                     column.displayName, | ||||
|                     column.tooltip); | ||||
| 
 | ||||
|             tableColumn.addListener(SWT.Resize, this::adaptColumnWidthChange); | ||||
|             tableColumn.setData(COLUMN_DEFINITION, column); | ||||
| 
 | ||||
|             if (column.sortable) { | ||||
|                 tableColumn.addListener(SWT.Selection, event -> { | ||||
|                     if (!column.columnName.equals(this.sortColumn)) { | ||||
|  | @ -206,7 +231,7 @@ public class EntityTable<ROW extends Entity> extends Composite { | |||
|         this.restCall.newBuilder() | ||||
|                 .withPaging(pageNumber, pageSize) | ||||
|                 .withSorting(sortColumn, sortOrder) | ||||
|                 .withFilterAttributes(this.filter) | ||||
|                 .withQueryParams((this.filter != null) ? this.filter.getFilterParameter() : null) | ||||
|                 .call() | ||||
|                 .map(this::createTableRowsFromPage) | ||||
|                 .map(this.navigator::update) | ||||
|  | @ -214,7 +239,7 @@ public class EntityTable<ROW extends Entity> extends Composite { | |||
|                     // TODO error handling | ||||
|                 }); | ||||
| 
 | ||||
|         this.layout(); | ||||
|         this.layout(true, true); | ||||
|     } | ||||
| 
 | ||||
|     private Page<ROW> createTableRowsFromPage(final Page<ROW> page) { | ||||
|  | @ -223,31 +248,55 @@ public class EntityTable<ROW extends Entity> extends Composite { | |||
|             item.setData(TABLE_ROW_DATA, row); | ||||
|             int index = 0; | ||||
|             for (final ColumnDefinition<ROW> column : this.columns) { | ||||
|                 final Object value = column.valueSupplier.apply(row); | ||||
|                 if (value instanceof Boolean) { | ||||
|                     // TODO set an image or HTML with checkbox | ||||
|                     item.setText(index, String.valueOf(value)); | ||||
|                 } else { | ||||
|                     if (value != null) { | ||||
|                         item.setText(index, String.valueOf(value)); | ||||
|                     } else { | ||||
|                         item.setText(index, Constants.EMPTY_NOTE); | ||||
|                     } | ||||
|                 } | ||||
|                 setValueToCell(item, index, column.valueSupplier.apply(row)); | ||||
|                 index++; | ||||
|             } | ||||
|             if (this.actions != null) { | ||||
|                 // TODO | ||||
|                 // TODO?? | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         return page; | ||||
|     } | ||||
| 
 | ||||
|     private void updateValues() { | ||||
|         final TableItem[] items = this.table.getItems(); | ||||
|         final TableColumn[] columns = this.table.getColumns(); | ||||
|         for (int i = 0; i < columns.length; i++) { | ||||
|             final ColumnDefinition<ROW> columnDefinition = this.columns.get(i); | ||||
|             if (columnDefinition.localized) { | ||||
|                 for (int j = 0; j < items.length; j++) { | ||||
|                     @SuppressWarnings("unchecked") | ||||
|                     final ROW rowData = (ROW) items[j].getData(TABLE_ROW_DATA); | ||||
|                     setValueToCell(items[j], i, columnDefinition.valueSupplier.apply(rowData)); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     private void setValueToCell(final TableItem item, final int index, final Object value) { | ||||
|         if (value instanceof Boolean) { | ||||
|             addBooleanCell(item, index, value); | ||||
|         } else { | ||||
|             if (value != null) { | ||||
|                 item.setText(index, String.valueOf(value)); | ||||
|             } else { | ||||
|                 item.setText(index, Constants.EMPTY_NOTE); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     private void addBooleanCell(final TableItem item, final int index, final Object value) { | ||||
|         if ((Boolean) value) { | ||||
|             item.setImage(index, ImageIcon.ACTIVE.getImage(item.getDisplay())); | ||||
|         } else { | ||||
|             item.setImage(index, ImageIcon.INACTIVE.getImage(item.getDisplay())); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     private void adaptColumnWidth(final Event event) { | ||||
|         try { | ||||
|             final int currentTableWidth = this.table.getParent().getClientArea().width; | ||||
| 
 | ||||
|             int index = 0; | ||||
|             for (final ColumnDefinition<ROW> column : this.columns) { | ||||
| 
 | ||||
|  | @ -256,16 +305,37 @@ public class EntityTable<ROW extends Entity> extends Composite { | |||
|                         : column.widthPercent; | ||||
| 
 | ||||
|                 final TableColumn tableColumn = this.table.getColumn(index); | ||||
|                 tableColumn.setWidth(currentTableWidth / 100 * percentage); | ||||
|                 final int newWidth = currentTableWidth / 100 * percentage; | ||||
|                 tableColumn.setWidth(newWidth); | ||||
|                 if (this.filter != null) { | ||||
|                     this.filter.adaptColumnWidth(this.table.indexOf(tableColumn), newWidth); | ||||
|                 } | ||||
| 
 | ||||
|                 index++; | ||||
|             } | ||||
| 
 | ||||
|             // NOTE this.layout() triggers the navigation to disappear unexpectedly!? | ||||
|             this.table.layout(true, true); | ||||
| 
 | ||||
|         } catch (final Exception e) { | ||||
|             log.warn("Failed to adaptColumnWidth: ", e); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     private void adaptColumnWidthChange(final Event event) { | ||||
|         final Widget widget = event.widget; | ||||
|         if (widget instanceof TableColumn) { | ||||
|             final TableColumn tableColumn = ((TableColumn) widget); | ||||
|             if (this.filter != null && | ||||
|                     this.filter.adaptColumnWidth( | ||||
|                             this.table.indexOf(tableColumn), | ||||
|                             tableColumn.getWidth())) { | ||||
| 
 | ||||
|                 this.layout(true, true); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     @SuppressWarnings("unchecked") | ||||
|     private ROW getRowData(final TableItem item) { | ||||
|         return (ROW) item.getData(TABLE_ROW_DATA); | ||||
|  |  | |||
|  | @ -22,14 +22,14 @@ import ch.ethz.seb.sebserver.gui.service.widget.WidgetFactory; | |||
| /** <code> | ||||
|  *  new TableBuilder<T>(RestCall) | ||||
|  *      .withPaging(pageSize) | ||||
|  *      .withFilterAttribute(attribute.TableFilterAttribute) | ||||
|  *      .withColumn(new ColumnDefinition( | ||||
|  *          columnName:String, | ||||
|  *          displayName:LocTextKey, | ||||
|  *          tooltip:LocTextKey, | ||||
|  *          width:int, | ||||
|  *          valueSupplier:Function<ROW, String>, | ||||
|  *          sortable:boolean, | ||||
|  *          columnFilter:TableColumnFilter)) | ||||
|  *          sortable:boolean | ||||
|  *      .withAction(action:TableRowAction) | ||||
|  *      .withSelectableRows(boolean) | ||||
|  *      .compose(parent:Composit, group:Composite); | ||||
|  | @ -38,6 +38,7 @@ public class TableBuilder<ROW extends Entity> { | |||
| 
 | ||||
|     private final WidgetFactory widgetFactory; | ||||
|     final RestCall<Page<ROW>> restCall; | ||||
| //    final List<TableFilterAttribute> filter = new ArrayList<>(); | ||||
|     final List<ColumnDefinition<ROW>> columns = new ArrayList<>(); | ||||
|     final List<TableRowAction> actions = new ArrayList<>(); | ||||
| 
 | ||||
|  | @ -57,8 +58,8 @@ public class TableBuilder<ROW extends Entity> { | |||
|         return this; | ||||
|     } | ||||
| 
 | ||||
|     public TableBuilder<ROW> withColumn(final ColumnDefinition<ROW> columnDef) { | ||||
|         this.columns.add(columnDef); | ||||
|     public TableBuilder<ROW> withColumn(final ColumnDefinition<ROW> columnDefinition) { | ||||
|         this.columns.add(columnDefinition); | ||||
|         return this; | ||||
|     } | ||||
| 
 | ||||
|  | @ -73,12 +74,6 @@ public class TableBuilder<ROW extends Entity> { | |||
|     } | ||||
| 
 | ||||
|     public EntityTable<ROW> compose(final Composite parent) { | ||||
|         final boolean withFilter = this.columns | ||||
|                 .stream() | ||||
|                 .filter(c -> c.filter != null) | ||||
|                 .findFirst() | ||||
|                 .isPresent(); | ||||
| 
 | ||||
|         return new EntityTable<>( | ||||
|                 this.type, | ||||
|                 parent, | ||||
|  | @ -86,8 +81,7 @@ public class TableBuilder<ROW extends Entity> { | |||
|                 this.widgetFactory, | ||||
|                 this.columns, | ||||
|                 this.actions, | ||||
|                 this.pageSize, | ||||
|                 withFilter); | ||||
|                 this.pageSize); | ||||
|     } | ||||
| 
 | ||||
| } | ||||
|  |  | |||
|  | @ -8,24 +8,250 @@ | |||
| 
 | ||||
| package ch.ethz.seb.sebserver.gui.service.table; | ||||
| 
 | ||||
| import java.util.Arrays; | ||||
| import java.util.List; | ||||
| import java.util.stream.Collectors; | ||||
| 
 | ||||
| import org.apache.commons.lang3.StringUtils; | ||||
| import org.eclipse.swt.SWT; | ||||
| import org.eclipse.swt.layout.GridData; | ||||
| import org.eclipse.swt.layout.RowData; | ||||
| import org.eclipse.swt.layout.RowLayout; | ||||
| import org.eclipse.swt.widgets.Composite; | ||||
| import org.eclipse.swt.widgets.Label; | ||||
| import org.eclipse.swt.widgets.Text; | ||||
| import org.springframework.util.LinkedMultiValueMap; | ||||
| import org.springframework.util.MultiValueMap; | ||||
| 
 | ||||
| import ch.ethz.seb.sebserver.gbl.model.Entity; | ||||
| import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.FilterAttributeSupplier; | ||||
| import ch.ethz.seb.sebserver.gui.service.i18n.LocTextKey; | ||||
| import ch.ethz.seb.sebserver.gui.service.table.ColumnDefinition.TableFilterAttribute; | ||||
| import ch.ethz.seb.sebserver.gui.service.widget.LanguageSelector; | ||||
| import ch.ethz.seb.sebserver.gui.service.widget.WidgetFactory.ImageIcon; | ||||
| 
 | ||||
| public class TableFilter<ROW extends Entity> extends Composite implements FilterAttributeSupplier { | ||||
| public class TableFilter<ROW extends Entity> extends Composite { | ||||
| 
 | ||||
|     private static final long serialVersionUID = -2460403977147440766L; | ||||
| 
 | ||||
|     TableFilter(final EntityTable<ROW> parent) { | ||||
|         super(parent, SWT.NONE); | ||||
|     public static enum CriteriaType { | ||||
|         TEXT, | ||||
|         SINGLE_SELECTION, | ||||
|         COUNTRY_SELECTION, | ||||
|         DATE | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public MultiValueMap<String, String> getAttributes() { | ||||
|         // TODO Auto-generated method stub | ||||
|         return null; | ||||
|     private final EntityTable<ROW> entityTable; | ||||
|     private final List<FilterComponent> components; | ||||
| 
 | ||||
|     TableFilter(final EntityTable<ROW> entityTable) { | ||||
|         super(entityTable, SWT.NONE); | ||||
|         final GridData gridData = new GridData(SWT.FILL, SWT.TOP, true, false); | ||||
|         super.setLayoutData(gridData); | ||||
|         final RowLayout layout = new RowLayout(SWT.HORIZONTAL); | ||||
|         layout.spacing = 5; | ||||
|         layout.wrap = false; | ||||
|         super.setLayout(layout); | ||||
| 
 | ||||
|         this.entityTable = entityTable; | ||||
|         this.components = entityTable.columns | ||||
|                 .stream() | ||||
|                 .map(column -> column.filterAttribute) | ||||
|                 //.filter(Objects::nonNull) | ||||
|                 .map(this::createFilterComponent) | ||||
|                 .map(comp -> comp.build(this)) | ||||
|                 .map(comp -> comp.reset()) | ||||
|                 .collect(Collectors.toList()); | ||||
| 
 | ||||
|         final FilterComponent lastComp = this.components.get(this.components.size() - 1); | ||||
|         if (lastComp instanceof TableFilter.NullFilter) { | ||||
|             this.components.remove(lastComp); | ||||
|         } | ||||
| 
 | ||||
|         addActions(); | ||||
|     } | ||||
| 
 | ||||
|     public MultiValueMap<String, String> getFilterParameter() { | ||||
|         return this.components | ||||
|                 .stream() | ||||
|                 .reduce(new LinkedMultiValueMap<String, String>(), | ||||
|                         (map, comp) -> comp.putFilterParameter(map), | ||||
|                         (map1, map2) -> { | ||||
|                             map1.putAll(map2); | ||||
|                             return map1; | ||||
|                         }); | ||||
|     } | ||||
| 
 | ||||
|     public void reset() { | ||||
|         this.components | ||||
|                 .stream() | ||||
|                 .forEach(comp -> comp.reset()); | ||||
|     } | ||||
| 
 | ||||
|     private FilterComponent createFilterComponent(final TableFilterAttribute attribute) { | ||||
|         if (attribute == null) { | ||||
|             return new NullFilter(); | ||||
|         } | ||||
| 
 | ||||
|         switch (attribute.type) { | ||||
|             case TEXT: | ||||
|                 return new TextFilter(attribute); | ||||
|             case COUNTRY_SELECTION: | ||||
|                 return new LanguageFilter(attribute); | ||||
|             default: | ||||
|                 throw new IllegalArgumentException("Unsupported FilterAttributeType: " + attribute.type); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     boolean adaptColumnWidth(final int columnIndex, final int width) { | ||||
|         if (columnIndex < this.components.size()) { | ||||
|             return this.components.get(columnIndex).adaptWidth(width); | ||||
|         } | ||||
| 
 | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|     private void addActions() { | ||||
|         this.entityTable.widgetFactory.imageButton( | ||||
|                 ImageIcon.SEARCH, | ||||
|                 this, | ||||
|                 new LocTextKey("sebserver.overall.action.filter"), | ||||
|                 event -> { | ||||
|                     this.entityTable.applyFilter(); | ||||
|                 }); | ||||
|         this.entityTable.widgetFactory.imageButton( | ||||
|                 ImageIcon.CANCEL, | ||||
|                 this, | ||||
|                 new LocTextKey("sebserver.overall.action.filter.clear"), | ||||
|                 event -> { | ||||
|                     reset(); | ||||
|                 }); | ||||
|     } | ||||
| 
 | ||||
|     private static abstract class FilterComponent { | ||||
| 
 | ||||
|         static final int CELL_WIDTH_ADJUSTMENT = -30; | ||||
| 
 | ||||
|         protected RowData rowData; | ||||
|         final TableFilterAttribute attribute; | ||||
| 
 | ||||
|         FilterComponent(final TableFilterAttribute attribute) { | ||||
|             this.attribute = attribute; | ||||
|         } | ||||
| 
 | ||||
|         LinkedMultiValueMap<String, String> putFilterParameter( | ||||
|                 final LinkedMultiValueMap<String, String> filterParameter) { | ||||
| 
 | ||||
|             final String value = getValue(); | ||||
|             if (StringUtils.isNotBlank(value)) { | ||||
|                 filterParameter.put(this.attribute.columnName, Arrays.asList(value)); | ||||
|             } | ||||
|             return filterParameter; | ||||
|         } | ||||
| 
 | ||||
|         abstract FilterComponent build(Composite parent); | ||||
| 
 | ||||
|         abstract FilterComponent reset(); | ||||
| 
 | ||||
|         abstract String getValue(); | ||||
| 
 | ||||
|         boolean adaptWidth(final int width) { | ||||
|             final int _width = width + CELL_WIDTH_ADJUSTMENT; | ||||
|             if (_width != this.rowData.width) { | ||||
|                 this.rowData.width = _width; | ||||
|                 return true; | ||||
|             } | ||||
| 
 | ||||
|             return false; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     private class NullFilter extends FilterComponent { | ||||
| 
 | ||||
|         private Label label; | ||||
| 
 | ||||
|         NullFilter() { | ||||
|             super(null); | ||||
|         } | ||||
| 
 | ||||
|         @Override | ||||
|         FilterComponent build(final Composite parent) { | ||||
|             this.label = new Label(parent, SWT.NONE); | ||||
|             this.rowData = new RowData(); | ||||
|             this.label.setLayoutData(this.rowData); | ||||
|             return this; | ||||
|         } | ||||
| 
 | ||||
|         @Override | ||||
|         boolean adaptWidth(final int width) { | ||||
|             return super.adaptWidth(width - CELL_WIDTH_ADJUSTMENT); | ||||
|         } | ||||
| 
 | ||||
|         @Override | ||||
|         FilterComponent reset() { | ||||
|             return this; | ||||
|         } | ||||
| 
 | ||||
|         @Override | ||||
|         String getValue() { | ||||
|             return null; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     private class TextFilter extends FilterComponent { | ||||
| 
 | ||||
|         private Text textInput; | ||||
| 
 | ||||
|         TextFilter(final TableFilterAttribute attribute) { | ||||
|             super(attribute); | ||||
|         } | ||||
| 
 | ||||
|         @Override | ||||
|         FilterComponent reset() { | ||||
|             this.textInput.setText(super.attribute.initValue); | ||||
|             return this; | ||||
|         } | ||||
| 
 | ||||
|         @Override | ||||
|         FilterComponent build(final Composite parent) { | ||||
|             this.textInput = new Text(parent, SWT.LEFT | SWT.BORDER); | ||||
|             this.rowData = new RowData(); | ||||
|             this.textInput.setLayoutData(this.rowData); | ||||
|             return this; | ||||
|         } | ||||
| 
 | ||||
|         @Override | ||||
|         String getValue() { | ||||
|             return this.textInput.getText(); | ||||
|         } | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
|     private class LanguageFilter extends FilterComponent { | ||||
| 
 | ||||
|         private LanguageSelector selector; | ||||
| 
 | ||||
|         LanguageFilter(final TableFilterAttribute attribute) { | ||||
|             super(attribute); | ||||
|         } | ||||
| 
 | ||||
|         @Override | ||||
|         FilterComponent build(final Composite parent) { | ||||
|             this.selector = TableFilter.this.entityTable.widgetFactory.countrySelector(parent); | ||||
|             this.rowData = new RowData(); | ||||
|             this.selector.setLayoutData(this.rowData); | ||||
|             return this; | ||||
|         } | ||||
| 
 | ||||
|         @Override | ||||
|         FilterComponent reset() { | ||||
|             this.selector.clear(); | ||||
|             this.selector.layout(); | ||||
|             return this; | ||||
|         } | ||||
| 
 | ||||
|         @Override | ||||
|         String getValue() { | ||||
|             return this.selector.getSelectionValue(); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  |  | |||
|  | @ -68,8 +68,6 @@ public class TableNavigator extends Composite { | |||
|             } | ||||
|         } | ||||
| 
 | ||||
|         this.layout(); | ||||
| 
 | ||||
|         return pageData; | ||||
|     } | ||||
| 
 | ||||
|  |  | |||
|  | @ -29,6 +29,7 @@ import org.eclipse.swt.layout.GridLayout; | |||
| import org.eclipse.swt.widgets.Composite; | ||||
| import org.slf4j.Logger; | ||||
| import org.slf4j.LoggerFactory; | ||||
| import org.springframework.util.StreamUtils; | ||||
| 
 | ||||
| import ch.ethz.seb.sebserver.gui.service.push.ServerPushContext; | ||||
| import ch.ethz.seb.sebserver.gui.service.push.ServerPushService; | ||||
|  | @ -62,10 +63,12 @@ public class ImageUpload extends Composite { | |||
| 
 | ||||
|                 @Override | ||||
|                 public void receive(final InputStream stream, final FileDetails details) throws IOException { | ||||
| 
 | ||||
|                     try { | ||||
|                         final String contentType = details.getContentType(); | ||||
|                         if (contentType != null && contentType.startsWith("image")) { | ||||
|                             ImageUpload.this.imageBase64 = Base64.getEncoder().encodeToString(stream.readAllBytes()); | ||||
|                             ImageUpload.this.imageBase64 = Base64.getEncoder() | ||||
|                                     .encodeToString(StreamUtils.copyToByteArray(stream)); | ||||
|                         } | ||||
|                     } catch (final Exception e) { | ||||
|                         log.error("Error while trying to upload image", e); | ||||
|  |  | |||
|  | @ -0,0 +1,55 @@ | |||
| /* | ||||
|  * 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.widget; | ||||
| 
 | ||||
| import static ch.ethz.seb.sebserver.gui.service.i18n.PolyglotPageService.POLYGLOT_WIDGET_FUNCTION_KEY; | ||||
| 
 | ||||
| import java.util.List; | ||||
| import java.util.Locale; | ||||
| import java.util.function.Consumer; | ||||
| import java.util.stream.Collectors; | ||||
| 
 | ||||
| import org.apache.commons.lang3.StringUtils; | ||||
| import org.eclipse.swt.widgets.Composite; | ||||
| 
 | ||||
| import ch.ethz.seb.sebserver.gbl.util.Tuple; | ||||
| import ch.ethz.seb.sebserver.gui.service.i18n.I18nSupport; | ||||
| 
 | ||||
| public class LanguageSelector extends SingleSelection { | ||||
| 
 | ||||
|     private static final long serialVersionUID = -8590909580787576722L; | ||||
| 
 | ||||
|     private final Consumer<LanguageSelector> updateFunction; | ||||
| 
 | ||||
|     public LanguageSelector(final Composite parent, final I18nSupport i18nSupport) { | ||||
|         super(parent, getLanguages(i18nSupport)); | ||||
|         this.updateFunction = updateFunction(i18nSupport); | ||||
|         this.setData(POLYGLOT_WIDGET_FUNCTION_KEY, this.updateFunction); | ||||
|     } | ||||
| 
 | ||||
|     private static final Consumer<LanguageSelector> updateFunction(final I18nSupport i18nSupport) { | ||||
|         return selection -> selection.applyNewMapping(getLanguages(i18nSupport)); | ||||
|     } | ||||
| 
 | ||||
|     public static final List<Tuple<String>> getLanguages(final I18nSupport i18nSupport) { | ||||
|         final Locale currentLocale = i18nSupport.getCurrentLocale(); | ||||
|         return i18nSupport.supportedLanguages() | ||||
|                 .stream() | ||||
|                 .map(locale -> new Tuple<>(locale.toLanguageTag(), locale.getDisplayLanguage(currentLocale))) | ||||
|                 .filter(tuple -> StringUtils.isNoneBlank(tuple._2)) | ||||
|                 .sorted((t1, t2) -> t1._2.compareTo(t2._2)) | ||||
|                 .collect(Collectors.toList()); | ||||
|     } | ||||
| 
 | ||||
|     public void clear() { | ||||
|         super.clearSelection(); | ||||
|         this.updateFunction.accept(this); | ||||
|     } | ||||
| 
 | ||||
| } | ||||
|  | @ -8,6 +8,7 @@ | |||
| 
 | ||||
| package ch.ethz.seb.sebserver.gui.service.widget; | ||||
| 
 | ||||
| import java.util.ArrayList; | ||||
| import java.util.List; | ||||
| import java.util.stream.Collectors; | ||||
| 
 | ||||
|  | @ -26,12 +27,20 @@ public class SingleSelection extends Combo { | |||
| 
 | ||||
|     public SingleSelection(final Composite parent, final List<Tuple<String>> mapping) { | ||||
|         super(parent, SWT.READ_ONLY); | ||||
|         this.valueMapping = mapping.stream() | ||||
|         this.valueMapping = new ArrayList<>(); | ||||
|         this.keyMapping = new ArrayList<>(); | ||||
|         applyNewMapping(mapping); | ||||
|     } | ||||
| 
 | ||||
|     protected void applyNewMapping(final List<Tuple<String>> mapping) { | ||||
|         this.valueMapping.clear(); | ||||
|         this.keyMapping.clear(); | ||||
|         this.valueMapping.addAll(mapping.stream() | ||||
|                 .map(t -> t._2) | ||||
|                 .collect(Collectors.toList()); | ||||
|         this.keyMapping = mapping.stream() | ||||
|                 .collect(Collectors.toList())); | ||||
|         this.keyMapping.addAll(mapping.stream() | ||||
|                 .map(t -> t._1) | ||||
|                 .collect(Collectors.toList()); | ||||
|                 .collect(Collectors.toList())); | ||||
|         super.setItems(this.valueMapping.toArray(new String[mapping.size()])); | ||||
|     } | ||||
| 
 | ||||
|  |  | |||
|  | @ -15,6 +15,7 @@ import java.util.Iterator; | |||
| import java.util.List; | ||||
| import java.util.Locale; | ||||
| import java.util.function.Consumer; | ||||
| import java.util.function.Function; | ||||
| 
 | ||||
| import org.apache.commons.lang3.StringUtils; | ||||
| import org.eclipse.rap.rwt.RWT; | ||||
|  | @ -23,6 +24,7 @@ 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.layout.GridLayout; | ||||
| import org.eclipse.swt.widgets.Button; | ||||
| import org.eclipse.swt.widgets.Combo; | ||||
| import org.eclipse.swt.widgets.Composite; | ||||
|  | @ -49,6 +51,9 @@ 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; | ||||
| import ch.ethz.seb.sebserver.gui.service.page.action.ActionDefinition; | ||||
| 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.push.ServerPushService; | ||||
| import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestCall; | ||||
| import ch.ethz.seb.sebserver.gui.service.table.TableBuilder; | ||||
|  | @ -60,40 +65,24 @@ public class WidgetFactory { | |||
| 
 | ||||
|     private static final Logger log = LoggerFactory.getLogger(WidgetFactory.class); | ||||
| 
 | ||||
|     public enum CustomVariant { | ||||
|         TEXT_H1("h1"), | ||||
|         TEXT_H2("h2"), | ||||
|         TEXT_H3("h3"), | ||||
|         TEXT_ACTION("action"), | ||||
| 
 | ||||
|         FOOTER("footer"), | ||||
| 
 | ||||
|         ; | ||||
| 
 | ||||
|         public final String key; | ||||
| 
 | ||||
|         private CustomVariant(final String key) { | ||||
|             this.key = key; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     public enum IconButtonType { | ||||
|     public enum ImageIcon { | ||||
|         MAXIMIZE("maximize.png"), | ||||
|         MINIMIZE("minimize.png"), | ||||
|         MODIFY_ACTION("editAction.png"), | ||||
|         CANCEL_ACTION("cancelEditAction.png"), | ||||
|         VIEW_ACTION("viewAction.png"), | ||||
|         ACTIVATE_ACTION("inactive.png"), | ||||
|         DEACTIVATE_ACTION("active.png"), | ||||
|         SAVE_ACTION("saveAction.png"), | ||||
|         NEW_ACTION("newAction.png"), | ||||
|         DELETE_ACTION("deleteAction.png"), | ||||
|         ; | ||||
|         EDIT("edit.png"), | ||||
|         CANCEL("cancel.png"), | ||||
|         CANCEL_EDIT("cancelEdit.png"), | ||||
|         SHOW("show.png"), | ||||
|         ACTIVE("active.png"), | ||||
|         INACTIVE("inactive.png"), | ||||
|         SAVE("save.png"), | ||||
|         NEW("new.png"), | ||||
|         DELETE("delete.png"), | ||||
|         SEARCH("lens.png"); | ||||
| 
 | ||||
|         private String fileName; | ||||
|         private ImageData image = null; | ||||
| 
 | ||||
|         private IconButtonType(final String fileName) { | ||||
|         private ImageIcon(final String fileName) { | ||||
|             this.fileName = fileName; | ||||
|         } | ||||
| 
 | ||||
|  | @ -113,6 +102,24 @@ public class WidgetFactory { | |||
| 
 | ||||
|     } | ||||
| 
 | ||||
|     public enum CustomVariant { | ||||
|         TEXT_H1("h1"), | ||||
|         TEXT_H2("h2"), | ||||
|         TEXT_H3("h3"), | ||||
|         IMAGE_BUTTON("imageButton"), | ||||
|         TEXT_ACTION("action"), | ||||
| 
 | ||||
|         FOOTER("footer"), | ||||
| 
 | ||||
|         ; | ||||
| 
 | ||||
|         public final String key; | ||||
| 
 | ||||
|         private CustomVariant(final String key) { | ||||
|             this.key = key; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     private final PolyglotPageService polyglotPageService; | ||||
|     private final I18nSupport i18nSupport; | ||||
|     private final ServerPushService serverPushService; | ||||
|  | @ -126,6 +133,42 @@ public class WidgetFactory { | |||
|         this.serverPushService = serverPushService; | ||||
|     } | ||||
| 
 | ||||
|     public I18nSupport getI18nSupport() { | ||||
|         return this.i18nSupport; | ||||
|     } | ||||
| 
 | ||||
|     public Composite defaultPageLayout(final Composite parent) { | ||||
|         final Composite content = new Composite(parent, SWT.NONE); | ||||
|         final GridLayout contentLayout = new GridLayout(); | ||||
|         contentLayout.marginLeft = 10; | ||||
|         content.setLayout(contentLayout); | ||||
|         content.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); | ||||
|         return content; | ||||
|     } | ||||
| 
 | ||||
|     public Composite defaultPageLayout(final Composite parent, final LocTextKey title) { | ||||
|         final Composite defaultPageLayout = defaultPageLayout(parent); | ||||
|         final Label labelLocalizedTitle = labelLocalizedTitle(defaultPageLayout, title); | ||||
|         labelLocalizedTitle.setLayoutData(new GridData(SWT.TOP, SWT.LEFT, true, false)); | ||||
|         return defaultPageLayout; | ||||
|     } | ||||
| 
 | ||||
|     public Composite defaultPageLayout( | ||||
|             final Composite parent, | ||||
|             final LocTextKey title, | ||||
|             final ActionDefinition actionDefinition, | ||||
|             final Function<Label, Consumer<ActionEvent>> eventFunction) { | ||||
| 
 | ||||
|         final Composite defaultPageLayout = defaultPageLayout(parent); | ||||
|         final Label labelLocalizedTitle = labelLocalizedTitle(defaultPageLayout, title); | ||||
|         labelLocalizedTitle.setLayoutData(new GridData(SWT.TOP, SWT.LEFT, true, false)); | ||||
|         ActionEventListener.injectListener( | ||||
|                 labelLocalizedTitle, | ||||
|                 actionDefinition, | ||||
|                 eventFunction.apply(labelLocalizedTitle)); | ||||
|         return defaultPageLayout; | ||||
|     } | ||||
| 
 | ||||
|     public Button buttonLocalized(final Composite parent, final String locTextKey) { | ||||
|         final Button button = new Button(parent, SWT.NONE); | ||||
|         this.injectI18n(button, new LocTextKey(locTextKey)); | ||||
|  | @ -254,13 +297,13 @@ public class WidgetFactory { | |||
|     } | ||||
| 
 | ||||
|     public Label imageButton( | ||||
|             final IconButtonType type, | ||||
|             final ImageIcon 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.setData(RWT.CUSTOM_VARIANT, CustomVariant.IMAGE_BUTTON.name()); | ||||
|         imageButton.setImage(type.getImage(parent.getDisplay())); | ||||
|         if (listener != null) { | ||||
|             imageButton.addListener(SWT.MouseDown, listener); | ||||
|  | @ -339,6 +382,10 @@ public class WidgetFactory { | |||
|         return combo; | ||||
|     } | ||||
| 
 | ||||
|     public LanguageSelector countrySelector(final Composite parent) { | ||||
|         return new LanguageSelector(parent, this.i18nSupport); | ||||
|     } | ||||
| 
 | ||||
|     public ImageUpload formImageUpload( | ||||
|             final Composite parent, | ||||
|             final String value, | ||||
|  | @ -497,6 +544,18 @@ public class WidgetFactory { | |||
|             } | ||||
|             if (locToolTipKey != null) { | ||||
|                 label.setToolTipText(i18nSupport.getText(locToolTipKey)); | ||||
|                 // TODO managing a tool-tip delay is not working as expected | ||||
|                 //      is there another way to achieve this? | ||||
| //                label.addListener(SWT.MouseEnter, event -> { | ||||
| //                    System.out.println("*************** set tooltip delay"); | ||||
| //                    label.getDisplay().timerExec(1000, () -> { | ||||
| //                        System.out.println("*************** set tooltip"); | ||||
| //                        label.setToolTipText(i18nSupport.getText(locToolTipKey)); | ||||
| //                    }); | ||||
| //                }); | ||||
| //                label.addListener(SWT.MouseExit, event -> { | ||||
| //                    label.setToolTipText(null); | ||||
| //                }); | ||||
|             } | ||||
|         }; | ||||
|     } | ||||
|  |  | |||
|  | @ -28,8 +28,8 @@ import org.springframework.web.bind.annotation.RequestParam; | |||
| 
 | ||||
| import ch.ethz.seb.sebserver.gbl.Constants; | ||||
| import ch.ethz.seb.sebserver.gbl.api.API; | ||||
| import ch.ethz.seb.sebserver.gbl.api.EntityType; | ||||
| import ch.ethz.seb.sebserver.gbl.api.API.BulkActionType; | ||||
| import ch.ethz.seb.sebserver.gbl.api.EntityType; | ||||
| import ch.ethz.seb.sebserver.gbl.api.POSTMapper; | ||||
| import ch.ethz.seb.sebserver.gbl.authorization.PrivilegeType; | ||||
| import ch.ethz.seb.sebserver.gbl.model.Entity; | ||||
|  | @ -100,9 +100,16 @@ public abstract class EntityController<T extends GrantEntity, M extends GrantEnt | |||
|             @RequestParam(name = Page.ATTR_SORT, required = false) final String sort, | ||||
|             @RequestParam final MultiValueMap<String, String> allRequestParams) { | ||||
| 
 | ||||
|         // at least current user must have read access for specified entity type within its own institution | ||||
|         checkReadPrivilege(institutionId); | ||||
|         final FilterMap filterMap = new FilterMap(allRequestParams) | ||||
|                 .putIfAbsent(API.PARAM_INSTITUTION_ID, String.valueOf(institutionId)); | ||||
| 
 | ||||
|         final FilterMap filterMap = new FilterMap(allRequestParams); | ||||
| 
 | ||||
|         // if current user has no read access for specified entity type within other institution then its own institution, | ||||
|         // then the current users institutionId is put as a SQL filter criteria attribute to extends query performance | ||||
|         if (!this.authorization.hasGrant(PrivilegeType.READ_ONLY, this.entityDAO.entityType())) { | ||||
|             filterMap.putIfAbsent(API.PARAM_INSTITUTION_ID, String.valueOf(institutionId)); | ||||
|         } | ||||
| 
 | ||||
|         return this.paginationService.getPage( | ||||
|                 pageNumber, | ||||
|  | @ -128,9 +135,16 @@ public abstract class EntityController<T extends GrantEntity, M extends GrantEnt | |||
|                     defaultValue = UserService.USERS_INSTITUTION_AS_DEFAULT) final Long institutionId, | ||||
|             @RequestParam final MultiValueMap<String, String> allRequestParams) { | ||||
| 
 | ||||
|         // at least current user must have read access for specified entity type within its own institution | ||||
|         checkReadPrivilege(institutionId); | ||||
|         final FilterMap filterMap = new FilterMap(allRequestParams) | ||||
|                 .putIfAbsent(API.PARAM_INSTITUTION_ID, String.valueOf(institutionId)); | ||||
| 
 | ||||
|         final FilterMap filterMap = new FilterMap(allRequestParams); | ||||
| 
 | ||||
|         // if current user has no read access for specified entity type within other institution then its own institution, | ||||
|         // then the current users institutionId is put as a SQL filter criteria attribute to extends query performance | ||||
|         if (!this.authorization.hasGrant(PrivilegeType.READ_ONLY, this.entityDAO.entityType())) { | ||||
|             filterMap.putIfAbsent(API.PARAM_INSTITUTION_ID, String.valueOf(institutionId)); | ||||
|         } | ||||
| 
 | ||||
|         return getAll(filterMap) | ||||
|                 .getOrThrow() | ||||
|  |  | |||
|  | @ -13,4 +13,6 @@ sebserver.gui.webservice.apipath=/admin-api/v1 | |||
| 
 | ||||
| 
 | ||||
| sebserver.gui.theme=css/sebserver.css | ||||
| sebserver.gui.list.page.size=20 | ||||
| sebserver.gui.date.displayformat=EEEE, dd MMMM yyyy - HH:mm | ||||
| 
 | ||||
|  |  | |||
|  | @ -2,6 +2,7 @@ spring.application.name=SEB Server | |||
| spring.profiles.active=dev | ||||
| 
 | ||||
| sebserver.version=1.0 beta | ||||
| sebserver.supported.languages=en,de | ||||
| 
 | ||||
| # comma separated list of known possible OpenEdX API access token request endpoints | ||||
| sebserver.lms.openedix.api.token.request.paths=/oauth2/access_token | ||||
|  | @ -10,6 +10,8 @@ sebserver.overall.message.leave.without.save=You are leaving this page without s | |||
| sebserver.overall.upload=Please Select | ||||
| sebserver.overall.action.modify.cancel=Cancel | ||||
| sebserver.overall.action.modify.cancel.confirm=Are you sure to cancel? Modifications will be lost. | ||||
| sebserver.overall.action.filter=Apply Filter | ||||
| sebserver.overall.action.filter.clear=Clear Filter Criteria | ||||
| 
 | ||||
| ################################ | ||||
| # Login Page | ||||
|  | @ -32,10 +34,11 @@ 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 | ||||
| sebserver.page.message=Information | ||||
| # Activities | ||||
| sebserver.activities.institution=Institution | ||||
| sebserver.activities.useraccount=User Account | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| ################################ | ||||
|  | @ -64,10 +67,25 @@ sebserver.institution.form.logoImage=Logo Image | |||
| sebserver.institution.form.confirm.deactivation=Note that there are {0} other entities that belongs to this Institution.<br/>Those will also be deactivated by deactivating this Institution.<br/><br/>Are You sure to deactivate this Institution? | ||||
| sebserver.institution.form.confirm.deactivation.noDependencies=Are You sure to deactivate this Institution? | ||||
| 
 | ||||
| sebserver.form.validation.fieldError.name=Name is mandatory and must have a size between 3 and 255 character | ||||
| sebserver.form.validation.fieldError.urlSuffix=URL Suffix must have a size between 3 and 255 character | ||||
| ################################ | ||||
| # Form validation | ||||
| # User Account | ||||
| ################################ | ||||
| 
 | ||||
| sebserver.form.validation.fieldError.size=The size must be between {3} and {4} | ||||
| sebserver.useraccount.list.title=User Accounts | ||||
| sebserver.useraccount.list.column.name=Name | ||||
| sebserver.useraccount.list.column.username=User Name | ||||
| sebserver.useraccount.list.column.email=Mail | ||||
| sebserver.useraccount.list.column.language=Language | ||||
| sebserver.useraccount.list.column.active=Active | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| ################################ | ||||
| # Form validation and messages | ||||
| ################################ | ||||
| 
 | ||||
| sebserver.form.validation.fieldError.size=The size must be between {3} and {4} | ||||
| sebserver.form.validation.fieldError.name=Name is mandatory and must have a size between 3 and 255 character | ||||
| sebserver.form.validation.fieldError.urlSuffix=URL Suffix must have a size between 3 and 255 character | ||||
| sebserver.error.unexpected=Unexpected Error | ||||
| sebserver.page.message=Information | ||||
|  | @ -62,10 +62,16 @@ Label.h3 { | |||
|     color: #1f407a; | ||||
| } | ||||
| 
 | ||||
| /* | ||||
| Label:hover.imageButton { | ||||
|     background-color: #82BE1E; | ||||
|     background-gradient-color: #82BE1E; | ||||
|     background-image: gradient( linear, left top, left bottom, from( #82BE1E ), to( #82BE1E ) ); | ||||
| }*/ | ||||
| 
 | ||||
| Label:hover.imageButton { | ||||
|     background-color: transparent; | ||||
|     background-repeat: no-repeat; | ||||
| } | ||||
| 
 | ||||
| Composite.bordered { | ||||
|  | @ -661,7 +667,7 @@ Table-RowOverlay:selected:unfocused { | |||
| Table-RowOverlay:linesvisible:even:hover { | ||||
|     color: #4a4a4a; | ||||
|     background-color: #b5b5b5; | ||||
|     background-image: gradient(linear, left top, left bottom, from(#b5b5b5),to(#d5d5d5)); | ||||
|     background-image: gradient(linear, left top, left bottom, from(#b5b5b5),to(#b5b5b5)); | ||||
| } | ||||
| 
 | ||||
| Table-RowOverlay:linesvisible:even:selected { | ||||
|  |  | |||
| Before Width: | Height: | Size: 246 B After Width: | Height: | Size: 246 B | 
| Before Width: | Height: | Size: 168 B After Width: | Height: | Size: 168 B | 
| Before Width: | Height: | Size: 186 B After Width: | Height: | Size: 148 B | 
| Before Width: | Height: | Size: 148 B | 
							
								
								
									
										
											BIN
										
									
								
								src/main/resources/static/images/deletePermanent.png
									
										
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 186 B | 
| Before Width: | Height: | Size: 165 B After Width: | Height: | Size: 165 B | 
							
								
								
									
										
											BIN
										
									
								
								src/main/resources/static/images/lens.png
									
										
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 220 B | 
| Before Width: | Height: | Size: 121 B After Width: | Height: | Size: 121 B | 
| Before Width: | Height: | Size: 165 B After Width: | Height: | Size: 165 B | 
| Before Width: | Height: | Size: 211 B After Width: | Height: | Size: 211 B | 
|  | @ -31,8 +31,8 @@ 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.APIMessage; | ||||
| import ch.ethz.seb.sebserver.gbl.api.API; | ||||
| import ch.ethz.seb.sebserver.gbl.api.APIMessage; | ||||
| import ch.ethz.seb.sebserver.gbl.model.Domain; | ||||
| import ch.ethz.seb.sebserver.gbl.model.Entity; | ||||
| import ch.ethz.seb.sebserver.gbl.model.EntityKey; | ||||
|  | @ -138,7 +138,7 @@ public class UserAPITest extends AdministrationAPIIntegrationTester { | |||
|     public void getAllUserInfoNoFilter() throws Exception { | ||||
|         Page<UserInfo> userInfos = new RestAPITestHelper() | ||||
|                 .withAccessToken(getSebAdminAccess()) | ||||
|                 .withPath(API.USER_ACCOUNT_ENDPOINT) | ||||
|                 .withPath(API.USER_ACCOUNT_ENDPOINT + "?institutionId=1") | ||||
|                 .withExpectedStatus(HttpStatus.OK) | ||||
|                 .getAsObject(new TypeReference<Page<UserInfo>>() { | ||||
|                 }); | ||||
|  | @ -205,7 +205,7 @@ public class UserAPITest extends AdministrationAPIIntegrationTester { | |||
| 
 | ||||
|         final String token = getSebAdminAccess(); | ||||
|         final Page<UserInfo> userInfos = this.jsonMapper.readValue( | ||||
|                 this.mockMvc.perform(get(this.endpoint + API.USER_ACCOUNT_ENDPOINT) | ||||
|                 this.mockMvc.perform(get(this.endpoint + API.USER_ACCOUNT_ENDPOINT + "?institutionId=1") | ||||
|                         .contentType(MediaType.APPLICATION_FORM_URLENCODED) | ||||
|                         .header("Authorization", "Bearer " + token)) | ||||
|                         .andExpect(status().isOk()) | ||||
|  | @ -247,7 +247,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 + API.USER_ACCOUNT_ENDPOINT + "?sort=-") | ||||
|                 this.mockMvc.perform(get(this.endpoint + API.USER_ACCOUNT_ENDPOINT + "?sort=-&institutionId=1") | ||||
|                         .contentType(MediaType.APPLICATION_FORM_URLENCODED) | ||||
|                         .header("Authorization", "Bearer " + token)) | ||||
|                         .andExpect(status().isOk()) | ||||
|  | @ -345,7 +345,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 + API.USER_ACCOUNT_ENDPOINT) | ||||
|                 this.mockMvc.perform(get(this.endpoint + API.USER_ACCOUNT_ENDPOINT + "?institutionId=1") | ||||
|                         .contentType(MediaType.APPLICATION_FORM_URLENCODED) | ||||
|                         .header("Authorization", "Bearer " + token)) | ||||
|                         .andExpect(status().isOk()) | ||||
|  | @ -382,7 +382,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 + API.USER_ACCOUNT_ENDPOINT + "?active=false") | ||||
|                 this.mockMvc.perform(get(this.endpoint + API.USER_ACCOUNT_ENDPOINT + "?active=false&institutionId=1") | ||||
|                         .contentType(MediaType.APPLICATION_FORM_URLENCODED) | ||||
|                         .header("Authorization", "Bearer " + token)) | ||||
|                         .andExpect(status().isOk()) | ||||
|  | @ -415,7 +415,7 @@ public class UserAPITest extends AdministrationAPIIntegrationTester { | |||
|         final String token = getSebAdminAccess(); | ||||
|         final Page<UserInfo> userInfos = this.jsonMapper.readValue( | ||||
|                 this.mockMvc | ||||
|                         .perform(get(this.endpoint + API.USER_ACCOUNT_ENDPOINT + "?username=exam") | ||||
|                         .perform(get(this.endpoint + API.USER_ACCOUNT_ENDPOINT + "?username=exam&institutionId=1") | ||||
|                                 .contentType(MediaType.APPLICATION_FORM_URLENCODED) | ||||
|                                 .header("Authorization", "Bearer " + token)) | ||||
|                         .andExpect(status().isOk()) | ||||
|  | @ -1080,7 +1080,7 @@ public class UserAPITest extends AdministrationAPIIntegrationTester { | |||
|         // for SEB Admin | ||||
|         Collection<EntityName> names = this.jsonMapper.readValue( | ||||
|                 this.mockMvc | ||||
|                         .perform(get(this.endpoint + API.USER_ACCOUNT_ENDPOINT + "/names") | ||||
|                         .perform(get(this.endpoint + API.USER_ACCOUNT_ENDPOINT + "/names" + "?institutionId=1") | ||||
|                                 .contentType(MediaType.APPLICATION_FORM_URLENCODED) | ||||
|                                 .header("Authorization", "Bearer " + sebAdminToken)) | ||||
|                         .andExpect(status().isOk()) | ||||
|  | @ -1098,7 +1098,7 @@ public class UserAPITest extends AdministrationAPIIntegrationTester { | |||
|         final String instAdminToken = getAdminInstitution2Access(); | ||||
|         names = this.jsonMapper.readValue( | ||||
|                 this.mockMvc | ||||
|                         .perform(get(this.endpoint + API.USER_ACCOUNT_ENDPOINT + "/names") | ||||
|                         .perform(get(this.endpoint + API.USER_ACCOUNT_ENDPOINT + "/names" + "?institutionId=2") | ||||
|                                 .contentType(MediaType.APPLICATION_FORM_URLENCODED) | ||||
|                                 .header("Authorization", "Bearer " + instAdminToken)) | ||||
|                         .andExpect(status().isOk()) | ||||
|  | @ -1117,7 +1117,7 @@ public class UserAPITest extends AdministrationAPIIntegrationTester { | |||
|         names = this.jsonMapper.readValue( | ||||
|                 this.mockMvc | ||||
|                         .perform( | ||||
|                                 get(this.endpoint + API.USER_ACCOUNT_ENDPOINT + "/names?active=true") | ||||
|                                 get(this.endpoint + API.USER_ACCOUNT_ENDPOINT + "/names?active=true&institutionId=2") | ||||
|                                         .contentType(MediaType.APPLICATION_FORM_URLENCODED) | ||||
|                                         .header("Authorization", "Bearer " + instAdminToken)) | ||||
|                         .andExpect(status().isOk()) | ||||
|  |  | |||
 anhefti
						anhefti