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 */
|
/** Global Constants used in SEB Server web-service as well as in web-gui component */
|
||||||
public final class Constants {
|
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 SECOND_IN_MILLIS = 1000;
|
||||||
public static final long MINUTE_IN_MILLIS = 60 * SECOND_IN_MILLIS;
|
public static final long MINUTE_IN_MILLIS = 60 * SECOND_IN_MILLIS;
|
||||||
public static final long HOUR_IN_MILLIS = 60 * MINUTE_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_USER_NAME = "username";
|
||||||
public static final String FILTER_ATTR_EMAIL = "email";
|
public static final String FILTER_ATTR_EMAIL = "email";
|
||||||
public static final String FILTER_ATTR_LANGUAGE = "language";
|
public static final String FILTER_ATTR_LANGUAGE = "language";
|
||||||
|
public static final String FILTER_ATTR_ROLE = "role";
|
||||||
|
|
||||||
/** The user's UUID */
|
/** The user's UUID */
|
||||||
@JsonProperty(USER.ATTR_UUID)
|
@JsonProperty(USER.ATTR_UUID)
|
||||||
|
|
|
@ -10,7 +10,11 @@ package ch.ethz.seb.sebserver.gui.content;
|
||||||
|
|
||||||
import java.util.function.Function;
|
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.Composite;
|
||||||
|
import org.eclipse.swt.widgets.Label;
|
||||||
|
import org.eclipse.swt.widgets.Text;
|
||||||
import org.springframework.beans.factory.annotation.Value;
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
import org.springframework.context.annotation.Lazy;
|
import org.springframework.context.annotation.Lazy;
|
||||||
import org.springframework.stereotype.Component;
|
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.ResourceService;
|
||||||
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.page.ModalInputDialog;
|
||||||
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.Action;
|
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);
|
final GrantCheck userGrant = currentUser.grantCheck(EntityType.EXAM);
|
||||||
pageContext.clearEntityKeys()
|
pageContext.clearEntityKeys()
|
||||||
|
|
||||||
|
.createAction(ActionDefinition.TEST_ACTION)
|
||||||
|
.withExec(this::testModalInput)
|
||||||
|
.publish()
|
||||||
|
|
||||||
.createAction(ActionDefinition.EXAM_IMPORT)
|
.createAction(ActionDefinition.EXAM_IMPORT)
|
||||||
.publishIf(userGrant::im)
|
.publishIf(userGrant::im)
|
||||||
|
|
||||||
|
@ -163,4 +172,29 @@ public class ExamList implements TemplateComposer {
|
||||||
.getText("sebserver.exam.type." + exam.type.name());
|
.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(
|
EXAM_INDICATOR_NEW(
|
||||||
new LocTextKey("sebserver.exam.indicator.action.list.new"),
|
new LocTextKey("sebserver.exam.indicator.action.list.new"),
|
||||||
ImageIcon.NEW,
|
ImageIcon.NEW,
|
||||||
LmsSetupForm.class,
|
IndicatorForm.class,
|
||||||
EXAM_VIEW_FROM_LIST,
|
EXAM_VIEW_FROM_LIST,
|
||||||
ActionCategory.FORM,
|
ActionCategory.FORM,
|
||||||
false),
|
false),
|
||||||
|
@ -350,6 +350,12 @@ public enum ActionDefinition {
|
||||||
EXAM_VIEW_FROM_LIST,
|
EXAM_VIEW_FROM_LIST,
|
||||||
ActionCategory.FORM,
|
ActionCategory.FORM,
|
||||||
true),
|
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;
|
package ch.ethz.seb.sebserver.gui.form;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
import java.util.function.BiConsumer;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
import java.util.function.Predicate;
|
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.Constants;
|
||||||
import ch.ethz.seb.sebserver.gbl.api.JSONMapper;
|
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.service.remote.webservice.api.FormBinding;
|
||||||
import ch.ethz.seb.sebserver.gui.widget.ImageUpload;
|
import ch.ethz.seb.sebserver.gui.widget.ImageUpload;
|
||||||
import ch.ethz.seb.sebserver.gui.widget.MultiSelection;
|
import ch.ethz.seb.sebserver.gui.widget.Selection;
|
||||||
import ch.ethz.seb.sebserver.gui.widget.SingleSelection;
|
|
||||||
|
|
||||||
public final class Form implements FormBinding {
|
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 Map<String, String> staticValues = new LinkedHashMap<>();
|
||||||
private final MultiValueMap<String, FormFieldAccessor> formFields = new LinkedMultiValueMap<>();
|
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<>();
|
private final Map<String, Set<String>> groups = new LinkedHashMap<>();
|
||||||
|
|
||||||
Form(final JSONMapper jsonMapper) {
|
Form(final JSONMapper jsonMapper) {
|
||||||
|
@ -104,40 +102,23 @@ public final class Form implements FormBinding {
|
||||||
return this;
|
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));
|
this.formFields.add(name, createAccessor(label, field));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void putField(final String name, final Label label, final MultiSelection field) {
|
public void putField(
|
||||||
this.formFields.add(name, createAccessor(label, field));
|
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) {
|
public void putField(final String name, final Label label, final ImageUpload imageUpload) {
|
||||||
this.formFields.add(name, createAccessor(label, 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() {
|
public void allVisible() {
|
||||||
process(
|
process(
|
||||||
name -> true,
|
name -> true,
|
||||||
|
@ -196,30 +177,12 @@ public final class Form implements FormBinding {
|
||||||
.filter(ffa -> StringUtils.isNoneBlank(ffa.getValue()))
|
.filter(ffa -> StringUtils.isNoneBlank(ffa.getValue()))
|
||||||
.forEach(ffa -> ffa.putJsonValue(entry.getKey(), this.objectRoot));
|
.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
|
// following are FormFieldAccessor implementations for all field types
|
||||||
//@formatter:off
|
//@formatter:off
|
||||||
private FormFieldAccessor createAccessor(final Label label, final Label field) {
|
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 String getValue() { return null; }
|
||||||
@Override public void setValue(final String value) { field.setText(value); }
|
@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); }
|
@Override public void setValue(final String value) { text.setText(value); }
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
private FormFieldAccessor createAccessor(final Label label, final SingleSelection singleSelection) {
|
private FormFieldAccessor createAccessor(final Label label, final Selection selection) {
|
||||||
return new FormFieldAccessor(label, singleSelection) {
|
switch (selection.type()) {
|
||||||
@Override public String getValue() { return singleSelection.getSelectionValue(); }
|
case MULTI : return createAccessor(label, selection, Form::adaptCommaSeparatedStringToJsonArray);
|
||||||
@Override public void setValue(final String value) { singleSelection.select(value); }
|
default : return createAccessor(label, selection, null);
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
private FormFieldAccessor createAccessor(final Label label,final MultiSelection multiSelection) {
|
private FormFieldAccessor createAccessor(
|
||||||
return new FormFieldAccessor(label, multiSelection) {
|
final Label label,
|
||||||
@Override public String getValue() { return multiSelection.getSelectionValue(); }
|
final Selection selection,
|
||||||
@Override public void setValue(final String value) { multiSelection.select(value); }
|
final BiConsumer<Tuple<String>, ObjectNode> jsonValueAdapter) {
|
||||||
@Override public void putJsonValue(final String key, final ObjectNode objectRoot) {
|
|
||||||
final String value = getValue();
|
return new FormFieldAccessor(label, selection.adaptToControl(), jsonValueAdapter) {
|
||||||
if (StringUtils.isNoneBlank(value)) {
|
@Override public String getValue() { return selection.getSelectionValue(); }
|
||||||
final ArrayNode arrayNode = objectRoot.putArray(key);
|
@Override public void setValue(final String value) { selection.select(value); }
|
||||||
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 ImageUpload imageUpload) {
|
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 static abstract class FormFieldAccessor {
|
||||||
|
|
||||||
public final Label label;
|
public final Label label;
|
||||||
public final Control control;
|
public final Control control;
|
||||||
|
private final BiConsumer<Tuple<String>, ObjectNode> jsonValueAdapter;
|
||||||
private boolean hasError;
|
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.label = label;
|
||||||
this.control = control;
|
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();
|
public abstract String getValue();
|
||||||
|
@ -301,11 +287,8 @@ public final class Form implements FormBinding {
|
||||||
this.control.setVisible(visible);
|
this.control.setVisible(visible);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void putJsonValue(final String key, final ObjectNode objectRoot) {
|
public final void putJsonValue(final String key, final ObjectNode objectRoot) {
|
||||||
final String value = getValue();
|
this.jsonValueAdapter.accept(new Tuple<>(key, getValue()), objectRoot);
|
||||||
if (StringUtils.isNoneBlank(value)) {
|
|
||||||
objectRoot.put(key, value);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setError(final String errorTooltip) {
|
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.i18n.PolyglotPageService;
|
||||||
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.remote.webservice.api.RestCall;
|
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;
|
||||||
import ch.ethz.seb.sebserver.gui.widget.WidgetFactory.CustomVariant;
|
import ch.ethz.seb.sebserver.gui.widget.WidgetFactory.CustomVariant;
|
||||||
|
|
||||||
|
@ -194,7 +195,7 @@ public class FormBuilder {
|
||||||
final String value,
|
final String value,
|
||||||
final Supplier<List<Tuple<String>>> itemsSupplier) {
|
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(
|
public static SelectionFieldBuilder multiSelection(
|
||||||
|
@ -203,8 +204,16 @@ public class FormBuilder {
|
||||||
final String value,
|
final String value,
|
||||||
final Supplier<List<Tuple<String>>> itemsSupplier) {
|
final Supplier<List<Tuple<String>>> itemsSupplier) {
|
||||||
|
|
||||||
return new SelectionFieldBuilder(name, label, value, itemsSupplier)
|
return new SelectionFieldBuilder(Selection.Type.MULTI, name, label, value, itemsSupplier);
|
||||||
.asMultiSelection();
|
}
|
||||||
|
|
||||||
|
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) {
|
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.Constants;
|
||||||
import ch.ethz.seb.sebserver.gbl.util.Tuple;
|
import ch.ethz.seb.sebserver.gbl.util.Tuple;
|
||||||
import ch.ethz.seb.sebserver.gui.service.i18n.PolyglotPageService;
|
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.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;
|
import ch.ethz.seb.sebserver.gui.widget.WidgetFactory.CustomVariant;
|
||||||
|
|
||||||
public final class SelectionFieldBuilder extends FieldBuilder {
|
public final class SelectionFieldBuilder extends FieldBuilder {
|
||||||
|
|
||||||
final Supplier<List<Tuple<String>>> itemsSupplier;
|
final Supplier<List<Tuple<String>>> itemsSupplier;
|
||||||
Consumer<Form> selectionListener = null;
|
Consumer<Form> selectionListener = null;
|
||||||
boolean multi = false;
|
final Selection.Type type;
|
||||||
|
|
||||||
SelectionFieldBuilder(
|
SelectionFieldBuilder(
|
||||||
|
final Selection.Type type,
|
||||||
final String name,
|
final String name,
|
||||||
final String label,
|
final String label,
|
||||||
final String value,
|
final String value,
|
||||||
final Supplier<List<Tuple<String>>> itemsSupplier) {
|
final Supplier<List<Tuple<String>>> itemsSupplier) {
|
||||||
|
|
||||||
super(name, label, value);
|
super(name, label, value);
|
||||||
|
this.type = type;
|
||||||
this.itemsSupplier = itemsSupplier;
|
this.itemsSupplier = itemsSupplier;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -52,11 +53,6 @@ public final class SelectionFieldBuilder extends FieldBuilder {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public SelectionFieldBuilder asMultiSelection() {
|
|
||||||
this.multi = true;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
void build(final FormBuilder builder) {
|
void build(final FormBuilder builder) {
|
||||||
final Label lab = builder.labelLocalized(builder.formParent, this.label, this.spanLabel);
|
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) {
|
private void buildInput(final FormBuilder builder, final Label lab) {
|
||||||
final Selection selection = (this.multi)
|
|
||||||
? builder.widgetFactory.multiSelectionLocalized(builder.formParent, this.itemsSupplier)
|
final Selection selection = builder.widgetFactory.selectionLocalized(
|
||||||
: builder.widgetFactory.singleSelectionLocalized(builder.formParent, this.itemsSupplier);
|
this.type,
|
||||||
|
builder.formParent,
|
||||||
|
this.itemsSupplier);
|
||||||
|
|
||||||
final GridData gridData = new GridData(SWT.FILL, SWT.FILL, true, false, this.spanInput, 1);
|
final GridData gridData = new GridData(SWT.FILL, SWT.FILL, true, false, this.spanInput, 1);
|
||||||
((Control) selection).setLayoutData(gridData);
|
((Control) selection).setLayoutData(gridData);
|
||||||
selection.select(this.value);
|
selection.select(this.value);
|
||||||
if (this.multi) {
|
builder.form.putField(this.name, lab, selection);
|
||||||
builder.form.putField(this.name, lab, selection.<MultiSelection> getTypeInstance());
|
|
||||||
} else {
|
|
||||||
builder.form.putField(this.name, lab, selection.<SingleSelection> getTypeInstance());
|
|
||||||
}
|
|
||||||
if (this.selectionListener != null) {
|
if (this.selectionListener != null) {
|
||||||
((Control) selection).addListener(SWT.Selection, e -> {
|
((Control) selection).addListener(SWT.Selection, e -> {
|
||||||
this.selectionListener.accept(builder.form);
|
this.selectionListener.accept(builder.form);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
builder.setFieldVisible(this.visible, this.name);
|
builder.setFieldVisible(this.visible, this.name);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Build the read-only representation of the selection field */
|
/* Build the read-only representation of the selection field */
|
||||||
private void buildReadOnly(final FormBuilder builder, final Label lab) {
|
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 Composite composite = new Composite(builder.formParent, SWT.NONE);
|
||||||
final GridLayout gridLayout = new GridLayout(1, true);
|
final GridLayout gridLayout = new GridLayout(1, true);
|
||||||
gridLayout.horizontalSpacing = 0;
|
gridLayout.horizontalSpacing = 0;
|
||||||
|
@ -99,7 +95,11 @@ public final class SelectionFieldBuilder extends FieldBuilder {
|
||||||
gridLayout.marginWidth = 0;
|
gridLayout.marginWidth = 0;
|
||||||
composite.setLayout(gridLayout);
|
composite.setLayout(gridLayout);
|
||||||
if (StringUtils.isBlank(this.value)) {
|
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 {
|
} else {
|
||||||
final Collection<String> keys = Arrays.asList(StringUtils.split(this.value, Constants.LIST_SEPARATOR));
|
final Collection<String> keys = Arrays.asList(StringUtils.split(this.value, Constants.LIST_SEPARATOR));
|
||||||
this.itemsSupplier.get()
|
this.itemsSupplier.get()
|
||||||
|
@ -137,12 +137,4 @@ public final class SelectionFieldBuilder extends FieldBuilder {
|
||||||
return label;
|
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 org.springframework.stereotype.Service;
|
||||||
|
|
||||||
import ch.ethz.seb.sebserver.gbl.Constants;
|
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.Exam.ExamType;
|
||||||
import ch.ethz.seb.sebserver.gbl.model.exam.Indicator.IndicatorType;
|
import ch.ethz.seb.sebserver.gbl.model.exam.Indicator.IndicatorType;
|
||||||
import ch.ethz.seb.sebserver.gbl.model.institution.LmsSetup.LmsType;
|
import ch.ethz.seb.sebserver.gbl.model.institution.LmsSetup.LmsType;
|
||||||
|
@ -104,7 +104,7 @@ public class ResourceService {
|
||||||
|
|
||||||
public List<Tuple<String>> institutionResource() {
|
public List<Tuple<String>> institutionResource() {
|
||||||
return this.restService.getBuilder(GetInstitutionNames.class)
|
return this.restService.getBuilder(GetInstitutionNames.class)
|
||||||
.withQueryParam(Domain.INSTITUTION.ATTR_ACTIVE, "true")
|
.withQueryParam(Entity.FILTER_ATTR_ACTIVE, Constants.TRUE_STRING)
|
||||||
.call()
|
.call()
|
||||||
.getOr(Collections.emptyList())
|
.getOr(Collections.emptyList())
|
||||||
.stream()
|
.stream()
|
||||||
|
@ -115,7 +115,7 @@ public class ResourceService {
|
||||||
public Function<String, String> getInstitutionNameFunction() {
|
public Function<String, String> getInstitutionNameFunction() {
|
||||||
|
|
||||||
final Map<String, String> idNameMap = this.restService.getBuilder(GetInstitutionNames.class)
|
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()
|
.call()
|
||||||
.getOr(Collections.emptyList())
|
.getOr(Collections.emptyList())
|
||||||
.stream()
|
.stream()
|
||||||
|
@ -133,8 +133,8 @@ public class ResourceService {
|
||||||
final boolean isSEBAdmin = this.currentUser.get().hasRole(UserRole.SEB_SERVER_ADMIN);
|
final boolean isSEBAdmin = this.currentUser.get().hasRole(UserRole.SEB_SERVER_ADMIN);
|
||||||
final String institutionId = (isSEBAdmin) ? "" : String.valueOf(this.currentUser.get().institutionId);
|
final String institutionId = (isSEBAdmin) ? "" : String.valueOf(this.currentUser.get().institutionId);
|
||||||
return this.restService.getBuilder(GetLmsSetupNames.class)
|
return this.restService.getBuilder(GetLmsSetupNames.class)
|
||||||
.withQueryParam(Domain.LMS_SETUP.ATTR_INSTITUTION_ID, institutionId)
|
.withQueryParam(Entity.FILTER_ATTR_INSTITUTION, institutionId)
|
||||||
.withQueryParam(Domain.LMS_SETUP.ATTR_ACTIVE, "true")
|
.withQueryParam(Entity.FILTER_ATTR_ACTIVE, Constants.TRUE_STRING)
|
||||||
.call()
|
.call()
|
||||||
.getOr(Collections.emptyList())
|
.getOr(Collections.emptyList())
|
||||||
.stream()
|
.stream()
|
||||||
|
@ -146,8 +146,8 @@ public class ResourceService {
|
||||||
final boolean isSEBAdmin = this.currentUser.get().hasRole(UserRole.SEB_SERVER_ADMIN);
|
final boolean isSEBAdmin = this.currentUser.get().hasRole(UserRole.SEB_SERVER_ADMIN);
|
||||||
final String institutionId = (isSEBAdmin) ? "" : String.valueOf(this.currentUser.get().institutionId);
|
final String institutionId = (isSEBAdmin) ? "" : String.valueOf(this.currentUser.get().institutionId);
|
||||||
final Map<String, String> idNameMap = this.restService.getBuilder(GetLmsSetupNames.class)
|
final Map<String, String> idNameMap = this.restService.getBuilder(GetLmsSetupNames.class)
|
||||||
.withQueryParam(Domain.LMS_SETUP.ATTR_INSTITUTION_ID, institutionId)
|
.withQueryParam(Entity.FILTER_ATTR_INSTITUTION, institutionId)
|
||||||
.withQueryParam(Domain.INSTITUTION.ATTR_ACTIVE, "true")
|
.withQueryParam(Entity.FILTER_ATTR_ACTIVE, Constants.TRUE_STRING)
|
||||||
.call()
|
.call()
|
||||||
.getOr(Collections.emptyList())
|
.getOr(Collections.emptyList())
|
||||||
.stream()
|
.stream()
|
||||||
|
@ -196,11 +196,24 @@ public class ResourceService {
|
||||||
.collect(Collectors.toList());
|
.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() {
|
public List<Tuple<String>> userResources() {
|
||||||
final UserInfo userInfo = this.currentUser.get();
|
final UserInfo userInfo = this.currentUser.get();
|
||||||
return this.restService.getBuilder(GetUserAccountNames.class)
|
return this.restService.getBuilder(GetUserAccountNames.class)
|
||||||
.withQueryParam(Domain.USER.ATTR_INSTITUTION_ID, String.valueOf(userInfo.institutionId))
|
.withQueryParam(Entity.FILTER_ATTR_INSTITUTION, String.valueOf(userInfo.institutionId))
|
||||||
.withQueryParam(Domain.USER.ATTR_ACTIVE, "true")
|
.withQueryParam(Entity.FILTER_ATTR_ACTIVE, Constants.TRUE_STRING)
|
||||||
.call()
|
.call()
|
||||||
.getOr(Collections.emptyList())
|
.getOr(Collections.emptyList())
|
||||||
.stream()
|
.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++;
|
index++;
|
||||||
}
|
}
|
||||||
|
|
||||||
// NOTE this.layout() triggers the navigation to disappear unexpectedly!?
|
|
||||||
//this.table.layout(true, true);
|
|
||||||
//this.composite.layout(true, true);
|
|
||||||
} catch (final Exception e) {
|
} catch (final Exception e) {
|
||||||
log.warn("Failed to adaptColumnWidth: ", 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.gbl.model.Entity;
|
||||||
import ch.ethz.seb.sebserver.gui.service.i18n.LocTextKey;
|
import ch.ethz.seb.sebserver.gui.service.i18n.LocTextKey;
|
||||||
import ch.ethz.seb.sebserver.gui.table.ColumnDefinition.TableFilterAttribute;
|
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;
|
import ch.ethz.seb.sebserver.gui.widget.WidgetFactory.ImageIcon;
|
||||||
|
|
||||||
public class TableFilter<ROW extends Entity> {
|
public class TableFilter<ROW extends Entity> {
|
||||||
|
@ -105,7 +105,7 @@ public class TableFilter<ROW extends Entity> {
|
||||||
case TEXT:
|
case TEXT:
|
||||||
return new TextFilter(attribute);
|
return new TextFilter(attribute);
|
||||||
case SINGLE_SELECTION:
|
case SINGLE_SELECTION:
|
||||||
return new Selection(attribute);
|
return new SelectionFilter(attribute);
|
||||||
case DATE:
|
case DATE:
|
||||||
return new Date(attribute);
|
return new Date(attribute);
|
||||||
default:
|
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);
|
super(attribute);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
FilterComponent build(final Composite parent) {
|
FilterComponent build(final Composite parent) {
|
||||||
this.selector = TableFilter.this.entityTable.widgetFactory
|
this.selector = TableFilter.this.entityTable.widgetFactory
|
||||||
.singleSelectionLocalized(
|
.selectionLocalized(
|
||||||
|
ch.ethz.seb.sebserver.gui.widget.Selection.Type.SINGLE,
|
||||||
parent,
|
parent,
|
||||||
this.attribute.resourceSupplier);
|
this.attribute.resourceSupplier);
|
||||||
this.selector.setLayoutData(this.rowData);
|
this.selector
|
||||||
|
.adaptToControl()
|
||||||
|
.setLayoutData(this.rowData);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -34,7 +34,7 @@ public class MultiSelection extends Composite implements Selection {
|
||||||
private final List<Label> labels = new ArrayList<>();
|
private final List<Label> labels = new ArrayList<>();
|
||||||
private final List<Label> selected = new ArrayList<>();
|
private final List<Label> selected = new ArrayList<>();
|
||||||
|
|
||||||
public MultiSelection(final Composite parent) {
|
MultiSelection(final Composite parent) {
|
||||||
super(parent, SWT.NONE);
|
super(parent, SWT.NONE);
|
||||||
final GridLayout gridLayout = new GridLayout(1, true);
|
final GridLayout gridLayout = new GridLayout(1, true);
|
||||||
gridLayout.verticalSpacing = 1;
|
gridLayout.verticalSpacing = 1;
|
||||||
|
@ -44,6 +44,11 @@ public class MultiSelection extends Composite implements Selection {
|
||||||
setLayout(gridLayout);
|
setLayout(gridLayout);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Type type() {
|
||||||
|
return Type.MULTI;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void applyNewMapping(final List<Tuple<String>> mapping) {
|
public void applyNewMapping(final List<Tuple<String>> mapping) {
|
||||||
final String selectionValue = getSelectionValue();
|
final String selectionValue = getSelectionValue();
|
||||||
|
@ -134,10 +139,4 @@ public class MultiSelection extends Composite implements Selection {
|
||||||
deselectAll();
|
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 java.util.List;
|
||||||
|
|
||||||
|
import org.eclipse.swt.widgets.Control;
|
||||||
|
|
||||||
import ch.ethz.seb.sebserver.gbl.util.Tuple;
|
import ch.ethz.seb.sebserver.gbl.util.Tuple;
|
||||||
|
|
||||||
public interface Selection {
|
public interface Selection {
|
||||||
|
|
||||||
|
enum Type {
|
||||||
|
SINGLE,
|
||||||
|
MULTI,
|
||||||
|
MULTI_COMBO
|
||||||
|
}
|
||||||
|
|
||||||
|
Type type();
|
||||||
|
|
||||||
void applyNewMapping(final List<Tuple<String>> mapping);
|
void applyNewMapping(final List<Tuple<String>> mapping);
|
||||||
|
|
||||||
void select(final String keys);
|
void select(final String keys);
|
||||||
|
@ -24,6 +34,10 @@ public interface Selection {
|
||||||
|
|
||||||
void setVisible(boolean visible);
|
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> valueMapping;
|
||||||
final List<String> keyMapping;
|
final List<String> keyMapping;
|
||||||
|
|
||||||
public SingleSelection(final Composite parent) {
|
SingleSelection(final Composite parent) {
|
||||||
super(parent, SWT.READ_ONLY);
|
super(parent, SWT.READ_ONLY);
|
||||||
this.valueMapping = new ArrayList<>();
|
this.valueMapping = new ArrayList<>();
|
||||||
this.keyMapping = 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()]));
|
super.setItems(this.valueMapping.toArray(new String[this.valueMapping.size()]));
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
@Override
|
@Override
|
||||||
public SingleSelection getTypeInstance() {
|
public Type type() {
|
||||||
return this;
|
return Type.SINGLE;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -60,6 +60,8 @@ public class WidgetFactory {
|
||||||
public enum ImageIcon {
|
public enum ImageIcon {
|
||||||
MAXIMIZE("maximize.png"),
|
MAXIMIZE("maximize.png"),
|
||||||
MINIMIZE("minimize.png"),
|
MINIMIZE("minimize.png"),
|
||||||
|
ADD("add.png"),
|
||||||
|
REMOVE("remove.png"),
|
||||||
EDIT("edit.png"),
|
EDIT("edit.png"),
|
||||||
TEST("test.png"),
|
TEST("test.png"),
|
||||||
IMPORT("import.png"),
|
IMPORT("import.png"),
|
||||||
|
@ -307,27 +309,64 @@ public class WidgetFactory {
|
||||||
return imageButton;
|
return imageButton;
|
||||||
}
|
}
|
||||||
|
|
||||||
public SingleSelection singleSelectionLocalized(
|
public Selection selectionLocalized(
|
||||||
|
final Selection.Type type,
|
||||||
final Composite parent,
|
final Composite parent,
|
||||||
final Supplier<List<Tuple<String>>> itemsSupplier) {
|
final Supplier<List<Tuple<String>>> itemsSupplier) {
|
||||||
|
|
||||||
final SingleSelection singleSelection = new SingleSelection(parent);
|
final Selection selection;
|
||||||
final Consumer<SingleSelection> updateFunction = ss -> ss.applyNewMapping(itemsSupplier.get());
|
switch (type) {
|
||||||
singleSelection.setData(POLYGLOT_WIDGET_FUNCTION_KEY, updateFunction);
|
case SINGLE:
|
||||||
updateFunction.accept(singleSelection);
|
selection = new SingleSelection(parent);
|
||||||
return singleSelection;
|
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(
|
// public SingleSelection singleSelectionLocalized(
|
||||||
final Composite parent,
|
// final Composite parent,
|
||||||
final Supplier<List<Tuple<String>>> itemsSupplier) {
|
// final Supplier<List<Tuple<String>>> itemsSupplier) {
|
||||||
|
//
|
||||||
final MultiSelection multiSelection = new MultiSelection(parent);
|
// final SingleSelection singleSelection = new SingleSelection(parent);
|
||||||
final Consumer<MultiSelection> updateFunction = ss -> ss.applyNewMapping(itemsSupplier.get());
|
// final Consumer<SingleSelection> updateFunction = ss -> ss.applyNewMapping(itemsSupplier.get());
|
||||||
multiSelection.setData(POLYGLOT_WIDGET_FUNCTION_KEY, updateFunction);
|
// singleSelection.setData(POLYGLOT_WIDGET_FUNCTION_KEY, updateFunction);
|
||||||
updateFunction.accept(multiSelection);
|
// updateFunction.accept(singleSelection);
|
||||||
return multiSelection;
|
// 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(
|
public ImageUpload imageUploadLocalized(
|
||||||
final Composite parent,
|
final Composite parent,
|
||||||
|
|
|
@ -170,7 +170,7 @@ public class ClientCredentialServiceImpl implements ClientCredentialService {
|
||||||
}
|
}
|
||||||
|
|
||||||
private final static char[] possibleCharacters = (new String(
|
private final static char[] possibleCharacters = (new String(
|
||||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789~`!@#$%^&*()-_=+[{]}?"))
|
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789~`!@#$%^*()-_=+[{]}?"))
|
||||||
.toCharArray();
|
.toCharArray();
|
||||||
|
|
||||||
private CharSequence getSalt(final CharSequence saltPlain) throws UnsupportedEncodingException {
|
private CharSequence getSalt(final CharSequence saltPlain) throws UnsupportedEncodingException {
|
||||||
|
|
|
@ -67,6 +67,10 @@ public class FilterMap extends POSTMapper {
|
||||||
return getString(UserInfo.FILTER_ATTR_LANGUAGE);
|
return getString(UserInfo.FILTER_ATTR_LANGUAGE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getUserRole() {
|
||||||
|
return getString(UserInfo.FILTER_ATTR_ROLE);
|
||||||
|
}
|
||||||
|
|
||||||
public String getLmsSetupType() {
|
public String getLmsSetupType() {
|
||||||
return getString(LmsSetup.FILTER_ATTR_LMS_TYPE);
|
return getString(LmsSetup.FILTER_ATTR_LMS_TYPE);
|
||||||
}
|
}
|
||||||
|
|
|
@ -150,33 +150,40 @@ public class UserDAOImpl implements UserDAO {
|
||||||
@Override
|
@Override
|
||||||
@Transactional(readOnly = true)
|
@Transactional(readOnly = true)
|
||||||
public Result<Collection<UserInfo>> allMatching(final FilterMap filterMap, final Predicate<UserInfo> predicate) {
|
public Result<Collection<UserInfo>> allMatching(final FilterMap filterMap, final Predicate<UserInfo> predicate) {
|
||||||
return Result.tryCatch(() -> this.userRecordMapper
|
return Result.tryCatch(() -> {
|
||||||
.selectByExample()
|
final String userRole = filterMap.getUserRole();
|
||||||
.where(
|
final Predicate<UserInfo> _predicate = (StringUtils.isNotBlank(userRole))
|
||||||
UserRecordDynamicSqlSupport.active,
|
? predicate.and(ui -> ui.roles.contains(userRole))
|
||||||
isEqualToWhenPresent(filterMap.getActiveAsInt()))
|
: predicate;
|
||||||
.and(
|
|
||||||
UserRecordDynamicSqlSupport.institutionId,
|
return this.userRecordMapper
|
||||||
isEqualToWhenPresent(filterMap.getInstitutionId()))
|
.selectByExample()
|
||||||
.and(
|
.where(
|
||||||
UserRecordDynamicSqlSupport.name,
|
UserRecordDynamicSqlSupport.active,
|
||||||
isLikeWhenPresent(filterMap.getName()))
|
isEqualToWhenPresent(filterMap.getActiveAsInt()))
|
||||||
.and(
|
.and(
|
||||||
UserRecordDynamicSqlSupport.username,
|
UserRecordDynamicSqlSupport.institutionId,
|
||||||
isLikeWhenPresent(filterMap.getUserUsername()))
|
isEqualToWhenPresent(filterMap.getInstitutionId()))
|
||||||
.and(
|
.and(
|
||||||
UserRecordDynamicSqlSupport.email,
|
UserRecordDynamicSqlSupport.name,
|
||||||
isLikeWhenPresent(filterMap.getUserEmail()))
|
isLikeWhenPresent(filterMap.getName()))
|
||||||
.and(
|
.and(
|
||||||
UserRecordDynamicSqlSupport.language,
|
UserRecordDynamicSqlSupport.username,
|
||||||
isLikeWhenPresent(filterMap.getUserLanguage()))
|
isLikeWhenPresent(filterMap.getUserUsername()))
|
||||||
.build()
|
.and(
|
||||||
.execute()
|
UserRecordDynamicSqlSupport.email,
|
||||||
.stream()
|
isLikeWhenPresent(filterMap.getUserEmail()))
|
||||||
.map(this::toDomainModel)
|
.and(
|
||||||
.flatMap(DAOLoggingSupport::logUnexpectedErrorAndSkip)
|
UserRecordDynamicSqlSupport.language,
|
||||||
.filter(predicate)
|
isLikeWhenPresent(filterMap.getUserLanguage()))
|
||||||
.collect(Collectors.toList()));
|
.build()
|
||||||
|
.execute()
|
||||||
|
.stream()
|
||||||
|
.map(this::toDomainModel)
|
||||||
|
.flatMap(DAOLoggingSupport::logUnexpectedErrorAndSkip)
|
||||||
|
.filter(_predicate)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@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.modify.cancel.confirm=Are you sure to cancel? Modifications will be lost.
|
||||||
sebserver.overall.action.filter=Apply Filter
|
sebserver.overall.action.filter=Apply Filter
|
||||||
sebserver.overall.action.filter.clear=Clear Filter Criteria
|
sebserver.overall.action.filter.clear=Clear Filter Criteria
|
||||||
|
sebserver.overall.action.ok=OK
|
||||||
|
sebserver.overall.action.cancel=Cancel
|
||||||
|
|
||||||
sebserver.overall.action.category.varia=Varia
|
sebserver.overall.action.category.varia=Varia
|
||||||
|
|
||||||
|
|
|
@ -96,7 +96,7 @@ Label.selected {
|
||||||
Label-SeparatorLine {
|
Label-SeparatorLine {
|
||||||
background-image: none;
|
background-image: none;
|
||||||
background-color: transparent;
|
background-color: transparent;
|
||||||
border: 1px solid #595959;
|
border: 1px solid #bdbdbd;
|
||||||
border-radius: 0px;
|
border-radius: 0px;
|
||||||
height: 1px;
|
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…
Reference in a new issue