From 584a900529020fd4b9785449ce2f05112810603f Mon Sep 17 00:00:00 2001 From: anhefti Date: Thu, 21 Mar 2019 16:55:11 +0100 Subject: [PATCH] SEBSERV-29 new multi selection with combo supporter selection --- .../ch/ethz/seb/sebserver/gbl/Constants.java | 3 + .../sebserver/gbl/model/user/UserInfo.java | 1 + .../seb/sebserver/gui/content/ExamList.java | 34 +++++ .../gui/content/action/ActionDefinition.java | 8 +- .../ch/ethz/seb/sebserver/gui/form/Form.java | 131 ++++++++---------- .../seb/sebserver/gui/form/FormBuilder.java | 15 +- .../gui/form/SelectionFieldBuilder.java | 44 +++--- .../gui/service/ResourceService.java | 31 +++-- .../gui/service/page/ModalInputDialog.java | 101 ++++++++++++++ .../page/ModalInputDialogComposer.java | 18 +++ .../seb/sebserver/gui/table/EntityTable.java | 4 - .../seb/sebserver/gui/table/TableFilter.java | 17 ++- .../sebserver/gui/widget/MultiSelection.java | 13 +- .../gui/widget/MultiSelectionCombo.java | 131 ++++++++++++++++++ .../seb/sebserver/gui/widget/Selection.java | 16 ++- .../sebserver/gui/widget/SingleSelection.java | 7 +- .../sebserver/gui/widget/WidgetFactory.java | 71 +++++++--- .../client/ClientCredentialServiceImpl.java | 2 +- .../servicelayer/dao/FilterMap.java | 4 + .../servicelayer/dao/impl/UserDAOImpl.java | 61 ++++---- src/main/resources/messages.properties | 2 + src/main/resources/static/css/sebserver.css | 2 +- src/main/resources/static/images/add.png | Bin 0 -> 98 bytes src/main/resources/static/images/remove.png | Bin 0 -> 85 bytes 24 files changed, 535 insertions(+), 181 deletions(-) create mode 100644 src/main/java/ch/ethz/seb/sebserver/gui/service/page/ModalInputDialog.java create mode 100644 src/main/java/ch/ethz/seb/sebserver/gui/service/page/ModalInputDialogComposer.java create mode 100644 src/main/java/ch/ethz/seb/sebserver/gui/widget/MultiSelectionCombo.java create mode 100644 src/main/resources/static/images/add.png create mode 100644 src/main/resources/static/images/remove.png diff --git a/src/main/java/ch/ethz/seb/sebserver/gbl/Constants.java b/src/main/java/ch/ethz/seb/sebserver/gbl/Constants.java index a753a7bd..81f56ef8 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gbl/Constants.java +++ b/src/main/java/ch/ethz/seb/sebserver/gbl/Constants.java @@ -14,6 +14,9 @@ import org.joda.time.format.DateTimeFormatter; /** Global Constants used in SEB Server web-service as well as in web-gui component */ public final class Constants { + public static final String TRUE_STRING = Boolean.TRUE.toString(); + public static final String FALSE_STRING = Boolean.FALSE.toString(); + public static final long SECOND_IN_MILLIS = 1000; public static final long MINUTE_IN_MILLIS = 60 * SECOND_IN_MILLIS; public static final long HOUR_IN_MILLIS = 60 * MINUTE_IN_MILLIS; diff --git a/src/main/java/ch/ethz/seb/sebserver/gbl/model/user/UserInfo.java b/src/main/java/ch/ethz/seb/sebserver/gbl/model/user/UserInfo.java index 77f27776..218928f4 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gbl/model/user/UserInfo.java +++ b/src/main/java/ch/ethz/seb/sebserver/gbl/model/user/UserInfo.java @@ -48,6 +48,7 @@ public final class UserInfo implements UserAccount, Activatable, Serializable { public static final String FILTER_ATTR_USER_NAME = "username"; public static final String FILTER_ATTR_EMAIL = "email"; public static final String FILTER_ATTR_LANGUAGE = "language"; + public static final String FILTER_ATTR_ROLE = "role"; /** The user's UUID */ @JsonProperty(USER.ATTR_UUID) diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/content/ExamList.java b/src/main/java/ch/ethz/seb/sebserver/gui/content/ExamList.java index 849d9345..250ec71d 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/content/ExamList.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/content/ExamList.java @@ -10,7 +10,11 @@ package ch.ethz.seb.sebserver.gui.content; import java.util.function.Function; +import org.eclipse.swt.SWT; +import org.eclipse.swt.layout.GridData; import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.Text; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Component; @@ -28,6 +32,7 @@ import ch.ethz.seb.sebserver.gui.content.action.ActionDefinition; import ch.ethz.seb.sebserver.gui.service.ResourceService; import ch.ethz.seb.sebserver.gui.service.i18n.I18nSupport; import ch.ethz.seb.sebserver.gui.service.i18n.LocTextKey; +import ch.ethz.seb.sebserver.gui.service.page.ModalInputDialog; 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.action.Action; @@ -131,6 +136,10 @@ public class ExamList implements TemplateComposer { final GrantCheck userGrant = currentUser.grantCheck(EntityType.EXAM); pageContext.clearEntityKeys() + .createAction(ActionDefinition.TEST_ACTION) + .withExec(this::testModalInput) + .publish() + .createAction(ActionDefinition.EXAM_IMPORT) .publishIf(userGrant::im) @@ -163,4 +172,29 @@ public class ExamList implements TemplateComposer { .getText("sebserver.exam.type." + exam.type.name()); } + private Action testModalInput(final Action action) { + final ModalInputDialog dialog = new ModalInputDialog<>( + action.pageContext().getParent().getShell(), + this.widgetFactory); + + dialog.open( + "Test Input Dialog", + action.pageContext(), + value -> { + System.out.println("********************** value: " + value); + }, + pc -> { + final Composite parent = pc.getParent(); + final Label label = new Label(parent, SWT.NONE); + label.setText("Please Enter:"); + label.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); + + final Text text = new Text(parent, SWT.LEFT | SWT.BORDER); + text.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); + return () -> text.getText(); + }); + + return action; + } + } diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/content/action/ActionDefinition.java b/src/main/java/ch/ethz/seb/sebserver/gui/content/action/ActionDefinition.java index da0ef9d2..8a8efc27 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/content/action/ActionDefinition.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/content/action/ActionDefinition.java @@ -319,7 +319,7 @@ public enum ActionDefinition { EXAM_INDICATOR_NEW( new LocTextKey("sebserver.exam.indicator.action.list.new"), ImageIcon.NEW, - LmsSetupForm.class, + IndicatorForm.class, EXAM_VIEW_FROM_LIST, ActionCategory.FORM, false), @@ -350,6 +350,12 @@ public enum ActionDefinition { EXAM_VIEW_FROM_LIST, ActionCategory.FORM, true), + TEST_ACTION( + new LocTextKey("TEST"), + ImageIcon.TEST, + ExamList.class, + EXAM_VIEW_LIST, + ActionCategory.FORM), ; diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/form/Form.java b/src/main/java/ch/ethz/seb/sebserver/gui/form/Form.java index d59e31e8..cd021a3a 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/form/Form.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/form/Form.java @@ -8,12 +8,12 @@ package ch.ethz.seb.sebserver.gui.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.BiConsumer; import java.util.function.Consumer; import java.util.function.Predicate; @@ -30,10 +30,10 @@ import com.fasterxml.jackson.databind.node.ObjectNode; import ch.ethz.seb.sebserver.gbl.Constants; import ch.ethz.seb.sebserver.gbl.api.JSONMapper; +import ch.ethz.seb.sebserver.gbl.util.Tuple; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.FormBinding; import ch.ethz.seb.sebserver.gui.widget.ImageUpload; -import ch.ethz.seb.sebserver.gui.widget.MultiSelection; -import ch.ethz.seb.sebserver.gui.widget.SingleSelection; +import ch.ethz.seb.sebserver.gui.widget.Selection; public final class Form implements FormBinding { @@ -42,8 +42,6 @@ public final class Form implements FormBinding { private final Map staticValues = new LinkedHashMap<>(); private final MultiValueMap formFields = new LinkedMultiValueMap<>(); - private final Map subForms = new LinkedHashMap<>(); - private final Map> subLists = new LinkedHashMap<>(); private final Map> groups = new LinkedHashMap<>(); Form(final JSONMapper jsonMapper) { @@ -104,40 +102,23 @@ public final class Form implements FormBinding { return this; } - public void putField(final String name, final Label label, final SingleSelection field) { + public void putField(final String name, final Label label, final Selection field) { this.formFields.add(name, createAccessor(label, field)); } - public void putField(final String name, final Label label, final MultiSelection field) { - this.formFields.add(name, createAccessor(label, field)); + public void putField( + final String name, + final Label label, + final Selection field, + final BiConsumer, ObjectNode> jsonValueAdapter) { + + this.formFields.add(name, createAccessor(label, field, jsonValueAdapter)); } public void putField(final String name, final Label label, final ImageUpload imageUpload) { this.formFields.add(name, createAccessor(label, imageUpload)); } - 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
array = this.subLists.computeIfAbsent(arrayName, k -> new ArrayList<>()); - array.add(form); - } - - public Form getSubForm(final String arrayName, final int index) { - final List array = this.subLists.get(arrayName); - if (array == null) { - return null; - } - - return array.get(index); - } - public void allVisible() { process( name -> true, @@ -196,30 +177,12 @@ public final class Form implements FormBinding { .filter(ffa -> StringUtils.isNoneBlank(ffa.getValue())) .forEach(ffa -> ffa.putJsonValue(entry.getKey(), this.objectRoot)); } - - for (final Map.Entry 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> entry : this.subLists.entrySet()) { - final List 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); - } } // following are FormFieldAccessor implementations for all field types //@formatter:off private FormFieldAccessor createAccessor(final Label label, final Label field) { - return new FormFieldAccessor(label, field) { + return new FormFieldAccessor(label, field) { @Override public String getValue() { return null; } @Override public void setValue(final String value) { field.setText(value); } }; @@ -230,26 +193,20 @@ public final class Form implements FormBinding { @Override public void setValue(final String value) { text.setText(value); } }; } - private FormFieldAccessor 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); } - }; + private FormFieldAccessor createAccessor(final Label label, final Selection selection) { + switch (selection.type()) { + case MULTI : return createAccessor(label, selection, Form::adaptCommaSeparatedStringToJsonArray); + default : return createAccessor(label, selection, null); + } } - private FormFieldAccessor createAccessor(final Label label,final MultiSelection multiSelection) { - return new FormFieldAccessor(label, multiSelection) { - @Override public String getValue() { return multiSelection.getSelectionValue(); } - @Override public void setValue(final String value) { multiSelection.select(value); } - @Override public void putJsonValue(final String key, final ObjectNode objectRoot) { - final String value = getValue(); - if (StringUtils.isNoneBlank(value)) { - final ArrayNode arrayNode = objectRoot.putArray(key); - final String[] split = StringUtils.split(value, Constants.LIST_SEPARATOR); - for (int i = 0; i < split.length; i++) { - arrayNode.add(split[i]); - } - } - } + private FormFieldAccessor createAccessor( + final Label label, + final Selection selection, + final BiConsumer, ObjectNode> jsonValueAdapter) { + + return new FormFieldAccessor(label, selection.adaptToControl(), jsonValueAdapter) { + @Override public String getValue() { return selection.getSelectionValue(); } + @Override public void setValue(final String value) { selection.select(value); } }; } private FormFieldAccessor createAccessor(final Label label, final ImageUpload imageUpload) { @@ -281,15 +238,44 @@ public final class Form implements FormBinding { } } + private static final void adaptCommaSeparatedStringToJsonArray(final Tuple tuple, + final ObjectNode jsonNode) { + if (StringUtils.isNoneBlank(tuple._2)) { + final ArrayNode arrayNode = jsonNode.putArray(tuple._1); + final String[] split = StringUtils.split(tuple._2, Constants.LIST_SEPARATOR); + for (int i = 0; i < split.length; i++) { + arrayNode.add(split[i]); + } + } + } + public static abstract class FormFieldAccessor { public final Label label; public final Control control; + private final BiConsumer, ObjectNode> jsonValueAdapter; private boolean hasError; - public FormFieldAccessor(final Label label, final Control control) { + FormFieldAccessor(final Label label, final Control control) { + this(label, control, null); + } + + FormFieldAccessor( + final Label label, + final Control control, + final BiConsumer, ObjectNode> jsonValueAdapter) { + this.label = label; this.control = control; + if (jsonValueAdapter != null) { + this.jsonValueAdapter = jsonValueAdapter; + } else { + this.jsonValueAdapter = (tuple, jsonObject) -> { + if (StringUtils.isNoneBlank(tuple._2)) { + jsonObject.put(tuple._1, tuple._2); + } + }; + } } public abstract String getValue(); @@ -301,11 +287,8 @@ public final class Form implements FormBinding { this.control.setVisible(visible); } - public void putJsonValue(final String key, final ObjectNode objectRoot) { - final String value = getValue(); - if (StringUtils.isNoneBlank(value)) { - objectRoot.put(key, value); - } + public final void putJsonValue(final String key, final ObjectNode objectRoot) { + this.jsonValueAdapter.accept(new Tuple<>(key, getValue()), objectRoot); } public void setError(final String errorTooltip) { diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/form/FormBuilder.java b/src/main/java/ch/ethz/seb/sebserver/gui/form/FormBuilder.java index 05212a3c..ce1bccbf 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/form/FormBuilder.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/form/FormBuilder.java @@ -30,6 +30,7 @@ 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.widget.Selection; import ch.ethz.seb.sebserver.gui.widget.WidgetFactory; import ch.ethz.seb.sebserver.gui.widget.WidgetFactory.CustomVariant; @@ -194,7 +195,7 @@ public class FormBuilder { final String value, final Supplier>> itemsSupplier) { - return new SelectionFieldBuilder(name, label, value, itemsSupplier); + return new SelectionFieldBuilder(Selection.Type.SINGLE, name, label, value, itemsSupplier); } public static SelectionFieldBuilder multiSelection( @@ -203,8 +204,16 @@ public class FormBuilder { final String value, final Supplier>> itemsSupplier) { - return new SelectionFieldBuilder(name, label, value, itemsSupplier) - .asMultiSelection(); + return new SelectionFieldBuilder(Selection.Type.MULTI, name, label, value, itemsSupplier); + } + + public static SelectionFieldBuilder multiComboSelection( + final String name, + final String label, + final String value, + final Supplier>> itemsSupplier) { + + return new SelectionFieldBuilder(Selection.Type.MULTI_COMBO, name, label, value, itemsSupplier); } public static ImageUploadFieldBuilder imageUpload(final String name, final String label, final String value) { diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/form/SelectionFieldBuilder.java b/src/main/java/ch/ethz/seb/sebserver/gui/form/SelectionFieldBuilder.java index 5900552a..1f09f780 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/form/SelectionFieldBuilder.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/form/SelectionFieldBuilder.java @@ -26,24 +26,25 @@ import org.eclipse.swt.widgets.Label; import ch.ethz.seb.sebserver.gbl.Constants; import ch.ethz.seb.sebserver.gbl.util.Tuple; import ch.ethz.seb.sebserver.gui.service.i18n.PolyglotPageService; -import ch.ethz.seb.sebserver.gui.widget.MultiSelection; import ch.ethz.seb.sebserver.gui.widget.Selection; -import ch.ethz.seb.sebserver.gui.widget.SingleSelection; +import ch.ethz.seb.sebserver.gui.widget.Selection.Type; import ch.ethz.seb.sebserver.gui.widget.WidgetFactory.CustomVariant; public final class SelectionFieldBuilder extends FieldBuilder { final Supplier>> itemsSupplier; Consumer selectionListener = null; - boolean multi = false; + final Selection.Type type; SelectionFieldBuilder( + final Selection.Type type, final String name, final String label, final String value, final Supplier>> itemsSupplier) { super(name, label, value); + this.type = type; this.itemsSupplier = itemsSupplier; } @@ -52,11 +53,6 @@ public final class SelectionFieldBuilder extends FieldBuilder { return this; } - public SelectionFieldBuilder asMultiSelection() { - this.multi = true; - return this; - } - @Override void build(final FormBuilder builder) { final Label lab = builder.labelLocalized(builder.formParent, this.label, this.spanLabel); @@ -68,29 +64,29 @@ public final class SelectionFieldBuilder extends FieldBuilder { } private void buildInput(final FormBuilder builder, final Label lab) { - final Selection selection = (this.multi) - ? builder.widgetFactory.multiSelectionLocalized(builder.formParent, this.itemsSupplier) - : builder.widgetFactory.singleSelectionLocalized(builder.formParent, this.itemsSupplier); + + final Selection selection = builder.widgetFactory.selectionLocalized( + this.type, + builder.formParent, + this.itemsSupplier); final GridData gridData = new GridData(SWT.FILL, SWT.FILL, true, false, this.spanInput, 1); ((Control) selection).setLayoutData(gridData); selection.select(this.value); - if (this.multi) { - builder.form.putField(this.name, lab, selection. getTypeInstance()); - } else { - builder.form.putField(this.name, lab, selection. getTypeInstance()); - } + builder.form.putField(this.name, lab, selection); + if (this.selectionListener != null) { ((Control) selection).addListener(SWT.Selection, e -> { this.selectionListener.accept(builder.form); }); } + builder.setFieldVisible(this.visible, this.name); } /* Build the read-only representation of the selection field */ private void buildReadOnly(final FormBuilder builder, final Label lab) { - if (this.multi) { + if (this.type == Type.MULTI || this.type == Type.MULTI_COMBO) { final Composite composite = new Composite(builder.formParent, SWT.NONE); final GridLayout gridLayout = new GridLayout(1, true); gridLayout.horizontalSpacing = 0; @@ -99,7 +95,11 @@ public final class SelectionFieldBuilder extends FieldBuilder { gridLayout.marginWidth = 0; composite.setLayout(gridLayout); if (StringUtils.isBlank(this.value)) { - createMuliSelectionReadonlyLabel(composite, Constants.EMPTY_NOTE); + final Label label = new Label(composite, SWT.NONE); + final GridData gridData = new GridData(SWT.FILL, SWT.CENTER, true, true); + label.setLayoutData(gridData); + label.setData(RWT.CUSTOM_VARIANT, CustomVariant.SELECTION_READONLY.key); + label.setText(this.value); } else { final Collection keys = Arrays.asList(StringUtils.split(this.value, Constants.LIST_SEPARATOR)); this.itemsSupplier.get() @@ -137,12 +137,4 @@ public final class SelectionFieldBuilder extends FieldBuilder { return label; } - private void createMuliSelectionReadonlyLabel(final Composite composite, final String value) { - final Label label = new Label(composite, SWT.NONE); - final GridData gridData = new GridData(SWT.FILL, SWT.CENTER, true, true); - label.setLayoutData(gridData); - label.setData(RWT.CUSTOM_VARIANT, CustomVariant.SELECTION_READONLY.key); - label.setText(value); - } - } \ No newline at end of file diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/service/ResourceService.java b/src/main/java/ch/ethz/seb/sebserver/gui/service/ResourceService.java index 75e57a4e..789f2511 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/service/ResourceService.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/service/ResourceService.java @@ -23,7 +23,7 @@ import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Service; import ch.ethz.seb.sebserver.gbl.Constants; -import ch.ethz.seb.sebserver.gbl.model.Domain; +import ch.ethz.seb.sebserver.gbl.model.Entity; import ch.ethz.seb.sebserver.gbl.model.exam.Exam.ExamType; import ch.ethz.seb.sebserver.gbl.model.exam.Indicator.IndicatorType; import ch.ethz.seb.sebserver.gbl.model.institution.LmsSetup.LmsType; @@ -104,7 +104,7 @@ public class ResourceService { public List> institutionResource() { return this.restService.getBuilder(GetInstitutionNames.class) - .withQueryParam(Domain.INSTITUTION.ATTR_ACTIVE, "true") + .withQueryParam(Entity.FILTER_ATTR_ACTIVE, Constants.TRUE_STRING) .call() .getOr(Collections.emptyList()) .stream() @@ -115,7 +115,7 @@ public class ResourceService { public Function getInstitutionNameFunction() { final Map idNameMap = this.restService.getBuilder(GetInstitutionNames.class) - .withQueryParam(Domain.INSTITUTION.ATTR_ACTIVE, "true") + .withQueryParam(Entity.FILTER_ATTR_ACTIVE, Constants.TRUE_STRING) .call() .getOr(Collections.emptyList()) .stream() @@ -133,8 +133,8 @@ public class ResourceService { final boolean isSEBAdmin = this.currentUser.get().hasRole(UserRole.SEB_SERVER_ADMIN); final String institutionId = (isSEBAdmin) ? "" : String.valueOf(this.currentUser.get().institutionId); return this.restService.getBuilder(GetLmsSetupNames.class) - .withQueryParam(Domain.LMS_SETUP.ATTR_INSTITUTION_ID, institutionId) - .withQueryParam(Domain.LMS_SETUP.ATTR_ACTIVE, "true") + .withQueryParam(Entity.FILTER_ATTR_INSTITUTION, institutionId) + .withQueryParam(Entity.FILTER_ATTR_ACTIVE, Constants.TRUE_STRING) .call() .getOr(Collections.emptyList()) .stream() @@ -146,8 +146,8 @@ public class ResourceService { final boolean isSEBAdmin = this.currentUser.get().hasRole(UserRole.SEB_SERVER_ADMIN); final String institutionId = (isSEBAdmin) ? "" : String.valueOf(this.currentUser.get().institutionId); final Map idNameMap = this.restService.getBuilder(GetLmsSetupNames.class) - .withQueryParam(Domain.LMS_SETUP.ATTR_INSTITUTION_ID, institutionId) - .withQueryParam(Domain.INSTITUTION.ATTR_ACTIVE, "true") + .withQueryParam(Entity.FILTER_ATTR_INSTITUTION, institutionId) + .withQueryParam(Entity.FILTER_ATTR_ACTIVE, Constants.TRUE_STRING) .call() .getOr(Collections.emptyList()) .stream() @@ -196,11 +196,24 @@ public class ResourceService { .collect(Collectors.toList()); } + public List> examSupporterResources() { + final UserInfo userInfo = this.currentUser.get(); + return this.restService.getBuilder(GetUserAccountNames.class) + .withQueryParam(Entity.FILTER_ATTR_INSTITUTION, String.valueOf(userInfo.institutionId)) + .withQueryParam(Entity.FILTER_ATTR_ACTIVE, Constants.TRUE_STRING) + .withQueryParam(UserInfo.FILTER_ATTR_ROLE, UserRole.EXAM_SUPPORTER.name()) + .call() + .getOr(Collections.emptyList()) + .stream() + .map(entityName -> new Tuple<>(entityName.modelId, entityName.name)) + .collect(Collectors.toList()); + } + public List> userResources() { final UserInfo userInfo = this.currentUser.get(); return this.restService.getBuilder(GetUserAccountNames.class) - .withQueryParam(Domain.USER.ATTR_INSTITUTION_ID, String.valueOf(userInfo.institutionId)) - .withQueryParam(Domain.USER.ATTR_ACTIVE, "true") + .withQueryParam(Entity.FILTER_ATTR_INSTITUTION, String.valueOf(userInfo.institutionId)) + .withQueryParam(Entity.FILTER_ATTR_ACTIVE, Constants.TRUE_STRING) .call() .getOr(Collections.emptyList()) .stream() diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/service/page/ModalInputDialog.java b/src/main/java/ch/ethz/seb/sebserver/gui/service/page/ModalInputDialog.java new file mode 100644 index 00000000..99566a6f --- /dev/null +++ b/src/main/java/ch/ethz/seb/sebserver/gui/service/page/ModalInputDialog.java @@ -0,0 +1,101 @@ +/* + * Copyright (c) 2019 ETH Zürich, Educational Development and Technology (LET) + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +package ch.ethz.seb.sebserver.gui.service.page; + +import java.util.function.Consumer; +import java.util.function.Supplier; + +import org.eclipse.rap.rwt.RWT; +import org.eclipse.swt.SWT; +import org.eclipse.swt.graphics.Rectangle; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Button; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Dialog; +import org.eclipse.swt.widgets.Shell; + +import ch.ethz.seb.sebserver.gui.service.i18n.LocTextKey; +import ch.ethz.seb.sebserver.gui.widget.WidgetFactory; + +public class ModalInputDialog extends Dialog { + + private static final long serialVersionUID = -3448614119078234374L; + + private final WidgetFactory widgetFactory; + + private T returnValue = null; + + public ModalInputDialog( + final Shell parent, + final WidgetFactory widgetFactory) { + + super(parent, SWT.BORDER | SWT.TITLE | SWT.APPLICATION_MODAL); + this.widgetFactory = widgetFactory; + } + + public void open( + final String title, + final PageContext pageContext, + final Consumer callback, + final ModalInputDialogComposer contentComposer) { + + // Create the dialog window + final Shell shell = new Shell(getParent(), getStyle()); + shell.setText(getText()); + shell.setData(RWT.CUSTOM_VARIANT, "message"); + shell.setText(title); + shell.setLayout(new GridLayout(2, true)); + shell.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); + + final Composite main = new Composite(shell, SWT.NONE); + main.setLayout(new GridLayout()); + final GridData gridData = new GridData(SWT.FILL, SWT.TOP, true, true); + gridData.horizontalSpan = 2; + main.setLayoutData(gridData); + + final PageContext internalPageContext = pageContext.copyOf(main); + final Supplier valueSuppier = contentComposer.compose(internalPageContext); + + final Button ok = this.widgetFactory.buttonLocalized( + shell, + new LocTextKey("sebserver.overall.action.ok")); + GridData data = new GridData(GridData.HORIZONTAL_ALIGN_END); + data.widthHint = 100; + ok.setLayoutData(data); + ok.addListener(SWT.Selection, event -> { + callback.accept(valueSuppier.get()); + this.returnValue = valueSuppier.get(); + shell.close(); + }); + + shell.setDefaultButton(ok); + + final Button cancel = this.widgetFactory.buttonLocalized( + shell, + new LocTextKey("sebserver.overall.action.cancel")); + data = new GridData(GridData.CENTER); + data.widthHint = 100; + cancel.setLayoutData(data); + cancel.addListener(SWT.Selection, event -> { + shell.close(); + }); + + shell.pack(); + final Rectangle bounds = shell.getBounds(); + final Rectangle bounds2 = super.getParent().getDisplay().getBounds(); + bounds.width = 400; + bounds.x = (bounds2.width - bounds.width) / 2; + bounds.y = (bounds2.height - bounds.height) / 2; + shell.setBounds(bounds); + + shell.open(); + } + +} diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/service/page/ModalInputDialogComposer.java b/src/main/java/ch/ethz/seb/sebserver/gui/service/page/ModalInputDialogComposer.java new file mode 100644 index 00000000..41b92ab7 --- /dev/null +++ b/src/main/java/ch/ethz/seb/sebserver/gui/service/page/ModalInputDialogComposer.java @@ -0,0 +1,18 @@ +/* + * Copyright (c) 2019 ETH Zürich, Educational Development and Technology (LET) + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +package ch.ethz.seb.sebserver.gui.service.page; + +import java.util.function.Supplier; + +@FunctionalInterface +public interface ModalInputDialogComposer { + + Supplier compose(PageContext pageContext); + +} diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/table/EntityTable.java b/src/main/java/ch/ethz/seb/sebserver/gui/table/EntityTable.java index 0f5038ed..2409844d 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/table/EntityTable.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/table/EntityTable.java @@ -325,10 +325,6 @@ public class EntityTable { index++; } - - // NOTE this.layout() triggers the navigation to disappear unexpectedly!? - //this.table.layout(true, true); - //this.composite.layout(true, true); } catch (final Exception e) { log.warn("Failed to adaptColumnWidth: ", e); } diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/table/TableFilter.java b/src/main/java/ch/ethz/seb/sebserver/gui/table/TableFilter.java index 5d554d12..88de55ea 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/table/TableFilter.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/table/TableFilter.java @@ -30,7 +30,7 @@ import ch.ethz.seb.sebserver.gbl.Constants; import ch.ethz.seb.sebserver.gbl.model.Entity; import ch.ethz.seb.sebserver.gui.service.i18n.LocTextKey; import ch.ethz.seb.sebserver.gui.table.ColumnDefinition.TableFilterAttribute; -import ch.ethz.seb.sebserver.gui.widget.SingleSelection; +import ch.ethz.seb.sebserver.gui.widget.Selection; import ch.ethz.seb.sebserver.gui.widget.WidgetFactory.ImageIcon; public class TableFilter { @@ -105,7 +105,7 @@ public class TableFilter { case TEXT: return new TextFilter(attribute); case SINGLE_SELECTION: - return new Selection(attribute); + return new SelectionFilter(attribute); case DATE: return new Date(attribute); default: @@ -245,21 +245,24 @@ public class TableFilter { } - private class Selection extends FilterComponent { + private class SelectionFilter extends FilterComponent { - protected SingleSelection selector; + protected Selection selector; - Selection(final TableFilterAttribute attribute) { + SelectionFilter(final TableFilterAttribute attribute) { super(attribute); } @Override FilterComponent build(final Composite parent) { this.selector = TableFilter.this.entityTable.widgetFactory - .singleSelectionLocalized( + .selectionLocalized( + ch.ethz.seb.sebserver.gui.widget.Selection.Type.SINGLE, parent, this.attribute.resourceSupplier); - this.selector.setLayoutData(this.rowData); + this.selector + .adaptToControl() + .setLayoutData(this.rowData); return this; } diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/widget/MultiSelection.java b/src/main/java/ch/ethz/seb/sebserver/gui/widget/MultiSelection.java index 742eafcb..1aee259a 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/widget/MultiSelection.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/widget/MultiSelection.java @@ -34,7 +34,7 @@ public class MultiSelection extends Composite implements Selection { private final List