SEBSERV-45 seb configuration table implementation
This commit is contained in:
		
							parent
							
								
									0a648d8c84
								
							
						
					
					
						commit
						5032e39352
					
				
					 18 changed files with 619 additions and 151 deletions
				
			
		| 
						 | 
				
			
			@ -41,10 +41,6 @@ public final class API {
 | 
			
		|||
    public static final String PRIVILEGES_ENDPOINT = INFO_ENDPOINT + PRIVILEGES_PATH_SEGMENT;
 | 
			
		||||
 | 
			
		||||
    public static final String INSTITUTION_ENDPOINT = "/institution";
 | 
			
		||||
//    public static final String SEB_CONFIG_EXPORT_PATH_SEGMENT = "/sebconfig";
 | 
			
		||||
//    public static final String SEB_CONFIG_EXPORT_ENDPOINT = INSTITUTION_ENDPOINT
 | 
			
		||||
//            + INSTITUTION_VAR_PATH_SEGMENT
 | 
			
		||||
//            + SEB_CONFIG_EXPORT_PATH_SEGMENT;
 | 
			
		||||
 | 
			
		||||
    public static final String LMS_SETUP_ENDPOINT = "/lms_setup";
 | 
			
		||||
    public static final String LMS_SETUP_TEST_PATH_SEGMENT = "/test";
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -24,8 +24,7 @@ import ch.ethz.seb.sebserver.gbl.model.GrantEntity;
 | 
			
		|||
@JsonIgnoreProperties(ignoreUnknown = true)
 | 
			
		||||
public final class ConfigurationTableValue implements GrantEntity {
 | 
			
		||||
 | 
			
		||||
    public static final String ATTR_COLUMNS = "columnAttributeIds";
 | 
			
		||||
    public static final String ATTR_VALUES = "values";
 | 
			
		||||
    public static final String ATTR_TABLE_VALUES = "tableValues";
 | 
			
		||||
 | 
			
		||||
    @NotNull
 | 
			
		||||
    @JsonProperty(CONFIGURATION_VALUE.ATTR_INSTITUTION_ID)
 | 
			
		||||
| 
						 | 
				
			
			@ -39,24 +38,19 @@ public final class ConfigurationTableValue implements GrantEntity {
 | 
			
		|||
    @JsonProperty(CONFIGURATION_VALUE.ATTR_CONFIGURATION_ATTRIBUTE_ID)
 | 
			
		||||
    public final Long attributeId;
 | 
			
		||||
 | 
			
		||||
    @JsonProperty(ATTR_COLUMNS)
 | 
			
		||||
    public final List<Long> columnAttributeIds;
 | 
			
		||||
 | 
			
		||||
    @JsonProperty(ATTR_VALUES)
 | 
			
		||||
    public final List<String> values;
 | 
			
		||||
    @JsonProperty(ATTR_TABLE_VALUES)
 | 
			
		||||
    public final List<TableValue> values;
 | 
			
		||||
 | 
			
		||||
    @JsonCreator
 | 
			
		||||
    public ConfigurationTableValue(
 | 
			
		||||
            @JsonProperty(CONFIGURATION_VALUE.ATTR_INSTITUTION_ID) final Long institutionId,
 | 
			
		||||
            @JsonProperty(CONFIGURATION_VALUE.ATTR_CONFIGURATION_ID) final Long configurationId,
 | 
			
		||||
            @JsonProperty(CONFIGURATION_VALUE.ATTR_CONFIGURATION_ATTRIBUTE_ID) final Long attributeId,
 | 
			
		||||
            @JsonProperty(ATTR_COLUMNS) final List<Long> columns,
 | 
			
		||||
            @JsonProperty(ATTR_VALUES) final List<String> values) {
 | 
			
		||||
            @JsonProperty(ATTR_TABLE_VALUES) final List<TableValue> values) {
 | 
			
		||||
 | 
			
		||||
        this.institutionId = institutionId;
 | 
			
		||||
        this.configurationId = configurationId;
 | 
			
		||||
        this.attributeId = attributeId;
 | 
			
		||||
        this.columnAttributeIds = Collections.unmodifiableList(columns);
 | 
			
		||||
        this.values = Collections.unmodifiableList(values);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -88,11 +82,7 @@ public final class ConfigurationTableValue implements GrantEntity {
 | 
			
		|||
        return this.attributeId;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public List<Long> getColumnAttributeIds() {
 | 
			
		||||
        return this.columnAttributeIds;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public List<String> getValues() {
 | 
			
		||||
    public List<TableValue> getValues() {
 | 
			
		||||
        return this.values;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -100,8 +90,31 @@ public final class ConfigurationTableValue implements GrantEntity {
 | 
			
		|||
    public String toString() {
 | 
			
		||||
        return "ConfigurationTableValue [institutionId=" + this.institutionId + ", configurationId="
 | 
			
		||||
                + this.configurationId
 | 
			
		||||
                + ", attributeId=" + this.attributeId + ", columnAttributeIds=" + this.columnAttributeIds + ", values="
 | 
			
		||||
                + this.values
 | 
			
		||||
                + "]";
 | 
			
		||||
                + ", attributeId=" + this.attributeId + ", values=" + this.values + "]";
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @JsonIgnoreProperties(ignoreUnknown = true)
 | 
			
		||||
    public static final class TableValue {
 | 
			
		||||
 | 
			
		||||
        @JsonProperty(CONFIGURATION_VALUE.ATTR_CONFIGURATION_ATTRIBUTE_ID)
 | 
			
		||||
        public final Long attributeId;
 | 
			
		||||
        @JsonProperty(CONFIGURATION_VALUE.ATTR_LIST_INDEX)
 | 
			
		||||
        public final Integer listIndex;
 | 
			
		||||
        @JsonProperty(CONFIGURATION_VALUE.ATTR_VALUE)
 | 
			
		||||
        public final String value;
 | 
			
		||||
 | 
			
		||||
        public TableValue(
 | 
			
		||||
                @JsonProperty(CONFIGURATION_VALUE.ATTR_CONFIGURATION_ATTRIBUTE_ID) final Long attributeId,
 | 
			
		||||
                @JsonProperty(CONFIGURATION_VALUE.ATTR_LIST_INDEX) final Integer listIndex,
 | 
			
		||||
                @JsonProperty(CONFIGURATION_VALUE.ATTR_VALUE) final String value) {
 | 
			
		||||
 | 
			
		||||
            this.attributeId = attributeId;
 | 
			
		||||
            this.listIndex = listIndex;
 | 
			
		||||
            this.value = value;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public static TableValue of(final ConfigurationValue value) {
 | 
			
		||||
            return new TableValue(value.attributeId, value.listIndex, value.value);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -28,7 +28,7 @@ public abstract class AbstractInputField<T extends Control> implements InputFiel
 | 
			
		|||
    protected String initValue = "";
 | 
			
		||||
    protected int listIndex = 0;
 | 
			
		||||
 | 
			
		||||
    AbstractInputField(
 | 
			
		||||
    protected AbstractInputField(
 | 
			
		||||
            final ConfigurationAttribute attribute,
 | 
			
		||||
            final Orientation orientation,
 | 
			
		||||
            final T control,
 | 
			
		||||
| 
						 | 
				
			
			@ -93,11 +93,15 @@ public abstract class AbstractInputField<T extends Control> implements InputFiel
 | 
			
		|||
                .map(v -> {
 | 
			
		||||
                    this.initValue = v.value;
 | 
			
		||||
                    this.listIndex = (v.listIndex != null) ? v.listIndex : 0;
 | 
			
		||||
                    setDefaultValue();
 | 
			
		||||
                    setValueToControl(this.initValue);
 | 
			
		||||
                    return this.initValue;
 | 
			
		||||
                });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected abstract void setDefaultValue();
 | 
			
		||||
    protected void setDefaultValue() {
 | 
			
		||||
        setValueToControl(this.attribute.defaultValue);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected abstract void setValueToControl(String value);
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -81,7 +81,7 @@ public class CheckBoxBuilder implements InputFieldBuilder {
 | 
			
		|||
        }
 | 
			
		||||
 | 
			
		||||
        @Override
 | 
			
		||||
        protected void setDefaultValue() {
 | 
			
		||||
        protected void setValueToControl(final String value) {
 | 
			
		||||
            this.control.setSelection(Boolean.valueOf(this.initValue));
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -51,6 +51,7 @@ import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.seb.examconfig.Ge
 | 
			
		|||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.seb.examconfig.GetConfigurationValues;
 | 
			
		||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.seb.examconfig.GetOrientations;
 | 
			
		||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.seb.examconfig.GetViewList;
 | 
			
		||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.seb.examconfig.SaveExamConfigTableValue;
 | 
			
		||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.seb.examconfig.SaveExamConfigValue;
 | 
			
		||||
import ch.ethz.seb.sebserver.gui.widget.WidgetFactory;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -269,8 +270,9 @@ public class ExamConfigurationServiceImpl implements ExamConfigurationService {
 | 
			
		|||
 | 
			
		||||
        @Override
 | 
			
		||||
        public void tableChanged(final ConfigurationTableValue tableValue) {
 | 
			
		||||
            // TODO Auto-generated method stub
 | 
			
		||||
 | 
			
		||||
            this.restService.getBuilder(SaveExamConfigTableValue.class)
 | 
			
		||||
                    .withBody(tableValue)
 | 
			
		||||
                    .call();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private String verifyErrorMessage(final Throwable error) {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -70,7 +70,7 @@ public class LabelBuilder implements InputFieldBuilder {
 | 
			
		|||
        }
 | 
			
		||||
 | 
			
		||||
        @Override
 | 
			
		||||
        protected void setDefaultValue() {
 | 
			
		||||
        protected void setValueToControl(final String value) {
 | 
			
		||||
            // Does Nothing, Label has no default value
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -146,11 +146,11 @@ public class PassworFieldBuilder implements InputFieldBuilder {
 | 
			
		|||
        }
 | 
			
		||||
 | 
			
		||||
        @Override
 | 
			
		||||
        protected void setDefaultValue() {
 | 
			
		||||
        protected void setValueToControl(final String value) {
 | 
			
		||||
            // TODO clarify setting some "fake" input when a password is set (like in config tool)
 | 
			
		||||
            if (this.initValue != null) {
 | 
			
		||||
                this.control.setText(this.initValue);
 | 
			
		||||
                this.confirm.setText(this.initValue);
 | 
			
		||||
                this.control.setText(value);
 | 
			
		||||
                this.confirm.setText(value);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -97,10 +97,9 @@ public class TextFieldBuilder implements InputFieldBuilder {
 | 
			
		|||
        }
 | 
			
		||||
 | 
			
		||||
        @Override
 | 
			
		||||
        protected void setDefaultValue() {
 | 
			
		||||
            this.control.setText(this.initValue);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        protected void setValueToControl(final String value) {
 | 
			
		||||
            this.control.setText(value);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -10,10 +10,12 @@ package ch.ethz.seb.sebserver.gui.service.examconfig.impl;
 | 
			
		|||
 | 
			
		||||
import java.util.Collection;
 | 
			
		||||
import java.util.HashMap;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
import java.util.Map;
 | 
			
		||||
import java.util.Objects;
 | 
			
		||||
 | 
			
		||||
import ch.ethz.seb.sebserver.gbl.model.sebconfig.Configuration;
 | 
			
		||||
import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationAttribute;
 | 
			
		||||
import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationValue;
 | 
			
		||||
import ch.ethz.seb.sebserver.gbl.model.sebconfig.View;
 | 
			
		||||
import ch.ethz.seb.sebserver.gui.service.examconfig.InputField;
 | 
			
		||||
| 
						 | 
				
			
			@ -84,6 +86,11 @@ public final class ViewContext {
 | 
			
		|||
        return this.rows;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public List<ConfigurationAttribute> getChildAttributes(final ConfigurationAttribute attribute) {
 | 
			
		||||
        // TODO Auto-generated method stub
 | 
			
		||||
        return null;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public ValueChangeListener getValueChangeListener() {
 | 
			
		||||
        return this.valueChangeListener;
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -19,6 +19,8 @@ import org.eclipse.swt.layout.GridLayout;
 | 
			
		|||
import org.eclipse.swt.widgets.Composite;
 | 
			
		||||
import org.eclipse.swt.widgets.Group;
 | 
			
		||||
import org.eclipse.swt.widgets.Label;
 | 
			
		||||
import org.slf4j.Logger;
 | 
			
		||||
import org.slf4j.LoggerFactory;
 | 
			
		||||
 | 
			
		||||
import ch.ethz.seb.sebserver.gbl.model.sebconfig.AttributeType;
 | 
			
		||||
import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationAttribute;
 | 
			
		||||
| 
						 | 
				
			
			@ -31,6 +33,8 @@ import ch.ethz.seb.sebserver.gui.widget.WidgetFactory;
 | 
			
		|||
 | 
			
		||||
public class ViewGridBuilder {
 | 
			
		||||
 | 
			
		||||
    private static final Logger log = LoggerFactory.getLogger(ViewGridBuilder.class);
 | 
			
		||||
 | 
			
		||||
    private final ExamConfigurationService examConfigurationService;
 | 
			
		||||
    private final Composite parent;
 | 
			
		||||
    private final ViewContext viewContext;
 | 
			
		||||
| 
						 | 
				
			
			@ -85,35 +89,30 @@ public class ViewGridBuilder {
 | 
			
		|||
                attribute,
 | 
			
		||||
                orientation);
 | 
			
		||||
 | 
			
		||||
        final CellFieldBuilderAdapter fieldBuilderAdapter = fieldBuilderAdapter(
 | 
			
		||||
        this.grid[ypos][xpos] = fieldBuilderAdapter(
 | 
			
		||||
                inputFieldBuilder,
 | 
			
		||||
                attribute);
 | 
			
		||||
 | 
			
		||||
        try {
 | 
			
		||||
            switch (orientation.title) {
 | 
			
		||||
                case RIGHT: {
 | 
			
		||||
                this.grid[ypos][xpos] = fieldBuilderAdapter;
 | 
			
		||||
                    this.grid[ypos][xpos + 1] = labelBuilder(attribute, orientation);
 | 
			
		||||
                if (attribute.type == AttributeType.PASSWORD_FIELD) {
 | 
			
		||||
                    this.grid[ypos + 1][xpos + 1] = passwordConfirmLabel(attribute, orientation);
 | 
			
		||||
                }
 | 
			
		||||
                    break;
 | 
			
		||||
                }
 | 
			
		||||
                case LEFT: {
 | 
			
		||||
                this.grid[ypos][xpos] = labelBuilder(attribute, orientation);
 | 
			
		||||
                if (attribute.type == AttributeType.PASSWORD_FIELD) {
 | 
			
		||||
                    this.grid[ypos + 1][xpos] = passwordConfirmLabel(attribute, orientation);
 | 
			
		||||
                }
 | 
			
		||||
                this.grid[ypos][xpos + 1] = fieldBuilderAdapter;
 | 
			
		||||
                    this.grid[ypos][xpos - 1] = labelBuilder(attribute, orientation);
 | 
			
		||||
                    break;
 | 
			
		||||
                }
 | 
			
		||||
                case TOP: {
 | 
			
		||||
                this.grid[ypos][xpos] = labelBuilder(attribute, orientation);
 | 
			
		||||
                this.grid[ypos + 1][xpos] = fieldBuilderAdapter;
 | 
			
		||||
                    this.grid[ypos - 1][xpos] = labelBuilder(attribute, orientation);
 | 
			
		||||
                    break;
 | 
			
		||||
                }
 | 
			
		||||
                default: {
 | 
			
		||||
                this.grid[ypos][xpos] = fieldBuilderAdapter;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        } catch (final ArrayIndexOutOfBoundsException e) {
 | 
			
		||||
            log.error("Failed to set title as configured in: {} for attribute: {}", orientation, attribute, e);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return this;
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			@ -136,6 +135,7 @@ public class ViewGridBuilder {
 | 
			
		|||
    private static interface CellFieldBuilderAdapter {
 | 
			
		||||
 | 
			
		||||
        void createCell(ViewGridBuilder builder);
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private CellFieldBuilderAdapter dummyBuilderAdapter() {
 | 
			
		||||
| 
						 | 
				
			
			@ -143,6 +143,12 @@ public class ViewGridBuilder {
 | 
			
		|||
            @Override
 | 
			
		||||
            public void createCell(final ViewGridBuilder builder) {
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            @Override
 | 
			
		||||
            public String toString() {
 | 
			
		||||
                return "[DUMMY]";
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
        };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -161,26 +167,10 @@ public class ViewGridBuilder {
 | 
			
		|||
 | 
			
		||||
                ViewGridBuilder.this.viewContext.registerInputField(inputField);
 | 
			
		||||
            }
 | 
			
		||||
        };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private CellFieldBuilderAdapter passwordConfirmLabel(
 | 
			
		||||
            final ConfigurationAttribute attribute,
 | 
			
		||||
            final Orientation orientation) {
 | 
			
		||||
 | 
			
		||||
        return new CellFieldBuilderAdapter() {
 | 
			
		||||
            @Override
 | 
			
		||||
            public void createCell(final ViewGridBuilder builder) {
 | 
			
		||||
                final WidgetFactory widgetFactory = builder.examConfigurationService.getWidgetFactory();
 | 
			
		||||
                final Label label = widgetFactory.labelLocalized(
 | 
			
		||||
                        ViewGridBuilder.this.parent,
 | 
			
		||||
                        new LocTextKey(
 | 
			
		||||
                                ExamConfigurationService.ATTRIBUTE_LABEL_LOC_TEXT_PREFIX + attribute.name + ".confirm"),
 | 
			
		||||
                        "Confirm Password");
 | 
			
		||||
                final GridData gridData = new GridData(SWT.FILL, SWT.TOP, true, false);
 | 
			
		||||
                label.setAlignment(SWT.LEFT);
 | 
			
		||||
                gridData.verticalIndent = 10;
 | 
			
		||||
                label.setLayoutData(gridData);
 | 
			
		||||
            public String toString() {
 | 
			
		||||
                return "[FIELD]";
 | 
			
		||||
            }
 | 
			
		||||
        };
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			@ -189,6 +179,10 @@ public class ViewGridBuilder {
 | 
			
		|||
            final ConfigurationAttribute attribute,
 | 
			
		||||
            final Orientation orientation) {
 | 
			
		||||
 | 
			
		||||
        if (attribute.type == AttributeType.PASSWORD_FIELD) {
 | 
			
		||||
            return passwordConfirmLabel(attribute, orientation);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return new CellFieldBuilderAdapter() {
 | 
			
		||||
            @Override
 | 
			
		||||
            public void createCell(final ViewGridBuilder builder) {
 | 
			
		||||
| 
						 | 
				
			
			@ -208,7 +202,7 @@ public class ViewGridBuilder {
 | 
			
		|||
                        break;
 | 
			
		||||
                    }
 | 
			
		||||
                    case TOP: {
 | 
			
		||||
                        label.setAlignment(SWT.BOTTOM);
 | 
			
		||||
                        label.setAlignment(SWT.LEFT);
 | 
			
		||||
                        break;
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -221,6 +215,27 @@ public class ViewGridBuilder {
 | 
			
		|||
        };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private CellFieldBuilderAdapter passwordConfirmLabel(
 | 
			
		||||
            final ConfigurationAttribute attribute,
 | 
			
		||||
            final Orientation orientation) {
 | 
			
		||||
 | 
			
		||||
        return new CellFieldBuilderAdapter() {
 | 
			
		||||
            @Override
 | 
			
		||||
            public void createCell(final ViewGridBuilder builder) {
 | 
			
		||||
                final WidgetFactory widgetFactory = builder.examConfigurationService.getWidgetFactory();
 | 
			
		||||
                final Label label = widgetFactory.labelLocalized(
 | 
			
		||||
                        ViewGridBuilder.this.parent,
 | 
			
		||||
                        new LocTextKey(
 | 
			
		||||
                                ExamConfigurationService.ATTRIBUTE_LABEL_LOC_TEXT_PREFIX + attribute.name + ".confirm"),
 | 
			
		||||
                        "Confirm Password");
 | 
			
		||||
                final GridData gridData = new GridData(SWT.FILL, SWT.TOP, true, false);
 | 
			
		||||
                label.setAlignment(SWT.LEFT);
 | 
			
		||||
                gridData.verticalIndent = 10;
 | 
			
		||||
                label.setLayoutData(gridData);
 | 
			
		||||
            }
 | 
			
		||||
        };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static class GroupCellFieldBuilderAdapter implements CellFieldBuilderAdapter {
 | 
			
		||||
 | 
			
		||||
        final ViewGridBuilder builder;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,365 @@
 | 
			
		|||
/*
 | 
			
		||||
 * 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.examconfig.impl.table;
 | 
			
		||||
 | 
			
		||||
import java.util.ArrayList;
 | 
			
		||||
import java.util.Collection;
 | 
			
		||||
import java.util.HashMap;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
import java.util.Map;
 | 
			
		||||
import java.util.function.Function;
 | 
			
		||||
import java.util.stream.Collectors;
 | 
			
		||||
 | 
			
		||||
import org.eclipse.swt.SWT;
 | 
			
		||||
import org.eclipse.swt.layout.GridData;
 | 
			
		||||
import org.eclipse.swt.layout.GridLayout;
 | 
			
		||||
import org.eclipse.swt.widgets.Composite;
 | 
			
		||||
import org.eclipse.swt.widgets.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 org.springframework.context.annotation.Lazy;
 | 
			
		||||
import org.springframework.stereotype.Component;
 | 
			
		||||
 | 
			
		||||
import ch.ethz.seb.sebserver.gbl.model.sebconfig.AttributeType;
 | 
			
		||||
import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationAttribute;
 | 
			
		||||
import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationTableValue;
 | 
			
		||||
import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationTableValue.TableValue;
 | 
			
		||||
import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationValue;
 | 
			
		||||
import ch.ethz.seb.sebserver.gbl.model.sebconfig.Orientation;
 | 
			
		||||
import ch.ethz.seb.sebserver.gbl.profile.GuiProfile;
 | 
			
		||||
import ch.ethz.seb.sebserver.gui.service.examconfig.ExamConfigurationService;
 | 
			
		||||
import ch.ethz.seb.sebserver.gui.service.examconfig.InputField;
 | 
			
		||||
import ch.ethz.seb.sebserver.gui.service.examconfig.InputFieldBuilder;
 | 
			
		||||
import ch.ethz.seb.sebserver.gui.service.examconfig.impl.AbstractInputField;
 | 
			
		||||
import ch.ethz.seb.sebserver.gui.service.examconfig.impl.ViewContext;
 | 
			
		||||
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.impl.ModalInputDialog;
 | 
			
		||||
import ch.ethz.seb.sebserver.gui.widget.WidgetFactory;
 | 
			
		||||
import ch.ethz.seb.sebserver.gui.widget.WidgetFactory.ImageIcon;
 | 
			
		||||
 | 
			
		||||
@Lazy
 | 
			
		||||
@Component
 | 
			
		||||
@GuiProfile
 | 
			
		||||
public class TableFieldBuilder implements InputFieldBuilder {
 | 
			
		||||
 | 
			
		||||
    private static final Logger log = LoggerFactory.getLogger(TableFieldBuilder.class);
 | 
			
		||||
 | 
			
		||||
    private static final String ROW_VALUE_KEY = "RowValues";
 | 
			
		||||
 | 
			
		||||
    private final WidgetFactory widgetFactory;
 | 
			
		||||
 | 
			
		||||
    public TableFieldBuilder(final WidgetFactory widgetFactory) {
 | 
			
		||||
        this.widgetFactory = widgetFactory;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public boolean builderFor(
 | 
			
		||||
            final ConfigurationAttribute attribute,
 | 
			
		||||
            final Orientation orientation) {
 | 
			
		||||
 | 
			
		||||
        if (attribute == null) {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return AttributeType.TABLE == attribute.type;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public InputField createInputField(
 | 
			
		||||
            final Composite parent,
 | 
			
		||||
            final ConfigurationAttribute attribute,
 | 
			
		||||
            final ViewContext viewContext) {
 | 
			
		||||
 | 
			
		||||
        final I18nSupport i18nSupport = viewContext.getI18nSupport();
 | 
			
		||||
 | 
			
		||||
        final Orientation orientation = viewContext.attributeMapping
 | 
			
		||||
                .getOrientation(attribute.id);
 | 
			
		||||
        final List<ConfigurationAttribute> childAttributes =
 | 
			
		||||
                viewContext.attributeMapping.childAttributeMapping.get(attribute.id);
 | 
			
		||||
        final List<ConfigurationAttribute> columnAttributes = childAttributes
 | 
			
		||||
                .stream()
 | 
			
		||||
                .filter(attr -> viewContext.attributeMapping.getOrientation(attr.id).xPosition > 0)
 | 
			
		||||
                .sorted((attr1, attr2) -> viewContext.attributeMapping.getOrientation(attr1.id).xPosition.compareTo(
 | 
			
		||||
                        viewContext.attributeMapping.getOrientation(attr2.id).xPosition))
 | 
			
		||||
                .collect(Collectors.toList());
 | 
			
		||||
 | 
			
		||||
        final Table table = new Table(parent, SWT.NONE | SWT.H_SCROLL);
 | 
			
		||||
        table.setLayout(new GridLayout());
 | 
			
		||||
        final GridData gridData = new GridData(
 | 
			
		||||
                SWT.FILL, SWT.FILL,
 | 
			
		||||
                true, false,
 | 
			
		||||
                (orientation != null) ? orientation.width() : 1,
 | 
			
		||||
                (orientation != null) ? orientation.height() : 1);
 | 
			
		||||
        gridData.heightHint = orientation.height * 40;
 | 
			
		||||
        table.setLayoutData(gridData);
 | 
			
		||||
        table.setHeaderVisible(true);
 | 
			
		||||
        table.addListener(SWT.Resize, this::adaptColumnWidth);
 | 
			
		||||
 | 
			
		||||
        for (final ConfigurationAttribute columnAttribute : columnAttributes) {
 | 
			
		||||
            final TableColumn column = new TableColumn(table, SWT.NONE);
 | 
			
		||||
            final String text = i18nSupport.getText(
 | 
			
		||||
                    ExamConfigurationService.ATTRIBUTE_LABEL_LOC_TEXT_PREFIX + columnAttribute.name,
 | 
			
		||||
                    columnAttribute.name);
 | 
			
		||||
            column.setText(text);
 | 
			
		||||
            column.setWidth(100);
 | 
			
		||||
            column.setResizable(false);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        final TableInputField tableField = new TableInputField(
 | 
			
		||||
                this.widgetFactory,
 | 
			
		||||
                attribute,
 | 
			
		||||
                orientation,
 | 
			
		||||
                table,
 | 
			
		||||
                childAttributes,
 | 
			
		||||
                columnAttributes,
 | 
			
		||||
                viewContext);
 | 
			
		||||
 | 
			
		||||
        TableColumn column = new TableColumn(table, SWT.NONE);
 | 
			
		||||
        column.setImage(ImageIcon.ADD_BOX.getImage(parent.getDisplay()));
 | 
			
		||||
 | 
			
		||||
        column.setWidth(20);
 | 
			
		||||
        column.setResizable(false);
 | 
			
		||||
        column.setMoveable(false);
 | 
			
		||||
 | 
			
		||||
        column.addListener(SWT.Selection, event -> {
 | 
			
		||||
            tableField.addRow();
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        column = new TableColumn(table, SWT.NONE);
 | 
			
		||||
        column.setImage(ImageIcon.REMOVE_BOX.getImage(parent.getDisplay()));
 | 
			
		||||
 | 
			
		||||
        column.setWidth(20);
 | 
			
		||||
        column.setResizable(false);
 | 
			
		||||
        column.setMoveable(false);
 | 
			
		||||
 | 
			
		||||
        column.addListener(SWT.Selection, event -> {
 | 
			
		||||
            final int selectionIndex = table.getSelectionIndex();
 | 
			
		||||
            if (selectionIndex >= 0) {
 | 
			
		||||
                tableField.deleteRow(selectionIndex);
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        table.addListener(SWT.MouseDoubleClick, event -> {
 | 
			
		||||
            final int selectionIndex = table.getSelectionIndex();
 | 
			
		||||
            if (selectionIndex >= 0) {
 | 
			
		||||
                tableField.openForm(selectionIndex);
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        return tableField;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void adaptColumnWidth(final Event event) {
 | 
			
		||||
        try {
 | 
			
		||||
            final Table table = (Table) event.widget;
 | 
			
		||||
            final int currentTableWidth = table.getClientArea().width - 50;
 | 
			
		||||
            final TableColumn[] columns = table.getColumns();
 | 
			
		||||
            final int columnWidth = currentTableWidth / (columns.length - 2);
 | 
			
		||||
            for (int i = 0; i < columns.length - 2; i++) {
 | 
			
		||||
                columns[i].setWidth(columnWidth);
 | 
			
		||||
            }
 | 
			
		||||
        } catch (final Exception e) {
 | 
			
		||||
            log.warn("Failed to adaptColumnWidth: ", e);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    static final class TableInputField extends AbstractInputField<Table> {
 | 
			
		||||
 | 
			
		||||
        private final List<ConfigurationAttribute> childAttributes;
 | 
			
		||||
        private final List<ConfigurationAttribute> columnAttributes;
 | 
			
		||||
        private final ViewContext viewContext;
 | 
			
		||||
        private final WidgetFactory widgetFactory;
 | 
			
		||||
 | 
			
		||||
        private List<Map<Long, TableValue>> values;
 | 
			
		||||
 | 
			
		||||
        TableInputField(
 | 
			
		||||
                final WidgetFactory widgetFactory,
 | 
			
		||||
                final ConfigurationAttribute attribute,
 | 
			
		||||
                final Orientation orientation,
 | 
			
		||||
                final Table control,
 | 
			
		||||
                final List<ConfigurationAttribute> childAttributes,
 | 
			
		||||
                final List<ConfigurationAttribute> columnAttributes,
 | 
			
		||||
                final ViewContext viewContext) {
 | 
			
		||||
 | 
			
		||||
            super(attribute, orientation, control, null);
 | 
			
		||||
            this.childAttributes = childAttributes;
 | 
			
		||||
            this.columnAttributes = columnAttributes;
 | 
			
		||||
            this.viewContext = viewContext;
 | 
			
		||||
            this.widgetFactory = widgetFactory;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        @Override
 | 
			
		||||
        public void initValue(final Collection<ConfigurationValue> values) {
 | 
			
		||||
            // get all child values as TableValues
 | 
			
		||||
            final List<TableValue> tableValues = values.stream()
 | 
			
		||||
                    .filter(this::isChildValue)
 | 
			
		||||
                    .map(TableValue::of)
 | 
			
		||||
                    .collect(Collectors.toList());
 | 
			
		||||
 | 
			
		||||
            final Map<Integer, Map<Long, TableValue>> _initValue = new HashMap<>();
 | 
			
		||||
            for (final TableValue tableValue : tableValues) {
 | 
			
		||||
                final Map<Long, TableValue> rowValues = _initValue.computeIfAbsent(
 | 
			
		||||
                        tableValue.listIndex,
 | 
			
		||||
                        key -> new HashMap<>());
 | 
			
		||||
                rowValues.put(tableValue.attributeId, tableValue);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            final List<Integer> rows = new ArrayList<>(_initValue.keySet());
 | 
			
		||||
            rows.sort((i1, i2) -> i1.compareTo(i2));
 | 
			
		||||
 | 
			
		||||
            this.values = new ArrayList<>();
 | 
			
		||||
            rows
 | 
			
		||||
                    .stream()
 | 
			
		||||
                    .forEach(i -> {
 | 
			
		||||
                        final Map<Long, TableValue> rowValues = _initValue.get(i);
 | 
			
		||||
                        this.values.add(rowValues);
 | 
			
		||||
                        addTableRow(rowValues);
 | 
			
		||||
                    });
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private boolean isChildValue(final ConfigurationValue value) {
 | 
			
		||||
            return this.attribute.id.equals(
 | 
			
		||||
                    this.viewContext.attributeMapping.attributeIdMapping
 | 
			
		||||
                            .get(value.attributeId).parentId);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private void deleteRow(final int selectionIndex) {
 | 
			
		||||
            this.control.remove(selectionIndex);
 | 
			
		||||
            this.values.remove(selectionIndex);
 | 
			
		||||
            // send new values to web-service
 | 
			
		||||
            this.viewContext.getValueChangeListener()
 | 
			
		||||
                    .tableChanged(extractTableValue());
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private void addRow() {
 | 
			
		||||
            final int index = this.values.size();
 | 
			
		||||
            // create new values form default values
 | 
			
		||||
            final Map<Long, TableValue> rowValues = this.childAttributes
 | 
			
		||||
                    .stream()
 | 
			
		||||
                    .map(attr -> new TableValue(attr.id, index, attr.defaultValue))
 | 
			
		||||
                    .collect(Collectors.toMap(
 | 
			
		||||
                            tv -> tv.attributeId,
 | 
			
		||||
                            Function.identity()));
 | 
			
		||||
 | 
			
		||||
            this.values.add(rowValues);
 | 
			
		||||
            addTableRow(rowValues);
 | 
			
		||||
            this.control.layout();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private void addTableRow(final Map<Long, TableValue> rowValues) {
 | 
			
		||||
            final TableItem tableItem = new TableItem(this.control, SWT.NONE);
 | 
			
		||||
            applyTableRowValues(this.values.size() - 1);
 | 
			
		||||
 | 
			
		||||
// TODO delete icon is not working on row as expected
 | 
			
		||||
//            final TableEditor editor = new TableEditor(this.control);
 | 
			
		||||
//            editor.horizontalAlignment = SWT.CENTER;
 | 
			
		||||
//            editor.grabHorizontal = true;
 | 
			
		||||
//            editor.minimumWidth = 20;
 | 
			
		||||
//            final Image image = ImageIcon.REMOVE_BOX.getImage(this.control.getDisplay());
 | 
			
		||||
//            final Label imageLabel = new Label(this.control, SWT.NONE);
 | 
			
		||||
//            imageLabel.setAlignment(SWT.CENTER);
 | 
			
		||||
//            imageLabel.setImage(image);
 | 
			
		||||
//            imageLabel.addListener(SWT.MouseDown, event -> System.out.println("*************** removeRow"));
 | 
			
		||||
//            editor.setEditor(imageLabel, tableItem, this.columnAttributes.size());
 | 
			
		||||
//            tableItem.setData("EDITOR", editor);
 | 
			
		||||
//
 | 
			
		||||
//            editor.layout();
 | 
			
		||||
//            this.control.layout(true, true);
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private void applyRowValues(
 | 
			
		||||
                final int rowIndex,
 | 
			
		||||
                final Map<Long, TableValue> rowValues) {
 | 
			
		||||
 | 
			
		||||
            // set the new values
 | 
			
		||||
            this.values.set(rowIndex, rowValues);
 | 
			
		||||
            // update table row
 | 
			
		||||
            applyTableRowValues(rowIndex);
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private void applyTableRowValues(final int index) {
 | 
			
		||||
            final TableItem item = this.control.getItem(index);
 | 
			
		||||
            final Map<Long, TableValue> rowValues = this.values.get(index);
 | 
			
		||||
 | 
			
		||||
            int cellIndex = 0;
 | 
			
		||||
            for (final ConfigurationAttribute attr : this.columnAttributes) {
 | 
			
		||||
                final String value = rowValues.containsKey(attr.id)
 | 
			
		||||
                        ? rowValues.get(attr.id).value
 | 
			
		||||
                        : null;
 | 
			
		||||
                item.setText(cellIndex, value);
 | 
			
		||||
                cellIndex++;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            item.setData(ROW_VALUE_KEY, item);
 | 
			
		||||
 | 
			
		||||
            // send new values to web-service
 | 
			
		||||
            this.viewContext.getValueChangeListener()
 | 
			
		||||
                    .tableChanged(extractTableValue());
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private void openForm(final int selectionIndex) {
 | 
			
		||||
            final Map<Long, TableValue> rowValues = this.values.get(selectionIndex);
 | 
			
		||||
            final ModalInputDialog<Map<Long, TableValue>> dialog = new ModalInputDialog<>(
 | 
			
		||||
                    this.control.getShell(),
 | 
			
		||||
                    this.widgetFactory);
 | 
			
		||||
 | 
			
		||||
            final TableRowFormBuilder builder = new TableRowFormBuilder(rowValues);
 | 
			
		||||
            dialog.open(
 | 
			
		||||
                    new LocTextKey("Title"),
 | 
			
		||||
                    v -> System.out.println("Values Applied"),
 | 
			
		||||
                    builder);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private ConfigurationTableValue extractTableValue() {
 | 
			
		||||
            final List<TableValue> collect = this.values
 | 
			
		||||
                    .stream()
 | 
			
		||||
                    .flatMap(map -> map.values().stream())
 | 
			
		||||
                    .collect(Collectors.toList());
 | 
			
		||||
 | 
			
		||||
            return new ConfigurationTableValue(
 | 
			
		||||
                    this.viewContext.getInstitutionId(),
 | 
			
		||||
                    this.viewContext.getConfigurationId(),
 | 
			
		||||
                    this.attribute.id,
 | 
			
		||||
                    collect);
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        @Override
 | 
			
		||||
        protected void setDefaultValue() {
 | 
			
		||||
            // NOTE this just empty the list for now
 | 
			
		||||
            // TODO do we need default values for lists?
 | 
			
		||||
            this.control.setSelection(-1);
 | 
			
		||||
            if (this.control.getItemCount() > 0) {
 | 
			
		||||
                for (final TableItem item : this.control.getItems()) {
 | 
			
		||||
                    item.dispose();
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            final List<TableValue> values = new ArrayList<>();
 | 
			
		||||
            this.viewContext.getValueChangeListener().tableChanged(
 | 
			
		||||
                    new ConfigurationTableValue(
 | 
			
		||||
                            this.viewContext.getInstitutionId(),
 | 
			
		||||
                            this.viewContext.getConfigurationId(),
 | 
			
		||||
                            this.attribute.id,
 | 
			
		||||
                            values));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        @Override
 | 
			
		||||
        protected void setValueToControl(final String value) {
 | 
			
		||||
            throw new UnsupportedOperationException();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,36 @@
 | 
			
		|||
/*
 | 
			
		||||
 * 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.examconfig.impl.table;
 | 
			
		||||
 | 
			
		||||
import java.util.Map;
 | 
			
		||||
import java.util.function.Supplier;
 | 
			
		||||
 | 
			
		||||
import org.eclipse.swt.SWT;
 | 
			
		||||
import org.eclipse.swt.widgets.Composite;
 | 
			
		||||
import org.eclipse.swt.widgets.Label;
 | 
			
		||||
 | 
			
		||||
import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationTableValue.TableValue;
 | 
			
		||||
import ch.ethz.seb.sebserver.gui.service.page.ModalInputDialogComposer;
 | 
			
		||||
 | 
			
		||||
public class TableRowFormBuilder implements ModalInputDialogComposer<Map<Long, TableValue>> {
 | 
			
		||||
 | 
			
		||||
    private final Map<Long, TableValue> rowValues;
 | 
			
		||||
 | 
			
		||||
    public TableRowFormBuilder(final Map<Long, TableValue> rowValues) {
 | 
			
		||||
        this.rowValues = rowValues;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public Supplier<Map<Long, TableValue>> compose(final Composite parent) {
 | 
			
		||||
        final Label test = new Label(parent, SWT.NONE);
 | 
			
		||||
        test.setText("TEST");
 | 
			
		||||
        return () -> null;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -10,9 +10,11 @@ package ch.ethz.seb.sebserver.gui.service.page;
 | 
			
		|||
 | 
			
		||||
import java.util.function.Supplier;
 | 
			
		||||
 | 
			
		||||
import org.eclipse.swt.widgets.Composite;
 | 
			
		||||
 | 
			
		||||
@FunctionalInterface
 | 
			
		||||
public interface ModalInputDialogComposer<T> {
 | 
			
		||||
 | 
			
		||||
    Supplier<T> compose(PageContext pageContext);
 | 
			
		||||
    Supplier<T> compose(Composite parent);
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -50,7 +50,6 @@ public class ModalInputDialog<T> extends Dialog {
 | 
			
		|||
 | 
			
		||||
    public void open(
 | 
			
		||||
            final LocTextKey title,
 | 
			
		||||
            final PageContext pageContext,
 | 
			
		||||
            final Consumer<T> callback,
 | 
			
		||||
            final ModalInputDialogComposer<T> contentComposer) {
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -68,8 +67,7 @@ public class ModalInputDialog<T> extends Dialog {
 | 
			
		|||
        gridData.horizontalSpan = 2;
 | 
			
		||||
        main.setLayoutData(gridData);
 | 
			
		||||
 | 
			
		||||
        final PageContext internalPageContext = pageContext.copyOf(main);
 | 
			
		||||
        final Supplier<T> valueSuppier = contentComposer.compose(internalPageContext);
 | 
			
		||||
        final Supplier<T> valueSuppier = contentComposer.compose(main);
 | 
			
		||||
 | 
			
		||||
        final Button ok = this.widgetFactory.buttonLocalized(shell, OK_TEXT_KEY);
 | 
			
		||||
        GridData data = new GridData(GridData.HORIZONTAL_ALIGN_END);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,40 @@
 | 
			
		|||
/*
 | 
			
		||||
 * Copyright (c) 2019 ETH Zürich, Educational Development and Technology (LET)
 | 
			
		||||
 *
 | 
			
		||||
 * This Source Code Form is subject to the terms of the Mozilla Public
 | 
			
		||||
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 | 
			
		||||
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
package ch.ethz.seb.sebserver.gui.service.remote.webservice.api.seb.examconfig;
 | 
			
		||||
 | 
			
		||||
import org.springframework.context.annotation.Lazy;
 | 
			
		||||
import org.springframework.http.HttpMethod;
 | 
			
		||||
import org.springframework.http.MediaType;
 | 
			
		||||
import org.springframework.stereotype.Component;
 | 
			
		||||
 | 
			
		||||
import com.fasterxml.jackson.core.type.TypeReference;
 | 
			
		||||
 | 
			
		||||
import ch.ethz.seb.sebserver.gbl.api.API;
 | 
			
		||||
import ch.ethz.seb.sebserver.gbl.api.EntityType;
 | 
			
		||||
import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationTableValue;
 | 
			
		||||
import ch.ethz.seb.sebserver.gbl.profile.GuiProfile;
 | 
			
		||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestCall;
 | 
			
		||||
 | 
			
		||||
@Lazy
 | 
			
		||||
@Component
 | 
			
		||||
@GuiProfile
 | 
			
		||||
public class SaveExamConfigTableValue extends RestCall<ConfigurationTableValue> {
 | 
			
		||||
 | 
			
		||||
    protected SaveExamConfigTableValue() {
 | 
			
		||||
        super(new TypeKey<>(
 | 
			
		||||
                CallType.SAVE,
 | 
			
		||||
                EntityType.CONFIGURATION_VALUE,
 | 
			
		||||
                new TypeReference<ConfigurationTableValue>() {
 | 
			
		||||
                }),
 | 
			
		||||
                HttpMethod.PUT,
 | 
			
		||||
                MediaType.APPLICATION_JSON_UTF8,
 | 
			
		||||
                API.CONFIGURATION_VALUE_ENDPOINT + API.CONFIGURATION_TABLE_VALUE_PATH_SEGMENT);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -35,7 +35,7 @@ import ch.ethz.seb.sebserver.gbl.util.Result;
 | 
			
		|||
 * </code> */
 | 
			
		||||
public interface BulkActionService {
 | 
			
		||||
 | 
			
		||||
    /** Use this to collect all EntityKey's of all dependent entities for a given BulkAction.
 | 
			
		||||
    /** Use this to collect all EntityKey's of dependent entities for a given BulkAction.
 | 
			
		||||
     *
 | 
			
		||||
     * @param action the BulkAction defining the source entity keys and acts also as the
 | 
			
		||||
     *            dependency collector */
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -92,7 +92,15 @@ public interface BulkActionSupportDAO<T extends Entity> {
 | 
			
		|||
                .collect(Collectors.toList());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Transactional(readOnly = true)
 | 
			
		||||
    /** Get dependency keys of all source entities of a given BulkAction
 | 
			
		||||
     * This method simply goes through all source EntityKeys of the given BulkAction
 | 
			
		||||
     * and applies the selection functions for each, collecting the resulting dependency EntityKeys
 | 
			
		||||
     * into one Set of all dependency keys for all source keys
 | 
			
		||||
     *
 | 
			
		||||
     * 
 | 
			
		||||
     * @param bulkAction The BulkAction that defines the source keys
 | 
			
		||||
     * @param selectionFunction a selection functions that gives all dependency keys for a given source key
 | 
			
		||||
     * @return */
 | 
			
		||||
    default Set<EntityKey> getDependencies(
 | 
			
		||||
            final BulkAction bulkAction,
 | 
			
		||||
            final Function<EntityKey, Result<Collection<EntityKey>>> selectionFunction) {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -13,10 +13,10 @@ import static org.mybatis.dynamic.sql.SqlBuilder.isIn;
 | 
			
		|||
 | 
			
		||||
import java.util.ArrayList;
 | 
			
		||||
import java.util.Collection;
 | 
			
		||||
import java.util.LinkedHashMap;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
import java.util.Map;
 | 
			
		||||
import java.util.Set;
 | 
			
		||||
import java.util.function.Function;
 | 
			
		||||
import java.util.function.Predicate;
 | 
			
		||||
import java.util.stream.Collectors;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -30,6 +30,7 @@ import ch.ethz.seb.sebserver.gbl.api.EntityType;
 | 
			
		|||
import ch.ethz.seb.sebserver.gbl.model.sebconfig.AttributeType;
 | 
			
		||||
import ch.ethz.seb.sebserver.gbl.model.sebconfig.AttributeValueType;
 | 
			
		||||
import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationTableValue;
 | 
			
		||||
import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationTableValue.TableValue;
 | 
			
		||||
import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationValue;
 | 
			
		||||
import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile;
 | 
			
		||||
import ch.ethz.seb.sebserver.gbl.util.Result;
 | 
			
		||||
| 
						 | 
				
			
			@ -213,13 +214,12 @@ public class ConfigurationValueDAOImpl implements ConfigurationValueDAO {
 | 
			
		|||
                                    .build()
 | 
			
		||||
                                    .execute();
 | 
			
		||||
 | 
			
		||||
                    final List<Long> columnAttributeIds = columnAttributes.stream()
 | 
			
		||||
                            .map(a -> a.getId())
 | 
			
		||||
                            .collect(Collectors.toList());
 | 
			
		||||
                    final Map<Long, ConfigurationAttributeRecord> attributeMapping = columnAttributes
 | 
			
		||||
                            .stream()
 | 
			
		||||
                            .collect(Collectors.toMap(attr -> attr.getId(), Function.identity()));
 | 
			
		||||
 | 
			
		||||
                    // get all values of the table and group them by attribute and sorted by list/row index
 | 
			
		||||
                    final List<ConfigurationValueRecord> valueRecords =
 | 
			
		||||
                            this.configurationValueRecordMapper.selectByExample()
 | 
			
		||||
                    final List<TableValue> values = this.configurationValueRecordMapper.selectByExample()
 | 
			
		||||
                            .where(
 | 
			
		||||
                                    ConfigurationValueRecordDynamicSqlSupport.institutionId,
 | 
			
		||||
                                    isEqualTo(institutionId))
 | 
			
		||||
| 
						 | 
				
			
			@ -228,46 +228,35 @@ public class ConfigurationValueDAOImpl implements ConfigurationValueDAO {
 | 
			
		|||
                                    isEqualTo(configurationId))
 | 
			
		||||
                            .and(
 | 
			
		||||
                                    ConfigurationValueRecordDynamicSqlSupport.configurationAttributeId,
 | 
			
		||||
                                            SqlBuilder.isIn(columnAttributeIds))
 | 
			
		||||
                                    SqlBuilder.isIn(new ArrayList<>(attributeMapping.keySet())))
 | 
			
		||||
                            .build()
 | 
			
		||||
                                    .execute();
 | 
			
		||||
 | 
			
		||||
                    int rows = 0;
 | 
			
		||||
                    final List<String> values = new ArrayList<>();
 | 
			
		||||
                    final Map<Long, List<ConfigurationValueRecord>> valueMapping = new LinkedHashMap<>();
 | 
			
		||||
                    for (final ConfigurationValueRecord valueRecord : valueRecords) {
 | 
			
		||||
                        final List<ConfigurationValueRecord> list = valueMapping.putIfAbsent(
 | 
			
		||||
                                valueRecord.getId(),
 | 
			
		||||
                                new ArrayList<>());
 | 
			
		||||
                        list.add(valueRecord);
 | 
			
		||||
                        list.sort((r1, r2) -> r1.getListIndex().compareTo(r2.getListIndex()));
 | 
			
		||||
                        rows = list.size();
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    for (int row = 0; row < rows; row++) {
 | 
			
		||||
                        for (final ConfigurationAttributeRecord aRecord : columnAttributes) {
 | 
			
		||||
                            final List<ConfigurationValueRecord> list = valueMapping.get(aRecord.getId());
 | 
			
		||||
                            if (list != null) {
 | 
			
		||||
                                final ConfigurationValueRecord valueRecord = list.get(row);
 | 
			
		||||
                                if (valueRecord != null) {
 | 
			
		||||
                                    values.add((isBigValue(aRecord)) ? valueRecord.getText() : valueRecord.getValue());
 | 
			
		||||
                                    continue;
 | 
			
		||||
                                }
 | 
			
		||||
                            }
 | 
			
		||||
 | 
			
		||||
                            values.add(null);
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                            .execute()
 | 
			
		||||
                            .stream()
 | 
			
		||||
                            .map(value -> getTableValue(value, attributeMapping))
 | 
			
		||||
                            .collect(Collectors.toList());
 | 
			
		||||
 | 
			
		||||
                    return new ConfigurationTableValue(
 | 
			
		||||
                            institutionId,
 | 
			
		||||
                            configurationId,
 | 
			
		||||
                            attributeId,
 | 
			
		||||
                            new ArrayList<>(valueMapping.keySet()),
 | 
			
		||||
                            values);
 | 
			
		||||
                });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private TableValue getTableValue(
 | 
			
		||||
            final ConfigurationValueRecord value,
 | 
			
		||||
            final Map<Long, ConfigurationAttributeRecord> attributeMapping) {
 | 
			
		||||
 | 
			
		||||
        final Long configurationAttributeId = value.getConfigurationAttributeId();
 | 
			
		||||
        final ConfigurationAttributeRecord configurationAttributeRecord = attributeMapping
 | 
			
		||||
                .get(configurationAttributeId);
 | 
			
		||||
        final boolean bigValue = isBigValue(configurationAttributeRecord);
 | 
			
		||||
        return new TableValue(
 | 
			
		||||
                value.getConfigurationAttributeId(),
 | 
			
		||||
                value.getListIndex(),
 | 
			
		||||
                bigValue ? value.getText() : value.getValue());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    @Transactional
 | 
			
		||||
    public Result<ConfigurationTableValue> saveTableValue(final ConfigurationTableValue value) {
 | 
			
		||||
| 
						 | 
				
			
			@ -276,18 +265,20 @@ public class ConfigurationValueDAOImpl implements ConfigurationValueDAO {
 | 
			
		|||
                .flatMap(val -> attributeRecordById(val.attributeId))
 | 
			
		||||
                .map(attributeRecord -> {
 | 
			
		||||
 | 
			
		||||
                    final List<ConfigurationAttributeRecord> columnAttributes =
 | 
			
		||||
                            this.configurationAttributeRecordMapper.selectByExample()
 | 
			
		||||
                    final Map<Long, ConfigurationAttributeRecord> attributeMap = this.configurationAttributeRecordMapper
 | 
			
		||||
                            .selectByExample()
 | 
			
		||||
                            .where(
 | 
			
		||||
                                    ConfigurationAttributeRecordDynamicSqlSupport.parentId,
 | 
			
		||||
                                    isEqualTo(attributeRecord.getId()))
 | 
			
		||||
                            .build()
 | 
			
		||||
                                    .execute();
 | 
			
		||||
                            .execute()
 | 
			
		||||
                            .stream()
 | 
			
		||||
                            .collect(Collectors.toMap(rec -> rec.getId(), Function.identity()));
 | 
			
		||||
 | 
			
		||||
                    final List<Long> columnAttributeIds = columnAttributes.stream()
 | 
			
		||||
                    final List<Long> columnAttributeIds = attributeMap.values()
 | 
			
		||||
                            .stream()
 | 
			
		||||
                            .map(a -> a.getId())
 | 
			
		||||
                            .collect(Collectors.toList());
 | 
			
		||||
                    final int columns = columnAttributeIds.size();
 | 
			
		||||
 | 
			
		||||
                    // first delete all old values of this table
 | 
			
		||||
                    this.configurationValueRecordMapper.deleteByExample()
 | 
			
		||||
| 
						 | 
				
			
			@ -301,27 +292,19 @@ public class ConfigurationValueDAOImpl implements ConfigurationValueDAO {
 | 
			
		|||
                            .execute();
 | 
			
		||||
 | 
			
		||||
                    // then add the new values
 | 
			
		||||
                    int columnIndex = 0;
 | 
			
		||||
                    int rowIndex = 0;
 | 
			
		||||
                    for (final String val : value.values) {
 | 
			
		||||
                        final ConfigurationAttributeRecord columnAttr = columnAttributes.get(columnIndex);
 | 
			
		||||
                    for (final TableValue tableValue : value.values) {
 | 
			
		||||
                        final ConfigurationAttributeRecord columnAttr = attributeMap.get(tableValue.attributeId);
 | 
			
		||||
                        final boolean bigValue = isBigValue(columnAttr);
 | 
			
		||||
                        final ConfigurationValueRecord valueRecord = new ConfigurationValueRecord(
 | 
			
		||||
                                null,
 | 
			
		||||
                                value.institutionId,
 | 
			
		||||
                                value.configurationId,
 | 
			
		||||
                                columnAttr.getId(),
 | 
			
		||||
                                rowIndex,
 | 
			
		||||
                                (bigValue) ? null : val,
 | 
			
		||||
                                (bigValue) ? val : null);
 | 
			
		||||
                                tableValue.listIndex,
 | 
			
		||||
                                (bigValue) ? null : tableValue.value,
 | 
			
		||||
                                (bigValue) ? tableValue.value : null);
 | 
			
		||||
 | 
			
		||||
                        this.configurationValueRecordMapper.insert(valueRecord);
 | 
			
		||||
 | 
			
		||||
                        columnIndex++;
 | 
			
		||||
                        if (columnIndex >= columns) {
 | 
			
		||||
                            columnIndex = 0;
 | 
			
		||||
                            rowIndex++;
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    return value;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		
		Reference in a new issue