SEBSERV-27 #Insitution List and actions
This commit is contained in:
		
							parent
							
								
									60bd32c2cb
								
							
						
					
					
						commit
						04d438923d
					
				
					 50 changed files with 1296 additions and 335 deletions
				
			
		|  | @ -56,6 +56,11 @@ public class EntityName implements ModelIdAware, ModelNameAware { | ||||||
|         return this.modelId; |         return this.modelId; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     @JsonIgnore | ||||||
|  |     public EntityKey getEntityKey() { | ||||||
|  |         return new EntityKey(getModelId(), getEntityType()); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     @Override |     @Override | ||||||
|     public int hashCode() { |     public int hashCode() { | ||||||
|         final int prime = 31; |         final int prime = 31; | ||||||
|  |  | ||||||
|  | @ -95,7 +95,7 @@ public final class LmsSetupTestResult { | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     public static final LmsSetupTestResult ofQuizRequestError(final String message) { |     public static final LmsSetupTestResult ofQuizRequestError(final String message) { | ||||||
|         return new LmsSetupTestResult(false, null, null, message); |         return new LmsSetupTestResult(false, Collections.emptyList(), null, message); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -88,6 +88,12 @@ public final class Result<T> { | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     public void handleError(final Consumer<Throwable> errorHandler) { | ||||||
|  |         if (this.error != null) { | ||||||
|  |             errorHandler.accept(this.error); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     /** Use this to get the resulting value or (if null) to get a given other value |     /** Use this to get the resulting value or (if null) to get a given other value | ||||||
|      * |      * | ||||||
|      * @param other the other value to get if the computed value is null |      * @param other the other value to get if the computed value is null | ||||||
|  |  | ||||||
|  | @ -1,28 +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; |  | ||||||
| 
 |  | ||||||
| import org.eclipse.swt.widgets.Composite; |  | ||||||
| import org.eclipse.swt.widgets.Control; |  | ||||||
| 
 |  | ||||||
| public final class RWTUtils { |  | ||||||
| 
 |  | ||||||
|     public static final String TEXT_NAME_H2 = "h2"; |  | ||||||
| 
 |  | ||||||
|     public static void clearComposite(final Composite parent) { |  | ||||||
|         if (parent == null) { |  | ||||||
|             return; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         for (final Control control : parent.getChildren()) { |  | ||||||
|             control.dispose(); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
| } |  | ||||||
|  | @ -11,6 +11,9 @@ package ch.ethz.seb.sebserver.gui.service.page; | ||||||
| import org.eclipse.swt.widgets.Composite; | import org.eclipse.swt.widgets.Composite; | ||||||
| import org.eclipse.swt.widgets.Shell; | import org.eclipse.swt.widgets.Shell; | ||||||
| 
 | 
 | ||||||
|  | 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.activity.ActivitySelection; | ||||||
| import ch.ethz.seb.sebserver.gui.service.page.event.PageEvent; | import ch.ethz.seb.sebserver.gui.service.page.event.PageEvent; | ||||||
| 
 | 
 | ||||||
| public interface PageContext { | public interface PageContext { | ||||||
|  | @ -30,7 +33,10 @@ public interface PageContext { | ||||||
| 
 | 
 | ||||||
|         public static final String ATTR_PAGE_TEMPLATE_COMPOSER_NAME = "ATTR_PAGE_TEMPLATE_COMPOSER_NAME"; |         public static final String ATTR_PAGE_TEMPLATE_COMPOSER_NAME = "ATTR_PAGE_TEMPLATE_COMPOSER_NAME"; | ||||||
| 
 | 
 | ||||||
|         public static final String INSTITUTION_ID = "INSTITUTION_ID"; |         public static final String ATTR_ENTITY_ID = "ENTITY_ID"; | ||||||
|  |         public static final String ATTR_PARENT_ENTITY_ID = "PARENT_ENTITY_ID"; | ||||||
|  |         public static final String ATTR_ENTITY_TYPE = "ENTITY_TYPE"; | ||||||
|  |         public static final String ATTR_PARENT_ENTITY_TYPE = "PARENT_ENTITY_TYPE"; | ||||||
| 
 | 
 | ||||||
| //        public static final String USER_NAME = "USER_NAME"; | //        public static final String USER_NAME = "USER_NAME"; | ||||||
| //        public static final String PASSWORD = "PASSWORD"; | //        public static final String PASSWORD = "PASSWORD"; | ||||||
|  | @ -86,6 +92,8 @@ public interface PageContext { | ||||||
|      * @return this PageContext instance (builder pattern) */ |      * @return this PageContext instance (builder pattern) */ | ||||||
|     PageContext withAttr(String key, String value); |     PageContext withAttr(String key, String value); | ||||||
| 
 | 
 | ||||||
|  |     PageContext withSelection(ActivitySelection selection); | ||||||
|  | 
 | ||||||
|     String getAttribute(String name); |     String getAttribute(String name); | ||||||
| 
 | 
 | ||||||
|     String getAttribute(String name, String def); |     String getAttribute(String name, String def); | ||||||
|  | @ -99,6 +107,8 @@ public interface PageContext { | ||||||
|      * @param event the concrete PageEvent instance */ |      * @param event the concrete PageEvent instance */ | ||||||
|     <T extends PageEvent> void publishPageEvent(T event); |     <T extends PageEvent> void publishPageEvent(T event); | ||||||
| 
 | 
 | ||||||
|  |     Action createAction(ActionDefinition actionDefinition); | ||||||
|  | 
 | ||||||
|     /** Apply a confirm dialog with a specified confirm message and a callback code |     /** Apply a confirm dialog with a specified confirm message and a callback code | ||||||
|      * block that will be executed on users OK selection. |      * block that will be executed on users OK selection. | ||||||
|      * |      * | ||||||
|  | @ -125,4 +135,6 @@ public interface PageContext { | ||||||
| 
 | 
 | ||||||
|     <T> T logoutOnError(Throwable error); |     <T> T logoutOnError(Throwable error); | ||||||
| 
 | 
 | ||||||
|  |     void publishPageMessage(PageMessageException pme); | ||||||
|  | 
 | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -1,25 +0,0 @@ | ||||||
| /* |  | ||||||
|  * Copyright (c) 2018 ETH Zürich, Educational Development and Technology (LET) |  | ||||||
|  * |  | ||||||
|  * This Source Code Form is subject to the terms of the Mozilla Public |  | ||||||
|  * License, v. 2.0. If a copy of the MPL was not distributed with this |  | ||||||
|  * file, You can obtain one at http://mozilla.org/MPL/2.0/. |  | ||||||
|  */ |  | ||||||
| 
 |  | ||||||
| package ch.ethz.seb.sebserver.gui.service.page; |  | ||||||
| 
 |  | ||||||
| import ch.ethz.seb.sebserver.gui.service.page.event.PageEvent; |  | ||||||
| 
 |  | ||||||
| public interface PageEventListener<T extends PageEvent> { |  | ||||||
| 
 |  | ||||||
|     String LISTENER_ATTRIBUTE_KEY = "PageEventListener"; |  | ||||||
| 
 |  | ||||||
|     boolean match(Class<? extends PageEvent> eventType); |  | ||||||
| 
 |  | ||||||
|     default int priority() { |  | ||||||
|         return 1; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     void notify(T event); |  | ||||||
| 
 |  | ||||||
| } |  | ||||||
|  | @ -0,0 +1,23 @@ | ||||||
|  | /* | ||||||
|  |  * Copyright (c) 2019 ETH Zürich, Educational Development and Technology (LET) | ||||||
|  |  * | ||||||
|  |  * This Source Code Form is subject to the terms of the Mozilla Public | ||||||
|  |  * License, v. 2.0. If a copy of the MPL was not distributed with this | ||||||
|  |  * file, You can obtain one at http://mozilla.org/MPL/2.0/. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | package ch.ethz.seb.sebserver.gui.service.page; | ||||||
|  | 
 | ||||||
|  | public class PageMessageException extends RuntimeException { | ||||||
|  | 
 | ||||||
|  |     private static final long serialVersionUID = -6967378384991469166L; | ||||||
|  | 
 | ||||||
|  |     public PageMessageException(final String message, final Throwable cause) { | ||||||
|  |         super(message, cause); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public PageMessageException(final String message) { | ||||||
|  |         super(message); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  | } | ||||||
|  | @ -0,0 +1,110 @@ | ||||||
|  | /* | ||||||
|  |  * 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.action; | ||||||
|  | 
 | ||||||
|  | import java.util.Set; | ||||||
|  | import java.util.function.Function; | ||||||
|  | import java.util.function.Supplier; | ||||||
|  | 
 | ||||||
|  | import org.apache.commons.lang3.StringUtils; | ||||||
|  | import org.slf4j.Logger; | ||||||
|  | import org.slf4j.LoggerFactory; | ||||||
|  | 
 | ||||||
|  | import ch.ethz.seb.sebserver.gbl.util.Result; | ||||||
|  | import ch.ethz.seb.sebserver.gui.service.page.PageContext; | ||||||
|  | import ch.ethz.seb.sebserver.gui.service.page.PageMessageException; | ||||||
|  | import ch.ethz.seb.sebserver.gui.service.page.event.ActionEvent; | ||||||
|  | import ch.ethz.seb.sebserver.gui.service.page.event.ActionPublishEvent; | ||||||
|  | import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestService; | ||||||
|  | 
 | ||||||
|  | public class Action implements Runnable { | ||||||
|  | 
 | ||||||
|  |     private static final Logger log = LoggerFactory.getLogger(Action.class); | ||||||
|  | 
 | ||||||
|  |     public final ActionDefinition definition; | ||||||
|  |     String confirmationMessage; | ||||||
|  |     String successMessage; | ||||||
|  |     boolean updateOnSelection; | ||||||
|  | 
 | ||||||
|  |     final RestService restService; | ||||||
|  |     final PageContext pageContext; | ||||||
|  |     Function<Action, Result<?>> exec; | ||||||
|  |     Supplier<Set<String>> selectionSupplier; | ||||||
|  | 
 | ||||||
|  |     public Action( | ||||||
|  |             final ActionDefinition definition, | ||||||
|  |             final PageContext pageContext, | ||||||
|  |             final RestService restService) { | ||||||
|  | 
 | ||||||
|  |         this.definition = definition; | ||||||
|  |         this.pageContext = pageContext; | ||||||
|  |         this.restService = restService; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     @Override | ||||||
|  |     public void run() { | ||||||
|  |         if (StringUtils.isNotBlank(this.confirmationMessage)) { | ||||||
|  |             this.pageContext.applyConfirmDialog( | ||||||
|  |                     this.confirmationMessage, | ||||||
|  |                     () -> exec()); | ||||||
|  |         } else { | ||||||
|  |             exec(); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     private void exec() { | ||||||
|  |         try { | ||||||
|  | 
 | ||||||
|  |             this.exec.apply(this) | ||||||
|  |                     .map(value -> { | ||||||
|  |                         this.pageContext.publishPageEvent( | ||||||
|  |                                 new ActionEvent(this.definition, value)); | ||||||
|  |                         return value; | ||||||
|  |                     }) | ||||||
|  |                     .getOrThrow(); | ||||||
|  | 
 | ||||||
|  |         } catch (final PageMessageException pme) { | ||||||
|  |             Action.this.pageContext.publishPageMessage(pme); | ||||||
|  |         } catch (final Throwable t) { | ||||||
|  |             log.error("Failed to execute action: {}", Action.this, t); | ||||||
|  |             Action.this.pageContext.notifyError("action.error.unexpected.message", t); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public Action withExec(final Function<Action, Result<?>> exec) { | ||||||
|  |         this.exec = exec; | ||||||
|  |         return this; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public Action withSelectionSupplier(final Supplier<Set<String>> selectionSupplier) { | ||||||
|  |         this.selectionSupplier = selectionSupplier; | ||||||
|  |         return this; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public Action withConfirm(final String confirmationMessage) { | ||||||
|  |         this.confirmationMessage = confirmationMessage; | ||||||
|  |         return this; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public Action withSuccess(final String successMessage) { | ||||||
|  |         this.successMessage = successMessage; | ||||||
|  |         return this; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public Action withUpdateOnSelection() { | ||||||
|  |         this.updateOnSelection = true; | ||||||
|  |         return this; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public PageContext publish() { | ||||||
|  |         this.pageContext.publishPageEvent(new ActionPublishEvent(this)); | ||||||
|  |         return this.pageContext; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  | } | ||||||
|  | @ -13,10 +13,14 @@ import ch.ethz.seb.sebserver.gui.service.widget.WidgetFactory.IconButtonType; | ||||||
| public enum ActionDefinition { | public enum ActionDefinition { | ||||||
| 
 | 
 | ||||||
|     INSTITUTION_NEW( |     INSTITUTION_NEW( | ||||||
|             "actions.new.institution", |             "sebserver.institution.action.new", | ||||||
|             IconButtonType.NEW_ACTION), |             IconButtonType.NEW_ACTION), | ||||||
| 
 | 
 | ||||||
|     INSTITUTION_MODIFY( |     INSTITUTION_MODIFY( | ||||||
|  |             "sebserver.institution.action.modify", | ||||||
|  |             IconButtonType.MODIFY_ACTION), | ||||||
|  | 
 | ||||||
|  |     INSTITUTION_SAVE( | ||||||
|             "actions.modify.institution", |             "actions.modify.institution", | ||||||
|             IconButtonType.SAVE_ACTION), |             IconButtonType.SAVE_ACTION), | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -22,11 +22,12 @@ import org.springframework.stereotype.Component; | ||||||
| 
 | 
 | ||||||
| import ch.ethz.seb.sebserver.gui.service.i18n.LocTextKey; | 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; | ||||||
| import ch.ethz.seb.sebserver.gui.service.page.PageEventListener; |  | ||||||
| import ch.ethz.seb.sebserver.gui.service.page.TemplateComposer; | import ch.ethz.seb.sebserver.gui.service.page.TemplateComposer; | ||||||
| import ch.ethz.seb.sebserver.gui.service.page.event.ActionPublishEvent; | import ch.ethz.seb.sebserver.gui.service.page.event.ActionPublishEvent; | ||||||
| import ch.ethz.seb.sebserver.gui.service.page.event.ActionPublishEventListener; | import ch.ethz.seb.sebserver.gui.service.page.event.ActionPublishEventListener; | ||||||
|  | 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; | ||||||
|  | import ch.ethz.seb.sebserver.gui.service.widget.WidgetFactory.CustomVariant; | ||||||
| 
 | 
 | ||||||
| @Lazy | @Lazy | ||||||
| @Component | @Component | ||||||
|  | @ -42,18 +43,21 @@ public class ActionPane implements TemplateComposer { | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     @Override |     @Override | ||||||
|     public void compose(final PageContext composerCtx) { |     public void compose(final PageContext pageContext) { | ||||||
| 
 | 
 | ||||||
|         final Label label = this.widgetFactory.labelLocalized( |         final Label label = this.widgetFactory.labelLocalized( | ||||||
|                 composerCtx.getParent(), |                 pageContext.getParent(), | ||||||
|                 "h3", |                 CustomVariant.TEXT_H2, | ||||||
|                 new LocTextKey("sebserver.actionpane.title")); |                 new LocTextKey("sebserver.actionpane.title")); | ||||||
|  | 
 | ||||||
|         final GridData titleLayout = new GridData(SWT.FILL, SWT.TOP, true, false); |         final GridData titleLayout = new GridData(SWT.FILL, SWT.TOP, true, false); | ||||||
|         titleLayout.verticalIndent = 10; |         titleLayout.verticalIndent = 10; | ||||||
|         titleLayout.horizontalIndent = 10; |         titleLayout.horizontalIndent = 10; | ||||||
|         label.setLayoutData(titleLayout); |         label.setLayoutData(titleLayout); | ||||||
| 
 | 
 | ||||||
|         final Tree actions = this.widgetFactory.treeLocalized(composerCtx.getParent(), SWT.SINGLE | SWT.FULL_SELECTION); |         final Tree actions = this.widgetFactory.treeLocalized( | ||||||
|  |                 pageContext.getParent(), | ||||||
|  |                 SWT.SINGLE | SWT.FULL_SELECTION); | ||||||
|         actions.setData(RWT.CUSTOM_VARIANT, "actions"); |         actions.setData(RWT.CUSTOM_VARIANT, "actions"); | ||||||
|         final GridData gridData = new GridData(SWT.FILL, SWT.FILL, true, true); |         final GridData gridData = new GridData(SWT.FILL, SWT.FILL, true, true); | ||||||
|         actions.setLayoutData(gridData); |         actions.setLayoutData(gridData); | ||||||
|  | @ -69,7 +73,7 @@ public class ActionPane implements TemplateComposer { | ||||||
|         actions.addListener(SWT.Selection, event -> { |         actions.addListener(SWT.Selection, event -> { | ||||||
|             final TreeItem treeItem = (TreeItem) event.item; |             final TreeItem treeItem = (TreeItem) event.item; | ||||||
| 
 | 
 | ||||||
|             final Runnable action = (Runnable) treeItem.getData(ACTION_EVENT_CALL_KEY); |             final Action action = (Action) treeItem.getData(ACTION_EVENT_CALL_KEY); | ||||||
|             action.run(); |             action.run(); | ||||||
| 
 | 
 | ||||||
|             if (!treeItem.isDisposed()) { |             if (!treeItem.isDisposed()) { | ||||||
|  | @ -82,12 +86,16 @@ public class ActionPane implements TemplateComposer { | ||||||
|                 new ActionPublishEventListener() { |                 new ActionPublishEventListener() { | ||||||
|                     @Override |                     @Override | ||||||
|                     public void notify(final ActionPublishEvent event) { |                     public void notify(final ActionPublishEvent event) { | ||||||
|  | 
 | ||||||
|                         final TreeItem actionItem = ActionPane.this.widgetFactory.treeItemLocalized( |                         final TreeItem actionItem = ActionPane.this.widgetFactory.treeItemLocalized( | ||||||
|                                 actions, |                                 actions, | ||||||
|                                 event.actionDefinition.name); |                                 event.action.definition.name); | ||||||
|                         actionItem.setImage(event.actionDefinition.icon.getImage(composerCtx.getParent().getDisplay())); | 
 | ||||||
|                         actionItem.setData(ACTION_EVENT_CALL_KEY, |                         actionItem.setImage(event.action.definition.icon.getImage( | ||||||
|                                 new SafeActionExecution(composerCtx, event, event.run)); |                                 pageContext.getParent().getDisplay())); | ||||||
|  | 
 | ||||||
|  |                         actionItem.setData(ACTION_EVENT_CALL_KEY, event.action); | ||||||
|  | 
 | ||||||
|                     } |                     } | ||||||
|                 }); |                 }); | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -8,9 +8,47 @@ | ||||||
| 
 | 
 | ||||||
| package ch.ethz.seb.sebserver.gui.service.page.action; | package ch.ethz.seb.sebserver.gui.service.page.action; | ||||||
| 
 | 
 | ||||||
| public interface InstitutionActions { | import static ch.ethz.seb.sebserver.gui.service.page.activity.ActivitySelection.Activity.INSTITUTION_NODE; | ||||||
| 
 | 
 | ||||||
| //    /** Use this higher-order function to create a new Institution action Runnable. | import java.util.Collection; | ||||||
|  | import java.util.function.Function; | ||||||
|  | 
 | ||||||
|  | import ch.ethz.seb.sebserver.gbl.model.EntityKey; | ||||||
|  | import ch.ethz.seb.sebserver.gbl.model.EntityType; | ||||||
|  | import ch.ethz.seb.sebserver.gbl.util.Result; | ||||||
|  | import ch.ethz.seb.sebserver.gui.service.page.PageMessageException; | ||||||
|  | import ch.ethz.seb.sebserver.gui.service.page.event.ActivitySelectionEvent; | ||||||
|  | import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.institution.NewInstitution; | ||||||
|  | import ch.ethz.seb.sebserver.gui.service.table.EntityTable; | ||||||
|  | 
 | ||||||
|  | public final class InstitutionActions { | ||||||
|  | 
 | ||||||
|  |     public static Result<?> newInstitution(final Action action) { | ||||||
|  |         return action.restService | ||||||
|  |                 .getBuilder(NewInstitution.class) | ||||||
|  |                 .call(); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public static Function<Action, Result<?>> editInstitution(final EntityTable<?> fromTable) { | ||||||
|  |         return action -> { | ||||||
|  |             final Collection<String> selection = fromTable.getSelection(); | ||||||
|  |             if (selection.isEmpty()) { | ||||||
|  |                 return Result.ofError(new PageMessageException("sebserver.institution.info.pleaseSelect")); | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             final EntityKey entityKey = new EntityKey( | ||||||
|  |                     selection.iterator().next(), | ||||||
|  |                     EntityType.INSTITUTION); | ||||||
|  |             action.pageContext.publishPageEvent(new ActivitySelectionEvent( | ||||||
|  |                     INSTITUTION_NODE | ||||||
|  |                             .createSelection() | ||||||
|  |                             .withEntity(entityKey))); | ||||||
|  | 
 | ||||||
|  |             return Result.of(entityKey); | ||||||
|  |         }; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  | //    /** Use this higher-order function to create a new Institution action function. | ||||||
| //     * | //     * | ||||||
| //     * @return */ | //     * @return */ | ||||||
| //    static Runnable newInstitution(final PageContext composerCtx, final RestServices restServices) { | //    static Runnable newInstitution(final PageContext composerCtx, final RestServices restServices) { | ||||||
|  | @ -23,7 +61,7 @@ public interface InstitutionActions { | ||||||
| //        }; | //        }; | ||||||
| //    } | //    } | ||||||
| // | // | ||||||
| //    /** Use this higher-order function to create a delete Institution action Runnable. | //    /** Use this higher-order function to create a delete Institution action function. | ||||||
| //     * | //     * | ||||||
| //     * @return */ | //     * @return */ | ||||||
| //    static Runnable deleteInstitution(final PageContext composerCtx, final RestServices restServices, | //    static Runnable deleteInstitution(final PageContext composerCtx, final RestServices restServices, | ||||||
|  |  | ||||||
|  | @ -1,68 +0,0 @@ | ||||||
| /* |  | ||||||
|  * Copyright (c) 2018 ETH Zürich, Educational Development and Technology (LET) |  | ||||||
|  * |  | ||||||
|  * This Source Code Form is subject to the terms of the Mozilla Public |  | ||||||
|  * License, v. 2.0. If a copy of the MPL was not distributed with this |  | ||||||
|  * file, You can obtain one at http://mozilla.org/MPL/2.0/. |  | ||||||
|  */ |  | ||||||
| 
 |  | ||||||
| package ch.ethz.seb.sebserver.gui.service.page.action; |  | ||||||
| 
 |  | ||||||
| import org.apache.commons.lang3.StringUtils; |  | ||||||
| import org.slf4j.Logger; |  | ||||||
| import org.slf4j.LoggerFactory; |  | ||||||
| 
 |  | ||||||
| import ch.ethz.seb.sebserver.gui.service.page.PageContext; |  | ||||||
| import ch.ethz.seb.sebserver.gui.service.page.event.ActionPublishEvent; |  | ||||||
| 
 |  | ||||||
| public class SafeActionExecution implements Runnable { |  | ||||||
| 
 |  | ||||||
|     private static final Logger log = LoggerFactory.getLogger(SafeActionExecution.class); |  | ||||||
| 
 |  | ||||||
|     private final PageContext pageContext; |  | ||||||
|     private final ActionPublishEvent actionEvent; |  | ||||||
|     private final Runnable actionExecution; |  | ||||||
| 
 |  | ||||||
|     public SafeActionExecution( |  | ||||||
|             final PageContext pageContext, |  | ||||||
|             final ActionPublishEvent actionEvent, |  | ||||||
|             final Runnable actionExecution) { |  | ||||||
| 
 |  | ||||||
|         this.pageContext = pageContext; |  | ||||||
|         this.actionEvent = actionEvent; |  | ||||||
|         this.actionExecution = actionExecution; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     @Override |  | ||||||
|     public void run() { |  | ||||||
|         try { |  | ||||||
|             if (StringUtils.isNotBlank(this.actionEvent.confirmationMessage)) { |  | ||||||
|                 this.pageContext.applyConfirmDialog( |  | ||||||
|                         this.actionEvent.confirmationMessage, |  | ||||||
|                         createConfirmationCallback()); |  | ||||||
|             } else { |  | ||||||
|                 this.actionExecution.run(); |  | ||||||
|             } |  | ||||||
|         } catch (final Throwable t) { |  | ||||||
|             log.error("Failed to execute action: {}", this.actionEvent, t); |  | ||||||
|             this.pageContext.notifyError("action.error.unexpected.message", t); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     private final Runnable createConfirmationCallback() { |  | ||||||
|         return new Runnable() { |  | ||||||
| 
 |  | ||||||
|             @Override |  | ||||||
|             public void run() { |  | ||||||
|                 try { |  | ||||||
|                     SafeActionExecution.this.actionExecution.run(); |  | ||||||
|                 } catch (final Throwable t) { |  | ||||||
|                     log.error("Failed to execute action: {}", SafeActionExecution.this.actionEvent, t); |  | ||||||
|                     SafeActionExecution.this.pageContext.notifyError("action.error.unexpected.message", t); |  | ||||||
|                 } |  | ||||||
| 
 |  | ||||||
|             } |  | ||||||
|         }; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
| } |  | ||||||
|  | @ -9,9 +9,7 @@ | ||||||
| package ch.ethz.seb.sebserver.gui.service.page.activity; | package ch.ethz.seb.sebserver.gui.service.page.activity; | ||||||
| 
 | 
 | ||||||
| import java.util.Collection; | import java.util.Collection; | ||||||
| import java.util.Collections; |  | ||||||
| import java.util.EnumMap; | import java.util.EnumMap; | ||||||
| import java.util.List; |  | ||||||
| import java.util.Map; | import java.util.Map; | ||||||
| 
 | 
 | ||||||
| import org.eclipse.swt.SWT; | import org.eclipse.swt.SWT; | ||||||
|  | @ -28,18 +26,18 @@ import ch.ethz.seb.sebserver.gbl.model.user.UserInfo; | ||||||
| import ch.ethz.seb.sebserver.gbl.model.user.UserRole; | import ch.ethz.seb.sebserver.gbl.model.user.UserRole; | ||||||
| import ch.ethz.seb.sebserver.gui.service.i18n.LocTextKey; | import ch.ethz.seb.sebserver.gui.service.i18n.LocTextKey; | ||||||
| import ch.ethz.seb.sebserver.gui.service.page.PageContext; | import ch.ethz.seb.sebserver.gui.service.page.PageContext; | ||||||
| import ch.ethz.seb.sebserver.gui.service.page.PageEventListener; |  | ||||||
| import ch.ethz.seb.sebserver.gui.service.page.TemplateComposer; | import ch.ethz.seb.sebserver.gui.service.page.TemplateComposer; | ||||||
| import ch.ethz.seb.sebserver.gui.service.page.action.ActionDefinition; | import ch.ethz.seb.sebserver.gui.service.page.action.ActionDefinition; | ||||||
| import ch.ethz.seb.sebserver.gui.service.page.activity.ActivitySelection.Activity; | import ch.ethz.seb.sebserver.gui.service.page.activity.ActivitySelection.Activity; | ||||||
| import ch.ethz.seb.sebserver.gui.service.page.event.ActionEvent; | import ch.ethz.seb.sebserver.gui.service.page.event.ActionEvent; | ||||||
| import ch.ethz.seb.sebserver.gui.service.page.event.ActionEventListener; | import ch.ethz.seb.sebserver.gui.service.page.event.ActionEventListener; | ||||||
| import ch.ethz.seb.sebserver.gui.service.page.event.ActivitySelectionEvent; | import ch.ethz.seb.sebserver.gui.service.page.event.ActivitySelectionEvent; | ||||||
|  | import ch.ethz.seb.sebserver.gui.service.page.event.PageEventListener; | ||||||
| import ch.ethz.seb.sebserver.gui.service.page.impl.MainPageState; | import ch.ethz.seb.sebserver.gui.service.page.impl.MainPageState; | ||||||
| import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestService; | import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestService; | ||||||
| import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.institution.GetInstitutionNames; |  | ||||||
| import ch.ethz.seb.sebserver.gui.service.remote.webservice.auth.AuthorizationContextHolder; | import ch.ethz.seb.sebserver.gui.service.remote.webservice.auth.AuthorizationContextHolder; | ||||||
| import ch.ethz.seb.sebserver.gui.service.widget.WidgetFactory; | import ch.ethz.seb.sebserver.gui.service.widget.WidgetFactory; | ||||||
|  | import ch.ethz.seb.sebserver.gui.service.widget.WidgetFactory.CustomVariant; | ||||||
| 
 | 
 | ||||||
| @Lazy | @Lazy | ||||||
| @Component | @Component | ||||||
|  | @ -76,7 +74,7 @@ public class ActivitiesPane implements TemplateComposer { | ||||||
| 
 | 
 | ||||||
|         final Label activities = this.widgetFactory.labelLocalized( |         final Label activities = this.widgetFactory.labelLocalized( | ||||||
|                 pageContext.getParent(), |                 pageContext.getParent(), | ||||||
|                 "h3", |                 CustomVariant.TEXT_H2, | ||||||
|                 new LocTextKey("sebserver.activitiespane.title")); |                 new LocTextKey("sebserver.activitiespane.title")); | ||||||
|         final GridData activitiesGridData = new GridData(SWT.FILL, SWT.TOP, true, false); |         final GridData activitiesGridData = new GridData(SWT.FILL, SWT.TOP, true, false); | ||||||
|         activitiesGridData.horizontalIndent = 20; |         activitiesGridData.horizontalIndent = 20; | ||||||
|  | @ -88,10 +86,10 @@ public class ActivitiesPane implements TemplateComposer { | ||||||
|         navigationGridData.horizontalIndent = 10; |         navigationGridData.horizontalIndent = 10; | ||||||
|         navigation.setLayoutData(navigationGridData); |         navigation.setLayoutData(navigationGridData); | ||||||
| 
 | 
 | ||||||
|         final List<EntityName> insitutionNames = this.restService | //        final List<EntityName> insitutionNames = this.restService | ||||||
|                 .getBuilder(GetInstitutionNames.class) | //                .getBuilder(GetInstitutionNames.class) | ||||||
|                 .call() | //                .call() | ||||||
|                 .get(pageContext::notifyError, () -> Collections.emptyList()); | //                .get(pageContext::notifyError, () -> Collections.emptyList()); | ||||||
| 
 | 
 | ||||||
|         if (userInfo.hasRole(UserRole.SEB_SERVER_ADMIN)) { |         if (userInfo.hasRole(UserRole.SEB_SERVER_ADMIN)) { | ||||||
|             // institutions (list) as root |             // institutions (list) as root | ||||||
|  | @ -100,12 +98,17 @@ public class ActivitiesPane implements TemplateComposer { | ||||||
|                     Activity.INSTITUTION_ROOT.title); |                     Activity.INSTITUTION_ROOT.title); | ||||||
|             ActivitySelection.inject(institutions, Activity.INSTITUTION_ROOT.createSelection()); |             ActivitySelection.inject(institutions, Activity.INSTITUTION_ROOT.createSelection()); | ||||||
| 
 | 
 | ||||||
|             for (final EntityName inst : insitutionNames) { | //            for (final EntityName inst : insitutionNames) { | ||||||
|                 createInstitutionItem(institutions, inst); | //                createInstitutionItem(institutions, inst); | ||||||
|             } | //            } | ||||||
|         } else { |         } else { | ||||||
|             final EntityName inst = insitutionNames.iterator().next(); |             // institution node as none root | ||||||
|             createInstitutionItem(navigation, inst); |             final TreeItem institutions = this.widgetFactory.treeItemLocalized( | ||||||
|  |                     navigation, | ||||||
|  |                     Activity.INSTITUTION_ROOT.title); | ||||||
|  |             ActivitySelection.inject(institutions, Activity.INSTITUTION_NODE.createSelection()); | ||||||
|  | //            final EntityName inst = insitutionNames.iterator().next(); | ||||||
|  | //            createInstitutionItem(navigation, inst); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
| //        final TreeItem user = this.widgetFactory.treeItemLocalized( | //        final TreeItem user = this.widgetFactory.treeItemLocalized( | ||||||
|  | @ -222,21 +225,25 @@ public class ActivitiesPane implements TemplateComposer { | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     static TreeItem createInstitutionItem(final Tree parent, final EntityName idAndName) { |     static TreeItem createInstitutionItem(final Tree parent, final EntityName entityName) { | ||||||
|         final TreeItem institution = new TreeItem(parent, SWT.NONE); |         final TreeItem institution = new TreeItem(parent, SWT.NONE); | ||||||
|         createInstitutionItem(idAndName, institution); |         createInstitutionItem(entityName, institution); | ||||||
|         return institution; |         return institution; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     static TreeItem createInstitutionItem(final TreeItem parent, final EntityName idAndName) { |     static TreeItem createInstitutionItem(final TreeItem parent, final EntityName entityName) { | ||||||
|         final TreeItem institution = new TreeItem(parent, SWT.NONE); |         final TreeItem institution = new TreeItem(parent, SWT.NONE); | ||||||
|         createInstitutionItem(idAndName, institution); |         createInstitutionItem(entityName, institution); | ||||||
|         return institution; |         return institution; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     static void createInstitutionItem(final EntityName idAndName, final TreeItem institution) { |     static void createInstitutionItem(final EntityName entityName, final TreeItem institution) { | ||||||
|         institution.setText(idAndName.name); |         institution.setText(entityName.name); | ||||||
|         ActivitySelection.inject(institution, Activity.INSTITUTION_NODE.createSelection(idAndName)); |         ActivitySelection.inject( | ||||||
|  |                 institution, | ||||||
|  |                 Activity.INSTITUTION_NODE | ||||||
|  |                         .createSelection() | ||||||
|  |                         .withEntity(entityName.getEntityKey())); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     static final TreeItem findItemByActivity( |     static final TreeItem findItemByActivity( | ||||||
|  | @ -250,7 +257,7 @@ public class ActivitiesPane implements TemplateComposer { | ||||||
| 
 | 
 | ||||||
|         for (final TreeItem item : items) { |         for (final TreeItem item : items) { | ||||||
|             final ActivitySelection activitySelection = ActivitySelection.get(item); |             final ActivitySelection activitySelection = ActivitySelection.get(item); | ||||||
|             final String id = activitySelection.getObjectIdentifier(); |             final String id = activitySelection.getEntityId(); | ||||||
|             if (activitySelection != null && activitySelection.activity == activity && |             if (activitySelection != null && activitySelection.activity == activity && | ||||||
|                     (id == null || (objectId != null && objectId.equals(id)))) { |                     (id == null || (objectId != null && objectId.equals(id)))) { | ||||||
|                 return item; |                 return item; | ||||||
|  |  | ||||||
|  | @ -8,11 +8,14 @@ | ||||||
| 
 | 
 | ||||||
| package ch.ethz.seb.sebserver.gui.service.page.activity; | package ch.ethz.seb.sebserver.gui.service.page.activity; | ||||||
| 
 | 
 | ||||||
|  | import java.util.Collections; | ||||||
|  | import java.util.HashMap; | ||||||
|  | import java.util.Map; | ||||||
| import java.util.function.Consumer; | import java.util.function.Consumer; | ||||||
| 
 | 
 | ||||||
| import org.eclipse.swt.widgets.TreeItem; | import org.eclipse.swt.widgets.TreeItem; | ||||||
| 
 | 
 | ||||||
| import ch.ethz.seb.sebserver.gbl.model.EntityName; | import ch.ethz.seb.sebserver.gbl.model.EntityKey; | ||||||
| import ch.ethz.seb.sebserver.gui.service.i18n.LocTextKey; | import ch.ethz.seb.sebserver.gui.service.i18n.LocTextKey; | ||||||
| import ch.ethz.seb.sebserver.gui.service.page.PageContext.AttributeKeys; | import ch.ethz.seb.sebserver.gui.service.page.PageContext.AttributeKeys; | ||||||
| import ch.ethz.seb.sebserver.gui.service.page.TemplateComposer; | import ch.ethz.seb.sebserver.gui.service.page.TemplateComposer; | ||||||
|  | @ -30,7 +33,7 @@ public class ActivitySelection { | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
|     public enum Activity { |     public enum Activity { | ||||||
|         NONE(TODOTemplate.class, TODOTemplate.class, (String) null), |         NONE(TODOTemplate.class, TODOTemplate.class), | ||||||
|         INSTITUTION_ROOT( |         INSTITUTION_ROOT( | ||||||
|                 InstitutionList.class, |                 InstitutionList.class, | ||||||
|                 ActionPane.class, |                 ActionPane.class, | ||||||
|  | @ -38,7 +41,7 @@ public class ActivitySelection { | ||||||
|         INSTITUTION_NODE( |         INSTITUTION_NODE( | ||||||
|                 TODOTemplate.class, |                 TODOTemplate.class, | ||||||
|                 ActionPane.class, |                 ActionPane.class, | ||||||
|                 AttributeKeys.INSTITUTION_ID), |                 new LocTextKey("sebserver.activities.inst")), | ||||||
| // | // | ||||||
| //        USERS(UserAccountsForm.class, ActionPane.class), | //        USERS(UserAccountsForm.class, ActionPane.class), | ||||||
| // | // | ||||||
|  | @ -55,7 +58,7 @@ public class ActivitySelection { | ||||||
|         public final LocTextKey title; |         public final LocTextKey title; | ||||||
|         public final Class<? extends TemplateComposer> contentPaneComposer; |         public final Class<? extends TemplateComposer> contentPaneComposer; | ||||||
|         public final Class<? extends TemplateComposer> actionPaneComposer; |         public final Class<? extends TemplateComposer> actionPaneComposer; | ||||||
|         public final String objectIdentifierAttribute; |         //public final String modelIdAttribute; | ||||||
| 
 | 
 | ||||||
|         private Activity( |         private Activity( | ||||||
|                 final Class<? extends TemplateComposer> objectPaneComposer, |                 final Class<? extends TemplateComposer> objectPaneComposer, | ||||||
|  | @ -65,43 +68,54 @@ public class ActivitySelection { | ||||||
|             this.title = title; |             this.title = title; | ||||||
|             this.contentPaneComposer = objectPaneComposer; |             this.contentPaneComposer = objectPaneComposer; | ||||||
|             this.actionPaneComposer = selectionPaneComposer; |             this.actionPaneComposer = selectionPaneComposer; | ||||||
|             this.objectIdentifierAttribute = null; |  | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         private Activity( |         private Activity( | ||||||
|                 final Class<? extends TemplateComposer> objectPaneComposer, |                 final Class<? extends TemplateComposer> objectPaneComposer, | ||||||
|                 final Class<? extends TemplateComposer> selectionPaneComposer, |                 final Class<? extends TemplateComposer> selectionPaneComposer) { | ||||||
|                 final String objectIdentifierAttribute) { |  | ||||||
| 
 | 
 | ||||||
|             this.title = null; |             this.title = null; | ||||||
|             this.contentPaneComposer = objectPaneComposer; |             this.contentPaneComposer = objectPaneComposer; | ||||||
|             this.actionPaneComposer = selectionPaneComposer; |             this.actionPaneComposer = selectionPaneComposer; | ||||||
|             this.objectIdentifierAttribute = objectIdentifierAttribute; |  | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         public final ActivitySelection createSelection() { |         public final ActivitySelection createSelection() { | ||||||
|             return new ActivitySelection(this); |             return new ActivitySelection(this); | ||||||
|         } |         } | ||||||
| 
 |  | ||||||
|         public final ActivitySelection createSelection(final EntityName entityName) { |  | ||||||
|             return new ActivitySelection(this, entityName); |  | ||||||
|         } |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     private static final String ATTR_ACTIVITY_SELECTION = "ACTIVITY_SELECTION"; |     private static final String ATTR_ACTIVITY_SELECTION = "ACTIVITY_SELECTION"; | ||||||
| 
 | 
 | ||||||
|     public final Activity activity; |     public final Activity activity; | ||||||
|     public final EntityName entityName; |     final Map<String, String> attributes; | ||||||
|     Consumer<TreeItem> expandFunction = EMPTY_FUNCTION; |     Consumer<TreeItem> expandFunction = EMPTY_FUNCTION; | ||||||
| 
 | 
 | ||||||
|     ActivitySelection(final Activity activity) { |     ActivitySelection(final Activity activity) { | ||||||
|         this(activity, null); |         this.activity = activity; | ||||||
|  |         this.attributes = new HashMap<>(); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     ActivitySelection(final Activity activity, final EntityName entityName) { |     public ActivitySelection withEntity(final EntityKey entityKey) { | ||||||
|         this.activity = activity; |         if (entityKey != null) { | ||||||
|         this.entityName = entityName; |             this.attributes.put(AttributeKeys.ATTR_ENTITY_ID, entityKey.modelId); | ||||||
|         this.expandFunction = EMPTY_FUNCTION; |             this.attributes.put(AttributeKeys.ATTR_ENTITY_TYPE, entityKey.entityType.name()); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         return this; | ||||||
|  | 
 | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public ActivitySelection withParentEntity(final EntityKey parentEntityKey) { | ||||||
|  |         if (parentEntityKey != null) { | ||||||
|  |             this.attributes.put(AttributeKeys.ATTR_PARENT_ENTITY_ID, parentEntityKey.modelId); | ||||||
|  |             this.attributes.put(AttributeKeys.ATTR_PARENT_ENTITY_TYPE, parentEntityKey.entityType.name()); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         return this; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public Map<String, String> getAttributes() { | ||||||
|  |         return Collections.unmodifiableMap(this.attributes); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     public ActivitySelection withExpandFunction(final Consumer<TreeItem> expandFunction) { |     public ActivitySelection withExpandFunction(final Consumer<TreeItem> expandFunction) { | ||||||
|  | @ -112,44 +126,12 @@ public class ActivitySelection { | ||||||
|         return this; |         return this; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     public String getObjectIdentifier() { |  | ||||||
|         if (this.entityName == null) { |  | ||||||
|             return null; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         return this.entityName.modelId; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     public void processExpand(final TreeItem item) { |     public void processExpand(final TreeItem item) { | ||||||
|         this.expandFunction.accept(item); |         this.expandFunction.accept(item); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     @Override |     public String getEntityId() { | ||||||
|     public int hashCode() { |         return this.attributes.get(AttributeKeys.ATTR_ENTITY_ID); | ||||||
|         final int prime = 31; |  | ||||||
|         int result = 1; |  | ||||||
|         result = prime * result + ((this.activity == null) ? 0 : this.activity.hashCode()); |  | ||||||
|         result = prime * result + ((this.entityName == null) ? 0 : this.entityName.hashCode()); |  | ||||||
|         return result; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     @Override |  | ||||||
|     public boolean equals(final Object obj) { |  | ||||||
|         if (this == obj) |  | ||||||
|             return true; |  | ||||||
|         if (obj == null) |  | ||||||
|             return false; |  | ||||||
|         if (getClass() != obj.getClass()) |  | ||||||
|             return false; |  | ||||||
|         final ActivitySelection other = (ActivitySelection) obj; |  | ||||||
|         if (this.activity != other.activity) |  | ||||||
|             return false; |  | ||||||
|         if (this.entityName == null) { |  | ||||||
|             if (other.entityName != null) |  | ||||||
|                 return false; |  | ||||||
|         } else if (!this.entityName.equals(other.entityName)) |  | ||||||
|             return false; |  | ||||||
|         return true; |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     public static ActivitySelection get(final TreeItem item) { |     public static ActivitySelection get(final TreeItem item) { | ||||||
|  |  | ||||||
|  | @ -0,0 +1,13 @@ | ||||||
|  | /* | ||||||
|  |  * Copyright (c) 2019 ETH Zürich, Educational Development and Technology (LET) | ||||||
|  |  *  | ||||||
|  |  * This Source Code Form is subject to the terms of the Mozilla Public | ||||||
|  |  * License, v. 2.0. If a copy of the MPL was not distributed with this | ||||||
|  |  * file, You can obtain one at http://mozilla.org/MPL/2.0/. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | package ch.ethz.seb.sebserver.gui.service.page.content; | ||||||
|  | 
 | ||||||
|  | public class Institution { | ||||||
|  | 
 | ||||||
|  | } | ||||||
|  | @ -16,13 +16,17 @@ import org.springframework.context.annotation.Lazy; | ||||||
| import org.springframework.stereotype.Component; | import org.springframework.stereotype.Component; | ||||||
| 
 | 
 | ||||||
| import ch.ethz.seb.sebserver.gbl.model.Domain; | import ch.ethz.seb.sebserver.gbl.model.Domain; | ||||||
|  | import ch.ethz.seb.sebserver.gbl.model.institution.Institution; | ||||||
| import ch.ethz.seb.sebserver.gbl.profile.GuiProfile; | import ch.ethz.seb.sebserver.gbl.profile.GuiProfile; | ||||||
| import ch.ethz.seb.sebserver.gui.service.i18n.LocTextKey; | 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; | ||||||
| import ch.ethz.seb.sebserver.gui.service.page.TemplateComposer; | 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.remote.webservice.api.RestService; | import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestService; | ||||||
| import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.institution.GetInstitutions; | import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.institution.GetInstitutions; | ||||||
| import ch.ethz.seb.sebserver.gui.service.table.ColumnDefinition; | import ch.ethz.seb.sebserver.gui.service.table.ColumnDefinition; | ||||||
|  | import ch.ethz.seb.sebserver.gui.service.table.EntityTable; | ||||||
| import ch.ethz.seb.sebserver.gui.service.widget.WidgetFactory; | import ch.ethz.seb.sebserver.gui.service.widget.WidgetFactory; | ||||||
| 
 | 
 | ||||||
| @Lazy | @Lazy | ||||||
|  | @ -49,37 +53,39 @@ public class InstitutionList implements TemplateComposer { | ||||||
|         content.setLayout(contentLayout); |         content.setLayout(contentLayout); | ||||||
|         content.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); |         content.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); | ||||||
| 
 | 
 | ||||||
|  |         // title | ||||||
|         this.widgetFactory.labelLocalizedTitle( |         this.widgetFactory.labelLocalizedTitle( | ||||||
|                 content, |                 content, | ||||||
|                 new LocTextKey("sebserver.institution.list.title")); |                 new LocTextKey("sebserver.institution.list.title")); | ||||||
| 
 | 
 | ||||||
|         this.widgetFactory.entityTableBuilder(this.restService.getRestCall(GetInstitutions.class)) |         // table | ||||||
|                 .withPaging(3) |         final EntityTable<Institution> table = | ||||||
|                 .withColumn(new ColumnDefinition<>( |                 this.widgetFactory.entityTableBuilder(this.restService.getRestCall(GetInstitutions.class)) | ||||||
|                         Domain.INSTITUTION.ATTR_NAME, |                         .withPaging(3) | ||||||
|                         new LocTextKey("sebserver.institution.list.column.name"), |                         .withColumn(new ColumnDefinition<>( | ||||||
|                         null, |                                 Domain.INSTITUTION.ATTR_NAME, | ||||||
|                         0, |                                 new LocTextKey("sebserver.institution.list.column.name"), | ||||||
|                         entity -> entity.name, |                                 entity -> entity.name, | ||||||
|                         null, |                                 true)) | ||||||
|                         true)) |                         .withColumn(new ColumnDefinition<>( | ||||||
|                 .withColumn(new ColumnDefinition<>( |                                 Domain.INSTITUTION.ATTR_URL_SUFFIX, | ||||||
|                         Domain.INSTITUTION.ATTR_URL_SUFFIX, |                                 new LocTextKey("sebserver.institution.list.column.urlSuffix"), | ||||||
|                         new LocTextKey("sebserver.institution.list.column.urlSuffix"), |                                 entity -> entity.urlSuffix, | ||||||
|                         null, |                                 true)) | ||||||
|                         0, |                         .withColumn(new ColumnDefinition<>( | ||||||
|                         entity -> entity.urlSuffix, |                                 Domain.INSTITUTION.ATTR_ACTIVE, | ||||||
|                         null, |                                 new LocTextKey("sebserver.institution.list.column.active"), | ||||||
|                         true)) |                                 entity -> entity.active, | ||||||
|                 .withColumn(new ColumnDefinition<>( |                                 true)) | ||||||
|                         Domain.INSTITUTION.ATTR_ACTIVE, |                         .compose(content); | ||||||
|                         new LocTextKey("sebserver.institution.list.column.active"), | 
 | ||||||
|                         null, |         // propagate content actions to action-pane | ||||||
|                         0, |         pageContext.createAction(ActionDefinition.INSTITUTION_NEW) | ||||||
|                         entity -> entity.active, |                 .withExec(InstitutionActions::newInstitution) | ||||||
|                         null, |                 .publish() | ||||||
|                         true)) |                 .createAction(ActionDefinition.INSTITUTION_MODIFY) | ||||||
|                 .compose(content); |                 .withExec(InstitutionActions.editInstitution(table)) | ||||||
|  |                 .publish(); | ||||||
| 
 | 
 | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -10,6 +10,8 @@ package ch.ethz.seb.sebserver.gui.service.page.event; | ||||||
| 
 | 
 | ||||||
| import ch.ethz.seb.sebserver.gui.service.page.action.ActionDefinition; | import ch.ethz.seb.sebserver.gui.service.page.action.ActionDefinition; | ||||||
| 
 | 
 | ||||||
|  | /** This Event is used to propagate a user-action to the GUI system. | ||||||
|  |  * Potentially every component can listen to an Event and react on the user-action */ | ||||||
| public final class ActionEvent implements PageEvent { | public final class ActionEvent implements PageEvent { | ||||||
| 
 | 
 | ||||||
|     public final ActionDefinition actionDefinition; |     public final ActionDefinition actionDefinition; | ||||||
|  |  | ||||||
|  | @ -13,7 +13,6 @@ import java.util.function.Predicate; | ||||||
| 
 | 
 | ||||||
| import org.eclipse.swt.widgets.Widget; | import org.eclipse.swt.widgets.Widget; | ||||||
| 
 | 
 | ||||||
| import ch.ethz.seb.sebserver.gui.service.page.PageEventListener; |  | ||||||
| import ch.ethz.seb.sebserver.gui.service.page.action.ActionDefinition; | import ch.ethz.seb.sebserver.gui.service.page.action.ActionDefinition; | ||||||
| 
 | 
 | ||||||
| public interface ActionEventListener extends PageEventListener<ActionEvent> { | public interface ActionEventListener extends PageEventListener<ActionEvent> { | ||||||
|  |  | ||||||
|  | @ -8,46 +8,16 @@ | ||||||
| 
 | 
 | ||||||
| package ch.ethz.seb.sebserver.gui.service.page.event; | package ch.ethz.seb.sebserver.gui.service.page.event; | ||||||
| 
 | 
 | ||||||
| import ch.ethz.seb.sebserver.gui.service.page.action.ActionDefinition; | import ch.ethz.seb.sebserver.gui.service.page.action.Action; | ||||||
| 
 | 
 | ||||||
|  | /** This action is used to publish an Action to the Action-Pane for a specified context. | ||||||
|  |  * The ActionPane is listening to this events and render specified actions on notify */ | ||||||
| public class ActionPublishEvent implements PageEvent { | public class ActionPublishEvent implements PageEvent { | ||||||
| 
 | 
 | ||||||
|     public final ActionDefinition actionDefinition; |     public final Action action; | ||||||
|     public final Runnable run; |  | ||||||
|     public final String confirmationMessage; |  | ||||||
|     public final String successMessage; |  | ||||||
| 
 | 
 | ||||||
|     public ActionPublishEvent( |     public ActionPublishEvent(final Action action) { | ||||||
|             final ActionDefinition actionDefinition, |         this.action = action; | ||||||
|             final Runnable run) { |  | ||||||
| 
 |  | ||||||
|         this(actionDefinition, run, null, null); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     public ActionPublishEvent( |  | ||||||
|             final ActionDefinition actionDefinition, |  | ||||||
|             final Runnable run, |  | ||||||
|             final String confirmationMessage) { |  | ||||||
| 
 |  | ||||||
|         this(actionDefinition, run, confirmationMessage, null); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     public ActionPublishEvent( |  | ||||||
|             final ActionDefinition actionDefinition, |  | ||||||
|             final Runnable run, |  | ||||||
|             final String confirmationMessage, |  | ||||||
|             final String successMessage) { |  | ||||||
| 
 |  | ||||||
|         this.actionDefinition = actionDefinition; |  | ||||||
|         this.run = run; |  | ||||||
|         this.confirmationMessage = confirmationMessage; |  | ||||||
|         this.successMessage = successMessage; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     @Override |  | ||||||
|     public String toString() { |  | ||||||
|         return "ActionPublishEvent [actionDefinition=" + this.actionDefinition + ", confirmationMessage=" |  | ||||||
|                 + this.confirmationMessage + ", successMessage=" + this.successMessage + "]"; |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -8,8 +8,6 @@ | ||||||
| 
 | 
 | ||||||
| package ch.ethz.seb.sebserver.gui.service.page.event; | package ch.ethz.seb.sebserver.gui.service.page.event; | ||||||
| 
 | 
 | ||||||
| import ch.ethz.seb.sebserver.gui.service.page.PageEventListener; |  | ||||||
| 
 |  | ||||||
| public interface ActionPublishEventListener extends PageEventListener<ActionPublishEvent> { | public interface ActionPublishEventListener extends PageEventListener<ActionPublishEvent> { | ||||||
| 
 | 
 | ||||||
|     @Override |     @Override | ||||||
|  |  | ||||||
|  | @ -8,8 +8,6 @@ | ||||||
| 
 | 
 | ||||||
| package ch.ethz.seb.sebserver.gui.service.page.event; | package ch.ethz.seb.sebserver.gui.service.page.event; | ||||||
| 
 | 
 | ||||||
| import ch.ethz.seb.sebserver.gui.service.page.PageEventListener; |  | ||||||
| 
 |  | ||||||
| public interface ActivitySelectionListener extends PageEventListener<ActivitySelectionEvent> { | public interface ActivitySelectionListener extends PageEventListener<ActivitySelectionEvent> { | ||||||
| 
 | 
 | ||||||
|     @Override |     @Override | ||||||
|  |  | ||||||
|  | @ -8,8 +8,6 @@ | ||||||
| 
 | 
 | ||||||
| package ch.ethz.seb.sebserver.gui.service.page.event; | package ch.ethz.seb.sebserver.gui.service.page.event; | ||||||
| 
 | 
 | ||||||
| import ch.ethz.seb.sebserver.gui.service.page.PageEventListener; |  | ||||||
| 
 |  | ||||||
| public interface LogoutEventListener extends PageEventListener<LogoutEvent> { | public interface LogoutEventListener extends PageEventListener<LogoutEvent> { | ||||||
| 
 | 
 | ||||||
|     @Override |     @Override | ||||||
|  |  | ||||||
|  | @ -8,6 +8,11 @@ | ||||||
| 
 | 
 | ||||||
| package ch.ethz.seb.sebserver.gui.service.page.event; | package ch.ethz.seb.sebserver.gui.service.page.event; | ||||||
| 
 | 
 | ||||||
|  | /** This is just a marker interface for all page events. | ||||||
|  |  * Page events can be published to the actual page tree by using PageContext.publishPageEvent | ||||||
|  |  * | ||||||
|  |  * Potentially every component on the actual page tree can listen to an certain page event | ||||||
|  |  * by adding a specified listener within the components setData functionality. | ||||||
|  |  * see PageListener for more information */ | ||||||
| public interface PageEvent { | public interface PageEvent { | ||||||
| 
 |  | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -0,0 +1,41 @@ | ||||||
|  | /* | ||||||
|  |  * Copyright (c) 2018 ETH Zürich, Educational Development and Technology (LET) | ||||||
|  |  * | ||||||
|  |  * This Source Code Form is subject to the terms of the Mozilla Public | ||||||
|  |  * License, v. 2.0. If a copy of the MPL was not distributed with this | ||||||
|  |  * file, You can obtain one at http://mozilla.org/MPL/2.0/. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | package ch.ethz.seb.sebserver.gui.service.page.event; | ||||||
|  | 
 | ||||||
|  | /** Defines a listener for PageEvent. | ||||||
|  |  * | ||||||
|  |  * Potentially every component on the actual page tree can listen to an certain page event | ||||||
|  |  * by adding a specified listener within the components setData functionality. | ||||||
|  |  * | ||||||
|  |  * @param <T> the type of the PageEvent to listen to */ | ||||||
|  | public interface PageEventListener<T extends PageEvent> { | ||||||
|  | 
 | ||||||
|  |     /** The key name used to register PageEventListener instances within the | ||||||
|  |      * setData functionality of a component */ | ||||||
|  |     String LISTENER_ATTRIBUTE_KEY = "PageEventListener"; | ||||||
|  | 
 | ||||||
|  |     /** Used to check a concrete listener is interested in a specified type of PageEvent. | ||||||
|  |      * | ||||||
|  |      * @param eventType the PageEvent type | ||||||
|  |      * @return whether the listener is interested in being notified by the event or not */ | ||||||
|  |     boolean match(Class<? extends PageEvent> eventType); | ||||||
|  | 
 | ||||||
|  |     /** The listeners priority. | ||||||
|  |      * Use this if a dedicated order or sequence of listener notification is needed. | ||||||
|  |      * Default priority is 1 | ||||||
|  |      * | ||||||
|  |      * @return the priority of the listener that defines in witch sequence listeners of the same | ||||||
|  |      *         type get notified on a PageEvent propagation process on the current page-tree */ | ||||||
|  |     default int priority() { | ||||||
|  |         return 1; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     void notify(T event); | ||||||
|  | 
 | ||||||
|  | } | ||||||
|  | @ -0,0 +1,234 @@ | ||||||
|  | /* | ||||||
|  |  * Copyright (c) 2018 ETH Zürich, Educational Development and Technology (LET) | ||||||
|  |  * | ||||||
|  |  * This Source Code Form is subject to the terms of the Mozilla Public | ||||||
|  |  * License, v. 2.0. If a copy of the MPL was not distributed with this | ||||||
|  |  * file, You can obtain one at http://mozilla.org/MPL/2.0/. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | package ch.ethz.seb.sebserver.gui.service.page.form; | ||||||
|  | 
 | ||||||
|  | import java.util.ArrayList; | ||||||
|  | import java.util.HashSet; | ||||||
|  | import java.util.LinkedHashMap; | ||||||
|  | import java.util.List; | ||||||
|  | import java.util.Map; | ||||||
|  | import java.util.Set; | ||||||
|  | import java.util.function.Consumer; | ||||||
|  | import java.util.function.Predicate; | ||||||
|  | 
 | ||||||
|  | import org.eclipse.rap.rwt.RWT; | ||||||
|  | import org.eclipse.swt.widgets.Combo; | ||||||
|  | import org.eclipse.swt.widgets.Control; | ||||||
|  | import org.eclipse.swt.widgets.Label; | ||||||
|  | import org.eclipse.swt.widgets.Text; | ||||||
|  | 
 | ||||||
|  | import com.fasterxml.jackson.databind.node.ArrayNode; | ||||||
|  | import com.fasterxml.jackson.databind.node.ObjectNode; | ||||||
|  | 
 | ||||||
|  | import ch.ethz.seb.sebserver.gbl.api.JSONMapper; | ||||||
|  | import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.FormBinding; | ||||||
|  | import ch.ethz.seb.sebserver.gui.service.widget.SingleSelection; | ||||||
|  | 
 | ||||||
|  | public final class Form implements FormBinding { | ||||||
|  | 
 | ||||||
|  |     private final JSONMapper jsonMapper; | ||||||
|  |     private final ObjectNode objectRoot; | ||||||
|  | 
 | ||||||
|  |     private final Map<String, FormFieldAccessor<?>> formFields = new LinkedHashMap<>(); | ||||||
|  |     private final Map<String, Form> subForms = new LinkedHashMap<>(); | ||||||
|  |     private final Map<String, List<Form>> subLists = new LinkedHashMap<>(); | ||||||
|  |     private final Map<String, Set<String>> groups = new LinkedHashMap<>(); | ||||||
|  | 
 | ||||||
|  |     Form(final JSONMapper jsonMapper) { | ||||||
|  |         this.jsonMapper = jsonMapper; | ||||||
|  |         this.objectRoot = this.jsonMapper.createObjectNode(); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     @Override | ||||||
|  |     public String getFormAsJson() { | ||||||
|  |         try { | ||||||
|  |             flush(); | ||||||
|  |             return this.jsonMapper.writeValueAsString(this.objectRoot); | ||||||
|  |         } catch (final Exception e) { | ||||||
|  |             throw new RuntimeException("Unexpected error while trying to create json form Form post: ", e); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public String getValue(final String name) { | ||||||
|  |         final FormFieldAccessor<?> formFieldAccessor = this.formFields.get(name); | ||||||
|  |         if (formFieldAccessor != null) { | ||||||
|  |             return formFieldAccessor.getValue(); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         return null; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public void putStatic(final String name, final String value) { | ||||||
|  |         this.objectRoot.put(name, value); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public void addToGroup(final String groupName, final String fieldName) { | ||||||
|  |         if (this.formFields.containsKey(fieldName)) { | ||||||
|  |             this.groups.computeIfAbsent(groupName, k -> new HashSet<>()) | ||||||
|  |                     .add(fieldName); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public Form putField(final String name, final Label label, final Label field) { | ||||||
|  |         this.formFields.put(name, createAccessor(label, field)); | ||||||
|  |         return this; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public Form putField(final String name, final Label label, final Text field) { | ||||||
|  |         this.formFields.put(name, createAccessor(label, field)); | ||||||
|  |         return this; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public void putField(final String name, final Label label, final Combo field) { | ||||||
|  |         if (field instanceof SingleSelection) { | ||||||
|  |             this.formFields.put(name, createAccessor(label, (SingleSelection) field)); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public void putSubForm(final String name, final Form form) { | ||||||
|  |         this.subForms.put(name, form); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public Form getSubForm(final String name) { | ||||||
|  |         return this.subForms.get(name); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public void addSubForm(final String arrayName, final Form form) { | ||||||
|  |         final List<Form> array = this.subLists.computeIfAbsent(arrayName, k -> new ArrayList<>()); | ||||||
|  |         array.add(form); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public Form getSubForm(final String arrayName, final int index) { | ||||||
|  |         final List<Form> array = this.subLists.get(arrayName); | ||||||
|  |         if (array == null) { | ||||||
|  |             return null; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         return array.get(index); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public void allVisible() { | ||||||
|  |         process( | ||||||
|  |                 name -> true, | ||||||
|  |                 ffa -> ffa.setVisible(true)); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public void setVisible(final boolean visible, final String group) { | ||||||
|  |         if (!this.groups.containsKey(group)) { | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         final Set<String> namesSet = this.groups.get(group); | ||||||
|  |         process( | ||||||
|  |                 name -> namesSet.contains(name), | ||||||
|  |                 ffa -> ffa.setVisible(visible)); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public void process( | ||||||
|  |             final Predicate<String> nameFilter, | ||||||
|  |             final Consumer<FormFieldAccessor<?>> processor) { | ||||||
|  | 
 | ||||||
|  |         this.formFields.entrySet() | ||||||
|  |                 .stream() | ||||||
|  |                 .filter(entity -> nameFilter.test(entity.getKey())) | ||||||
|  |                 .map(entity -> entity.getValue()) | ||||||
|  |                 .forEach(processor); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public void flush() { | ||||||
|  |         for (final Map.Entry<String, FormFieldAccessor<?>> entry : this.formFields.entrySet()) { | ||||||
|  |             final FormFieldAccessor<?> accessor = entry.getValue(); | ||||||
|  |             if (accessor.control.isVisible()) { | ||||||
|  |                 this.objectRoot.put(entry.getKey(), accessor.getValue()); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         for (final Map.Entry<String, Form> entry : this.subForms.entrySet()) { | ||||||
|  |             final Form subForm = entry.getValue(); | ||||||
|  |             subForm.flush(); | ||||||
|  |             final ObjectNode objectNode = this.jsonMapper.createObjectNode(); | ||||||
|  |             this.objectRoot.set(entry.getKey(), objectNode); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         for (final Map.Entry<String, List<Form>> entry : this.subLists.entrySet()) { | ||||||
|  |             final List<Form> value = entry.getValue(); | ||||||
|  |             final ArrayNode arrayNode = this.jsonMapper.createArrayNode(); | ||||||
|  |             final int index = 0; | ||||||
|  |             for (final Form arrayForm : value) { | ||||||
|  |                 arrayForm.flush(); | ||||||
|  |                 arrayNode.insert(index, arrayForm.objectRoot); | ||||||
|  |             } | ||||||
|  |             this.objectRoot.set(entry.getKey(), arrayNode); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     //@formatter:off | ||||||
|  |     private FormFieldAccessor<?> createAccessor(final Label label, final Label field) { | ||||||
|  |         return new FormFieldAccessor<>(label, field) { | ||||||
|  |             @Override public String getValue() { return field.getText(); } | ||||||
|  |             @Override public void setValue(final String value) { field.setText(value); } | ||||||
|  |         }; | ||||||
|  |     } | ||||||
|  |     private FormFieldAccessor<Text> createAccessor(final Label label, final Text text) { | ||||||
|  |         return new FormFieldAccessor<>(label, text) { | ||||||
|  |             @Override public String getValue() { return text.getText(); } | ||||||
|  |             @Override public void setValue(final String value) { text.setText(value); } | ||||||
|  |         }; | ||||||
|  |     } | ||||||
|  |     private FormFieldAccessor<SingleSelection> createAccessor( | ||||||
|  |             final Label label, | ||||||
|  |             final SingleSelection singleSelection) { | ||||||
|  | 
 | ||||||
|  |         return new FormFieldAccessor<>(label, singleSelection) { | ||||||
|  |             @Override public String getValue() { return singleSelection.getSelectionValue(); } | ||||||
|  |             @Override public void setValue(final String value) { singleSelection.select(value); } | ||||||
|  |         }; | ||||||
|  |     } | ||||||
|  |     //@formatter:on | ||||||
|  | 
 | ||||||
|  |     public static abstract class FormFieldAccessor<T extends Control> { | ||||||
|  | 
 | ||||||
|  |         public final Label label; | ||||||
|  |         public final T control; | ||||||
|  |         private boolean hasError; | ||||||
|  | 
 | ||||||
|  |         public FormFieldAccessor(final Label label, final T control) { | ||||||
|  |             this.label = label; | ||||||
|  |             this.control = control; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public abstract String getValue(); | ||||||
|  | 
 | ||||||
|  |         public abstract void setValue(String value); | ||||||
|  | 
 | ||||||
|  |         public void setVisible(final boolean visible) { | ||||||
|  |             this.label.setVisible(visible); | ||||||
|  |             this.control.setVisible(visible); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public void setError(final String errorTooltip) { | ||||||
|  |             if (!this.hasError) { | ||||||
|  |                 this.control.setData(RWT.CUSTOM_VARIANT, "error"); | ||||||
|  |                 //this.control.setBackground(new Color(this.control.getDisplay(), 255, 0, 0, 50)); | ||||||
|  |                 this.control.setToolTipText(errorTooltip); | ||||||
|  |                 this.hasError = true; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public void resetError() { | ||||||
|  |             if (this.hasError) { | ||||||
|  |                 this.control.setData(RWT.CUSTOM_VARIANT, null); | ||||||
|  |                 //this.control.setBackground(new Color(this.control.getDisplay(), 0, 0, 0, 0)); | ||||||
|  |                 this.control.setToolTipText(null); | ||||||
|  |                 this.hasError = false; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  | } | ||||||
|  | @ -0,0 +1,188 @@ | ||||||
|  | /* | ||||||
|  |  * Copyright (c) 2018 ETH Zürich, Educational Development and Technology (LET) | ||||||
|  |  * | ||||||
|  |  * This Source Code Form is subject to the terms of the Mozilla Public | ||||||
|  |  * License, v. 2.0. If a copy of the MPL was not distributed with this | ||||||
|  |  * file, You can obtain one at http://mozilla.org/MPL/2.0/. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | package ch.ethz.seb.sebserver.gui.service.page.form; | ||||||
|  | 
 | ||||||
|  | import java.util.List; | ||||||
|  | import java.util.function.Consumer; | ||||||
|  | 
 | ||||||
|  | import org.apache.commons.lang3.StringUtils; | ||||||
|  | import org.eclipse.swt.SWT; | ||||||
|  | import org.eclipse.swt.layout.GridData; | ||||||
|  | import org.eclipse.swt.layout.GridLayout; | ||||||
|  | import org.eclipse.swt.widgets.Combo; | ||||||
|  | import org.eclipse.swt.widgets.Composite; | ||||||
|  | import org.eclipse.swt.widgets.Label; | ||||||
|  | import org.eclipse.swt.widgets.TabItem; | ||||||
|  | import org.slf4j.Logger; | ||||||
|  | import org.slf4j.LoggerFactory; | ||||||
|  | 
 | ||||||
|  | import ch.ethz.seb.sebserver.gbl.api.JSONMapper; | ||||||
|  | import ch.ethz.seb.sebserver.gbl.util.Tuple; | ||||||
|  | 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.remote.webservice.api.RestCall; | ||||||
|  | import ch.ethz.seb.sebserver.gui.service.widget.WidgetFactory; | ||||||
|  | 
 | ||||||
|  | public class FormBuilder { | ||||||
|  | 
 | ||||||
|  |     private static final Logger log = LoggerFactory.getLogger(FormBuilder.class); | ||||||
|  | 
 | ||||||
|  |     private final WidgetFactory widgetFactory; | ||||||
|  |     private final PolyglotPageService polyglotPageService; | ||||||
|  |     public final PageContext pageContext; | ||||||
|  |     public final Composite formParent; | ||||||
|  |     public final Form form; | ||||||
|  | 
 | ||||||
|  |     private boolean readonly = false; | ||||||
|  | 
 | ||||||
|  |     FormBuilder( | ||||||
|  |             final JSONMapper jsonMapper, | ||||||
|  |             final WidgetFactory widgetFactory, | ||||||
|  |             final PolyglotPageService polyglotPageService, | ||||||
|  |             final PageContext pageContext, | ||||||
|  |             final int rows) { | ||||||
|  | 
 | ||||||
|  |         this.widgetFactory = widgetFactory; | ||||||
|  |         this.polyglotPageService = polyglotPageService; | ||||||
|  |         this.pageContext = pageContext; | ||||||
|  |         this.form = new Form(jsonMapper); | ||||||
|  | 
 | ||||||
|  |         this.formParent = new Composite(pageContext.getParent(), SWT.NONE); | ||||||
|  |         final GridLayout layout = new GridLayout(rows, true); | ||||||
|  |         layout.horizontalSpacing = 10; | ||||||
|  |         layout.verticalSpacing = 10; | ||||||
|  |         layout.marginLeft = 10; | ||||||
|  |         layout.marginTop = 10; | ||||||
|  |         this.formParent.setLayout(layout); | ||||||
|  |         this.formParent.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public FormBuilder readonly(final boolean readonly) { | ||||||
|  |         this.readonly = readonly; | ||||||
|  |         return this; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public FormBuilder setVisible(final boolean visible, final String group) { | ||||||
|  |         this.form.setVisible(visible, group); | ||||||
|  |         return this; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public FormBuilder setControl(final TabItem instTab) { | ||||||
|  |         instTab.setControl(this.formParent); | ||||||
|  |         return this; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public FormBuilder addEmptyCell() { | ||||||
|  |         return addEmptyCell(1); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public FormBuilder addEmptyCell(final int span) { | ||||||
|  |         this.widgetFactory.formEmpty(this.formParent, span, 1); | ||||||
|  |         return this; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public FormBuilder putStaticValue(final String name, final String value) { | ||||||
|  |         try { | ||||||
|  |             this.form.putStatic(name, value); | ||||||
|  |         } catch (final Exception e) { | ||||||
|  |             log.error("Failed to put static field value to json object: ", e); | ||||||
|  |         } | ||||||
|  |         return this; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public FormBuilder addTextField( | ||||||
|  |             final String name, | ||||||
|  |             final String label, | ||||||
|  |             final String value) { | ||||||
|  | 
 | ||||||
|  |         return addTextField(name, label, value, 1, null); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public FormBuilder addTextField( | ||||||
|  |             final String name, | ||||||
|  |             final String label, | ||||||
|  |             final String value, | ||||||
|  |             final int span) { | ||||||
|  | 
 | ||||||
|  |         return addTextField(name, label, value, span, null); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public FormBuilder addTextField( | ||||||
|  |             final String name, | ||||||
|  |             final String label, | ||||||
|  |             final String value, | ||||||
|  |             final int span, | ||||||
|  |             final String group) { | ||||||
|  | 
 | ||||||
|  |         final Label lab = this.widgetFactory.formLabelLocalized(this.formParent, label); | ||||||
|  |         if (this.readonly) { | ||||||
|  |             this.form.putField(name, lab, this.widgetFactory.formValueLabel(this.formParent, value, span)); | ||||||
|  |         } else { | ||||||
|  |             this.form.putField(name, lab, this.widgetFactory.formTextInput(this.formParent, value, span, 1)); | ||||||
|  |         } | ||||||
|  |         if (StringUtils.isNoneBlank(group)) { | ||||||
|  |             this.form.addToGroup(group, name); | ||||||
|  |         } | ||||||
|  |         return this; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public FormBuilder addSingleSelection( | ||||||
|  |             final String name, | ||||||
|  |             final String label, | ||||||
|  |             final String value, | ||||||
|  |             final List<Tuple<String>> items, | ||||||
|  |             final Consumer<Form> selectionListener) { | ||||||
|  | 
 | ||||||
|  |         return addSingleSelection(name, label, value, items, selectionListener, 1, null); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public FormBuilder addSingleSelection( | ||||||
|  |             final String name, | ||||||
|  |             final String label, | ||||||
|  |             final String value, | ||||||
|  |             final List<Tuple<String>> items, | ||||||
|  |             final Consumer<Form> selectionListener, | ||||||
|  |             final int span) { | ||||||
|  | 
 | ||||||
|  |         return addSingleSelection(name, label, value, items, selectionListener, span, null); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public FormBuilder addSingleSelection( | ||||||
|  |             final String name, | ||||||
|  |             final String label, | ||||||
|  |             final String value, | ||||||
|  |             final List<Tuple<String>> items, | ||||||
|  |             final Consumer<Form> selectionListener, | ||||||
|  |             final int span, | ||||||
|  |             final String group) { | ||||||
|  | 
 | ||||||
|  |         final Label lab = this.widgetFactory.formLabelLocalized(this.formParent, label); | ||||||
|  |         if (this.readonly) { | ||||||
|  |             this.form.putField(name, lab, this.widgetFactory.formValueLabel(this.formParent, value, 2)); | ||||||
|  |         } else { | ||||||
|  |             final Combo selection = | ||||||
|  |                     this.widgetFactory.formSingleSelectionLocalized(this.formParent, value, items, span, 1); | ||||||
|  |             this.form.putField(name, lab, selection); | ||||||
|  |             if (selectionListener != null) { | ||||||
|  |                 selection.addListener(SWT.Selection, e -> { | ||||||
|  |                     selectionListener.accept(this.form); | ||||||
|  |                 }); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         if (StringUtils.isNoneBlank(group)) { | ||||||
|  |             this.form.addToGroup(group, name); | ||||||
|  |         } | ||||||
|  |         return this; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public <T> FormHandle<T> buildFor(final RestCall<T> post) { | ||||||
|  |         return new FormHandle<>(this.pageContext, this.form, post, this.polyglotPageService.getI18nSupport()); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  | } | ||||||
|  | @ -0,0 +1,91 @@ | ||||||
|  | /* | ||||||
|  |  * Copyright (c) 2018 ETH Zürich, Educational Development and Technology (LET) | ||||||
|  |  * | ||||||
|  |  * This Source Code Form is subject to the terms of the Mozilla Public | ||||||
|  |  * License, v. 2.0. If a copy of the MPL was not distributed with this | ||||||
|  |  * file, You can obtain one at http://mozilla.org/MPL/2.0/. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | package ch.ethz.seb.sebserver.gui.service.page.form; | ||||||
|  | 
 | ||||||
|  | import java.util.function.Consumer; | ||||||
|  | 
 | ||||||
|  | import org.slf4j.Logger; | ||||||
|  | import org.slf4j.LoggerFactory; | ||||||
|  | 
 | ||||||
|  | import ch.ethz.seb.sebserver.gui.service.i18n.I18nSupport; | ||||||
|  | import ch.ethz.seb.sebserver.gui.service.i18n.LocTextKey; | ||||||
|  | import ch.ethz.seb.sebserver.gui.service.page.PageContext; | ||||||
|  | import ch.ethz.seb.sebserver.gui.service.page.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; | ||||||
|  | 
 | ||||||
|  | public class FormHandle<T> { | ||||||
|  | 
 | ||||||
|  |     private static final Logger log = LoggerFactory.getLogger(FormHandle.class); | ||||||
|  | 
 | ||||||
|  |     public static final String FIELD_VALIDATION_LOCTEXT_PREFIX = "org.sebserver.form.validation.fieldError."; | ||||||
|  | 
 | ||||||
|  |     private final PageContext pageContext; | ||||||
|  |     private final Form form; | ||||||
|  |     private final RestCall<T> post; | ||||||
|  |     private final I18nSupport i18nSupport; | ||||||
|  | 
 | ||||||
|  |     FormHandle( | ||||||
|  |             final PageContext pageContext, | ||||||
|  |             final Form form, | ||||||
|  |             final RestCall<T> post, | ||||||
|  |             final I18nSupport i18nSupport) { | ||||||
|  | 
 | ||||||
|  |         this.pageContext = pageContext; | ||||||
|  |         this.form = form; | ||||||
|  |         this.post = post; | ||||||
|  |         this.i18nSupport = i18nSupport; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public void doAPIPost(final ActionDefinition action) { | ||||||
|  |         this.form.process( | ||||||
|  |                 name -> true, | ||||||
|  |                 fieldAccessor -> fieldAccessor.resetError()); | ||||||
|  | 
 | ||||||
|  |         this.post | ||||||
|  |                 .newBuilder() | ||||||
|  |                 .withFormBinding(this.form) | ||||||
|  |                 .call() | ||||||
|  |                 .map(result -> { | ||||||
|  |                     this.pageContext.publishPageEvent(new ActionEvent(action, result)); | ||||||
|  |                     return result; | ||||||
|  |                 }).onErrorDo(error -> { | ||||||
|  |                     if (error instanceof RestCallError) { | ||||||
|  |                         ((RestCallError) error) | ||||||
|  |                                 .getErrorMessages() | ||||||
|  |                                 .stream() | ||||||
|  |                                 .map(FieldValidationError::new) | ||||||
|  |                                 .forEach(fve -> this.form.process( | ||||||
|  |                                         name -> name.equals(fve.fieldName), | ||||||
|  |                                         fieldAccessor -> showValidationError(fieldAccessor, fve))); | ||||||
|  |                     } else { | ||||||
|  |                         log.error("Unexpected error while trying to post form: ", error); | ||||||
|  |                         this.pageContext.notifyError(error); | ||||||
|  |                     } | ||||||
|  |                 }); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     private final void showValidationError( | ||||||
|  |             final FormFieldAccessor<?> fieldAccessor, | ||||||
|  |             final FieldValidationError valError) { | ||||||
|  | 
 | ||||||
|  |         fieldAccessor.setError(this.i18nSupport.getText(new LocTextKey( | ||||||
|  |                 FIELD_VALIDATION_LOCTEXT_PREFIX + valError.errorType, | ||||||
|  |                 (Object[]) valError.attributes))); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public FormHandle<T> process(final Consumer<Form> consumer) { | ||||||
|  |         consumer.accept(this.form); | ||||||
|  |         return this; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  | } | ||||||
|  | @ -0,0 +1,54 @@ | ||||||
|  | /* | ||||||
|  |  * Copyright (c) 2018 ETH Zürich, Educational Development and Technology (LET) | ||||||
|  |  * | ||||||
|  |  * This Source Code Form is subject to the terms of the Mozilla Public | ||||||
|  |  * License, v. 2.0. If a copy of the MPL was not distributed with this | ||||||
|  |  * file, You can obtain one at http://mozilla.org/MPL/2.0/. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | package ch.ethz.seb.sebserver.gui.service.page.form; | ||||||
|  | 
 | ||||||
|  | import org.springframework.context.annotation.Lazy; | ||||||
|  | import org.springframework.stereotype.Component; | ||||||
|  | 
 | ||||||
|  | import ch.ethz.seb.sebserver.gbl.api.JSONMapper; | ||||||
|  | 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.widget.WidgetFactory; | ||||||
|  | 
 | ||||||
|  | @Lazy | ||||||
|  | @Component | ||||||
|  | public class PageFormService { | ||||||
|  | 
 | ||||||
|  |     private final JSONMapper jsonMapper; | ||||||
|  |     private final WidgetFactory widgetFactory; | ||||||
|  |     private final PolyglotPageService polyglotPageService; | ||||||
|  | 
 | ||||||
|  |     public PageFormService( | ||||||
|  |             final JSONMapper jsonMapper, | ||||||
|  |             final WidgetFactory widgetFactory, | ||||||
|  |             final PolyglotPageService polyglotPageService) { | ||||||
|  | 
 | ||||||
|  |         this.jsonMapper = jsonMapper; | ||||||
|  |         this.widgetFactory = widgetFactory; | ||||||
|  |         this.polyglotPageService = polyglotPageService; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public FormBuilder getBuilder(final PageContext pageContext, final int rows) { | ||||||
|  |         return new FormBuilder( | ||||||
|  |                 this.jsonMapper, | ||||||
|  |                 this.widgetFactory, | ||||||
|  |                 this.polyglotPageService, | ||||||
|  |                 pageContext, | ||||||
|  |                 rows); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public WidgetFactory getWidgetFactory() { | ||||||
|  |         return this.widgetFactory; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public PolyglotPageService getPolyglotPageService() { | ||||||
|  |         return this.polyglotPageService; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  | } | ||||||
|  | @ -20,13 +20,14 @@ import org.springframework.context.annotation.Lazy; | ||||||
| import org.springframework.stereotype.Service; | import org.springframework.stereotype.Service; | ||||||
| 
 | 
 | ||||||
| import ch.ethz.seb.sebserver.gbl.profile.GuiProfile; | import ch.ethz.seb.sebserver.gbl.profile.GuiProfile; | ||||||
| import ch.ethz.seb.sebserver.gui.service.RWTUtils; |  | ||||||
| import ch.ethz.seb.sebserver.gui.service.i18n.I18nSupport; | import ch.ethz.seb.sebserver.gui.service.i18n.I18nSupport; | ||||||
| import ch.ethz.seb.sebserver.gui.service.page.ComposerService; | import ch.ethz.seb.sebserver.gui.service.page.ComposerService; | ||||||
| import ch.ethz.seb.sebserver.gui.service.page.PageContext; | import ch.ethz.seb.sebserver.gui.service.page.PageContext; | ||||||
| import ch.ethz.seb.sebserver.gui.service.page.PageDefinition; | import ch.ethz.seb.sebserver.gui.service.page.PageDefinition; | ||||||
| import ch.ethz.seb.sebserver.gui.service.page.TemplateComposer; | 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.auth.AuthorizationContextHolder; | import ch.ethz.seb.sebserver.gui.service.remote.webservice.auth.AuthorizationContextHolder; | ||||||
|  | import ch.ethz.seb.sebserver.gui.service.widget.WidgetFactory; | ||||||
| 
 | 
 | ||||||
| @Lazy | @Lazy | ||||||
| @Service | @Service | ||||||
|  | @ -40,17 +41,20 @@ public class ComposerServiceImpl implements ComposerService { | ||||||
|     private final Class<? extends PageDefinition> mainPageType = DefaultMainPage.class; |     private final Class<? extends PageDefinition> mainPageType = DefaultMainPage.class; | ||||||
| 
 | 
 | ||||||
|     final AuthorizationContextHolder authorizationContextHolder; |     final AuthorizationContextHolder authorizationContextHolder; | ||||||
|  |     private final RestService restService; | ||||||
|     private final I18nSupport i18nSupport; |     private final I18nSupport i18nSupport; | ||||||
|     private final Map<String, TemplateComposer> composer; |     private final Map<String, TemplateComposer> composer; | ||||||
|     private final Map<String, PageDefinition> pages; |     private final Map<String, PageDefinition> pages; | ||||||
| 
 | 
 | ||||||
|     public ComposerServiceImpl( |     public ComposerServiceImpl( | ||||||
|             final AuthorizationContextHolder authorizationContextHolder, |             final AuthorizationContextHolder authorizationContextHolder, | ||||||
|  |             final RestService restService, | ||||||
|             final I18nSupport i18nSupport, |             final I18nSupport i18nSupport, | ||||||
|             final Collection<TemplateComposer> composer, |             final Collection<TemplateComposer> composer, | ||||||
|             final Collection<PageDefinition> pageDefinitions) { |             final Collection<PageDefinition> pageDefinitions) { | ||||||
| 
 | 
 | ||||||
|         this.authorizationContextHolder = authorizationContextHolder; |         this.authorizationContextHolder = authorizationContextHolder; | ||||||
|  |         this.restService = restService; | ||||||
|         this.i18nSupport = i18nSupport; |         this.i18nSupport = i18nSupport; | ||||||
|         this.composer = composer |         this.composer = composer | ||||||
|                 .stream() |                 .stream() | ||||||
|  | @ -108,7 +112,7 @@ public class ComposerServiceImpl implements ComposerService { | ||||||
| 
 | 
 | ||||||
|         if (composer.validate(pageContext)) { |         if (composer.validate(pageContext)) { | ||||||
| 
 | 
 | ||||||
|             RWTUtils.clearComposite(pageContext.getParent()); |             WidgetFactory.clearComposite(pageContext.getParent()); | ||||||
| 
 | 
 | ||||||
|             try { |             try { | ||||||
|                 composer.compose(pageContext); |                 composer.compose(pageContext); | ||||||
|  | @ -169,8 +173,7 @@ public class ComposerServiceImpl implements ComposerService { | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     private PageContext createPageContext(final Composite root) { |     private PageContext createPageContext(final Composite root) { | ||||||
|         return new PageContextImpl( |         return new PageContextImpl(this.restService, this.i18nSupport, this, root, root, null); | ||||||
|                 this.i18nSupport, this, root, root, null); |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -27,6 +27,7 @@ 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.TemplateComposer; | ||||||
| import ch.ethz.seb.sebserver.gui.service.remote.webservice.auth.AuthorizationContextHolder; | import ch.ethz.seb.sebserver.gui.service.remote.webservice.auth.AuthorizationContextHolder; | ||||||
| import ch.ethz.seb.sebserver.gui.service.widget.WidgetFactory; | import ch.ethz.seb.sebserver.gui.service.widget.WidgetFactory; | ||||||
|  | import ch.ethz.seb.sebserver.gui.service.widget.WidgetFactory.CustomVariant; | ||||||
| 
 | 
 | ||||||
| @Lazy | @Lazy | ||||||
| @Component | @Component | ||||||
|  | @ -243,11 +244,17 @@ public class DefaultPageLayout implements TemplateComposer { | ||||||
|         rowLayout.marginRight = 20; |         rowLayout.marginRight = 20; | ||||||
|         footerRight.setLayout(rowLayout); |         footerRight.setLayout(rowLayout); | ||||||
| 
 | 
 | ||||||
|         this.widgetFactory.labelLocalized(footerLeft, "footer", new LocTextKey("sebserver.overall.imprint")); |         this.widgetFactory.labelLocalized( | ||||||
|         this.widgetFactory.labelLocalized(footerLeft, "footer", new LocTextKey("sebserver.overall.about")); |                 footerLeft, | ||||||
|  |                 CustomVariant.FOOTER, | ||||||
|  |                 new LocTextKey("sebserver.overall.imprint")); | ||||||
|  |         this.widgetFactory.labelLocalized( | ||||||
|  |                 footerLeft, | ||||||
|  |                 CustomVariant.FOOTER, | ||||||
|  |                 new LocTextKey("sebserver.overall.about")); | ||||||
|         this.widgetFactory.labelLocalized( |         this.widgetFactory.labelLocalized( | ||||||
|                 footerRight, |                 footerRight, | ||||||
|                 "footer", |                 CustomVariant.FOOTER, | ||||||
|                 new LocTextKey("sebserver.overall.version", this.sebServerVersion)); |                 new LocTextKey("sebserver.overall.version", this.sebServerVersion)); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -29,8 +29,13 @@ import ch.ethz.seb.sebserver.gui.service.i18n.I18nSupport; | ||||||
| import ch.ethz.seb.sebserver.gui.service.page.ComposerService; | import ch.ethz.seb.sebserver.gui.service.page.ComposerService; | ||||||
| import ch.ethz.seb.sebserver.gui.service.page.PageContext; | import ch.ethz.seb.sebserver.gui.service.page.PageContext; | ||||||
| import ch.ethz.seb.sebserver.gui.service.page.PageDefinition; | import ch.ethz.seb.sebserver.gui.service.page.PageDefinition; | ||||||
| import ch.ethz.seb.sebserver.gui.service.page.PageEventListener; | import ch.ethz.seb.sebserver.gui.service.page.PageMessageException; | ||||||
|  | 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.activity.ActivitySelection; | ||||||
| import ch.ethz.seb.sebserver.gui.service.page.event.PageEvent; | import ch.ethz.seb.sebserver.gui.service.page.event.PageEvent; | ||||||
|  | import ch.ethz.seb.sebserver.gui.service.page.event.PageEventListener; | ||||||
|  | import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestService; | ||||||
| import ch.ethz.seb.sebserver.gui.service.widget.Message; | import ch.ethz.seb.sebserver.gui.service.widget.Message; | ||||||
| 
 | 
 | ||||||
| public class PageContextImpl implements PageContext { | public class PageContextImpl implements PageContext { | ||||||
|  | @ -39,6 +44,7 @@ public class PageContextImpl implements PageContext { | ||||||
| 
 | 
 | ||||||
|     private static final ListenerComparator LIST_COMPARATOR = new ListenerComparator(); |     private static final ListenerComparator LIST_COMPARATOR = new ListenerComparator(); | ||||||
| 
 | 
 | ||||||
|  |     private final RestService restService; | ||||||
|     private final I18nSupport i18nSupport; |     private final I18nSupport i18nSupport; | ||||||
|     private final ComposerService composerService; |     private final ComposerService composerService; | ||||||
|     private final Composite root; |     private final Composite root; | ||||||
|  | @ -46,12 +52,14 @@ public class PageContextImpl implements PageContext { | ||||||
|     private final Map<String, String> attributes; |     private final Map<String, String> attributes; | ||||||
| 
 | 
 | ||||||
|     PageContextImpl( |     PageContextImpl( | ||||||
|  |             final RestService restService, | ||||||
|             final I18nSupport i18nSupport, |             final I18nSupport i18nSupport, | ||||||
|             final ComposerService composerService, |             final ComposerService composerService, | ||||||
|             final Composite root, |             final Composite root, | ||||||
|             final Composite parent, |             final Composite parent, | ||||||
|             final Map<String, String> attributes) { |             final Map<String, String> attributes) { | ||||||
| 
 | 
 | ||||||
|  |         this.restService = restService; | ||||||
|         this.i18nSupport = i18nSupport; |         this.i18nSupport = i18nSupport; | ||||||
|         this.composerService = composerService; |         this.composerService = composerService; | ||||||
|         this.root = root; |         this.root = root; | ||||||
|  | @ -86,6 +94,7 @@ public class PageContextImpl implements PageContext { | ||||||
|     @Override |     @Override | ||||||
|     public PageContext copyOf(final Composite parent) { |     public PageContext copyOf(final Composite parent) { | ||||||
|         return new PageContextImpl( |         return new PageContextImpl( | ||||||
|  |                 this.restService, | ||||||
|                 this.i18nSupport, |                 this.i18nSupport, | ||||||
|                 this.composerService, |                 this.composerService, | ||||||
|                 this.root, |                 this.root, | ||||||
|  | @ -99,6 +108,7 @@ public class PageContextImpl implements PageContext { | ||||||
|         attrs.putAll(this.attributes); |         attrs.putAll(this.attributes); | ||||||
|         attrs.putAll(((PageContextImpl) otherContext).attributes); |         attrs.putAll(((PageContextImpl) otherContext).attributes); | ||||||
|         return new PageContextImpl( |         return new PageContextImpl( | ||||||
|  |                 this.restService, | ||||||
|                 this.i18nSupport, |                 this.i18nSupport, | ||||||
|                 this.composerService, |                 this.composerService, | ||||||
|                 this.root, |                 this.root, | ||||||
|  | @ -112,10 +122,31 @@ public class PageContextImpl implements PageContext { | ||||||
|         attrs.putAll(this.attributes); |         attrs.putAll(this.attributes); | ||||||
|         attrs.put(key, value); |         attrs.put(key, value); | ||||||
|         return new PageContextImpl( |         return new PageContextImpl( | ||||||
|  |                 this.restService, | ||||||
|                 this.i18nSupport, |                 this.i18nSupport, | ||||||
|                 this.composerService, |                 this.composerService, | ||||||
|                 this.root, |                 this.root, | ||||||
|                 this.parent, attrs); |                 this.parent, | ||||||
|  |                 attrs); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     @Override | ||||||
|  |     public PageContext withSelection(final ActivitySelection selection) { | ||||||
|  |         if (selection == null) { | ||||||
|  |             return this; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         final Map<String, String> attrs = new HashMap<>(); | ||||||
|  |         attrs.putAll(this.attributes); | ||||||
|  |         attrs.putAll(selection.getAttributes()); | ||||||
|  | 
 | ||||||
|  |         return new PageContextImpl( | ||||||
|  |                 this.restService, | ||||||
|  |                 this.i18nSupport, | ||||||
|  |                 this.composerService, | ||||||
|  |                 this.root, | ||||||
|  |                 this.parent, | ||||||
|  |                 attrs); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     @Override |     @Override | ||||||
|  | @ -160,6 +191,11 @@ public class PageContextImpl implements PageContext { | ||||||
|                 .forEach(listener -> listener.notify(event)); |                 .forEach(listener -> listener.notify(event)); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     @Override | ||||||
|  |     public Action createAction(final ActionDefinition actionDefinition) { | ||||||
|  |         return new Action(actionDefinition, this, this.restService); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     @Override |     @Override | ||||||
|     @SuppressWarnings("serial") |     @SuppressWarnings("serial") | ||||||
|     public void applyConfirmDialog(final String confirmMessage, final Runnable onOK) { |     public void applyConfirmDialog(final String confirmMessage, final Runnable onOK) { | ||||||
|  | @ -212,6 +248,17 @@ public class PageContextImpl implements PageContext { | ||||||
|         forwardToPage(this.composerService.loginPage(), pageContext); |         forwardToPage(this.composerService.loginPage(), pageContext); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     @Override | ||||||
|  |     public void publishPageMessage(final PageMessageException pme) { | ||||||
|  |         final MessageBox messageBox = new Message( | ||||||
|  |                 getShell(), | ||||||
|  |                 this.i18nSupport.getText("sebserver.page.message"), | ||||||
|  |                 this.i18nSupport.getText(pme.getMessage()), | ||||||
|  |                 SWT.NONE); | ||||||
|  |         messageBox.setMarkupEnabled(true); | ||||||
|  |         messageBox.open(null); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     @Override |     @Override | ||||||
|     public void notifyError(final String errorMessage, final Throwable error) { |     public void notifyError(final String errorMessage, final Throwable error) { | ||||||
|         if (error instanceof APIMessageError) { |         if (error instanceof APIMessageError) { | ||||||
|  |  | ||||||
|  | @ -60,8 +60,8 @@ public class SEBLogin implements TemplateComposer { | ||||||
|         if (pageContext.hasAttribute((AttributeKeys.LGOUT_SUCCESS))) { |         if (pageContext.hasAttribute((AttributeKeys.LGOUT_SUCCESS))) { | ||||||
|             final MessageBox logoutSuccess = new Message( |             final MessageBox logoutSuccess = new Message( | ||||||
|                     pageContext.getShell(), |                     pageContext.getShell(), | ||||||
|                     this.i18nSupport.getText("org.sebserver.logout"), |                     this.i18nSupport.getText("sebserver.logout"), | ||||||
|                     this.i18nSupport.getText("org.sebserver.logout.success.message"), |                     this.i18nSupport.getText("sebserver.logout.success.message"), | ||||||
|                     SWT.ICON_INFORMATION); |                     SWT.ICON_INFORMATION); | ||||||
|             logoutSuccess.open(null); |             logoutSuccess.open(null); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|  | @ -23,11 +23,11 @@ import org.springframework.stereotype.Component; | ||||||
| import ch.ethz.seb.sebserver.gbl.profile.GuiProfile; | import ch.ethz.seb.sebserver.gbl.profile.GuiProfile; | ||||||
| import ch.ethz.seb.sebserver.gui.service.i18n.LocTextKey; | 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; | ||||||
| import ch.ethz.seb.sebserver.gui.service.page.PageEventListener; |  | ||||||
| import ch.ethz.seb.sebserver.gui.service.page.TemplateComposer; | import ch.ethz.seb.sebserver.gui.service.page.TemplateComposer; | ||||||
| import ch.ethz.seb.sebserver.gui.service.page.activity.ActivitiesPane; | import ch.ethz.seb.sebserver.gui.service.page.activity.ActivitiesPane; | ||||||
| import ch.ethz.seb.sebserver.gui.service.page.event.ActivitySelectionEvent; | import ch.ethz.seb.sebserver.gui.service.page.event.ActivitySelectionEvent; | ||||||
| import ch.ethz.seb.sebserver.gui.service.page.event.ActivitySelectionListener; | 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; | ||||||
| import ch.ethz.seb.sebserver.gui.service.widget.WidgetFactory.IconButtonType; | import ch.ethz.seb.sebserver.gui.service.widget.WidgetFactory.IconButtonType; | ||||||
| 
 | 
 | ||||||
|  | @ -128,9 +128,9 @@ public class SEBMainPage implements TemplateComposer { | ||||||
|                     public void notify(final ActivitySelectionEvent event) { |                     public void notify(final ActivitySelectionEvent event) { | ||||||
|                         pageContext.composerService().compose( |                         pageContext.composerService().compose( | ||||||
|                                 event.selection.activity.contentPaneComposer, |                                 event.selection.activity.contentPaneComposer, | ||||||
|                                 pageContext.copyOf(contentObjects).withAttr( |                                 pageContext | ||||||
|                                         event.selection.activity.objectIdentifierAttribute, |                                         .copyOf(contentObjects) | ||||||
|                                         event.selection.getObjectIdentifier())); |                                         .withSelection(event.selection)); | ||||||
|                     } |                     } | ||||||
|                 }); |                 }); | ||||||
| 
 | 
 | ||||||
|  | @ -150,9 +150,9 @@ public class SEBMainPage implements TemplateComposer { | ||||||
|                     public void notify(final ActivitySelectionEvent event) { |                     public void notify(final ActivitySelectionEvent event) { | ||||||
|                         pageContext.composerService().compose( |                         pageContext.composerService().compose( | ||||||
|                                 event.selection.activity.actionPaneComposer, |                                 event.selection.activity.actionPaneComposer, | ||||||
|                                 pageContext.copyOf(actionPane).withAttr( |                                 pageContext | ||||||
|                                         event.selection.activity.objectIdentifierAttribute, |                                         .copyOf(actionPane) | ||||||
|                                         event.selection.getObjectIdentifier())); |                                         .withSelection(event.selection)); | ||||||
|                     } |                     } | ||||||
|                 }); |                 }); | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -0,0 +1,39 @@ | ||||||
|  | /* | ||||||
|  |  * Copyright (c) 2018 ETH Zürich, Educational Development and Technology (LET) | ||||||
|  |  * | ||||||
|  |  * This Source Code Form is subject to the terms of the Mozilla Public | ||||||
|  |  * License, v. 2.0. If a copy of the MPL was not distributed with this | ||||||
|  |  * file, You can obtain one at http://mozilla.org/MPL/2.0/. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | package ch.ethz.seb.sebserver.gui.service.page.validation; | ||||||
|  | 
 | ||||||
|  | import ch.ethz.seb.sebserver.gbl.api.APIMessage; | ||||||
|  | 
 | ||||||
|  | public class FieldValidationError { | ||||||
|  | 
 | ||||||
|  |     public final String messageCode; | ||||||
|  |     public final String domainName; | ||||||
|  |     public final String fieldName; | ||||||
|  |     public final String errorType; | ||||||
|  |     public final String[] attributes; | ||||||
|  | 
 | ||||||
|  |     public FieldValidationError(final APIMessage apiMessage) { | ||||||
|  |         this( | ||||||
|  |                 apiMessage.messageCode, | ||||||
|  |                 apiMessage.attributes.toArray(new String[apiMessage.attributes.size()])); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public FieldValidationError( | ||||||
|  |             final String messageCode, | ||||||
|  |             final String[] attributes) { | ||||||
|  | 
 | ||||||
|  |         this.messageCode = messageCode; | ||||||
|  |         this.attributes = attributes; | ||||||
|  | 
 | ||||||
|  |         this.domainName = (attributes != null && attributes.length > 0) ? attributes[0] : null; | ||||||
|  |         this.fieldName = (attributes != null && attributes.length > 1) ? attributes[1] : null; | ||||||
|  |         this.errorType = (attributes != null && attributes.length > 2) ? attributes[2] : null; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  | } | ||||||
|  | @ -0,0 +1,15 @@ | ||||||
|  | /* | ||||||
|  |  * 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; | ||||||
|  | 
 | ||||||
|  | public interface FormBinding { | ||||||
|  | 
 | ||||||
|  |     String getFormAsJson(); | ||||||
|  | 
 | ||||||
|  | } | ||||||
|  | @ -66,6 +66,9 @@ public abstract class RestCall<T> { | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     protected Result<T> exchange(final RestCallBuilder builder) { |     protected Result<T> exchange(final RestCallBuilder builder) { | ||||||
|  | 
 | ||||||
|  |         log.debug("Call webservice API on {} for {}", this.path, builder); | ||||||
|  | 
 | ||||||
|         try { |         try { | ||||||
|             final ResponseEntity<String> responseEntity = RestCall.this.restService |             final ResponseEntity<String> responseEntity = RestCall.this.restService | ||||||
|                     .getWebserviceAPIRestTemplate() |                     .getWebserviceAPIRestTemplate() | ||||||
|  | @ -90,6 +93,11 @@ public abstract class RestCall<T> { | ||||||
|                         responseEntity.getBody(), |                         responseEntity.getBody(), | ||||||
|                         new TypeReference<List<APIMessage>>() { |                         new TypeReference<List<APIMessage>>() { | ||||||
|                         })); |                         })); | ||||||
|  | 
 | ||||||
|  |                 log.debug( | ||||||
|  |                         "Webservice answered with well defined error- or validation-failure-response: ", | ||||||
|  |                         restCallError); | ||||||
|  | 
 | ||||||
|                 return Result.ofError(restCallError); |                 return Result.ofError(restCallError); | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|  | @ -102,7 +110,7 @@ public abstract class RestCall<T> { | ||||||
|                         new TypeReference<List<APIMessage>>() { |                         new TypeReference<List<APIMessage>>() { | ||||||
|                         })); |                         })); | ||||||
|             } catch (final Exception e) { |             } catch (final Exception e) { | ||||||
|                 log.error("Unable to handle rest call error: ", e); |                 log.error("Unexpected error-response while webservice API call for: {}", builder, e); | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|             return Result.ofError(restCallError); |             return Result.ofError(restCallError); | ||||||
|  | @ -180,6 +188,11 @@ public abstract class RestCall<T> { | ||||||
|             return this; |             return this; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|  |         public RestCallBuilder withFormBinding(final FormBinding formBinding) { | ||||||
|  |             // TODO Auto-generated method stub | ||||||
|  |             return this; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|         public RestCallBuilder onlyActive(final boolean active) { |         public RestCallBuilder onlyActive(final boolean active) { | ||||||
|             this.queryParams.put(Entity.FILTER_ATTR_ACTIVE, Arrays.asList(String.valueOf(active))); |             this.queryParams.put(Entity.FILTER_ATTR_ACTIVE, Arrays.asList(String.valueOf(active))); | ||||||
|             return this; |             return this; | ||||||
|  | @ -204,6 +217,13 @@ public abstract class RestCall<T> { | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|  |         @Override | ||||||
|  |         public String toString() { | ||||||
|  |             return "RestCallBuilder [httpHeaders=" + this.httpHeaders + ", body=" + this.body + ", queryParams=" | ||||||
|  |                     + this.queryParams | ||||||
|  |                     + ", uriVariables=" + this.uriVariables + "]"; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -37,4 +37,8 @@ public class RestCallError extends RuntimeException implements APIMessageError { | ||||||
|         return !this.errors.isEmpty(); |         return !this.errors.isEmpty(); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     @Override | ||||||
|  |     public String toString() { | ||||||
|  |         return "RestCallError [errors=" + this.errors + "]"; | ||||||
|  |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -12,8 +12,6 @@ import java.util.Collection; | ||||||
| import java.util.Map; | import java.util.Map; | ||||||
| import java.util.stream.Collectors; | import java.util.stream.Collectors; | ||||||
| 
 | 
 | ||||||
| import org.slf4j.Logger; |  | ||||||
| import org.slf4j.LoggerFactory; |  | ||||||
| import org.springframework.context.annotation.Lazy; | import org.springframework.context.annotation.Lazy; | ||||||
| import org.springframework.stereotype.Service; | import org.springframework.stereotype.Service; | ||||||
| import org.springframework.web.client.RestTemplate; | import org.springframework.web.client.RestTemplate; | ||||||
|  | @ -29,8 +27,6 @@ import ch.ethz.seb.sebserver.gui.service.remote.webservice.auth.WebserviceURISer | ||||||
| @GuiProfile | @GuiProfile | ||||||
| public class RestService { | public class RestService { | ||||||
| 
 | 
 | ||||||
|     private static final Logger log = LoggerFactory.getLogger(RestService.class); |  | ||||||
| 
 |  | ||||||
|     private final AuthorizationContextHolder authorizationContextHolder; |     private final AuthorizationContextHolder authorizationContextHolder; | ||||||
|     private final WebserviceURIService webserviceURIBuilderSupplier; |     private final WebserviceURIService webserviceURIBuilderSupplier; | ||||||
|     private final Map<String, RestCall<?>> calls; |     private final Map<String, RestCall<?>> calls; | ||||||
|  |  | ||||||
|  | @ -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.institution; | ||||||
|  | 
 | ||||||
|  | import org.springframework.context.annotation.Lazy; | ||||||
|  | import org.springframework.http.HttpMethod; | ||||||
|  | import org.springframework.http.MediaType; | ||||||
|  | import org.springframework.stereotype.Component; | ||||||
|  | 
 | ||||||
|  | import com.fasterxml.jackson.core.type.TypeReference; | ||||||
|  | 
 | ||||||
|  | import ch.ethz.seb.sebserver.gbl.api.SEBServerRestEndpoints; | ||||||
|  | import ch.ethz.seb.sebserver.gbl.model.institution.Institution; | ||||||
|  | import ch.ethz.seb.sebserver.gbl.profile.GuiProfile; | ||||||
|  | import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestCall; | ||||||
|  | 
 | ||||||
|  | @Lazy | ||||||
|  | @Component | ||||||
|  | @GuiProfile | ||||||
|  | public class GetInstitution extends RestCall<Institution> { | ||||||
|  | 
 | ||||||
|  |     protected GetInstitution() { | ||||||
|  |         super( | ||||||
|  |                 new TypeReference<Institution>() { | ||||||
|  |                 }, | ||||||
|  |                 HttpMethod.GET, | ||||||
|  |                 MediaType.APPLICATION_FORM_URLENCODED, | ||||||
|  |                 SEBServerRestEndpoints.ENDPOINT_INSTITUTION); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  | } | ||||||
|  | @ -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.institution; | ||||||
|  | 
 | ||||||
|  | import org.springframework.context.annotation.Lazy; | ||||||
|  | import org.springframework.http.HttpMethod; | ||||||
|  | import org.springframework.http.MediaType; | ||||||
|  | import org.springframework.stereotype.Component; | ||||||
|  | 
 | ||||||
|  | import com.fasterxml.jackson.core.type.TypeReference; | ||||||
|  | 
 | ||||||
|  | import ch.ethz.seb.sebserver.gbl.api.SEBServerRestEndpoints; | ||||||
|  | import ch.ethz.seb.sebserver.gbl.model.institution.Institution; | ||||||
|  | import ch.ethz.seb.sebserver.gbl.profile.GuiProfile; | ||||||
|  | import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestCall; | ||||||
|  | 
 | ||||||
|  | @Lazy | ||||||
|  | @Component | ||||||
|  | @GuiProfile | ||||||
|  | public class NewInstitution extends RestCall<Institution> { | ||||||
|  | 
 | ||||||
|  |     protected NewInstitution() { | ||||||
|  |         super( | ||||||
|  |                 new TypeReference<Institution>() { | ||||||
|  |                 }, | ||||||
|  |                 HttpMethod.POST, | ||||||
|  |                 MediaType.APPLICATION_FORM_URLENCODED, | ||||||
|  |                 SEBServerRestEndpoints.ENDPOINT_INSTITUTION); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  | } | ||||||
|  | @ -8,6 +8,7 @@ | ||||||
| 
 | 
 | ||||||
| package ch.ethz.seb.sebserver.gui.service.remote.webservice.auth; | package ch.ethz.seb.sebserver.gui.service.remote.webservice.auth; | ||||||
| 
 | 
 | ||||||
|  | import java.io.IOException; | ||||||
| import java.net.URI; | import java.net.URI; | ||||||
| import java.util.Arrays; | import java.util.Arrays; | ||||||
| import java.util.Collections; | import java.util.Collections; | ||||||
|  | @ -25,9 +26,11 @@ import org.springframework.http.HttpMethod; | ||||||
| import org.springframework.http.HttpStatus; | import org.springframework.http.HttpStatus; | ||||||
| import org.springframework.http.ResponseEntity; | import org.springframework.http.ResponseEntity; | ||||||
| import org.springframework.http.client.ClientHttpRequestFactory; | import org.springframework.http.client.ClientHttpRequestFactory; | ||||||
|  | import org.springframework.http.client.ClientHttpResponse; | ||||||
| import org.springframework.security.access.AccessDeniedException; | import org.springframework.security.access.AccessDeniedException; | ||||||
| import org.springframework.security.oauth2.client.DefaultOAuth2ClientContext; | import org.springframework.security.oauth2.client.DefaultOAuth2ClientContext; | ||||||
| import org.springframework.security.oauth2.client.OAuth2RestTemplate; | import org.springframework.security.oauth2.client.OAuth2RestTemplate; | ||||||
|  | import org.springframework.security.oauth2.client.http.OAuth2ErrorHandler; | ||||||
| import org.springframework.security.oauth2.client.resource.OAuth2AccessDeniedException; | import org.springframework.security.oauth2.client.resource.OAuth2AccessDeniedException; | ||||||
| import org.springframework.security.oauth2.client.resource.OAuth2ProtectedResourceDetails; | import org.springframework.security.oauth2.client.resource.OAuth2ProtectedResourceDetails; | ||||||
| import org.springframework.security.oauth2.client.token.AccessTokenRequest; | import org.springframework.security.oauth2.client.token.AccessTokenRequest; | ||||||
|  | @ -162,6 +165,14 @@ public class OAuth2AuthorizationContextHolder implements AuthorizationContextHol | ||||||
| 
 | 
 | ||||||
|             this.restTemplate = new DisposableOAuth2RestTemplate(this.resource); |             this.restTemplate = new DisposableOAuth2RestTemplate(this.resource); | ||||||
|             this.restTemplate.setRequestFactory(clientHttpRequestFactory); |             this.restTemplate.setRequestFactory(clientHttpRequestFactory); | ||||||
|  |             this.restTemplate.setErrorHandler(new OAuth2ErrorHandler(this.resource) { | ||||||
|  |                 @Override | ||||||
|  |                 public boolean hasError(final ClientHttpResponse response) throws IOException { | ||||||
|  |                     final HttpStatus statusCode = HttpStatus.resolve(response.getRawStatusCode()); | ||||||
|  |                     return (statusCode != null && statusCode.series() == HttpStatus.Series.SERVER_ERROR); | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|  |             }); | ||||||
| 
 | 
 | ||||||
|             this.revokeTokenURI = webserviceURIService.getOAuthRevokeTokenURI(); |             this.revokeTokenURI = webserviceURIService.getOAuthRevokeTokenURI(); | ||||||
|             this.currentUserURI = webserviceURIService.getCurrentUserRequestURI(); |             this.currentUserURI = webserviceURIService.getCurrentUserRequestURI(); | ||||||
|  |  | ||||||
|  | @ -40,6 +40,15 @@ public final class ColumnDefinition<ROW extends Entity> { | ||||||
|         this(columnName, displayName, null, widthPercent, null, null, false); |         this(columnName, displayName, null, widthPercent, null, null, false); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     public ColumnDefinition( | ||||||
|  |             final String columnName, | ||||||
|  |             final LocTextKey displayName, | ||||||
|  |             final Function<ROW, Object> valueSupplier, | ||||||
|  |             final boolean sortable) { | ||||||
|  | 
 | ||||||
|  |         this(columnName, displayName, null, -1, valueSupplier, null, sortable); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     public ColumnDefinition( |     public ColumnDefinition( | ||||||
|             final String columnName, |             final String columnName, | ||||||
|             final LocTextKey displayName, |             final LocTextKey displayName, | ||||||
|  |  | ||||||
|  | @ -8,7 +8,11 @@ | ||||||
| 
 | 
 | ||||||
| package ch.ethz.seb.sebserver.gui.service.table; | package ch.ethz.seb.sebserver.gui.service.table; | ||||||
| 
 | 
 | ||||||
|  | import java.util.Arrays; | ||||||
|  | import java.util.Collection; | ||||||
|  | import java.util.Collections; | ||||||
| import java.util.List; | import java.util.List; | ||||||
|  | import java.util.stream.Collectors; | ||||||
| 
 | 
 | ||||||
| import org.eclipse.swt.SWT; | import org.eclipse.swt.SWT; | ||||||
| import org.eclipse.swt.layout.GridData; | import org.eclipse.swt.layout.GridData; | ||||||
|  | @ -46,8 +50,6 @@ public class EntityTable<ROW extends Entity> extends Composite { | ||||||
|     private final Table table; |     private final Table table; | ||||||
|     private final TableNavigator navigator; |     private final TableNavigator navigator; | ||||||
| 
 | 
 | ||||||
|     private final boolean selectableRows; |  | ||||||
| 
 |  | ||||||
|     private int pageNumber = 1; |     private int pageNumber = 1; | ||||||
|     private int pageSize; |     private int pageSize; | ||||||
|     private String sortColumn = null; |     private String sortColumn = null; | ||||||
|  | @ -56,31 +58,34 @@ public class EntityTable<ROW extends Entity> extends Composite { | ||||||
|     private boolean columnsWithSameWidth = true; |     private boolean columnsWithSameWidth = true; | ||||||
| 
 | 
 | ||||||
|     EntityTable( |     EntityTable( | ||||||
|  |             final int type, | ||||||
|             final Composite parent, |             final Composite parent, | ||||||
|             final RestCall<Page<ROW>> restCall, |             final RestCall<Page<ROW>> restCall, | ||||||
|             final WidgetFactory widgetFactory, |             final WidgetFactory widgetFactory, | ||||||
|             final List<ColumnDefinition<ROW>> columns, |             final List<ColumnDefinition<ROW>> columns, | ||||||
|             final List<TableRowAction> actions, |             final List<TableRowAction> actions, | ||||||
|             final int pageSize, |             final int pageSize, | ||||||
|             final boolean withFilter, |             final boolean withFilter) { | ||||||
|             final boolean selectableRows) { |  | ||||||
| 
 | 
 | ||||||
|         super(parent, SWT.NONE); |         super(parent, type); | ||||||
|         this.widgetFactory = widgetFactory; |         this.widgetFactory = widgetFactory; | ||||||
|         this.restCall = restCall; |         this.restCall = restCall; | ||||||
|         this.columns = Utils.immutableListOf(columns); |         this.columns = Utils.immutableListOf(columns); | ||||||
|         this.actions = Utils.immutableListOf(actions); |         this.actions = Utils.immutableListOf(actions); | ||||||
| 
 | 
 | ||||||
|         super.setLayout(new GridLayout()); |         super.setLayout(new GridLayout()); | ||||||
|         super.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false)); |         GridData gridData = new GridData(SWT.FILL, SWT.TOP, true, false); | ||||||
|  | 
 | ||||||
|  |         gridData.heightHint = (pageSize + 1) * 40; | ||||||
|  |         super.setLayoutData(gridData); | ||||||
| 
 | 
 | ||||||
|         this.pageSize = pageSize; |         this.pageSize = pageSize; | ||||||
|         this.filter = (withFilter) ? new TableFilter<>(this) : null; |         this.filter = (withFilter) ? new TableFilter<>(this) : null; | ||||||
|         this.selectableRows = selectableRows; |  | ||||||
| 
 | 
 | ||||||
|         this.table = widgetFactory.tableLocalized(this); |         this.table = widgetFactory.tableLocalized(this); | ||||||
|         this.table.setLayout(new GridLayout(columns.size(), true)); |         this.table.setLayout(new GridLayout(columns.size(), true)); | ||||||
|         final GridData gridData = new GridData(SWT.FILL, SWT.CENTER, true, false); |         gridData = new GridData(SWT.FILL, SWT.CENTER, true, false); | ||||||
|  |         gridData.heightHint = (pageSize + 1) * 25; | ||||||
|         this.table.setLayoutData(gridData); |         this.table.setLayoutData(gridData); | ||||||
|         this.table.addListener(SWT.Resize, this::adaptColumnWidth); |         this.table.addListener(SWT.Resize, this::adaptColumnWidth); | ||||||
| 
 | 
 | ||||||
|  | @ -148,6 +153,18 @@ public class EntityTable<ROW extends Entity> extends Composite { | ||||||
|                 this.sortOrder); |                 this.sortOrder); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     public Collection<String> getSelection() { | ||||||
|  |         final TableItem[] selection = this.table.getSelection(); | ||||||
|  |         if (selection == null) { | ||||||
|  |             return Collections.emptyList(); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         return Arrays.asList(selection) | ||||||
|  |                 .stream() | ||||||
|  |                 .map(this::getRowDataId) | ||||||
|  |                 .collect(Collectors.toList()); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     private void createTableColumns() { |     private void createTableColumns() { | ||||||
|         for (final ColumnDefinition<ROW> column : this.columns) { |         for (final ColumnDefinition<ROW> column : this.columns) { | ||||||
|             final TableColumn tableColumn = this.widgetFactory.tableColumnLocalized( |             final TableColumn tableColumn = this.widgetFactory.tableColumnLocalized( | ||||||
|  | @ -204,9 +221,6 @@ public class EntityTable<ROW extends Entity> extends Composite { | ||||||
|             final TableItem item = new TableItem(this.table, SWT.NONE); |             final TableItem item = new TableItem(this.table, SWT.NONE); | ||||||
|             item.setData(TABLE_ROW_DATA, row); |             item.setData(TABLE_ROW_DATA, row); | ||||||
|             int index = 0; |             int index = 0; | ||||||
|             if (this.selectableRows) { |  | ||||||
|                 // TODO |  | ||||||
|             } |  | ||||||
|             for (final ColumnDefinition<ROW> column : this.columns) { |             for (final ColumnDefinition<ROW> column : this.columns) { | ||||||
|                 final Object value = column.valueSupplier.apply(row); |                 final Object value = column.valueSupplier.apply(row); | ||||||
|                 if (value instanceof Boolean) { |                 if (value instanceof Boolean) { | ||||||
|  | @ -247,4 +261,13 @@ public class EntityTable<ROW extends Entity> extends Composite { | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     @SuppressWarnings("unchecked") | ||||||
|  |     private ROW getRowData(final TableItem item) { | ||||||
|  |         return (ROW) item.getData(TABLE_ROW_DATA); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     private String getRowDataId(final TableItem item) { | ||||||
|  |         return getRowData(item).getModelId(); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -11,6 +11,7 @@ package ch.ethz.seb.sebserver.gui.service.table; | ||||||
| import java.util.ArrayList; | import java.util.ArrayList; | ||||||
| import java.util.List; | import java.util.List; | ||||||
| 
 | 
 | ||||||
|  | import org.eclipse.swt.SWT; | ||||||
| import org.eclipse.swt.widgets.Composite; | import org.eclipse.swt.widgets.Composite; | ||||||
| 
 | 
 | ||||||
| import ch.ethz.seb.sebserver.gbl.model.Entity; | import ch.ethz.seb.sebserver.gbl.model.Entity; | ||||||
|  | @ -41,7 +42,7 @@ public class TableBuilder<ROW extends Entity> { | ||||||
|     final List<TableRowAction> actions = new ArrayList<>(); |     final List<TableRowAction> actions = new ArrayList<>(); | ||||||
| 
 | 
 | ||||||
|     private int pageSize = -1; |     private int pageSize = -1; | ||||||
|     private boolean selectableRows = false; |     private int type = SWT.NONE; | ||||||
| 
 | 
 | ||||||
|     public TableBuilder( |     public TableBuilder( | ||||||
|             final WidgetFactory widgetFactory, |             final WidgetFactory widgetFactory, | ||||||
|  | @ -61,13 +62,13 @@ public class TableBuilder<ROW extends Entity> { | ||||||
|         return this; |         return this; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     public TableBuilder<ROW> withSelectableRows() { |     public TableBuilder<ROW> withAction(final TableRowAction action) { | ||||||
|         this.selectableRows = true; |         this.actions.add(action); | ||||||
|         return this; |         return this; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     public TableBuilder<ROW> withAction(final TableRowAction action) { |     public TableBuilder<ROW> withMultiselection() { | ||||||
|         this.actions.add(action); |         this.type |= SWT.MULTI; | ||||||
|         return this; |         return this; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | @ -79,14 +80,14 @@ public class TableBuilder<ROW extends Entity> { | ||||||
|                 .isPresent(); |                 .isPresent(); | ||||||
| 
 | 
 | ||||||
|         return new EntityTable<>( |         return new EntityTable<>( | ||||||
|  |                 this.type, | ||||||
|                 parent, |                 parent, | ||||||
|                 this.restCall, |                 this.restCall, | ||||||
|                 this.widgetFactory, |                 this.widgetFactory, | ||||||
|                 this.columns, |                 this.columns, | ||||||
|                 this.actions, |                 this.actions, | ||||||
|                 this.pageSize, |                 this.pageSize, | ||||||
|                 withFilter, |                 withFilter); | ||||||
|                 this.selectableRows); |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -17,7 +17,7 @@ import org.eclipse.swt.widgets.Composite; | ||||||
| import org.eclipse.swt.widgets.Label; | import org.eclipse.swt.widgets.Label; | ||||||
| 
 | 
 | ||||||
| import ch.ethz.seb.sebserver.gbl.model.Page; | import ch.ethz.seb.sebserver.gbl.model.Page; | ||||||
| import ch.ethz.seb.sebserver.gui.service.RWTUtils; | import ch.ethz.seb.sebserver.gui.service.widget.WidgetFactory; | ||||||
| 
 | 
 | ||||||
| public class TableNavigator extends Composite { | public class TableNavigator extends Composite { | ||||||
| 
 | 
 | ||||||
|  | @ -39,7 +39,7 @@ public class TableNavigator extends Composite { | ||||||
| 
 | 
 | ||||||
|     public Page<?> update(final Page<?> pageData) { |     public Page<?> update(final Page<?> pageData) { | ||||||
|         // clear all |         // clear all | ||||||
|         RWTUtils.clearComposite(this); |         WidgetFactory.clearComposite(this); | ||||||
| 
 | 
 | ||||||
|         final int pageNumber = pageData.getPageNumber(); |         final int pageNumber = pageData.getPageNumber(); | ||||||
|         final int numberOfPages = pageData.getNumberOfPages(); |         final int numberOfPages = pageData.getNumberOfPages(); | ||||||
|  |  | ||||||
|  | @ -25,6 +25,7 @@ import org.eclipse.swt.layout.GridData; | ||||||
| import org.eclipse.swt.widgets.Button; | import org.eclipse.swt.widgets.Button; | ||||||
| import org.eclipse.swt.widgets.Combo; | import org.eclipse.swt.widgets.Combo; | ||||||
| import org.eclipse.swt.widgets.Composite; | import org.eclipse.swt.widgets.Composite; | ||||||
|  | import org.eclipse.swt.widgets.Control; | ||||||
| import org.eclipse.swt.widgets.Label; | import org.eclipse.swt.widgets.Label; | ||||||
| import org.eclipse.swt.widgets.Listener; | import org.eclipse.swt.widgets.Listener; | ||||||
| import org.eclipse.swt.widgets.Table; | import org.eclipse.swt.widgets.Table; | ||||||
|  | @ -42,7 +43,6 @@ import ch.ethz.seb.sebserver.gbl.model.Entity; | ||||||
| import ch.ethz.seb.sebserver.gbl.model.Page; | import ch.ethz.seb.sebserver.gbl.model.Page; | ||||||
| import ch.ethz.seb.sebserver.gbl.profile.GuiProfile; | import ch.ethz.seb.sebserver.gbl.profile.GuiProfile; | ||||||
| import ch.ethz.seb.sebserver.gbl.util.Tuple; | import ch.ethz.seb.sebserver.gbl.util.Tuple; | ||||||
| import ch.ethz.seb.sebserver.gui.service.RWTUtils; |  | ||||||
| import ch.ethz.seb.sebserver.gui.service.i18n.I18nSupport; | 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.LocTextKey; | ||||||
| import ch.ethz.seb.sebserver.gui.service.i18n.PolyglotPageService; | import ch.ethz.seb.sebserver.gui.service.i18n.PolyglotPageService; | ||||||
|  | @ -57,9 +57,27 @@ public class WidgetFactory { | ||||||
| 
 | 
 | ||||||
|     private static final Logger log = LoggerFactory.getLogger(WidgetFactory.class); |     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 IconButtonType { | ||||||
|         MAXIMIZE("maximize.png"), |         MAXIMIZE("maximize.png"), | ||||||
|         MINIMIZE("minimize.png"), |         MINIMIZE("minimize.png"), | ||||||
|  |         MODIFY_ACTION("editAction.png"), | ||||||
|         SAVE_ACTION("saveAction.png"), |         SAVE_ACTION("saveAction.png"), | ||||||
|         NEW_ACTION("newAction.png"), |         NEW_ACTION("newAction.png"), | ||||||
|         DELETE_ACTION("deleteAction.png"), |         DELETE_ACTION("deleteAction.png"), | ||||||
|  | @ -108,10 +126,10 @@ public class WidgetFactory { | ||||||
|         return button; |         return button; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     public Button buttonLocalized(final Composite parent, final String style, final String locTextKey) { |     public Button buttonLocalized(final Composite parent, final CustomVariant variant, final String locTextKey) { | ||||||
|         final Button button = new Button(parent, SWT.NONE); |         final Button button = new Button(parent, SWT.NONE); | ||||||
|         this.injectI18n(button, new LocTextKey(locTextKey)); |         this.injectI18n(button, new LocTextKey(locTextKey)); | ||||||
|         button.setData(RWT.CUSTOM_VARIANT, style); |         button.setData(RWT.CUSTOM_VARIANT, variant.key); | ||||||
|         return button; |         return button; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | @ -133,10 +151,10 @@ public class WidgetFactory { | ||||||
|         return label; |         return label; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     public Label labelLocalized(final Composite parent, final String style, final LocTextKey locTextKey) { |     public Label labelLocalized(final Composite parent, final CustomVariant variant, final LocTextKey locTextKey) { | ||||||
|         final Label label = new Label(parent, SWT.NONE); |         final Label label = new Label(parent, SWT.NONE); | ||||||
|         this.injectI18n(label, locTextKey); |         this.injectI18n(label, locTextKey); | ||||||
|         label.setData(RWT.CUSTOM_VARIANT, style); |         label.setData(RWT.CUSTOM_VARIANT, variant.key); | ||||||
|         return label; |         return label; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | @ -152,24 +170,24 @@ public class WidgetFactory { | ||||||
| 
 | 
 | ||||||
|     public Label labelLocalized( |     public Label labelLocalized( | ||||||
|             final Composite parent, |             final Composite parent, | ||||||
|             final String style, |             final CustomVariant variant, | ||||||
|             final LocTextKey locTextKey, |             final LocTextKey locTextKey, | ||||||
|             final LocTextKey locToolTextKey) { |             final LocTextKey locToolTextKey) { | ||||||
| 
 | 
 | ||||||
|         final Label label = new Label(parent, SWT.NONE); |         final Label label = new Label(parent, SWT.NONE); | ||||||
|         this.injectI18n(label, locTextKey, locToolTextKey); |         this.injectI18n(label, locTextKey, locToolTextKey); | ||||||
|         label.setData(RWT.CUSTOM_VARIANT, style); |         label.setData(RWT.CUSTOM_VARIANT, variant.key); | ||||||
|         return label; |         return label; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     public Label labelLocalizedTitle(final Composite content, final LocTextKey locTextKey) { |     public Label labelLocalizedTitle(final Composite content, final LocTextKey locTextKey) { | ||||||
|         final Label labelLocalized = labelLocalized(content, RWTUtils.TEXT_NAME_H2, locTextKey); |         final Label labelLocalized = labelLocalized(content, CustomVariant.TEXT_H1, locTextKey); | ||||||
|         labelLocalized.setLayoutData(new GridData(SWT.TOP, SWT.LEFT, true, false)); |         labelLocalized.setLayoutData(new GridData(SWT.TOP, SWT.LEFT, true, false)); | ||||||
|         return labelLocalized; |         return labelLocalized; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     public Tree treeLocalized(final Composite parent, final int style) { |     public Tree treeLocalized(final Composite parent, final int style) { | ||||||
|         final Tree tree = new Tree(parent, SWT.SINGLE | SWT.FULL_SELECTION); |         final Tree tree = new Tree(parent, style); | ||||||
|         this.injectI18n(tree); |         this.injectI18n(tree); | ||||||
|         return tree; |         return tree; | ||||||
|     } |     } | ||||||
|  | @ -381,6 +399,16 @@ public class WidgetFactory { | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     public static void clearComposite(final Composite parent) { | ||||||
|  |         if (parent == null) { | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         for (final Control control : parent.getChildren()) { | ||||||
|  |             control.dispose(); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     private static Consumer<Tree> treeFunction(final I18nSupport i18nSupport) { |     private static Consumer<Tree> treeFunction(final I18nSupport i18nSupport) { | ||||||
|         return tree -> updateLocale(tree.getItems(), i18nSupport); |         return tree -> updateLocale(tree.getItems(), i18nSupport); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  | @ -16,6 +16,8 @@ sebserver.login.pwd=Password | ||||||
| sebserver.login.login=Sign In | sebserver.login.login=Sign In | ||||||
| sebserver.login.failed.title=Login Failed | sebserver.login.failed.title=Login Failed | ||||||
| sebserver.login.failed.message=Access Denied: Wrong username or password | sebserver.login.failed.message=Access Denied: Wrong username or password | ||||||
|  | sebserver.logout=Sign out | ||||||
|  | sebserver.logout.success.message=You have been successfully signed out. | ||||||
| 
 | 
 | ||||||
| ################################ | ################################ | ||||||
| # Main Page | # Main Page | ||||||
|  | @ -29,6 +31,7 @@ sebserver.actionpane.title=Actions | ||||||
| sebserver.activities.inst=Institution | sebserver.activities.inst=Institution | ||||||
| 
 | 
 | ||||||
| sebserver.error.unexpected=Unexpected Error | sebserver.error.unexpected=Unexpected Error | ||||||
|  | sebserver.page.message=Information | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| ################################ | ################################ | ||||||
|  | @ -39,3 +42,13 @@ sebserver.institution.list.title=Institutions | ||||||
| sebserver.institution.list.column.name=Name | sebserver.institution.list.column.name=Name | ||||||
| sebserver.institution.list.column.urlSuffix=URL Suffix | sebserver.institution.list.column.urlSuffix=URL Suffix | ||||||
| sebserver.institution.list.column.active=Active | sebserver.institution.list.column.active=Active | ||||||
|  | sebserver.institution.action.new=New Institution | ||||||
|  | sebserver.institution.action.modify=Edit Institution | ||||||
|  | sebserver.institution.action.delete=Delete Institution | ||||||
|  | sebserver.institution.info.pleaseSelect=Please Select an Institution from the Table first. | ||||||
|  | 
 | ||||||
|  | ################################ | ||||||
|  | # Form validation | ||||||
|  | ################################ | ||||||
|  | 
 | ||||||
|  | sebserver.form.validation.fieldError.size=The size must be between {3} and {4} | ||||||
							
								
								
									
										
											BIN
										
									
								
								src/main/resources/static/images/delete.png
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								src/main/resources/static/images/delete.png
									
										
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 186 B | 
							
								
								
									
										
											BIN
										
									
								
								src/main/resources/static/images/editAction.png
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								src/main/resources/static/images/editAction.png
									
										
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 165 B | 
										
											Binary file not shown.
										
									
								
							| Before Width: | Height: | Size: 165 B After Width: | Height: | Size: 121 B | 
		Loading…
	
	Add table
		
		Reference in a new issue
	
	 anhefti
						anhefti