diff --git a/src/main/java/ch/ethz/seb/sebserver/gbl/model/sebconfig/ExamConfigurationMap.java b/src/main/java/ch/ethz/seb/sebserver/gbl/model/exam/ExamConfigurationMap.java similarity index 57% rename from src/main/java/ch/ethz/seb/sebserver/gbl/model/sebconfig/ExamConfigurationMap.java rename to src/main/java/ch/ethz/seb/sebserver/gbl/model/exam/ExamConfigurationMap.java index 1e537d71..07994e52 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gbl/model/sebconfig/ExamConfigurationMap.java +++ b/src/main/java/ch/ethz/seb/sebserver/gbl/model/exam/ExamConfigurationMap.java @@ -6,7 +6,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -package ch.ethz.seb.sebserver.gbl.model.sebconfig; +package ch.ethz.seb.sebserver.gbl.model.exam; import javax.validation.constraints.NotNull; @@ -16,9 +16,12 @@ import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonProperty; import ch.ethz.seb.sebserver.gbl.api.EntityType; +import ch.ethz.seb.sebserver.gbl.api.POSTMapper; +import ch.ethz.seb.sebserver.gbl.model.Domain; +import ch.ethz.seb.sebserver.gbl.model.Domain.CONFIGURATION_NODE; import ch.ethz.seb.sebserver.gbl.model.Domain.EXAM_CONFIGURATION_MAP; -import ch.ethz.seb.sebserver.gbl.model.Domain.SEB_CLIENT_CONFIGURATION; import ch.ethz.seb.sebserver.gbl.model.GrantEntity; +import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationNode.ConfigurationStatus; @JsonIgnoreProperties(ignoreUnknown = true) public final class ExamConfigurationMap implements GrantEntity { @@ -35,14 +38,23 @@ public final class ExamConfigurationMap implements GrantEntity { @JsonProperty(EXAM_CONFIGURATION_MAP.ATTR_INSTITUTION_ID) public final Long institutionId; - @NotNull + @NotNull(message = "examConfigurationMap:examId:notNull") @JsonProperty(EXAM_CONFIGURATION_MAP.ATTR_EXAM_ID) public final Long examId; - @NotNull + @NotNull(message = "examConfigurationMap:configurationNodeId:notNull") @JsonProperty(EXAM_CONFIGURATION_MAP.ATTR_CONFIGURATION_NODE_ID) public final Long configurationNodeId; + @JsonProperty(CONFIGURATION_NODE.ATTR_NAME) + public final String configName; + + @JsonProperty(CONFIGURATION_NODE.ATTR_DESCRIPTION) + public final String configDescription; + + @JsonProperty(CONFIGURATION_NODE.ATTR_STATUS) + public final ConfigurationStatus configStatus; + @JsonProperty(EXAM_CONFIGURATION_MAP.ATTR_USER_NAMES) public final String userNames; @@ -59,8 +71,12 @@ public final class ExamConfigurationMap implements GrantEntity { @JsonProperty(EXAM_CONFIGURATION_MAP.ATTR_EXAM_ID) final Long examId, @JsonProperty(EXAM_CONFIGURATION_MAP.ATTR_CONFIGURATION_NODE_ID) final Long configurationNodeId, @JsonProperty(EXAM_CONFIGURATION_MAP.ATTR_USER_NAMES) final String userNames, - @JsonProperty(SEB_CLIENT_CONFIGURATION.ATTR_ENCRYPT_SECRET) final CharSequence encryptSecret, - @JsonProperty(ATTR_CONFIRM_ENCRYPT_SECRET) final CharSequence confirmEncryptSecret) { + @JsonProperty(EXAM_CONFIGURATION_MAP.ATTR_ENCRYPT_SECRET) final CharSequence encryptSecret, + @JsonProperty(ATTR_CONFIRM_ENCRYPT_SECRET) final CharSequence confirmEncryptSecret, + + @JsonProperty(CONFIGURATION_NODE.ATTR_NAME) final String configName, + @JsonProperty(CONFIGURATION_NODE.ATTR_DESCRIPTION) final String configDescription, + @JsonProperty(CONFIGURATION_NODE.ATTR_STATUS) final ConfigurationStatus configStatus) { this.id = id; this.institutionId = institutionId; @@ -69,6 +85,24 @@ public final class ExamConfigurationMap implements GrantEntity { this.userNames = userNames; this.encryptSecret = encryptSecret; this.confirmEncryptSecret = confirmEncryptSecret; + + this.configName = configName; + this.configDescription = configDescription; + this.configStatus = configStatus; + } + + public ExamConfigurationMap(final Long institutionId, final POSTMapper postParams) { + this.id = null; + this.institutionId = institutionId; + this.examId = postParams.getLong(Domain.EXAM_CONFIGURATION_MAP.ATTR_EXAM_ID); + this.configurationNodeId = postParams.getLong(Domain.EXAM_CONFIGURATION_MAP.ATTR_CONFIGURATION_NODE_ID); + this.userNames = postParams.getString(Domain.EXAM_CONFIGURATION_MAP.ATTR_USER_NAMES); + this.encryptSecret = postParams.getCharSequence(Domain.EXAM_CONFIGURATION_MAP.ATTR_ENCRYPT_SECRET); + this.confirmEncryptSecret = postParams.getCharSequence(ATTR_CONFIRM_ENCRYPT_SECRET); + + this.configName = postParams.getString(Domain.CONFIGURATION_NODE.ATTR_NAME); + this.configDescription = postParams.getString(Domain.CONFIGURATION_NODE.ATTR_DESCRIPTION); + this.configStatus = postParams.getEnum(Domain.CONFIGURATION_NODE.ATTR_STATUS, ConfigurationStatus.class); } @Override @@ -122,6 +156,18 @@ public final class ExamConfigurationMap implements GrantEntity { return this.encryptSecret != null && this.encryptSecret.length() > 0; } + public String getConfigName() { + return this.configName; + } + + public String getConfigDescription() { + return this.configDescription; + } + + public ConfigurationStatus getConfigStatus() { + return this.configStatus; + } + @Override public String toString() { final StringBuilder builder = new StringBuilder(); @@ -133,10 +179,20 @@ public final class ExamConfigurationMap implements GrantEntity { builder.append(this.examId); builder.append(", configurationNodeId="); builder.append(this.configurationNodeId); + builder.append(", configName="); + builder.append(this.configName); + builder.append(", configDescription="); + builder.append(this.configDescription); + builder.append(", configStatus="); + builder.append(this.configStatus); builder.append(", userNames="); builder.append(this.userNames); builder.append("]"); return builder.toString(); } + public static ExamConfigurationMap createNew(final Exam exam) { + return new ExamConfigurationMap(null, exam.institutionId, exam.id, null, null, null, null, null, null, null); + } + } diff --git a/src/main/java/ch/ethz/seb/sebserver/gbl/model/exam/Indicator.java b/src/main/java/ch/ethz/seb/sebserver/gbl/model/exam/Indicator.java index d5b22124..112c519d 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gbl/model/exam/Indicator.java +++ b/src/main/java/ch/ethz/seb/sebserver/gbl/model/exam/Indicator.java @@ -29,7 +29,7 @@ import ch.ethz.seb.sebserver.gbl.util.Utils; @JsonIgnoreProperties(ignoreUnknown = true) public final class Indicator implements Entity { - public static final String FILTER_ATTR_EXAM = "exam"; + public static final String FILTER_ATTR_EXAM_ID = "examId"; public enum IndicatorType { LAST_PING, diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/content/ExamForm.java b/src/main/java/ch/ethz/seb/sebserver/gui/content/ExamForm.java index 5dc34f8b..b70b9178 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/content/ExamForm.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/content/ExamForm.java @@ -8,6 +8,9 @@ package ch.ethz.seb.sebserver.gui.content; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; import java.util.function.BooleanSupplier; import org.apache.commons.lang3.BooleanUtils; @@ -23,6 +26,7 @@ import ch.ethz.seb.sebserver.gbl.api.API; import ch.ethz.seb.sebserver.gbl.model.Domain; import ch.ethz.seb.sebserver.gbl.model.EntityKey; import ch.ethz.seb.sebserver.gbl.model.exam.Exam; +import ch.ethz.seb.sebserver.gbl.model.exam.ExamConfigurationMap; import ch.ethz.seb.sebserver.gbl.model.exam.Exam.ExamStatus; import ch.ethz.seb.sebserver.gbl.model.exam.Indicator; import ch.ethz.seb.sebserver.gbl.model.exam.QuizData; @@ -42,8 +46,10 @@ import ch.ethz.seb.sebserver.gui.service.page.TemplateComposer; import ch.ethz.seb.sebserver.gui.service.page.event.ActionEvent; import ch.ethz.seb.sebserver.gui.service.page.impl.PageAction; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestService; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.DeleteExamConfigMapping; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.DeleteIndicator; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.GetExam; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.GetExamConfigMappingsPage; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.GetIndicators; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.SaveExam; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.quiz.GetQuizData; @@ -62,28 +68,50 @@ public class ExamForm implements TemplateComposer { private static final Logger log = LoggerFactory.getLogger(ExamForm.class); - private static final LocTextKey FORM_SUPPORTER_TEXT_KEY = new LocTextKey("sebserver.exam.form.supporter"); - private static final LocTextKey FORM_STATUS_TEXT_KEY = new LocTextKey("sebserver.exam.form.status"); - private static final LocTextKey FORM_TYPE_TEXT_KEY = new LocTextKey("sebserver.exam.form.type"); - private static final LocTextKey FORM_ENDTIME_TEXT_KEY = new LocTextKey("sebserver.exam.form.endtime"); - private static final LocTextKey FORM_STARTTIME_TEXT_KEY = new LocTextKey("sebserver.exam.form.starttime"); - private static final LocTextKey FORM_DESCRIPTION_TEXT_KEY = new LocTextKey("sebserver.exam.form.description"); - private static final LocTextKey FORM_NAME_TEXT_KEY = new LocTextKey("sebserver.exam.form.name"); - private static final LocTextKey FORM_QUIZID_TEXT_KEY = new LocTextKey("sebserver.exam.form.quizid"); - private static final LocTextKey FORM_LMSSETUP_TEXT_KEY = new LocTextKey("sebserver.exam.form.lmssetup"); - private final PageService pageService; private final ResourceService resourceService; - private final static LocTextKey listTitleKey = + private static final LocTextKey CONFIG_EMPTY_LIST_MESSAGE = + new LocTextKey("sebserver.exam.configuration.list.empty"); + private static final LocTextKey INDICATOR_EMPTY_LIST_MESSAGE = + new LocTextKey("sebserver.exam.indicator.list.empty"); + private static final LocTextKey FORM_SUPPORTER_TEXT_KEY = + new LocTextKey("sebserver.exam.form.supporter"); + private static final LocTextKey FORM_STATUS_TEXT_KEY = + new LocTextKey("sebserver.exam.form.status"); + private static final LocTextKey FORM_TYPE_TEXT_KEY = + new LocTextKey("sebserver.exam.form.type"); + private static final LocTextKey FORM_ENDTIME_TEXT_KEY = + new LocTextKey("sebserver.exam.form.endtime"); + private static final LocTextKey FORM_STARTTIME_TEXT_KEY = + new LocTextKey("sebserver.exam.form.starttime"); + private static final LocTextKey FORM_DESCRIPTION_TEXT_KEY = + new LocTextKey("sebserver.exam.form.description"); + private static final LocTextKey FORM_NAME_TEXT_KEY = + new LocTextKey("sebserver.exam.form.name"); + private static final LocTextKey FORM_QUIZID_TEXT_KEY = + new LocTextKey("sebserver.exam.form.quizid"); + private static final LocTextKey FORM_LMSSETUP_TEXT_KEY = + new LocTextKey("sebserver.exam.form.lmssetup"); + + private final static LocTextKey CONFIG_LIST_TITLE_KEY = + new LocTextKey("sebserver.exam.configuration.list.title"); + private final static LocTextKey CONFIG_NAME_COLUMN_KEY = + new LocTextKey("sebserver.exam.configuration.list.column.name"); + private final static LocTextKey CONFIG_DESCRIPTION_COLUMN_KEY = + new LocTextKey("sebserver.exam.configuration.list.column.description"); + private final static LocTextKey CONFIG_STATUS_COLUMN_KEY = + new LocTextKey("sebserver.exam.configuration.list.column.status"); + + private final static LocTextKey INDICATOR_LIST_TITLE_KEY = new LocTextKey("sebserver.exam.indicator.list.title"); - private final static LocTextKey typeColumnKey = + private final static LocTextKey INDICATOR_TYPE_COLUMN_KEY = new LocTextKey("sebserver.exam.indicator.list.column.type"); - private final static LocTextKey nameColumnKey = + private final static LocTextKey INDICATOR_NAME_COLUMN_KEY = new LocTextKey("sebserver.exam.indicator.list.column.name"); - private final static LocTextKey thresholdColumnKey = + private final static LocTextKey INDICATOR_THRESHOLD_COLUMN_KEY = new LocTextKey("sebserver.exam.indicator.list.column.thresholds"); - private final static LocTextKey emptySelectionTextKey = + private final static LocTextKey INDICATOR_EMPTY_SELECTION_TEXT_KEY = new LocTextKey("sebserver.exam.indicator.list.pleaseSelect"); protected ExamForm( @@ -162,7 +190,6 @@ public class ExamForm implements TemplateComposer { .putStaticValueIf(isNew, QuizData.QUIZ_ATTR_ID, exam.externalId) - .addField(FormBuilder.singleSelection( Domain.EXAM.ATTR_LMS_SETUP_ID, FORM_LMSSETUP_TEXT_KEY, @@ -237,35 +264,102 @@ public class ExamForm implements TemplateComposer { .publishIf(() -> !readonly); // additional data in read-only view - if (readonly) { + if (readonly && !importFromQuizData) { + + // List of SEB Configuration + widgetFactory.labelLocalized( + content, + CustomVariant.TEXT_H3, + CONFIG_LIST_TITLE_KEY); + + final EntityTable configurationTable = + this.pageService.entityTableBuilder(restService.getRestCall(GetExamConfigMappingsPage.class)) + .withRestCallAdapter(builder -> builder.withQueryParam( + ExamConfigurationMap.FILTER_ATTR_EXAM_ID, + entityKey.modelId)) + .withEmptyMessage(CONFIG_EMPTY_LIST_MESSAGE) + .withPaging(1) + .hideNavigation() + .withColumn(new ColumnDefinition<>( + Domain.CONFIGURATION_NODE.ATTR_NAME, + CONFIG_NAME_COLUMN_KEY, + ExamConfigurationMap::getConfigName, + false)) + .withColumn(new ColumnDefinition<>( + Domain.CONFIGURATION_NODE.ATTR_DESCRIPTION, + CONFIG_DESCRIPTION_COLUMN_KEY, + ExamConfigurationMap::getConfigDescription, + false)) + .withColumn(new ColumnDefinition<>( + Domain.CONFIGURATION_NODE.ATTR_STATUS, + CONFIG_STATUS_COLUMN_KEY, + this.resourceService::localizedExamConfigStatusName, + false)) + .withDefaultActionIf( + () -> editable, + () -> actionBuilder + .newAction(ActionDefinition.EXAM_CONFIGURATION_MODIFY_FROM_LIST) + .create()) + + .compose(content); + + final EntityKey configMapKey = (configurationTable.hasAnyContent()) + ? configurationTable.getFirstRowData().getEntityKey() + : null; + + actionBuilder + + .newAction(ActionDefinition.EXAM_CONFIGURATION_NEW) + .withParentEntityKey(entityKey) + .publishIf(() -> modifyGrant && editable && !configurationTable.hasAnyContent()) + + .newAction(ActionDefinition.EXAM_CONFIGURATION_MODIFY_FROM_LIST) + .withParentEntityKey(entityKey) + .withEntityKey(configMapKey) + .publishIf(() -> modifyGrant && editable && configurationTable.hasAnyContent()) + + .newAction(ActionDefinition.EXAM_CONFIGURATION_DELETE_FROM_LIST) + .withEntityKey(entityKey) + .withSelect( + () -> { + final ExamConfigurationMap firstRowData = configurationTable.getFirstRowData(); + if (firstRowData == null) { + return Collections.emptySet(); + } else { + return new HashSet<>(Arrays.asList(firstRowData.getEntityKey())); + } + }, + this::deleteExamConfigMapping, + null) + .publishIf(() -> modifyGrant && configurationTable.hasAnyContent() && editable); // List of Indicators widgetFactory.labelLocalized( content, CustomVariant.TEXT_H3, - listTitleKey); + INDICATOR_LIST_TITLE_KEY); final EntityTable indicatorTable = this.pageService.entityTableBuilder(restService.getRestCall(GetIndicators.class)) .withRestCallAdapter(builder -> builder.withQueryParam( - Indicator.FILTER_ATTR_EXAM, + Indicator.FILTER_ATTR_EXAM_ID, entityKey.modelId)) - .withEmptyMessage(new LocTextKey("sebserver.exam.indicator.list.empty")) + .withEmptyMessage(INDICATOR_EMPTY_LIST_MESSAGE) .withPaging(5) .hideNavigation() .withColumn(new ColumnDefinition<>( Domain.INDICATOR.ATTR_NAME, - nameColumnKey, + INDICATOR_NAME_COLUMN_KEY, Indicator::getName, false)) .withColumn(new ColumnDefinition<>( Domain.INDICATOR.ATTR_TYPE, - typeColumnKey, + INDICATOR_TYPE_COLUMN_KEY, this::indicatorTypeName, false)) .withColumn(new ColumnDefinition<>( Domain.THRESHOLD.REFERENCE_NAME, - thresholdColumnKey, + INDICATOR_THRESHOLD_COLUMN_KEY, ExamForm::thresholdsValue, false)) .withDefaultActionIf( @@ -285,18 +379,20 @@ public class ExamForm implements TemplateComposer { .newAction(ActionDefinition.EXAM_INDICATOR_MODIFY_FROM_LIST) .withParentEntityKey(entityKey) - .withSelect(indicatorTable::getSelection, PageAction::applySingleSelection, emptySelectionTextKey) + .withSelect( + indicatorTable::getSelection, + PageAction::applySingleSelection, + INDICATOR_EMPTY_SELECTION_TEXT_KEY) .publishIf(() -> modifyGrant && indicatorTable.hasAnyContent() && editable) .newAction(ActionDefinition.EXAM_INDICATOR_DELETE_FROM_LIST) .withEntityKey(entityKey) - .withSelect(indicatorTable::getSelection, this::deleteSelectedIndicator, emptySelectionTextKey) + .withSelect( + indicatorTable::getSelection, + this::deleteSelectedIndicator, + INDICATOR_EMPTY_SELECTION_TEXT_KEY) .publishIf(() -> modifyGrant && indicatorTable.hasAnyContent() && editable); - - // TODO List of attached SEB Configurations - } - } private PageAction deleteSelectedIndicator(final PageAction action) { @@ -308,6 +404,15 @@ public class ExamForm implements TemplateComposer { return action; } + private PageAction deleteExamConfigMapping(final PageAction action) { + final EntityKey examConfigMappingKey = action.getSingleSelection(); + this.resourceService.getRestService() + .getBuilder(DeleteExamConfigMapping.class) + .withURIVariable(API.PARAM_MODEL_ID, examConfigMappingKey.modelId) + .call(); + return action; + } + private Result getExistingExam(final EntityKey entityKey, final RestService restService) { return restService.getBuilder(GetExam.class) .withURIVariable(API.PARAM_MODEL_ID, entityKey.modelId) diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/content/ExamSebConfigMapForm.java b/src/main/java/ch/ethz/seb/sebserver/gui/content/ExamSebConfigMapForm.java new file mode 100644 index 00000000..76e646de --- /dev/null +++ b/src/main/java/ch/ethz/seb/sebserver/gui/content/ExamSebConfigMapForm.java @@ -0,0 +1,212 @@ +/* + * 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.content; + +import org.apache.commons.lang3.StringUtils; +import org.eclipse.swt.widgets.Composite; +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.api.API; +import ch.ethz.seb.sebserver.gbl.model.Domain; +import ch.ethz.seb.sebserver.gbl.model.EntityKey; +import ch.ethz.seb.sebserver.gbl.model.exam.Exam; +import ch.ethz.seb.sebserver.gbl.model.exam.ExamConfigurationMap; +import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationNode; +import ch.ethz.seb.sebserver.gbl.profile.GuiProfile; +import ch.ethz.seb.sebserver.gui.content.action.ActionDefinition; +import ch.ethz.seb.sebserver.gui.form.Form; +import ch.ethz.seb.sebserver.gui.form.FormBuilder; +import ch.ethz.seb.sebserver.gui.form.FormHandle; +import ch.ethz.seb.sebserver.gui.service.ResourceService; +import ch.ethz.seb.sebserver.gui.service.i18n.LocTextKey; +import ch.ethz.seb.sebserver.gui.service.page.PageContext; +import ch.ethz.seb.sebserver.gui.service.page.PageService; +import ch.ethz.seb.sebserver.gui.service.page.TemplateComposer; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestService; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.GetExam; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.GetExamConfigMapping; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.NewExamConfigMapping; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.SaveExamConfigMapping; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.seb.examconfig.GetExamConfigNode; +import ch.ethz.seb.sebserver.gui.widget.WidgetFactory; + +@Lazy +@Component +@GuiProfile +public class ExamSebConfigMapForm implements TemplateComposer { + + private static final Logger log = LoggerFactory.getLogger(ExamSebConfigMapForm.class); + + private static final LocTextKey NEW_CONFIG_MAPPING_TILE_TEXT_KEY = + new LocTextKey("sebserver.exam.configuration.form.title.new"); + private static final LocTextKey CONFIG_MAPPING_TILE_TEXT_KEY = + new LocTextKey("sebserver.exam.configuration.form.title"); + private static final LocTextKey CONFIG_MAPPING_NAME_TEXT_KEY = + new LocTextKey("sebserver.exam.configuration.form.name"); + private static final LocTextKey FORM_DESCRIPTION_TEXT_KEY = + new LocTextKey("sebserver.exam.configuration.form.description"); + private static final LocTextKey FORM_STATUS_TEXT_KEY = + new LocTextKey("sebserver.exam.configuration.form.status"); + private static final LocTextKey FORM_ENCRYPT_SECRET_TEXT_KEY = + new LocTextKey("sebserver.exam.configuration.form.encryptSecret"); + private static final LocTextKey FORM_CONFIRM_ENCRYPT_SECRET_TEXT_KEY = + new LocTextKey("sebserver.exam.configuration.form.encryptSecret.confirm"); + + private final PageService pageService; + private final ResourceService resourceService; + + protected ExamSebConfigMapForm( + final PageService pageService, + final ResourceService resourceService) { + + this.pageService = pageService; + this.resourceService = resourceService; + } + + @Override + public void compose(final PageContext pageContext) { + final RestService restService = this.resourceService.getRestService(); + final WidgetFactory widgetFactory = this.pageService.getWidgetFactory(); + + final EntityKey entityKey = pageContext.getEntityKey(); + final EntityKey parentEntityKey = pageContext.getParentEntityKey(); + final boolean isNew = entityKey == null; + final boolean isReadonly = pageContext.isReadonly(); + + final Exam exam = (isNew) + ? restService + .getBuilder(GetExam.class) + .withURIVariable(API.PARAM_MODEL_ID, parentEntityKey.modelId) + .call() + .get(pageContext::notifyError) + : null; + + // get data or create new. Handle error if happen + final ExamConfigurationMap examConfigurationMap = (isNew) + ? ExamConfigurationMap.createNew(exam) + : restService + .getBuilder(GetExamConfigMapping.class) + .withURIVariable(API.PARAM_MODEL_ID, entityKey.modelId) + .call() + .get(pageContext::notifyError); + + if (examConfigurationMap == null) { + log.error("Failed to get ExamConfigurationMap. " + + "Error is notified to the User. " + + "See previous logs for more infomation"); + return; + } + + // new PageContext with actual EntityKey + final PageContext formContext = pageContext.withEntityKey(examConfigurationMap.getEntityKey()); + + // the default page layout + final LocTextKey titleKey = (isNew) + ? NEW_CONFIG_MAPPING_TILE_TEXT_KEY + : CONFIG_MAPPING_TILE_TEXT_KEY; + final Composite content = widgetFactory.defaultPageLayout( + formContext.getParent(), + titleKey); + + final FormHandle formHandle = this.pageService.formBuilder( + formContext.copyOf(content), 4) + .readonly(isReadonly) + .putStaticValueIf(() -> !isNew, + Domain.EXAM_CONFIGURATION_MAP.ATTR_ID, + examConfigurationMap.getModelId()) + .putStaticValue( + Domain.EXAM_CONFIGURATION_MAP.ATTR_INSTITUTION_ID, + String.valueOf(examConfigurationMap.getInstitutionId())) + .putStaticValue( + Domain.EXAM_CONFIGURATION_MAP.ATTR_EXAM_ID, + String.valueOf(examConfigurationMap.examId)) + .addField(FormBuilder.singleSelection( + Domain.EXAM_CONFIGURATION_MAP.ATTR_CONFIGURATION_NODE_ID, + CONFIG_MAPPING_NAME_TEXT_KEY, + String.valueOf(examConfigurationMap.configurationNodeId), + this.resourceService::examConfigurationSelectionResources) + .withSelectionListener(this::updateFormValuesFromConfigSelection)) + + .addField(FormBuilder.text( + Domain.CONFIGURATION_NODE.ATTR_DESCRIPTION, + FORM_DESCRIPTION_TEXT_KEY, + examConfigurationMap.configDescription) + .asArea() + .readonly(true)) + + .addField(FormBuilder.text( + Domain.CONFIGURATION_NODE.ATTR_STATUS, + FORM_STATUS_TEXT_KEY, + this.resourceService.localizedExamConfigStatusName(examConfigurationMap)) + .readonly(true)) + + .addField(FormBuilder.text( + Domain.EXAM_CONFIGURATION_MAP.ATTR_ENCRYPT_SECRET, + FORM_ENCRYPT_SECRET_TEXT_KEY) + .asPasswordField()) + .addField(FormBuilder.text( + ExamConfigurationMap.ATTR_CONFIRM_ENCRYPT_SECRET, + FORM_CONFIRM_ENCRYPT_SECRET_TEXT_KEY) + .asPasswordField()) + + .buildFor((isNew) + ? restService.getRestCall(NewExamConfigMapping.class) + : restService.getRestCall(SaveExamConfigMapping.class)); + + // propagate content actions to action-pane + this.pageService.pageActionBuilder(formContext.clearEntityKeys()) + + .newAction(ActionDefinition.EXAM_CONFIGURATION_SAVE) + .withEntityKey(parentEntityKey) + .withExec(formHandle::processFormSave) + .ignoreMoveAwayFromEdit() + .publishIf(() -> !isReadonly) + + .newAction(ActionDefinition.EXAM_CONFIGURATION_CANCEL_MODIFY) + .withEntityKey(parentEntityKey) + .withExec(action -> this.pageService.onEmptyEntityKeyGoTo( + action, + ActionDefinition.EXAM_VIEW_LIST)) + .publishIf(() -> !isReadonly); + } + + private void updateFormValuesFromConfigSelection(final Form form) { + final String configId = form.getFieldValue(Domain.EXAM_CONFIGURATION_MAP.ATTR_CONFIGURATION_NODE_ID); + if (StringUtils.isBlank(configId)) { + form.setFieldValue(Domain.CONFIGURATION_NODE.ATTR_DESCRIPTION, null); + form.setFieldValue(Domain.CONFIGURATION_NODE.ATTR_STATUS, null); + } else { + try { + + final ConfigurationNode configuration = this.resourceService + .getRestService() + .getBuilder(GetExamConfigNode.class) + .withURIVariable(API.PARAM_MODEL_ID, configId) + .call() + .getOrThrow(); + + form.setFieldValue( + Domain.CONFIGURATION_NODE.ATTR_DESCRIPTION, + configuration.description); + form.setFieldValue( + Domain.CONFIGURATION_NODE.ATTR_STATUS, + this.resourceService.localizedExamConfigStatusName(configuration)); + + } catch (final Exception e) { + log.error("Failed to update form values from SEB Configuration selection", e); + form.setFieldValue(Domain.CONFIGURATION_NODE.ATTR_DESCRIPTION, null); + form.setFieldValue(Domain.CONFIGURATION_NODE.ATTR_STATUS, null); + } + } + } + +} diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/content/IndicatorForm.java b/src/main/java/ch/ethz/seb/sebserver/gui/content/IndicatorForm.java index 5d431863..3d0c8d66 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/content/IndicatorForm.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/content/IndicatorForm.java @@ -43,6 +43,10 @@ public class IndicatorForm implements TemplateComposer { private static final Logger log = LoggerFactory.getLogger(IndicatorForm.class); + private static final LocTextKey NEW_INDICATOR_TILE_TEXT_KEY = + new LocTextKey("sebserver.exam.indicator.form.title.new"); + private static final LocTextKey INDICATOR_TILE_TEXT_KEY = + new LocTextKey("sebserver.exam.indicator.form.title"); private static final LocTextKey FORM_THRESHOLDS_TEXT_KEY = new LocTextKey("sebserver.exam.indicator.form.thresholds"); private static final LocTextKey FORM_COLOR_TEXT_KEY = @@ -100,10 +104,9 @@ public class IndicatorForm implements TemplateComposer { final PageContext formContext = pageContext.withEntityKey(indicator.getEntityKey()); // the default page layout - final LocTextKey titleKey = new LocTextKey( - (isNew) - ? "sebserver.exam.indicator.form.title.new" - : "sebserver.exam.indicator.form.title"); + final LocTextKey titleKey = (isNew) + ? NEW_INDICATOR_TILE_TEXT_KEY + : INDICATOR_TILE_TEXT_KEY; final Composite content = widgetFactory.defaultPageLayout( formContext.getParent(), titleKey); diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/content/SebExamConfigList.java b/src/main/java/ch/ethz/seb/sebserver/gui/content/SebExamConfigList.java index 39664767..6f446635 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/content/SebExamConfigList.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/content/SebExamConfigList.java @@ -8,14 +8,11 @@ package ch.ethz.seb.sebserver.gui.content; -import java.util.function.Function; - import org.eclipse.swt.widgets.Composite; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Component; -import ch.ethz.seb.sebserver.gbl.Constants; import ch.ethz.seb.sebserver.gbl.api.EntityType; import ch.ethz.seb.sebserver.gbl.model.Domain; import ch.ethz.seb.sebserver.gbl.model.Entity; @@ -114,7 +111,7 @@ public class SebExamConfigList implements TemplateComposer { () -> new ColumnDefinition<>( Domain.LMS_SETUP.ATTR_INSTITUTION_ID, INSTITUTION_TEXT_KEY, - examConfigInstitutionNameFunction(this.resourceService), + this.resourceService::localizedExamConfigInstitutionName, this.institutionFilter, false)) .withColumn(new ColumnDefinition<>( @@ -132,7 +129,7 @@ public class SebExamConfigList implements TemplateComposer { .withColumn(new ColumnDefinition<>( Domain.CONFIGURATION_NODE.ATTR_STATUS, STATUS_TEXT_KEY, - this::examConfigStatusName, + this.resourceService::localizedExamConfigStatusName, this.statusFilter, true)) .withDefaultAction(pageActionBuilder @@ -141,7 +138,6 @@ public class SebExamConfigList implements TemplateComposer { .compose(content); final GrantCheck examConfigGrant = this.currentUser.grantCheck(EntityType.CONFIGURATION_NODE); - pageActionBuilder .newAction(ActionDefinition.SEB_EXAM_CONFIG_NEW) @@ -160,20 +156,4 @@ public class SebExamConfigList implements TemplateComposer { .publishIf(() -> examConfigGrant.im() && table.hasAnyContent()); } - private static Function examConfigInstitutionNameFunction( - final ResourceService resourceService) { - - return config -> resourceService.getInstitutionNameFunction() - .apply(String.valueOf(config.institutionId)); - } - - private String examConfigStatusName(final ConfigurationNode config) { - if (config.status == null) { - return Constants.EMPTY_NOTE; - } - - return this.resourceService.getI18nSupport() - .getText(ResourceService.EXAMCONFIG_STATUS_PREFIX + config.status.name()); - } - } diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/content/SebExamConfigPropForm.java b/src/main/java/ch/ethz/seb/sebserver/gui/content/SebExamConfigPropForm.java index 043463f5..75b45229 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/content/SebExamConfigPropForm.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/content/SebExamConfigPropForm.java @@ -134,7 +134,8 @@ public class SebExamConfigPropForm implements TemplateComposer { .addField(FormBuilder.text( Domain.CONFIGURATION_NODE.ATTR_DESCRIPTION, FORM_DESCRIPTION_TEXT_KEY, - examConfig.description).asArea()) + examConfig.description) + .asArea()) .addField(FormBuilder.singleSelection( Domain.CONFIGURATION_NODE.ATTR_STATUS, FORM_STATUS_TEXT_KEY, diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/content/action/ActionCategory.java b/src/main/java/ch/ethz/seb/sebserver/gui/content/action/ActionCategory.java index acefda66..cf81b3f0 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/content/action/ActionCategory.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/content/action/ActionCategory.java @@ -17,7 +17,8 @@ public enum ActionCategory { LMS_SETUP_LIST(new LocTextKey("sebserver.lmssetup.list.actions"), 1), QUIZ_LIST(new LocTextKey("sebserver.quizdiscovery.list.actions"), 1), EXAM_LIST(new LocTextKey("sebserver.exam.list.actions"), 1), - INDICATOR_LIST(new LocTextKey("sebserver.exam.indicator.list.actions"), 1), + EXAM_CONFIG_MAPPING_LIST(new LocTextKey("sebserver.exam.configuration.list.actions"), 1), + INDICATOR_LIST(new LocTextKey("sebserver.exam.indicator.list.actions"), 2), SEB_CLIENT_CONFIG_LIST(new LocTextKey("sebserver.clientconfig.list.actions"), 1), SEB_EXAM_CONFIG_LIST(new LocTextKey("sebserver.examconfig.list.actions"), 1), VARIA(new LocTextKey("sebserver.overall.action.category.varia"), 100), diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/content/action/ActionDefinition.java b/src/main/java/ch/ethz/seb/sebserver/gui/content/action/ActionDefinition.java index 963b8d51..1869265f 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/content/action/ActionDefinition.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/content/action/ActionDefinition.java @@ -228,10 +228,37 @@ public enum ActionDefinition { PageStateDefinition.EXAM_VIEW, ActionCategory.FORM), + EXAM_CONFIGURATION_NEW( + new LocTextKey("sebserver.exam.configuration.action.list.new"), + ImageIcon.NEW, + PageStateDefinition.EXAM_CONFIG_MAP_EDIT, + ActionCategory.EXAM_CONFIG_MAPPING_LIST), + EXAM_CONFIGURATION_MODIFY_FROM_LIST( + new LocTextKey("sebserver.exam.configuration.action.list.modify"), + ImageIcon.EDIT, + PageStateDefinition.EXAM_CONFIG_MAP_EDIT, + ActionCategory.EXAM_CONFIG_MAPPING_LIST), + EXAM_CONFIGURATION_DELETE_FROM_LIST( + new LocTextKey("sebserver.exam.configuration.action.list.delete"), + ImageIcon.DELETE, + PageStateDefinition.EXAM_VIEW, + ActionCategory.EXAM_CONFIG_MAPPING_LIST), + EXAM_CONFIGURATION_SAVE( + new LocTextKey("sebserver.exam.configuration.action.save"), + ImageIcon.SAVE, + PageStateDefinition.EXAM_VIEW, + ActionCategory.FORM), + EXAM_CONFIGURATION_CANCEL_MODIFY( + new LocTextKey("sebserver.overall.action.modify.cancel"), + ImageIcon.CANCEL, + PageStateDefinition.EXAM_VIEW, + ActionCategory.FORM), + EXAM_INDICATOR_NEW( new LocTextKey("sebserver.exam.indicator.action.list.new"), ImageIcon.NEW, - PageStateDefinition.INDICATOR_EDIT), + PageStateDefinition.INDICATOR_EDIT, + ActionCategory.INDICATOR_LIST), EXAM_INDICATOR_MODIFY_FROM_LIST( new LocTextKey("sebserver.exam.indicator.action.list.modify"), ImageIcon.EDIT, @@ -325,7 +352,7 @@ public enum ActionDefinition { ActionCategory.FORM), SEB_EXAM_CONFIG_MODIFY( new LocTextKey("sebserver.examconfig.action.modify"), - ImageIcon.EDIT, + ImageIcon.EDIT_SETTINGS, PageStateDefinition.SEB_EXAM_CONFIG_EDIT, ActionCategory.FORM), SEB_EXAM_CONFIG_CANCEL_MODIFY( @@ -341,7 +368,7 @@ public enum ActionDefinition { SEB_EXAM_CONFIG_MODIFY_FROM_LIST( new LocTextKey("sebserver.examconfig.action.list.modify"), - ImageIcon.EDIT, + ImageIcon.EDIT_SETTINGS, PageStateDefinition.SEB_EXAM_CONFIG_EDIT, ActionCategory.SEB_EXAM_CONFIG_LIST), @@ -352,7 +379,7 @@ public enum ActionDefinition { ActionCategory.SEB_EXAM_CONFIG_LIST), SEB_EXAM_CONFIG_UNDO( new LocTextKey("sebserver.examconfig.action.undo"), - ImageIcon.SAVE, + ImageIcon.UNDO, PageStateDefinition.SEB_EXAM_CONFIG_EDIT, ActionCategory.SEB_EXAM_CONFIG_LIST), diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/content/action/ActionPane.java b/src/main/java/ch/ethz/seb/sebserver/gui/content/action/ActionPane.java index bb6e3828..3bd4d6a4 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/content/action/ActionPane.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/content/action/ActionPane.java @@ -119,9 +119,9 @@ public class ActionPane implements TemplateComposer { } } - final Label labelSeparator = this.widgetFactory.labelSeparator(composite); - final GridData separatorLayout = new GridData(SWT.FILL, SWT.TOP, true, false); - labelSeparator.setLayoutData(separatorLayout); +// final Label labelSeparator = this.widgetFactory.labelSeparator(composite); +// final GridData separatorLayout = new GridData(SWT.FILL, SWT.TOP, true, false); +// labelSeparator.setLayoutData(separatorLayout); // title if (category.title != null) { @@ -140,7 +140,7 @@ public class ActionPane implements TemplateComposer { composite, SWT.SINGLE | SWT.FULL_SELECTION); actions.setData(RWT.CUSTOM_VARIANT, "actions"); - final GridData gridData = new GridData(SWT.FILL, SWT.FILL, true, true); + final GridData gridData = new GridData(SWT.FILL, SWT.TOP, true, false); actions.setLayoutData(gridData); final Template template = new Template(); final ImageCell imageCell = new ImageCell(template); diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/content/activity/PageStateDefinition.java b/src/main/java/ch/ethz/seb/sebserver/gui/content/activity/PageStateDefinition.java index 05864310..c4cf1a19 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/content/activity/PageStateDefinition.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/content/activity/PageStateDefinition.java @@ -10,6 +10,7 @@ package ch.ethz.seb.sebserver.gui.content.activity; import ch.ethz.seb.sebserver.gui.content.ExamForm; import ch.ethz.seb.sebserver.gui.content.ExamList; +import ch.ethz.seb.sebserver.gui.content.ExamSebConfigMapForm; import ch.ethz.seb.sebserver.gui.content.IndicatorForm; import ch.ethz.seb.sebserver.gui.content.InstitutionForm; import ch.ethz.seb.sebserver.gui.content.InstitutionList; @@ -49,6 +50,7 @@ public enum PageStateDefinition implements PageState { EXAM_LIST(Type.LIST_VIEW, ExamList.class, ActivityDefinition.EXAM), EXAM_VIEW(Type.FORM_VIEW, ExamForm.class, ActivityDefinition.EXAM), EXAM_EDIT(Type.FORM_EDIT, ExamForm.class, ActivityDefinition.EXAM), + EXAM_CONFIG_MAP_EDIT(Type.FORM_EDIT, ExamSebConfigMapForm.class, ActivityDefinition.EXAM), INDICATOR_EDIT(Type.FORM_EDIT, IndicatorForm.class, ActivityDefinition.EXAM), SEB_CLIENT_CONFIG_LIST(Type.LIST_VIEW, SebClientConfigList.class, ActivityDefinition.SEB_CLIENT_CONFIG), diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/form/Form.java b/src/main/java/ch/ethz/seb/sebserver/gui/form/Form.java index c76269f0..6a0c3113 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/form/Form.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/form/Form.java @@ -143,6 +143,24 @@ public final class Form implements FormBinding { this.formFields.add(name, createAccessor(label, imageUpload)); } + public String getFieldValue(final String attributeName) { + final FormFieldAccessor fieldAccessor = this.formFields.getFirst(attributeName); + if (fieldAccessor == null) { + return null; + } + + return fieldAccessor.getStringValue(); + } + + public void setFieldValue(final String attributeName, final String attributeValue) { + final FormFieldAccessor fieldAccessor = this.formFields.getFirst(attributeName); + if (fieldAccessor == null) { + return; + } + + fieldAccessor.setStringValue(attributeValue); + } + public void allVisible() { process( name -> true, @@ -208,11 +226,13 @@ public final class Form implements FormBinding { private FormFieldAccessor createAccessor(final Label label, final Label field) { return new FormFieldAccessor(label, field) { @Override public String getStringValue() { return null; } + @Override public void setStringValue(final String value) { field.setText( (value == null) ? StringUtils.EMPTY : value); } }; } private FormFieldAccessor createAccessor(final Label label, final Text text) { return new FormFieldAccessor(label, text) { @Override public String getStringValue() { return text.getText(); } + @Override public void setStringValue(final String value) { text.setText( (value == null) ? StringUtils.EMPTY : value); } }; } private FormFieldAccessor createAccessor(final Label label, final Selection selection) { @@ -234,6 +254,7 @@ public final class Form implements FormBinding { jsonValueAdapter, selection.type() != Type.SINGLE) { @Override public String getStringValue() { return selection.getSelectionValue(); } + @Override public void setStringValue(final String value) { selection.select(value); } }; } private FormFieldAccessor createAccessor(final Label label, final ThresholdList thresholdList) { @@ -356,6 +377,10 @@ public final class Form implements FormBinding { public abstract String getStringValue(); + public void setStringValue(final String value) { + throw new UnsupportedOperationException(); + } + public void setVisible(final boolean visible) { this.label.setVisible(visible); this.control.setVisible(visible); diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/form/FormBuilder.java b/src/main/java/ch/ethz/seb/sebserver/gui/form/FormBuilder.java index 143a7835..a98ce987 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/form/FormBuilder.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/form/FormBuilder.java @@ -256,7 +256,7 @@ public class FormBuilder { Label valueLabel(final Composite parent, final String value, final int hspan) { final Label label = new Label(parent, SWT.NONE); label.setText((StringUtils.isNoneBlank(value)) ? value : Constants.EMPTY_NOTE); - final GridData gridData = new GridData(SWT.LEFT, SWT.CENTER, true, false, hspan, 1); + final GridData gridData = new GridData(SWT.FILL, SWT.CENTER, true, false, hspan, 1); gridData.verticalIndent = 4; label.setLayoutData(gridData); return label; diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/service/ResourceService.java b/src/main/java/ch/ethz/seb/sebserver/gui/service/ResourceService.java index 54873000..e4186eb2 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/service/ResourceService.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/service/ResourceService.java @@ -24,17 +24,23 @@ import org.springframework.stereotype.Service; import ch.ethz.seb.sebserver.gbl.Constants; import ch.ethz.seb.sebserver.gbl.model.Entity; +import ch.ethz.seb.sebserver.gbl.model.EntityName; +import ch.ethz.seb.sebserver.gbl.model.exam.ExamConfigurationMap; import ch.ethz.seb.sebserver.gbl.model.exam.Exam.ExamType; import ch.ethz.seb.sebserver.gbl.model.exam.Indicator.IndicatorType; import ch.ethz.seb.sebserver.gbl.model.institution.LmsSetup.LmsType; +import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationNode; import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationNode.ConfigurationStatus; +import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationNode.ConfigurationType; import ch.ethz.seb.sebserver.gbl.model.user.UserInfo; import ch.ethz.seb.sebserver.gbl.model.user.UserRole; import ch.ethz.seb.sebserver.gbl.profile.GuiProfile; +import ch.ethz.seb.sebserver.gbl.util.Result; import ch.ethz.seb.sebserver.gbl.util.Tuple; 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.remote.webservice.api.RestService; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.GetExamConfigMappingNames; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.institution.GetInstitutionNames; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.lmssetup.GetLmsSetupNames; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.useraccount.GetUserAccountNames; @@ -105,6 +111,28 @@ public class ResourceService { .collect(Collectors.toList()); } + public List> examConfigurationSelectionResources() { + return getExamConfigurationSelection() + .getOr(Collections.emptyList()) + .stream() + .map(entityName -> new Tuple<>(entityName.modelId, entityName.name)) + .collect(Collectors.toList()); + } + +// public Function getExamConfigurationNameFunction() { +// final Map idNameMap = getExamConfigurationSelection() +// .getOr(Collections.emptyList()) +// .stream() +// .collect(Collectors.toMap(e -> e.modelId, e -> e.name)); +// +// return id -> { +// if (!idNameMap.containsKey(id)) { +// return Constants.EMPTY_NOTE; +// } +// return idNameMap.get(id); +// }; +// } + public List> userRoleResources() { return UserRole.publicRolesForUser(this.currentUser.get()) .stream() @@ -125,7 +153,6 @@ public class ResourceService { } public Function getInstitutionNameFunction() { - final Map idNameMap = this.restService.getBuilder(GetInstitutionNames.class) .withQueryParam(Entity.FILTER_ATTR_ACTIVE, Constants.TRUE_STRING) .call() @@ -248,4 +275,41 @@ public class ResourceService { : this.i18nSupport.getText(INACTIVE_TEXT_KEY); } + public String localizedExamConfigInstitutionName(final ConfigurationNode config) { + return getInstitutionNameFunction() + .apply(String.valueOf(config.institutionId)); + } + + public String localizedExamConfigStatusName(final ConfigurationNode config) { + if (config.status == null) { + return Constants.EMPTY_NOTE; + } + + return this.i18nSupport + .getText(ResourceService.EXAMCONFIG_STATUS_PREFIX + config.status.name()); + } + + public String localizedExamConfigStatusName(final ExamConfigurationMap config) { + if (config.configStatus == null) { + return Constants.EMPTY_NOTE; + } + + return this.i18nSupport + .getText(ResourceService.EXAMCONFIG_STATUS_PREFIX + config.configStatus.name()); + } + + private Result> getExamConfigurationSelection() { + return this.restService.getBuilder(GetExamConfigMappingNames.class) + .withQueryParam( + Entity.FILTER_ATTR_INSTITUTION, + String.valueOf(this.currentUser.get().institutionId)) + .withQueryParam( + ConfigurationNode.FILTER_ATTR_TYPE, + ConfigurationType.EXAM_CONFIG.name()) + .withQueryParam( + ConfigurationNode.FILTER_ATTR_STATUS, + ConfigurationStatus.READY_TO_USE.name()) + .call(); + } + } diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/service/examconfig/impl/TableFieldBuilder.java b/src/main/java/ch/ethz/seb/sebserver/gui/service/examconfig/impl/TableFieldBuilder.java index 54aa887a..54c8a433 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/service/examconfig/impl/TableFieldBuilder.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/service/examconfig/impl/TableFieldBuilder.java @@ -298,8 +298,8 @@ public class TableFieldBuilder implements InputFieldBuilder { item.setImage( cellIndex, (BooleanUtils.toBoolean((tableValue != null) ? tableValue.value : null)) - ? ImageIcon.ACTIVE.getImage(this.control.getDisplay()) - : ImageIcon.INACTIVE.getImage(this.control.getDisplay())); + ? ImageIcon.YES.getImage(this.control.getDisplay()) + : ImageIcon.NO.getImage(this.control.getDisplay())); } else { item.setText( cellIndex, diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/service/remote/webservice/api/exam/DeleteExamConfigMapping.java b/src/main/java/ch/ethz/seb/sebserver/gui/service/remote/webservice/api/exam/DeleteExamConfigMapping.java new file mode 100644 index 00000000..97087016 --- /dev/null +++ b/src/main/java/ch/ethz/seb/sebserver/gui/service/remote/webservice/api/exam/DeleteExamConfigMapping.java @@ -0,0 +1,39 @@ +/* + * 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.exam; + +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.EntityProcessingReport; +import ch.ethz.seb.sebserver.gbl.profile.GuiProfile; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestCall; + +@Lazy +@Component +@GuiProfile +public class DeleteExamConfigMapping extends RestCall { + + protected DeleteExamConfigMapping() { + super(new TypeKey<>( + CallType.DELETE, + EntityType.EXAM_CONFIGURATION_MAP, + new TypeReference() { + }), + HttpMethod.DELETE, + MediaType.APPLICATION_JSON_UTF8, + API.EXAM_CONFIGURATION_MAP_ENDPOINT + API.MODEL_ID_VAR_PATH_SEGMENT); + } +} diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/service/remote/webservice/api/exam/GetExamConfigMapping.java b/src/main/java/ch/ethz/seb/sebserver/gui/service/remote/webservice/api/exam/GetExamConfigMapping.java new file mode 100644 index 00000000..7172d8a5 --- /dev/null +++ b/src/main/java/ch/ethz/seb/sebserver/gui/service/remote/webservice/api/exam/GetExamConfigMapping.java @@ -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.exam; + +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.exam.ExamConfigurationMap; +import ch.ethz.seb.sebserver.gbl.profile.GuiProfile; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestCall; + +@Lazy +@Component +@GuiProfile +public class GetExamConfigMapping extends RestCall { + + protected GetExamConfigMapping() { + super(new TypeKey<>( + CallType.GET_SINGLE, + EntityType.EXAM_CONFIGURATION_MAP, + new TypeReference() { + }), + HttpMethod.GET, + MediaType.APPLICATION_FORM_URLENCODED, + API.EXAM_CONFIGURATION_MAP_ENDPOINT + API.MODEL_ID_VAR_PATH_SEGMENT); + } + +} diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/service/remote/webservice/api/exam/GetExamConfigMappingNames.java b/src/main/java/ch/ethz/seb/sebserver/gui/service/remote/webservice/api/exam/GetExamConfigMappingNames.java new file mode 100644 index 00000000..26f8647f --- /dev/null +++ b/src/main/java/ch/ethz/seb/sebserver/gui/service/remote/webservice/api/exam/GetExamConfigMappingNames.java @@ -0,0 +1,42 @@ +/* + * 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.exam; + +import java.util.List; + +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.EntityName; +import ch.ethz.seb.sebserver.gbl.profile.GuiProfile; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestCall; + +@Lazy +@Component +@GuiProfile +public class GetExamConfigMappingNames extends RestCall> { + + protected GetExamConfigMappingNames() { + super(new TypeKey<>( + CallType.GET_NAMES, + EntityType.CONFIGURATION_NODE, + new TypeReference>() { + }), + HttpMethod.GET, + MediaType.APPLICATION_FORM_URLENCODED, + API.CONFIGURATION_NODE_ENDPOINT + API.NAMES_PATH_SEGMENT); + } + +} diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/service/remote/webservice/api/exam/GetExamConfigMappingsPage.java b/src/main/java/ch/ethz/seb/sebserver/gui/service/remote/webservice/api/exam/GetExamConfigMappingsPage.java new file mode 100644 index 00000000..cd269c0b --- /dev/null +++ b/src/main/java/ch/ethz/seb/sebserver/gui/service/remote/webservice/api/exam/GetExamConfigMappingsPage.java @@ -0,0 +1,41 @@ +/* + * 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.exam; + +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.Page; +import ch.ethz.seb.sebserver.gbl.model.exam.ExamConfigurationMap; +import ch.ethz.seb.sebserver.gbl.profile.GuiProfile; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestCall; + +@Lazy +@Component +@GuiProfile +public class GetExamConfigMappingsPage extends RestCall> { + + protected GetExamConfigMappingsPage() { + super(new TypeKey<>( + CallType.GET_PAGE, + EntityType.EXAM_CONFIGURATION_MAP, + new TypeReference>() { + }), + HttpMethod.GET, + MediaType.APPLICATION_FORM_URLENCODED, + API.EXAM_CONFIGURATION_MAP_ENDPOINT); + } + +} diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/service/remote/webservice/api/exam/NewExamConfigMapping.java b/src/main/java/ch/ethz/seb/sebserver/gui/service/remote/webservice/api/exam/NewExamConfigMapping.java new file mode 100644 index 00000000..0056deda --- /dev/null +++ b/src/main/java/ch/ethz/seb/sebserver/gui/service/remote/webservice/api/exam/NewExamConfigMapping.java @@ -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.exam; + +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.exam.ExamConfigurationMap; +import ch.ethz.seb.sebserver.gbl.profile.GuiProfile; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestCall; + +@Lazy +@Component +@GuiProfile +public class NewExamConfigMapping extends RestCall { + + protected NewExamConfigMapping() { + super(new TypeKey<>( + CallType.NEW, + EntityType.EXAM_CONFIGURATION_MAP, + new TypeReference() { + }), + HttpMethod.POST, + MediaType.APPLICATION_FORM_URLENCODED, + API.EXAM_CONFIGURATION_MAP_ENDPOINT); + } + +} diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/service/remote/webservice/api/exam/SaveExamConfigMapping.java b/src/main/java/ch/ethz/seb/sebserver/gui/service/remote/webservice/api/exam/SaveExamConfigMapping.java new file mode 100644 index 00000000..ab5b21dc --- /dev/null +++ b/src/main/java/ch/ethz/seb/sebserver/gui/service/remote/webservice/api/exam/SaveExamConfigMapping.java @@ -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.exam; + +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.exam.ExamConfigurationMap; +import ch.ethz.seb.sebserver.gbl.profile.GuiProfile; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestCall; + +@Lazy +@Component +@GuiProfile +public class SaveExamConfigMapping extends RestCall { + + protected SaveExamConfigMapping() { + super(new TypeKey<>( + CallType.SAVE, + EntityType.EXAM_CONFIGURATION_MAP, + new TypeReference() { + }), + HttpMethod.PUT, + MediaType.APPLICATION_JSON_UTF8, + API.EXAM_CONFIGURATION_MAP_ENDPOINT); + } + +} diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/table/EntityTable.java b/src/main/java/ch/ethz/seb/sebserver/gui/table/EntityTable.java index f66b8920..863dc27d 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/table/EntityTable.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/table/EntityTable.java @@ -101,7 +101,6 @@ public class EntityTable { layout.verticalSpacing = 0; this.composite.setLayout(layout); GridData gridData = new GridData(SWT.FILL, SWT.TOP, true, false); - // gridData.heightHint = (pageSize + 2) * 40; this.composite.setLayoutData(gridData); // TODO just for debugging, remove when tested @@ -223,6 +222,19 @@ public class EntityTable { return getRowDataId(selection[0]); } + public ROW getFirstRowData() { + if (!this.hasAnyContent()) { + return null; + } + + final TableItem item = this.table.getItem(0); + if (item == null) { + return null; + } + + return getRowData(item); + } + public ROW getSelectedROWData() { final TableItem[] selection = this.table.getSelection(); if (selection == null || selection.length == 0) { @@ -296,9 +308,6 @@ public class EntityTable { // TODO error handling }); - final GridData gridData = (GridData) this.composite.getLayoutData(); - gridData.heightHint = (this.table.getItemCount() + 2) * 50; - this.composite.layout(true, true); } @@ -405,9 +414,9 @@ public class EntityTable { private static void addBooleanCell(final TableItem item, final int index, final Object value) { if ((Boolean) value) { - item.setImage(index, ImageIcon.ACTIVE.getImage(item.getDisplay())); + item.setImage(index, ImageIcon.YES.getImage(item.getDisplay())); } else { - item.setImage(index, ImageIcon.INACTIVE.getImage(item.getDisplay())); + item.setImage(index, ImageIcon.NO.getImage(item.getDisplay())); } } diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/widget/PopupListSelection.java b/src/main/java/ch/ethz/seb/sebserver/gui/widget/PopupListSelection.java new file mode 100644 index 00000000..36f75e97 --- /dev/null +++ b/src/main/java/ch/ethz/seb/sebserver/gui/widget/PopupListSelection.java @@ -0,0 +1,17 @@ +/* + * 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; + +public class PopupListSelection { + + public PopupListSelection() { + // TODO Auto-generated constructor stub + } + +} diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/widget/WidgetFactory.java b/src/main/java/ch/ethz/seb/sebserver/gui/widget/WidgetFactory.java index 58949702..3d5b300c 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/widget/WidgetFactory.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/widget/WidgetFactory.java @@ -68,6 +68,7 @@ public class WidgetFactory { ADD_BOX("add_box.png"), REMOVE_BOX("remove_box.png"), EDIT("edit.png"), + EDIT_SETTINGS("settings.png"), TEST("test.png"), IMPORT("import.png"), CANCEL("cancel.png"), @@ -75,10 +76,13 @@ public class WidgetFactory { SHOW("show.png"), ACTIVE("active.png"), INACTIVE("inactive.png"), + YES("yes.png"), + NO("no.png"), SAVE("save.png"), NEW("new.png"), DELETE("delete.png"), SEARCH("lens.png"), + UNDO("undo.png"), COLOR("color.png"); private String fileName; diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/ExamConfigurationMapDAO.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/ExamConfigurationMapDAO.java index 0f666e0d..a58ecaf5 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/ExamConfigurationMapDAO.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/ExamConfigurationMapDAO.java @@ -8,7 +8,7 @@ package ch.ethz.seb.sebserver.webservice.servicelayer.dao; -import ch.ethz.seb.sebserver.gbl.model.sebconfig.ExamConfigurationMap; +import ch.ethz.seb.sebserver.gbl.model.exam.ExamConfigurationMap; import ch.ethz.seb.sebserver.webservice.servicelayer.bulkaction.BulkActionSupportDAO; public interface ExamConfigurationMapDAO extends diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/FilterMap.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/FilterMap.java index c5f50409..5a11668a 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/FilterMap.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/FilterMap.java @@ -15,6 +15,7 @@ import org.springframework.util.MultiValueMap; import ch.ethz.seb.sebserver.gbl.api.POSTMapper; import ch.ethz.seb.sebserver.gbl.model.Entity; import ch.ethz.seb.sebserver.gbl.model.exam.Exam; +import ch.ethz.seb.sebserver.gbl.model.exam.ExamConfigurationMap; import ch.ethz.seb.sebserver.gbl.model.exam.Indicator; import ch.ethz.seb.sebserver.gbl.model.exam.QuizData; import ch.ethz.seb.sebserver.gbl.model.institution.LmsSetup; @@ -22,7 +23,6 @@ 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.ConfigurationNode; import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationValue; -import ch.ethz.seb.sebserver.gbl.model.sebconfig.ExamConfigurationMap; import ch.ethz.seb.sebserver.gbl.model.sebconfig.Orientation; import ch.ethz.seb.sebserver.gbl.model.user.UserInfo; import ch.ethz.seb.sebserver.gbl.util.Utils; @@ -100,7 +100,7 @@ public class FilterMap extends POSTMapper { } public Long getIndicatorExamId() { - return getLong(Indicator.FILTER_ATTR_EXAM); + return getLong(Indicator.FILTER_ATTR_EXAM_ID); } public String getIndicatorName() { diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/ConfigurationNodeDAOImpl.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/ConfigurationNodeDAOImpl.java index d3e6159d..66185e86 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/ConfigurationNodeDAOImpl.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/ConfigurationNodeDAOImpl.java @@ -213,10 +213,10 @@ public class ConfigurationNodeDAOImpl implements ConfigurationNodeDAO { data.id, null, null, + null, data.name, data.description, null, - null, (data.status != null) ? data.status.name() : ConfigurationStatus.CONSTRUCTION.name()); this.configurationNodeRecordMapper.updateByPrimaryKeySelective(newRecord); diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/ExamConfigurationMapDAOImpl.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/ExamConfigurationMapDAOImpl.java index 8bdc1781..71934c09 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/ExamConfigurationMapDAOImpl.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/ExamConfigurationMapDAOImpl.java @@ -19,6 +19,7 @@ import java.util.function.Function; import java.util.function.Predicate; import java.util.stream.Collectors; +import org.apache.commons.lang3.StringUtils; import org.mybatis.dynamic.sql.SqlBuilder; import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Component; @@ -29,7 +30,8 @@ import ch.ethz.seb.sebserver.gbl.api.APIMessage.APIMessageException; import ch.ethz.seb.sebserver.gbl.api.APIMessage.ErrorMessage; import ch.ethz.seb.sebserver.gbl.api.EntityType; import ch.ethz.seb.sebserver.gbl.model.EntityKey; -import ch.ethz.seb.sebserver.gbl.model.sebconfig.ExamConfigurationMap; +import ch.ethz.seb.sebserver.gbl.model.exam.ExamConfigurationMap; +import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationNode.ConfigurationStatus; import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile; import ch.ethz.seb.sebserver.gbl.util.Result; import ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.ConfigurationNodeRecordMapper; @@ -79,7 +81,7 @@ public class ExamConfigurationMapDAOImpl implements ExamConfigurationMapDAO { @Transactional(readOnly = true) public Result byPK(final Long id) { return recordById(id) - .flatMap(ExamConfigurationMapDAOImpl::toDomainModel); + .flatMap(this::toDomainModel); } @Override @@ -91,7 +93,7 @@ public class ExamConfigurationMapDAOImpl implements ExamConfigurationMapDAO { .build() .execute() .stream() - .map(ExamConfigurationMapDAOImpl::toDomainModel) + .map(this::toDomainModel) .flatMap(DAOLoggingSupport::logAndSkipOnError) .collect(Collectors.toList()); }); @@ -117,7 +119,7 @@ public class ExamConfigurationMapDAOImpl implements ExamConfigurationMapDAO { .build() .execute() .stream() - .map(ExamConfigurationMapDAOImpl::toDomainModel) + .map(this::toDomainModel) .flatMap(DAOLoggingSupport::logAndSkipOnError) .filter(predicate) .collect(Collectors.toList())); @@ -139,7 +141,7 @@ public class ExamConfigurationMapDAOImpl implements ExamConfigurationMapDAO { this.examConfigurationMapRecordMapper.insert(newRecord); return newRecord; }) - .flatMap(ExamConfigurationMapDAOImpl::toDomainModel) + .flatMap(this::toDomainModel) .onError(TransactionHandler::rollback); } @@ -159,7 +161,7 @@ public class ExamConfigurationMapDAOImpl implements ExamConfigurationMapDAO { this.examConfigurationMapRecordMapper.updateByPrimaryKeySelective(newRecord); return this.examConfigurationMapRecordMapper.selectByPrimaryKey(data.id); }) - .flatMap(ExamConfigurationMapDAOImpl::toDomainModel) + .flatMap(this::toDomainModel) .onError(TransactionHandler::rollback); } @@ -223,15 +225,25 @@ public class ExamConfigurationMapDAOImpl implements ExamConfigurationMapDAO { }); } - private static Result toDomainModel(final ExamConfigurationMapRecord record) { - return Result.tryCatch(() -> new ExamConfigurationMap( - record.getId(), - record.getInstitutionId(), - record.getExamId(), - record.getConfigurationNodeId(), - record.getUserNames(), - null, - null)); + private Result toDomainModel(final ExamConfigurationMapRecord record) { + return Result.tryCatch(() -> { + + final ConfigurationNodeRecord selectByPrimaryKey = this.configurationNodeRecordMapper + .selectByPrimaryKey(record.getConfigurationNodeId()); + final String status = selectByPrimaryKey.getStatus(); + + return new ExamConfigurationMap( + record.getId(), + record.getInstitutionId(), + record.getExamId(), + record.getConfigurationNodeId(), + record.getUserNames(), + null, + null, + selectByPrimaryKey.getName(), + selectByPrimaryKey.getDescription(), + (StringUtils.isNotBlank(status)) ? ConfigurationStatus.valueOf(status) : null); + }); } private Result checkMappingIntegrity(final ExamConfigurationMap data) { diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/EntityController.java b/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/EntityController.java index bb9055d9..03ff29be 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/EntityController.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/EntityController.java @@ -30,9 +30,9 @@ import org.springframework.web.bind.annotation.RequestParam; import ch.ethz.seb.sebserver.gbl.Constants; import ch.ethz.seb.sebserver.gbl.api.API; import ch.ethz.seb.sebserver.gbl.api.API.BulkActionType; -import ch.ethz.seb.sebserver.gbl.api.authorization.PrivilegeType; import ch.ethz.seb.sebserver.gbl.api.EntityType; import ch.ethz.seb.sebserver.gbl.api.POSTMapper; +import ch.ethz.seb.sebserver.gbl.api.authorization.PrivilegeType; import ch.ethz.seb.sebserver.gbl.model.Entity; import ch.ethz.seb.sebserver.gbl.model.EntityKey; import ch.ethz.seb.sebserver.gbl.model.EntityName; diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/ExamConfigurationMappingController.java b/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/ExamConfigurationMappingController.java new file mode 100644 index 00000000..7d637bb5 --- /dev/null +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/ExamConfigurationMappingController.java @@ -0,0 +1,121 @@ +/* + * 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.webservice.weblayer.api; + +import org.mybatis.dynamic.sql.SqlTable; +import org.springframework.validation.FieldError; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import ch.ethz.seb.sebserver.gbl.api.API; +import ch.ethz.seb.sebserver.gbl.api.APIMessage; +import ch.ethz.seb.sebserver.gbl.api.APIMessage.APIMessageException; +import ch.ethz.seb.sebserver.gbl.api.EntityType; +import ch.ethz.seb.sebserver.gbl.api.POSTMapper; +import ch.ethz.seb.sebserver.gbl.model.Domain; +import ch.ethz.seb.sebserver.gbl.model.GrantEntity; +import ch.ethz.seb.sebserver.gbl.model.exam.ExamConfigurationMap; +import ch.ethz.seb.sebserver.gbl.model.user.PasswordChange; +import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile; +import ch.ethz.seb.sebserver.gbl.util.Result; +import ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.ExamConfigurationMapRecordDynamicSqlSupport; +import ch.ethz.seb.sebserver.webservice.servicelayer.PaginationService; +import ch.ethz.seb.sebserver.webservice.servicelayer.authorization.AuthorizationService; +import ch.ethz.seb.sebserver.webservice.servicelayer.bulkaction.BulkActionService; +import ch.ethz.seb.sebserver.webservice.servicelayer.dao.EntityDAO; +import ch.ethz.seb.sebserver.webservice.servicelayer.dao.ExamDAO; +import ch.ethz.seb.sebserver.webservice.servicelayer.dao.UserActivityLogDAO; +import ch.ethz.seb.sebserver.webservice.servicelayer.validation.BeanValidationService; + +@WebServiceProfile +@RestController +@RequestMapping("/${sebserver.webservice.api.admin.endpoint}" + API.EXAM_CONFIGURATION_MAP_ENDPOINT) +public class ExamConfigurationMappingController extends EntityController { + + private final ExamDAO examDao; + + protected ExamConfigurationMappingController( + final AuthorizationService authorization, + final BulkActionService bulkActionService, + final EntityDAO entityDAO, + final UserActivityLogDAO userActivityLogDAO, + final PaginationService paginationService, + final BeanValidationService beanValidationService, + final ExamDAO examDao) { + + super( + authorization, + bulkActionService, + entityDAO, + userActivityLogDAO, + paginationService, + beanValidationService); + + this.examDao = examDao; + } + + @Override + protected ExamConfigurationMap createNew(final POSTMapper postParams) { + final Long institutionId = postParams.getLong(API.PARAM_INSTITUTION_ID); + return new ExamConfigurationMap(institutionId, postParams); + } + + @Override + protected SqlTable getSQLTableOfEntity() { + return ExamConfigurationMapRecordDynamicSqlSupport.examConfigurationMapRecord; + } + + @Override + protected EntityType getGrantEntityType() { + return EntityType.EXAM; + } + + @Override + protected GrantEntity toGrantEntity(final ExamConfigurationMap entity) { + if (entity == null) { + return null; + } + + return this.examDao + .byPK(entity.examId) + .getOrThrow(); + } + + @Override + protected Result checkCreateAccess(final ExamConfigurationMap entity) { + final GrantEntity grantEntity = toGrantEntity(entity); + this.authorization.checkWrite(grantEntity); + return Result.of(entity); + } + + @Override + protected Result validForCreate(final ExamConfigurationMap entity) { + return super.validForCreate(entity) + .map(this::checkPasswordMatch); + } + + @Override + protected Result validForSave(final ExamConfigurationMap entity) { + return super.validForSave(entity) + .map(this::checkPasswordMatch); + } + + private ExamConfigurationMap checkPasswordMatch(final ExamConfigurationMap entity) { + if (entity.hasEncryptionSecret() && !entity.encryptSecret.equals(entity.confirmEncryptSecret)) { + throw new APIMessageException(APIMessage.fieldValidationError( + new FieldError( + Domain.EXAM_CONFIGURATION_MAP.TYPE_NAME, + PasswordChange.ATTR_NAME_PASSWORD, + "examConfigMapping:confirm_encrypt_secret:password.mismatch"))); + } + + return entity; + } + +} diff --git a/src/main/resources/messages.properties b/src/main/resources/messages.properties index 6732e023..881c8579 100644 --- a/src/main/resources/messages.properties +++ b/src/main/resources/messages.properties @@ -272,7 +272,27 @@ sebserver.exam.status.UP_COMING=Up Coming sebserver.exam.status.RUNNING=Running sebserver.exam.status.FINISHED=Finished -sebserver.exam.indicator.list.actions=Selected Indicator +sebserver.exam.configuration.list.actions=SEB Configuration +sebserver.exam.configuration.list.title=SEB Configuration +sebserver.exam.configuration.list.column.name=Name +sebserver.exam.configuration.list.column.description=Description +sebserver.exam.configuration.list.column.status=Status +sebserver.exam.configuration.list.empty=There is currently no SEB Configuration defined for this Exam. Please add one + +sebserver.exam.configuration.action.list.new=Add +sebserver.exam.configuration.action.list.modify=Edit +sebserver.exam.configuration.action.list.delete=Delete +sebserver.exam.configuration.action.save=Save + +sebserver.exam.configuration.form.title.new=Add SEB Configuration Mapping +sebserver.exam.configuration.form.title=SEB Configuration Mapping +sebserver.exam.configuration.form.name=SEB Configuration +sebserver.exam.configuration.form.encryptSecret=Encryption Password +sebserver.exam.configuration.form.description=Description +sebserver.exam.configuration.form.status=Status +sebserver.exam.configuration.form.encryptSecret.confirm=Confirm Password + +sebserver.exam.indicator.list.actions=Indicator sebserver.exam.indicator.list.title=Indicators sebserver.exam.indicator.list.column.type=Type sebserver.exam.indicator.list.column.name=Name @@ -285,9 +305,9 @@ sebserver.exam.indicator.type.ERROR_COUNT=Error Count sebserver.exam.indicator.info.pleaseSelect=Please Select an Indicator first -sebserver.exam.indicator.action.list.new=New Indicator -sebserver.exam.indicator.action.list.modify=Edit -sebserver.exam.indicator.action.list.delete=Delete +sebserver.exam.indicator.action.list.new=New +sebserver.exam.indicator.action.list.modify=Edit Selected +sebserver.exam.indicator.action.list.delete=Delete Selected sebserver.exam.indicator.action.save=Save sebserver.exam.indicator.form.title=Indicator diff --git a/src/main/resources/messages_en.properties b/src/main/resources/messages_en.properties index 37e89037..a0553633 100644 --- a/src/main/resources/messages_en.properties +++ b/src/main/resources/messages_en.properties @@ -40,7 +40,6 @@ sebserver.error.unexpected=Unexpected Error sebserver.page.message=Information sebserver.dialog.confirm.title=Confirmation -sebserver.dialog.confirm.deactivation=Note that there are {0} other entities that belong to this entity.
These will also be deactivated by deactivating this entity.

Are You sure you want to deactivate this entity? sebserver.dialog.confirm.deactivation.noDependencies=Are You sure you want to deactivate? ################################ @@ -266,7 +265,7 @@ sebserver.exam.type.MANAGED=Managed Devices sebserver.exam.type.BYOD=Bring Your Own Device sebserver.exam.type.VDI=VDI (Virtual Desktop Infrastructure) -sebserver.exam.indicator.list.actions=Selected Indicator +sebserver.exam.indicator.list.actions=Indicator sebserver.exam.indicator.list.title=Indicators sebserver.exam.indicator.list.column.type=Type sebserver.exam.indicator.list.column.name=Name @@ -279,9 +278,9 @@ sebserver.exam.indicator.type.ERROR_COUNT=Error Count sebserver.exam.indicator.info.pleaseSelect=Please select an indicator first -sebserver.exam.indicator.action.list.new=New Indicator -sebserver.exam.indicator.action.list.modify=Edit -sebserver.exam.indicator.action.list.delete=Delete +sebserver.exam.indicator.action.list.new=New +sebserver.exam.indicator.action.list.modify=Edit Selected +sebserver.exam.indicator.action.list.delete=Delete Selected sebserver.exam.indicator.action.save=Save sebserver.exam.indicator.form.title=Indicator diff --git a/src/main/resources/static/images/no.png b/src/main/resources/static/images/no.png new file mode 100644 index 00000000..3ddfaf8a Binary files /dev/null and b/src/main/resources/static/images/no.png differ diff --git a/src/main/resources/static/images/settings.png b/src/main/resources/static/images/settings.png new file mode 100644 index 00000000..39c472f4 Binary files /dev/null and b/src/main/resources/static/images/settings.png differ diff --git a/src/main/resources/static/images/undo.png b/src/main/resources/static/images/undo.png new file mode 100644 index 00000000..fac790df Binary files /dev/null and b/src/main/resources/static/images/undo.png differ diff --git a/src/main/resources/static/images/yes.png b/src/main/resources/static/images/yes.png new file mode 100644 index 00000000..dc1dc063 Binary files /dev/null and b/src/main/resources/static/images/yes.png differ