SEBSERV-29 new multi selection with combo supporter selection
This commit is contained in:
		
							parent
							
								
									94858b757a
								
							
						
					
					
						commit
						584a900529
					
				
					 24 changed files with 535 additions and 181 deletions
				
			
		|  | @ -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; | ||||
|  |  | |||
|  | @ -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) | ||||
|  |  | |||
|  | @ -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<String> 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; | ||||
|     } | ||||
| 
 | ||||
| } | ||||
|  |  | |||
|  | @ -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), | ||||
| 
 | ||||
|     ; | ||||
| 
 | ||||
|  |  | |||
|  | @ -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<String, String> staticValues = new LinkedHashMap<>(); | ||||
|     private final MultiValueMap<String, FormFieldAccessor> formFields = new LinkedMultiValueMap<>(); | ||||
|     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) { | ||||
|  | @ -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<Tuple<String>, 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<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, | ||||
|  | @ -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<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); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     // 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<Tuple<String>, 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<String> 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<Tuple<String>, 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<Tuple<String>, 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) { | ||||
|  |  | |||
|  | @ -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<List<Tuple<String>>> 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<List<Tuple<String>>> 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<List<Tuple<String>>> 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) { | ||||
|  |  | |||
|  | @ -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<List<Tuple<String>>> itemsSupplier; | ||||
|     Consumer<Form> 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<List<Tuple<String>>> 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.<MultiSelection> getTypeInstance()); | ||||
|         } else { | ||||
|             builder.form.putField(this.name, lab, selection.<SingleSelection> 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<String> 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); | ||||
|     } | ||||
| 
 | ||||
| } | ||||
|  | @ -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<Tuple<String>> 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<String, String> getInstitutionNameFunction() { | ||||
| 
 | ||||
|         final Map<String, String> 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<String, String> 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<Tuple<String>> 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<Tuple<String>> 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() | ||||
|  |  | |||
|  | @ -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<T> 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<T> callback, | ||||
|             final ModalInputDialogComposer<T> 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<T> 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(); | ||||
|     } | ||||
| 
 | ||||
| } | ||||
|  | @ -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<T> { | ||||
| 
 | ||||
|     Supplier<T> compose(PageContext pageContext); | ||||
| 
 | ||||
| } | ||||
|  | @ -325,10 +325,6 @@ public class EntityTable<ROW extends Entity> { | |||
| 
 | ||||
|                 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); | ||||
|         } | ||||
|  |  | |||
|  | @ -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<ROW extends Entity> { | ||||
|  | @ -105,7 +105,7 @@ public class TableFilter<ROW extends Entity> { | |||
|             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<ROW extends Entity> { | |||
| 
 | ||||
|     } | ||||
| 
 | ||||
|     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; | ||||
|         } | ||||
| 
 | ||||
|  |  | |||
|  | @ -34,7 +34,7 @@ public class MultiSelection extends Composite implements Selection { | |||
|     private final List<Label> labels = new ArrayList<>(); | ||||
|     private final List<Label> selected = new ArrayList<>(); | ||||
| 
 | ||||
|     public MultiSelection(final Composite parent) { | ||||
|     MultiSelection(final Composite parent) { | ||||
|         super(parent, SWT.NONE); | ||||
|         final GridLayout gridLayout = new GridLayout(1, true); | ||||
|         gridLayout.verticalSpacing = 1; | ||||
|  | @ -44,6 +44,11 @@ public class MultiSelection extends Composite implements Selection { | |||
|         setLayout(gridLayout); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public Type type() { | ||||
|         return Type.MULTI; | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void applyNewMapping(final List<Tuple<String>> mapping) { | ||||
|         final String selectionValue = getSelectionValue(); | ||||
|  | @ -134,10 +139,4 @@ public class MultiSelection extends Composite implements Selection { | |||
|         deselectAll(); | ||||
|     } | ||||
| 
 | ||||
|     @SuppressWarnings("unchecked") | ||||
|     @Override | ||||
|     public MultiSelection getTypeInstance() { | ||||
|         return this; | ||||
|     } | ||||
| 
 | ||||
| } | ||||
|  |  | |||
|  | @ -0,0 +1,131 @@ | |||
| /* | ||||
|  * Copyright (c) 2019 ETH Zürich, Educational Development and Technology (LET) | ||||
|  * | ||||
|  * This Source Code Form is subject to the terms of the Mozilla Public | ||||
|  * License, v. 2.0. If a copy of the MPL was not distributed with this | ||||
|  * file, You can obtain one at http://mozilla.org/MPL/2.0/. | ||||
|  */ | ||||
| 
 | ||||
| package ch.ethz.seb.sebserver.gui.widget; | ||||
| 
 | ||||
| import java.util.ArrayList; | ||||
| import java.util.List; | ||||
| import java.util.stream.Collectors; | ||||
| 
 | ||||
| import org.eclipse.swt.SWT; | ||||
| import org.eclipse.swt.custom.TableEditor; | ||||
| import org.eclipse.swt.layout.GridLayout; | ||||
| import org.eclipse.swt.widgets.Combo; | ||||
| import org.eclipse.swt.widgets.Composite; | ||||
| import org.eclipse.swt.widgets.Event; | ||||
| import org.eclipse.swt.widgets.Table; | ||||
| import org.eclipse.swt.widgets.TableColumn; | ||||
| import org.eclipse.swt.widgets.TableItem; | ||||
| import org.slf4j.Logger; | ||||
| import org.slf4j.LoggerFactory; | ||||
| 
 | ||||
| import ch.ethz.seb.sebserver.gbl.Constants; | ||||
| import ch.ethz.seb.sebserver.gbl.util.Tuple; | ||||
| import ch.ethz.seb.sebserver.gui.service.i18n.LocTextKey; | ||||
| import ch.ethz.seb.sebserver.gui.widget.WidgetFactory.ImageIcon; | ||||
| 
 | ||||
| public class MultiSelectionCombo extends Composite implements Selection { | ||||
| 
 | ||||
|     private static final Logger log = LoggerFactory.getLogger(MultiSelectionCombo.class); | ||||
|     private static final long serialVersionUID = -7787134114963647332L; | ||||
|     private static final int ACTION_COLUMN_WIDTH = 20; | ||||
| 
 | ||||
|     private final WidgetFactory widgetFactory; | ||||
|     private final Table table; | ||||
|     private final Combo combo; | ||||
|     private final List<String> selected = new ArrayList<>(); | ||||
|     private final List<Tuple<String>> mapping = new ArrayList<>(); | ||||
| 
 | ||||
|     MultiSelectionCombo(final Composite parent, final WidgetFactory widgetFactory) { | ||||
|         super(parent, SWT.NONE); | ||||
|         this.widgetFactory = widgetFactory; | ||||
|         final GridLayout gridLayout = new GridLayout(1, true); | ||||
|         gridLayout.verticalSpacing = 1; | ||||
|         gridLayout.marginLeft = 0; | ||||
|         gridLayout.marginHeight = 0; | ||||
|         gridLayout.marginWidth = 0; | ||||
|         setLayout(gridLayout); | ||||
| 
 | ||||
|         this.table = new Table(this, SWT.NONE); | ||||
| 
 | ||||
|         TableColumn column = new TableColumn(this.table, SWT.NONE); | ||||
|         column = new TableColumn(this.table, SWT.NONE); | ||||
|         column.setWidth(ACTION_COLUMN_WIDTH); | ||||
|         this.table.setHeaderVisible(false); | ||||
|         this.table.addListener(SWT.Resize, this::adaptColumnWidth); | ||||
| 
 | ||||
|         final TableItem header = new TableItem(this.table, SWT.NONE); | ||||
|         final TableEditor editor = new TableEditor(this.table); | ||||
|         this.combo = new Combo(this.table, SWT.NONE); | ||||
|         editor.grabHorizontal = true; | ||||
|         editor.setEditor(this.combo, header, 0); | ||||
| 
 | ||||
|         widgetFactory.imageButton( | ||||
|                 ImageIcon.ADD, | ||||
|                 this.table, | ||||
|                 new LocTextKey("Add"), | ||||
|                 this::addComboSelection); | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public Type type() { | ||||
|         return Type.MULTI_COMBO; | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void applyNewMapping(final List<Tuple<String>> mapping) { | ||||
|         this.selected.clear(); | ||||
|         this.mapping.clear(); | ||||
|         this.mapping.addAll(mapping); | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void select(final String keys) { | ||||
|         clear(); | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public String getSelectionValue() { | ||||
|         if (this.selected.isEmpty()) { | ||||
|             return null; | ||||
|         } | ||||
|         return this.mapping | ||||
|                 .stream() | ||||
|                 .filter(t -> this.selected.contains(t._2)) | ||||
|                 .map(t -> t._1) | ||||
|                 .reduce("", (s1, s2) -> s1.concat(Constants.LIST_SEPARATOR).concat(s2)); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void clear() { | ||||
|         this.selected.clear(); | ||||
|         this.table.remove(1, this.table.getItemCount()); | ||||
|         final List<String> names = this.mapping | ||||
|                 .stream() | ||||
|                 .map(t -> t._2) | ||||
|                 .collect(Collectors.toList()); | ||||
|         this.combo.setItems(names.toArray(new String[names.size()])); | ||||
|     } | ||||
| 
 | ||||
|     private void addComboSelection(final Event event) { | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
|     private void adaptColumnWidth(final Event event) { | ||||
|         try { | ||||
|             final int currentTableWidth = this.table.getParent().getClientArea().width; | ||||
|             this.table.getColumn(0).setWidth(currentTableWidth - ACTION_COLUMN_WIDTH); | ||||
|         } catch (final Exception e) { | ||||
|             log.warn("Failed to adaptColumnWidth: ", e); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
| } | ||||
|  | @ -10,10 +10,20 @@ package ch.ethz.seb.sebserver.gui.widget; | |||
| 
 | ||||
| import java.util.List; | ||||
| 
 | ||||
| import org.eclipse.swt.widgets.Control; | ||||
| 
 | ||||
| import ch.ethz.seb.sebserver.gbl.util.Tuple; | ||||
| 
 | ||||
| public interface Selection { | ||||
| 
 | ||||
|     enum Type { | ||||
|         SINGLE, | ||||
|         MULTI, | ||||
|         MULTI_COMBO | ||||
|     } | ||||
| 
 | ||||
|     Type type(); | ||||
| 
 | ||||
|     void applyNewMapping(final List<Tuple<String>> mapping); | ||||
| 
 | ||||
|     void select(final String keys); | ||||
|  | @ -24,6 +34,10 @@ public interface Selection { | |||
| 
 | ||||
|     void setVisible(boolean visible); | ||||
| 
 | ||||
|     <T extends Selection> T getTypeInstance(); | ||||
|     default Control adaptToControl() { | ||||
|         return (Control) this; | ||||
|     } | ||||
| 
 | ||||
|     //<T extends Selection> T getTypeInstance(); | ||||
| 
 | ||||
| } | ||||
|  |  | |||
|  | @ -25,7 +25,7 @@ public class SingleSelection extends Combo implements Selection { | |||
|     final List<String> valueMapping; | ||||
|     final List<String> keyMapping; | ||||
| 
 | ||||
|     public SingleSelection(final Composite parent) { | ||||
|     SingleSelection(final Composite parent) { | ||||
|         super(parent, SWT.READ_ONLY); | ||||
|         this.valueMapping = new ArrayList<>(); | ||||
|         this.keyMapping = new ArrayList<>(); | ||||
|  | @ -72,10 +72,9 @@ public class SingleSelection extends Combo implements Selection { | |||
|         super.setItems(this.valueMapping.toArray(new String[this.valueMapping.size()])); | ||||
|     } | ||||
| 
 | ||||
|     @SuppressWarnings("unchecked") | ||||
|     @Override | ||||
|     public SingleSelection getTypeInstance() { | ||||
|         return this; | ||||
|     public Type type() { | ||||
|         return Type.SINGLE; | ||||
|     } | ||||
| 
 | ||||
| } | ||||
|  |  | |||
|  | @ -60,6 +60,8 @@ public class WidgetFactory { | |||
|     public enum ImageIcon { | ||||
|         MAXIMIZE("maximize.png"), | ||||
|         MINIMIZE("minimize.png"), | ||||
|         ADD("add.png"), | ||||
|         REMOVE("remove.png"), | ||||
|         EDIT("edit.png"), | ||||
|         TEST("test.png"), | ||||
|         IMPORT("import.png"), | ||||
|  | @ -307,27 +309,64 @@ public class WidgetFactory { | |||
|         return imageButton; | ||||
|     } | ||||
| 
 | ||||
|     public SingleSelection singleSelectionLocalized( | ||||
|     public Selection selectionLocalized( | ||||
|             final Selection.Type type, | ||||
|             final Composite parent, | ||||
|             final Supplier<List<Tuple<String>>> itemsSupplier) { | ||||
| 
 | ||||
|         final SingleSelection singleSelection = new SingleSelection(parent); | ||||
|         final Consumer<SingleSelection> updateFunction = ss -> ss.applyNewMapping(itemsSupplier.get()); | ||||
|         singleSelection.setData(POLYGLOT_WIDGET_FUNCTION_KEY, updateFunction); | ||||
|         updateFunction.accept(singleSelection); | ||||
|         return singleSelection; | ||||
|         final Selection selection; | ||||
|         switch (type) { | ||||
|             case SINGLE: | ||||
|                 selection = new SingleSelection(parent); | ||||
|                 break; | ||||
|             case MULTI: | ||||
|                 selection = new MultiSelection(parent); | ||||
|                 break; | ||||
|             case MULTI_COMBO: | ||||
|                 selection = new MultiSelectionCombo(parent, this); | ||||
|                 break; | ||||
|             default: | ||||
|                 throw new IllegalArgumentException("Unsupported Selection.Type: " + type); | ||||
|         } | ||||
| 
 | ||||
|         final Consumer<Selection> updateFunction = ss -> ss.applyNewMapping(itemsSupplier.get()); | ||||
|         selection.adaptToControl().setData(POLYGLOT_WIDGET_FUNCTION_KEY, updateFunction); | ||||
|         updateFunction.accept(selection); | ||||
|         return selection; | ||||
|     } | ||||
| 
 | ||||
|     public MultiSelection multiSelectionLocalized( | ||||
|             final Composite parent, | ||||
|             final Supplier<List<Tuple<String>>> itemsSupplier) { | ||||
| 
 | ||||
|         final MultiSelection multiSelection = new MultiSelection(parent); | ||||
|         final Consumer<MultiSelection> updateFunction = ss -> ss.applyNewMapping(itemsSupplier.get()); | ||||
|         multiSelection.setData(POLYGLOT_WIDGET_FUNCTION_KEY, updateFunction); | ||||
|         updateFunction.accept(multiSelection); | ||||
|         return multiSelection; | ||||
|     } | ||||
| //    public SingleSelection singleSelectionLocalized( | ||||
| //            final Composite parent, | ||||
| //            final Supplier<List<Tuple<String>>> itemsSupplier) { | ||||
| // | ||||
| //        final SingleSelection singleSelection = new SingleSelection(parent); | ||||
| //        final Consumer<SingleSelection> updateFunction = ss -> ss.applyNewMapping(itemsSupplier.get()); | ||||
| //        singleSelection.setData(POLYGLOT_WIDGET_FUNCTION_KEY, updateFunction); | ||||
| //        updateFunction.accept(singleSelection); | ||||
| //        return singleSelection; | ||||
| //    } | ||||
| // | ||||
| //    public MultiSelection multiSelectionLocalized( | ||||
| //            final Composite parent, | ||||
| //            final Supplier<List<Tuple<String>>> itemsSupplier) { | ||||
| // | ||||
| //        final MultiSelection multiSelection = new MultiSelection(parent); | ||||
| //        final Consumer<MultiSelection> updateFunction = ss -> ss.applyNewMapping(itemsSupplier.get()); | ||||
| //        multiSelection.setData(POLYGLOT_WIDGET_FUNCTION_KEY, updateFunction); | ||||
| //        updateFunction.accept(multiSelection); | ||||
| //        return multiSelection; | ||||
| //    } | ||||
| // | ||||
| //    public MultiSelectionCombo multiSelectionComboLocalized( | ||||
| //            final Composite parent, | ||||
| //            final Supplier<List<Tuple<String>>> itemsSupplier) { | ||||
| // | ||||
| //        final MultiSelectionCombo multiSelection = new MultiSelectionCombo(parent, this); | ||||
| //        final Consumer<MultiSelectionCombo> updateFunction = ss -> ss.applyNewMapping(itemsSupplier.get()); | ||||
| //        multiSelection.setData(POLYGLOT_WIDGET_FUNCTION_KEY, updateFunction); | ||||
| //        updateFunction.accept(multiSelection); | ||||
| //        return multiSelection; | ||||
| //    } | ||||
| 
 | ||||
|     public ImageUpload imageUploadLocalized( | ||||
|             final Composite parent, | ||||
|  |  | |||
|  | @ -170,7 +170,7 @@ public class ClientCredentialServiceImpl implements ClientCredentialService { | |||
|     } | ||||
| 
 | ||||
|     private final static char[] possibleCharacters = (new String( | ||||
|             "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789~`!@#$%^&*()-_=+[{]}?")) | ||||
|             "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789~`!@#$%^*()-_=+[{]}?")) | ||||
|                     .toCharArray(); | ||||
| 
 | ||||
|     private CharSequence getSalt(final CharSequence saltPlain) throws UnsupportedEncodingException { | ||||
|  |  | |||
|  | @ -67,6 +67,10 @@ public class FilterMap extends POSTMapper { | |||
|         return getString(UserInfo.FILTER_ATTR_LANGUAGE); | ||||
|     } | ||||
| 
 | ||||
|     public String getUserRole() { | ||||
|         return getString(UserInfo.FILTER_ATTR_ROLE); | ||||
|     } | ||||
| 
 | ||||
|     public String getLmsSetupType() { | ||||
|         return getString(LmsSetup.FILTER_ATTR_LMS_TYPE); | ||||
|     } | ||||
|  |  | |||
|  | @ -150,33 +150,40 @@ public class UserDAOImpl implements UserDAO { | |||
|     @Override | ||||
|     @Transactional(readOnly = true) | ||||
|     public Result<Collection<UserInfo>> allMatching(final FilterMap filterMap, final Predicate<UserInfo> predicate) { | ||||
|         return Result.tryCatch(() -> this.userRecordMapper | ||||
|                 .selectByExample() | ||||
|                 .where( | ||||
|                         UserRecordDynamicSqlSupport.active, | ||||
|                         isEqualToWhenPresent(filterMap.getActiveAsInt())) | ||||
|                 .and( | ||||
|                         UserRecordDynamicSqlSupport.institutionId, | ||||
|                         isEqualToWhenPresent(filterMap.getInstitutionId())) | ||||
|                 .and( | ||||
|                         UserRecordDynamicSqlSupport.name, | ||||
|                         isLikeWhenPresent(filterMap.getName())) | ||||
|                 .and( | ||||
|                         UserRecordDynamicSqlSupport.username, | ||||
|                         isLikeWhenPresent(filterMap.getUserUsername())) | ||||
|                 .and( | ||||
|                         UserRecordDynamicSqlSupport.email, | ||||
|                         isLikeWhenPresent(filterMap.getUserEmail())) | ||||
|                 .and( | ||||
|                         UserRecordDynamicSqlSupport.language, | ||||
|                         isLikeWhenPresent(filterMap.getUserLanguage())) | ||||
|                 .build() | ||||
|                 .execute() | ||||
|                 .stream() | ||||
|                 .map(this::toDomainModel) | ||||
|                 .flatMap(DAOLoggingSupport::logUnexpectedErrorAndSkip) | ||||
|                 .filter(predicate) | ||||
|                 .collect(Collectors.toList())); | ||||
|         return Result.tryCatch(() -> { | ||||
|             final String userRole = filterMap.getUserRole(); | ||||
|             final Predicate<UserInfo> _predicate = (StringUtils.isNotBlank(userRole)) | ||||
|                     ? predicate.and(ui -> ui.roles.contains(userRole)) | ||||
|                     : predicate; | ||||
| 
 | ||||
|             return this.userRecordMapper | ||||
|                     .selectByExample() | ||||
|                     .where( | ||||
|                             UserRecordDynamicSqlSupport.active, | ||||
|                             isEqualToWhenPresent(filterMap.getActiveAsInt())) | ||||
|                     .and( | ||||
|                             UserRecordDynamicSqlSupport.institutionId, | ||||
|                             isEqualToWhenPresent(filterMap.getInstitutionId())) | ||||
|                     .and( | ||||
|                             UserRecordDynamicSqlSupport.name, | ||||
|                             isLikeWhenPresent(filterMap.getName())) | ||||
|                     .and( | ||||
|                             UserRecordDynamicSqlSupport.username, | ||||
|                             isLikeWhenPresent(filterMap.getUserUsername())) | ||||
|                     .and( | ||||
|                             UserRecordDynamicSqlSupport.email, | ||||
|                             isLikeWhenPresent(filterMap.getUserEmail())) | ||||
|                     .and( | ||||
|                             UserRecordDynamicSqlSupport.language, | ||||
|                             isLikeWhenPresent(filterMap.getUserLanguage())) | ||||
|                     .build() | ||||
|                     .execute() | ||||
|                     .stream() | ||||
|                     .map(this::toDomainModel) | ||||
|                     .flatMap(DAOLoggingSupport::logUnexpectedErrorAndSkip) | ||||
|                     .filter(_predicate) | ||||
|                     .collect(Collectors.toList()); | ||||
|         }); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|  |  | |||
|  | @ -12,6 +12,8 @@ sebserver.overall.action.modify.cancel=Cancel | |||
| sebserver.overall.action.modify.cancel.confirm=Are you sure to cancel? Modifications will be lost. | ||||
| sebserver.overall.action.filter=Apply Filter | ||||
| sebserver.overall.action.filter.clear=Clear Filter Criteria | ||||
| sebserver.overall.action.ok=OK | ||||
| sebserver.overall.action.cancel=Cancel | ||||
| 
 | ||||
| sebserver.overall.action.category.varia=Varia | ||||
| 
 | ||||
|  |  | |||
|  | @ -96,7 +96,7 @@ Label.selected { | |||
| Label-SeparatorLine { | ||||
|     background-image: none; | ||||
|     background-color: transparent; | ||||
|     border: 1px solid #595959; | ||||
|     border: 1px solid #bdbdbd; | ||||
|     border-radius: 0px; | ||||
|     height: 1px; | ||||
| } | ||||
|  |  | |||
							
								
								
									
										
											BIN
										
									
								
								src/main/resources/static/images/add.png
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								src/main/resources/static/images/add.png
									
										
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 98 B | 
							
								
								
									
										
											BIN
										
									
								
								src/main/resources/static/images/remove.png
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								src/main/resources/static/images/remove.png
									
										
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 85 B | 
		Loading…
	
	Add table
		
		Reference in a new issue
	
	 anhefti
						anhefti