SEBSERV-44 SEBSERV-45 export of plain XML exam config implemented
This commit is contained in:
parent
f95485fb7d
commit
bbb15bba40
50 changed files with 1176 additions and 328 deletions
|
@ -11,6 +11,8 @@ package ch.ethz.seb.sebserver.gbl;
|
|||
import org.joda.time.format.DateTimeFormat;
|
||||
import org.joda.time.format.DateTimeFormatter;
|
||||
|
||||
import ch.ethz.seb.sebserver.gbl.util.Utils;
|
||||
|
||||
/** Global Constants used in SEB Server web-service as well as in web-gui component */
|
||||
public final class Constants {
|
||||
|
||||
|
@ -41,17 +43,24 @@ public final class Constants {
|
|||
.forPattern(DEFAULT_DATE_TIME_FORMAT)
|
||||
.withZoneUTC();
|
||||
|
||||
// /** Date-Time formatter without milliseconds using UTC time-zone. Pattern is yyyy-MM-dd HH:mm:ss */
|
||||
// // TODO check if this works with DEFAULT_DATE_TIME_FORMAT
|
||||
// @Deprecated
|
||||
// public static final DateTimeFormatter DATE_TIME_PATTERN_UTC_NO_MILLIS = DateTimeFormat
|
||||
// .forPattern("yyyy-MM-dd HH:mm:ss")
|
||||
// .withZoneUTC();
|
||||
//
|
||||
// /** Date-Time formatter with milliseconds using UTC time-zone. Pattern is yyyy-MM-dd HH:mm:ss.S */
|
||||
// @Deprecated
|
||||
// public static final DateTimeFormatter DATE_TIME_PATTERN_UTC_MILLIS = DateTimeFormat
|
||||
// .forPattern("yyyy-MM-dd HH:mm:ss.S")
|
||||
// .withZoneUTC();
|
||||
public static final String XML_VERSION_HEADER =
|
||||
"<?xml version=\"1.0\" encoding=\"utf-8\"?>";
|
||||
public static final String XML_DOCTYPE_HEADER =
|
||||
"<!DOCTYPE plist PUBLIC \"-//Apple Computer//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">";
|
||||
public static final String XML_PLIST_START_V1 =
|
||||
"<plist version=\"1.0\">";
|
||||
public static final String XML_PLIST_END =
|
||||
"</plist>";
|
||||
public static final String XML_DICT_START =
|
||||
"<dict>";
|
||||
public static final String XML_DICT_END =
|
||||
"</dict>";
|
||||
|
||||
public static final byte[] XML_VERSION_HEADER_UTF_8 = Utils.toByteArray(XML_VERSION_HEADER);
|
||||
public static final byte[] XML_DOCTYPE_HEADER_UTF_8 = Utils.toByteArray(XML_DOCTYPE_HEADER);
|
||||
public static final byte[] XML_PLIST_START_V1_UTF_8 = Utils.toByteArray(XML_PLIST_START_V1);
|
||||
public static final byte[] XML_PLIST_END_UTF_8 = Utils.toByteArray(XML_PLIST_END);
|
||||
public static final byte[] XML_DICT_START_UTF_8 = Utils.toByteArray(XML_DICT_START);
|
||||
public static final byte[] XML_DICT_END_UTF_8 = Utils.toByteArray(XML_DICT_END);
|
||||
|
||||
}
|
||||
|
|
|
@ -69,16 +69,12 @@ public final class API {
|
|||
public static final String CONFIGURATION_SAVE_TO_HISTORY_PATH_SEGMENT = "/save_to_history";
|
||||
public static final String CONFIGURATION_UNDO_PATH_SEGMENT = "/undo";
|
||||
public static final String CONFIGURATION_RESTORE_FROM_HISTORY_PATH_SEGMENT = "/restore";
|
||||
|
||||
public static final String CONFIGURATION_VALUE_ENDPOINT = "/configuration_value";
|
||||
public static final String CONFIGURATION_TABLE_VALUE_PATH_SEGMENT = "/table";
|
||||
public static final String CONFIGURATION_TABLE_ROW_VALUE_PATH_SEGMENT =
|
||||
CONFIGURATION_TABLE_VALUE_PATH_SEGMENT + "/row";
|
||||
|
||||
public static final String CONFIGURATION_ATTRIBUTE_ENDPOINT = "/configuration_attribute";
|
||||
public static final String CONFIGURATION_PLAIN_XML_DOWNLOAD_PATH_SEGMENT = "/downloadxml";
|
||||
|
||||
public static final String ORIENTATION_ENDPOINT = "/orientation";
|
||||
|
||||
public static final String VIEW_ENDPOINT = ORIENTATION_ENDPOINT + "/view";
|
||||
|
||||
public static final String EXAM_CONFIGURATION_MAP_ENDPOINT = "/exam_configuration_map";
|
||||
|
|
|
@ -32,7 +32,7 @@ import ch.ethz.seb.sebserver.gbl.model.Domain.CONFIGURATION_ATTRIBUTE;
|
|||
import ch.ethz.seb.sebserver.gbl.model.Entity;
|
||||
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
public final class ConfigurationAttribute implements Entity {
|
||||
public final class ConfigurationAttribute implements Entity, Comparable<ConfigurationAttribute> {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(ConfigurationAttribute.class);
|
||||
|
||||
|
@ -160,6 +160,12 @@ public final class ConfigurationAttribute implements Entity {
|
|||
return this.defaultValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(final ConfigurationAttribute attribute) {
|
||||
// TODO check if this is correct in reference to https://www.safeexambrowser.org/developer/seb-config-key.html
|
||||
return this.name.compareToIgnoreCase(attribute.name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
final StringBuilder builder = new StringBuilder();
|
||||
|
|
|
@ -13,6 +13,7 @@ import org.eclipse.rap.rwt.client.service.UrlLauncher;
|
|||
import org.eclipse.swt.widgets.Composite;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.context.annotation.Lazy;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
|
@ -31,6 +32,7 @@ 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.page.impl.PageUtils;
|
||||
import ch.ethz.seb.sebserver.gui.service.remote.DownloadService;
|
||||
import ch.ethz.seb.sebserver.gui.service.remote.SebClientConfigDownload;
|
||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestService;
|
||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.seb.clientconfig.ActivateClientConfig;
|
||||
|
@ -65,18 +67,21 @@ public class SebClientConfigForm implements TemplateComposer {
|
|||
private final PageService pageService;
|
||||
private final RestService restService;
|
||||
private final CurrentUser currentUser;
|
||||
private final SebClientConfigDownload sebClientConfigDownload;
|
||||
private final DownloadService downloadService;
|
||||
private final String downloadFileName;
|
||||
|
||||
protected SebClientConfigForm(
|
||||
final PageService pageService,
|
||||
final RestService restService,
|
||||
final CurrentUser currentUser,
|
||||
final SebClientConfigDownload sebClientConfigDownload) {
|
||||
final DownloadService downloadService,
|
||||
@Value("${sebserver.gui.seb.exam.config.download.filename}") final String downloadFileName) {
|
||||
|
||||
this.pageService = pageService;
|
||||
this.restService = restService;
|
||||
this.currentUser = currentUser;
|
||||
this.sebClientConfigDownload = sebClientConfigDownload;
|
||||
this.downloadService = downloadService;
|
||||
this.downloadFileName = downloadFileName;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -169,8 +174,10 @@ public class SebClientConfigForm implements TemplateComposer {
|
|||
.newAction(ActionDefinition.SEB_CLIENT_CONFIG_EXPORT)
|
||||
.withEntityKey(entityKey)
|
||||
.withExec(action -> {
|
||||
final String downloadURL = this.sebClientConfigDownload.downloadSEBClientConfigURL(
|
||||
entityKey.modelId);
|
||||
final String downloadURL = this.downloadService.createDownloadURL(
|
||||
entityKey.modelId,
|
||||
SebClientConfigDownload.class,
|
||||
this.downloadFileName);
|
||||
urlLauncher.openURL(downloadURL);
|
||||
return action;
|
||||
})
|
||||
|
|
|
@ -67,7 +67,9 @@ public class SebClientConfigList implements TemplateComposer {
|
|||
new TableFilterAttribute(
|
||||
CriteriaType.DATE,
|
||||
SebClientConfig.FILTER_ATTR_CREATION_DATE,
|
||||
DateTime.now(DateTimeZone.UTC).minusYears(1).toString(Constants.DEFAULT_DATE_TIME_FORMAT));
|
||||
DateTime.now(DateTimeZone.UTC)
|
||||
.minusYears(1)
|
||||
.toString(Constants.DEFAULT_DATE_TIME_FORMAT));
|
||||
|
||||
private final PageService pageService;
|
||||
private final RestService restService;
|
||||
|
|
|
@ -8,9 +8,12 @@
|
|||
|
||||
package ch.ethz.seb.sebserver.gui.content;
|
||||
|
||||
import org.eclipse.rap.rwt.RWT;
|
||||
import org.eclipse.rap.rwt.client.service.UrlLauncher;
|
||||
import org.eclipse.swt.widgets.Composite;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.context.annotation.Lazy;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
|
@ -29,6 +32,8 @@ 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.DownloadService;
|
||||
import ch.ethz.seb.sebserver.gui.service.remote.SebExamConfigDownload;
|
||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestService;
|
||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.seb.examconfig.GetExamConfigNode;
|
||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.seb.examconfig.NewExamConfig;
|
||||
|
@ -58,15 +63,21 @@ public class SebExamConfigPropForm implements TemplateComposer {
|
|||
private final PageService pageService;
|
||||
private final RestService restService;
|
||||
private final CurrentUser currentUser;
|
||||
private final DownloadService downloadService;
|
||||
private final String downloadFileName;
|
||||
|
||||
protected SebExamConfigPropForm(
|
||||
final PageService pageService,
|
||||
final RestService restService,
|
||||
final CurrentUser currentUser) {
|
||||
final CurrentUser currentUser,
|
||||
final DownloadService downloadService,
|
||||
@Value("${sebserver.gui.seb.exam.config.download.filename}") final String downloadFileName) {
|
||||
|
||||
this.pageService = pageService;
|
||||
this.restService = restService;
|
||||
this.currentUser = currentUser;
|
||||
this.downloadService = downloadService;
|
||||
this.downloadFileName = downloadFileName;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -99,6 +110,7 @@ public class SebExamConfigPropForm implements TemplateComposer {
|
|||
}
|
||||
|
||||
final EntityGrantCheck entityGrant = this.currentUser.entityGrantCheck(examConfig);
|
||||
final boolean readGrant = entityGrant.r();
|
||||
final boolean writeGrant = entityGrant.w();
|
||||
final boolean modifyGrant = entityGrant.m();
|
||||
final boolean isReadonly = pageContext.isReadonly();
|
||||
|
@ -145,6 +157,7 @@ public class SebExamConfigPropForm implements TemplateComposer {
|
|||
? this.restService.getRestCall(NewExamConfig.class)
|
||||
: this.restService.getRestCall(SaveExamConfig.class));
|
||||
|
||||
final UrlLauncher urlLauncher = RWT.getClient().getService(UrlLauncher.class);
|
||||
this.pageService.pageActionBuilder(formContext.clearEntityKeys())
|
||||
|
||||
.newAction(ActionDefinition.SEB_EXAM_CONFIG_NEW)
|
||||
|
@ -158,6 +171,18 @@ public class SebExamConfigPropForm implements TemplateComposer {
|
|||
.withEntityKey(entityKey)
|
||||
.publishIf(() -> modifyGrant && isReadonly)
|
||||
|
||||
.newAction(ActionDefinition.SEB_EXAM_CONFIG_EXPORT_PLAIN_XML)
|
||||
.withEntityKey(entityKey)
|
||||
.withExec(action -> {
|
||||
final String downloadURL = this.downloadService.createDownloadURL(
|
||||
entityKey.modelId,
|
||||
SebExamConfigDownload.class,
|
||||
this.downloadFileName);
|
||||
urlLauncher.openURL(downloadURL);
|
||||
return action;
|
||||
})
|
||||
.publishIf(() -> readGrant && isReadonly)
|
||||
|
||||
.newAction(ActionDefinition.SEB_EXAM_CONFIG_SAVE)
|
||||
.withEntityKey(entityKey)
|
||||
.withExec(formHandle::processFormSave)
|
||||
|
|
|
@ -324,7 +324,7 @@ public enum ActionDefinition {
|
|||
ActionCategory.FORM),
|
||||
SEB_CLIENT_CONFIG_EXPORT(
|
||||
new LocTextKey("sebserver.clientconfig.action.export"),
|
||||
ImageIcon.SAVE,
|
||||
ImageIcon.EXPORT,
|
||||
PageStateDefinition.SEB_CLIENT_CONFIG_VIEW,
|
||||
ActionCategory.FORM),
|
||||
|
||||
|
@ -365,6 +365,11 @@ public enum ActionDefinition {
|
|||
ImageIcon.SAVE,
|
||||
PageStateDefinition.SEB_EXAM_CONFIG_VIEW,
|
||||
ActionCategory.FORM),
|
||||
SEB_EXAM_CONFIG_EXPORT_PLAIN_XML(
|
||||
new LocTextKey("sebserver.examconfig.action.export.plainxml"),
|
||||
ImageIcon.EXPORT,
|
||||
PageStateDefinition.SEB_EXAM_CONFIG_VIEW,
|
||||
ActionCategory.FORM),
|
||||
|
||||
SEB_EXAM_CONFIG_MODIFY_FROM_LIST(
|
||||
new LocTextKey("sebserver.examconfig.action.list.modify"),
|
||||
|
|
|
@ -17,26 +17,65 @@ import ch.ethz.seb.sebserver.gbl.model.sebconfig.Orientation;
|
|||
/** Adapter interface for SEB Exam Configuration based input fields. */
|
||||
public interface InputField {
|
||||
|
||||
/** Get the underling ConfigurationAttribute of the InputField.
|
||||
*
|
||||
* @return the underling ConfigurationAttribute of the InputField. */
|
||||
ConfigurationAttribute getAttribute();
|
||||
|
||||
/** Get the underling Orientation of the InputField.
|
||||
*
|
||||
* @return the underling Orientation of the InputField. */
|
||||
Orientation getOrientation();
|
||||
|
||||
/** Initialize the field value from a collection of ConfigurationValue.
|
||||
* The input field searches the given values for a matching value of the input field regarding to its
|
||||
* ConfigurationAttribute and takes the first found.
|
||||
*
|
||||
* @param values collection of available ConfigurationValue
|
||||
* @return the ConfigurationValue that was used to initialize the field value */
|
||||
ConfigurationValue initValue(Collection<ConfigurationValue> values);
|
||||
|
||||
/** Initialize the field value directly by given value and list index.
|
||||
*
|
||||
* @param value the value to set as field value
|
||||
* @param listIndex the list index of the field */
|
||||
void initValue(final String value, final Integer listIndex);
|
||||
|
||||
/** Get the current field value.
|
||||
*
|
||||
* @return the current field value. */
|
||||
String getValue();
|
||||
|
||||
/** get the current human-readable field value.
|
||||
*
|
||||
* @return the current human-readable field value. */
|
||||
String getReadableValue();
|
||||
|
||||
/** Use this to show an error message below the input field.
|
||||
* This is only possible if the concrete input field has an error label, otherwise ignored
|
||||
*
|
||||
* @param errorMessage the error message to display below the input field */
|
||||
void showError(String errorMessage);
|
||||
|
||||
/** Indicated if the input field has an error on the currently set value.
|
||||
*
|
||||
* @return true if the input field has an error on the currently set value. */
|
||||
boolean hasError();
|
||||
|
||||
/** Use this to clear any error on the input field. */
|
||||
void clearError();
|
||||
|
||||
/** Use this to disable the input field.
|
||||
*
|
||||
* @param group indicates if instead of the field, the entire group of the field shall be disabled. */
|
||||
void disable(boolean group);
|
||||
|
||||
/** Use this to enable the input field.
|
||||
*
|
||||
* @param group indicates if instead of the field, the entire group of the field shall be enabled. */
|
||||
void enable(boolean group);
|
||||
|
||||
/** Use this to set/reset the default value of the underling ConfigurationAttribute to the field value. */
|
||||
void setDefaultValue();
|
||||
|
||||
}
|
||||
|
|
|
@ -89,13 +89,22 @@ public abstract class AbstractInputField<T extends Control> implements InputFiel
|
|||
this.errorLabel.setVisible(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasError() {
|
||||
if (this.errorLabel == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return this.errorLabel.isVisible();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearError() {
|
||||
if (this.errorLabel == null) {
|
||||
return;
|
||||
}
|
||||
this.errorLabel.setVisible(false);
|
||||
this.errorLabel.setText("rfbvgregre");
|
||||
this.errorLabel.setText("");
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -80,7 +80,6 @@ public abstract class AbstractTableFieldBuilder implements InputFieldBuilder {
|
|||
final TableContext tableContext = new TableContext(
|
||||
this.inputFieldBuilderSupplier,
|
||||
this.widgetFactory,
|
||||
this.restService,
|
||||
attribute,
|
||||
viewContext);
|
||||
return tableContext;
|
||||
|
@ -260,13 +259,7 @@ public abstract class AbstractTableFieldBuilder implements InputFieldBuilder {
|
|||
.forEach(i -> {
|
||||
final Map<Long, TableValue> rowValues = indexMapping.get(i);
|
||||
values.add(rowValues);
|
||||
// addTableRow(rowValues);
|
||||
});
|
||||
// for (int i = 0; i < rows.size(); i++) {
|
||||
// final Map<Long, TableValue> rowValues = indexMapping.get(i);
|
||||
// values.add(rowValues);
|
||||
// addTableRow(rowValues);
|
||||
// }
|
||||
}
|
||||
|
||||
protected void applyFormValues(
|
||||
|
|
|
@ -85,6 +85,11 @@ public class PassworFieldBuilder implements InputFieldBuilder {
|
|||
final String pwd = passwordInput.getText();
|
||||
final String confirm = confirmInput.getText();
|
||||
|
||||
if (passwordInputField.initValue != null && passwordInputField.initValue.equals(pwd)) {
|
||||
System.out.println("*********************************** ignore Password set");
|
||||
return;
|
||||
}
|
||||
|
||||
if (StringUtils.isBlank(pwd) && StringUtils.isBlank(confirm)) {
|
||||
return;
|
||||
}
|
||||
|
@ -132,7 +137,7 @@ public class PassworFieldBuilder implements InputFieldBuilder {
|
|||
@Override
|
||||
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) {
|
||||
if (value != null) {
|
||||
this.control.setText(value);
|
||||
this.confirm.setText(value);
|
||||
}
|
||||
|
|
|
@ -8,10 +8,8 @@
|
|||
|
||||
package ch.ethz.seb.sebserver.gui.service.examconfig.impl;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
@ -20,16 +18,12 @@ import org.apache.commons.lang3.StringUtils;
|
|||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import ch.ethz.seb.sebserver.gbl.model.Domain;
|
||||
import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationAttribute;
|
||||
import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationTableValues.TableValue;
|
||||
import ch.ethz.seb.sebserver.gbl.model.sebconfig.Orientation;
|
||||
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.ValueChangeListener;
|
||||
import ch.ethz.seb.sebserver.gui.service.i18n.I18nSupport;
|
||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestService;
|
||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.seb.examconfig.GetExamConfigTableRowValues;
|
||||
import ch.ethz.seb.sebserver.gui.widget.WidgetFactory;
|
||||
|
||||
public class TableContext {
|
||||
|
@ -38,7 +32,6 @@ public class TableContext {
|
|||
|
||||
private final InputFieldBuilderSupplier inputFieldBuilderSupplier;
|
||||
private final WidgetFactory widgetFactory;
|
||||
private final RestService restService;
|
||||
|
||||
public final ConfigurationAttribute attribute;
|
||||
public final Orientation orientation;
|
||||
|
@ -50,13 +43,11 @@ public class TableContext {
|
|||
public TableContext(
|
||||
final InputFieldBuilderSupplier inputFieldBuilderSupplier,
|
||||
final WidgetFactory widgetFactory,
|
||||
final RestService restService,
|
||||
final ConfigurationAttribute attribute,
|
||||
final ViewContext viewContext) {
|
||||
|
||||
this.inputFieldBuilderSupplier = Objects.requireNonNull(inputFieldBuilderSupplier);
|
||||
this.widgetFactory = Objects.requireNonNull(widgetFactory);
|
||||
this.restService = Objects.requireNonNull(restService);
|
||||
this.attribute = Objects.requireNonNull(attribute);
|
||||
this.viewContext = Objects.requireNonNull(viewContext);
|
||||
|
||||
|
@ -152,27 +143,6 @@ public class TableContext {
|
|||
return this.inputFieldBuilderSupplier.getInputFieldBuilder(attribute2, orientation);
|
||||
}
|
||||
|
||||
public Map<Long, TableValue> getTableRowValues(final int index) {
|
||||
return this.restService.getBuilder(GetExamConfigTableRowValues.class)
|
||||
.withQueryParam(
|
||||
Domain.CONFIGURATION_VALUE.ATTR_CONFIGURATION_ATTRIBUTE_ID,
|
||||
this.attribute.getModelId())
|
||||
.withQueryParam(
|
||||
Domain.CONFIGURATION_VALUE.ATTR_CONFIGURATION_ID,
|
||||
String.valueOf(this.getConfigurationId()))
|
||||
.withQueryParam(
|
||||
Domain.CONFIGURATION_VALUE.ATTR_LIST_INDEX,
|
||||
String.valueOf(index))
|
||||
.call()
|
||||
.get(
|
||||
error -> log.error("Failed to get table row values: ", error),
|
||||
() -> Collections.emptyList())
|
||||
.stream()
|
||||
.collect(Collectors.toMap(
|
||||
val -> val.attributeId,
|
||||
val -> TableValue.of(val)));
|
||||
}
|
||||
|
||||
public void registerInputField(final InputField inputField) {
|
||||
this.viewContext.registerInputField(inputField);
|
||||
}
|
||||
|
|
|
@ -105,11 +105,15 @@ public class TableRowFormBuilder implements ModalInputDialogComposer<Map<Long, T
|
|||
});
|
||||
|
||||
return () -> inputFields.stream()
|
||||
.map(field -> new TableValue(
|
||||
field.getAttribute().id,
|
||||
this.listIndex,
|
||||
field.getValue()))
|
||||
.collect(Collectors.toMap(tv -> tv.attributeId, Function.identity()));
|
||||
.map(field -> (field.hasError())
|
||||
? this.rowValues.get(field.getAttribute().id)
|
||||
: new TableValue(
|
||||
field.getAttribute().id,
|
||||
this.listIndex,
|
||||
field.getValue()))
|
||||
.collect(Collectors.toMap(
|
||||
tv -> tv.attributeId,
|
||||
Function.identity()));
|
||||
}
|
||||
|
||||
private InputField createInputField(
|
||||
|
|
|
@ -0,0 +1,79 @@
|
|||
/*
|
||||
* 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;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.MediaType;
|
||||
|
||||
import ch.ethz.seb.sebserver.gbl.api.API;
|
||||
|
||||
public abstract class AbstractDownloadServiceHandler implements DownloadServiceHandler {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(AbstractDownloadServiceHandler.class);
|
||||
|
||||
@Override
|
||||
public void processDownload(final HttpServletRequest request, final HttpServletResponse response) {
|
||||
try {
|
||||
|
||||
final String downloadFileName = request.getParameter(DownloadService.DOWNLOAD_FILE_NAME);
|
||||
if (StringUtils.isBlank(downloadFileName)) {
|
||||
log.error(
|
||||
"Mandatory downloadFileName parameter not found within HttpServletRequest. Download request is ignored");
|
||||
return;
|
||||
}
|
||||
|
||||
log.debug("download requested... trying to get needed parameter from request");
|
||||
|
||||
final String configId = request.getParameter(API.PARAM_MODEL_ID);
|
||||
if (StringUtils.isBlank(configId)) {
|
||||
log.error(
|
||||
"Mandatory modelId parameter not found within HttpServletRequest. Download request is ignored");
|
||||
return;
|
||||
}
|
||||
|
||||
log.debug(
|
||||
"Found modelId: {} for {} download. Trying to request webservice...",
|
||||
configId,
|
||||
downloadFileName);
|
||||
|
||||
final byte[] configFile = webserviceCall(configId);
|
||||
|
||||
if (configFile == null) {
|
||||
log.error("No or empty download received from webservice. Download request is ignored");
|
||||
return;
|
||||
}
|
||||
|
||||
log.debug("Sucessfully downloaded from webservice. File size: {}", configFile.length);
|
||||
|
||||
response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE);
|
||||
response.setContentLength(configFile.length);
|
||||
response.setHeader(
|
||||
HttpHeaders.CONTENT_DISPOSITION,
|
||||
"attachment; filename=\"" + downloadFileName + "\"");
|
||||
|
||||
log.debug("Write the download data to response output");
|
||||
|
||||
response.getOutputStream().write(configFile);
|
||||
|
||||
} catch (final Exception e) {
|
||||
log.error(
|
||||
"Unexpected error while trying to start download. The download is ignored. Cause: ",
|
||||
e);
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract byte[] webserviceCall(String configId);
|
||||
|
||||
}
|
|
@ -19,12 +19,15 @@ import javax.servlet.http.HttpServletRequest;
|
|||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.eclipse.rap.rwt.RWT;
|
||||
import org.eclipse.rap.rwt.service.ServiceHandler;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.context.annotation.Lazy;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import ch.ethz.seb.sebserver.gbl.Constants;
|
||||
import ch.ethz.seb.sebserver.gbl.api.API;
|
||||
import ch.ethz.seb.sebserver.gbl.profile.GuiProfile;
|
||||
|
||||
@Lazy
|
||||
|
@ -36,13 +39,16 @@ public class DownloadService implements ServiceHandler {
|
|||
|
||||
public static final String DOWNLOAD_SERVICE_NAME = "DOWNLOAD_SERVICE";
|
||||
public static final String HANDLER_NAME_PARAMETER = "download-handler-name";
|
||||
public static final String DOWNLOAD_FILE_NAME = "download-file-name";
|
||||
|
||||
private final Map<String, DownloadServiceHandler> handler;
|
||||
|
||||
protected DownloadService(final Collection<DownloadServiceHandler> handler) {
|
||||
this.handler = handler
|
||||
.stream()
|
||||
.collect(Collectors.toMap(h -> h.getFileName(), Function.identity()));
|
||||
.collect(Collectors.toMap(
|
||||
h -> h.getClass().getSimpleName(),
|
||||
Function.identity()));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -70,4 +76,27 @@ public class DownloadService implements ServiceHandler {
|
|||
.processDownload(request, response);
|
||||
}
|
||||
|
||||
public String createDownloadURL(
|
||||
final String modelId,
|
||||
final Class<? extends DownloadServiceHandler> handlerClass,
|
||||
final String downloadFileName) {
|
||||
|
||||
final StringBuilder url = new StringBuilder()
|
||||
.append(RWT.getServiceManager()
|
||||
.getServiceHandlerUrl(DownloadService.DOWNLOAD_SERVICE_NAME))
|
||||
.append(Constants.FORM_URL_ENCODED_SEPARATOR)
|
||||
.append(API.PARAM_MODEL_ID)
|
||||
.append(Constants.FORM_URL_ENCODED_NAME_VALUE_SEPARATOR)
|
||||
.append(modelId)
|
||||
.append(Constants.FORM_URL_ENCODED_SEPARATOR)
|
||||
.append(DownloadService.HANDLER_NAME_PARAMETER)
|
||||
.append(Constants.FORM_URL_ENCODED_NAME_VALUE_SEPARATOR)
|
||||
.append(handlerClass.getSimpleName())
|
||||
.append(Constants.FORM_URL_ENCODED_SEPARATOR)
|
||||
.append(DownloadService.DOWNLOAD_FILE_NAME)
|
||||
.append(Constants.FORM_URL_ENCODED_NAME_VALUE_SEPARATOR)
|
||||
.append(downloadFileName);
|
||||
return url.toString();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -13,8 +13,6 @@ import javax.servlet.http.HttpServletResponse;
|
|||
|
||||
public interface DownloadServiceHandler {
|
||||
|
||||
String getFileName();
|
||||
|
||||
void processDownload(final HttpServletRequest request, final HttpServletResponse response);
|
||||
|
||||
}
|
||||
|
|
|
@ -8,20 +8,10 @@
|
|||
|
||||
package ch.ethz.seb.sebserver.gui.service.remote;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.eclipse.rap.rwt.RWT;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.context.annotation.Lazy;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import ch.ethz.seb.sebserver.gbl.Constants;
|
||||
import ch.ethz.seb.sebserver.gbl.api.API;
|
||||
import ch.ethz.seb.sebserver.gbl.profile.GuiProfile;
|
||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestService;
|
||||
|
@ -30,9 +20,7 @@ import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.seb.clientconfig.
|
|||
@Lazy
|
||||
@Component
|
||||
@GuiProfile
|
||||
public class SebClientConfigDownload implements DownloadServiceHandler {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(SebClientConfigDownload.class);
|
||||
public class SebClientConfigDownload extends AbstractDownloadServiceHandler {
|
||||
|
||||
private final RestService restService;
|
||||
public final String downloadFileName;
|
||||
|
@ -46,68 +34,11 @@ public class SebClientConfigDownload implements DownloadServiceHandler {
|
|||
}
|
||||
|
||||
@Override
|
||||
public String getFileName() {
|
||||
return this.downloadFileName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void processDownload(final HttpServletRequest request, final HttpServletResponse response) {
|
||||
try {
|
||||
|
||||
log.debug("download requested... trying to get needed parameter from request");
|
||||
|
||||
final String configId = request.getParameter(API.PARAM_MODEL_ID);
|
||||
if (StringUtils.isBlank(configId)) {
|
||||
log.error(
|
||||
"Mandatory modelId parameter not found within HttpServletRequest. Download request is ignored");
|
||||
return;
|
||||
}
|
||||
|
||||
log.debug("Found modelId: {} for {} download. Trying to request webservice...", configId,
|
||||
this.downloadFileName);
|
||||
|
||||
final byte[] configFile = this.restService.getBuilder(ExportClientConfig.class)
|
||||
.withURIVariable(API.PARAM_MODEL_ID, configId)
|
||||
.call()
|
||||
.getOrThrow();
|
||||
|
||||
if (configFile == null) {
|
||||
log.error("No or empty SEB Client configuration received from webservice. Download request is ignored");
|
||||
return;
|
||||
}
|
||||
|
||||
log.debug("Sucessfully get SEB Client configuration from webservice. File size: {}", configFile.length);
|
||||
|
||||
response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE);
|
||||
response.setContentLength(configFile.length);
|
||||
response.setHeader(
|
||||
HttpHeaders.CONTENT_DISPOSITION,
|
||||
"attachment; filename=\"" + this.downloadFileName + "\"");
|
||||
|
||||
log.debug("Write the SEB Client configuration to response output");
|
||||
|
||||
response.getOutputStream().write(configFile);
|
||||
|
||||
} catch (final Exception e) {
|
||||
log.error(
|
||||
"Unexpected error while trying to start SEB Client configuration download. The download is ignored. Cause: ",
|
||||
e);
|
||||
}
|
||||
}
|
||||
|
||||
public String downloadSEBClientConfigURL(final String modelId) {
|
||||
final StringBuilder url = new StringBuilder()
|
||||
.append(RWT.getServiceManager()
|
||||
.getServiceHandlerUrl(DownloadService.DOWNLOAD_SERVICE_NAME))
|
||||
.append(Constants.FORM_URL_ENCODED_SEPARATOR)
|
||||
.append(API.PARAM_MODEL_ID)
|
||||
.append(Constants.FORM_URL_ENCODED_NAME_VALUE_SEPARATOR)
|
||||
.append(modelId)
|
||||
.append(Constants.FORM_URL_ENCODED_SEPARATOR)
|
||||
.append(DownloadService.HANDLER_NAME_PARAMETER)
|
||||
.append(Constants.FORM_URL_ENCODED_NAME_VALUE_SEPARATOR)
|
||||
.append(this.downloadFileName);
|
||||
return url.toString();
|
||||
protected byte[] webserviceCall(final String modelId) {
|
||||
return this.restService.getBuilder(ExportClientConfig.class)
|
||||
.withURIVariable(API.PARAM_MODEL_ID, modelId)
|
||||
.call()
|
||||
.getOrThrow();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,38 @@
|
|||
/*
|
||||
* 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;
|
||||
|
||||
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.profile.GuiProfile;
|
||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestService;
|
||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.seb.examconfig.ExportPlainXML;
|
||||
|
||||
@Lazy
|
||||
@Component
|
||||
@GuiProfile
|
||||
public class SebExamConfigDownload extends AbstractDownloadServiceHandler {
|
||||
|
||||
private final RestService restService;
|
||||
|
||||
protected SebExamConfigDownload(final RestService restService) {
|
||||
this.restService = restService;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected byte[] webserviceCall(final String modelId) {
|
||||
return this.restService.getBuilder(ExportPlainXML.class)
|
||||
.withURIVariable(API.PARAM_MODEL_ID, modelId)
|
||||
.call()
|
||||
.getOrThrow();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,52 @@
|
|||
/*
|
||||
* 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;
|
||||
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
|
||||
import ch.ethz.seb.sebserver.gbl.util.Result;
|
||||
|
||||
public abstract class AbstractExportCall extends RestCall<byte[]> {
|
||||
|
||||
protected AbstractExportCall(
|
||||
final TypeKey<byte[]> typeKey,
|
||||
final HttpMethod httpMethod,
|
||||
final MediaType contentType,
|
||||
final String path) {
|
||||
|
||||
super(typeKey, httpMethod, contentType, path);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Result<byte[]> exchange(final RestCallBuilder builder) {
|
||||
try {
|
||||
final ResponseEntity<byte[]> responseEntity = this.restService
|
||||
.getWebserviceAPIRestTemplate()
|
||||
.exchange(
|
||||
builder.buildURI(),
|
||||
this.httpMethod,
|
||||
builder.buildRequestEntity(),
|
||||
byte[].class,
|
||||
builder.getURIVariables());
|
||||
|
||||
if (responseEntity.getStatusCode() == HttpStatus.OK) {
|
||||
return Result.of(responseEntity.getBody());
|
||||
}
|
||||
|
||||
return Result.ofRuntimeError(
|
||||
"Error while trying to export from webservice. Response: " + responseEntity);
|
||||
} catch (final Throwable t) {
|
||||
return Result.ofError(t);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -10,9 +10,7 @@ package ch.ethz.seb.sebserver.gui.service.remote.webservice.api.seb.clientconfig
|
|||
|
||||
import org.springframework.context.annotation.Lazy;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
|
@ -20,48 +18,24 @@ 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.profile.GuiProfile;
|
||||
import ch.ethz.seb.sebserver.gbl.util.Result;
|
||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestCall;
|
||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.AbstractExportCall;
|
||||
|
||||
@Lazy
|
||||
@Component
|
||||
@GuiProfile
|
||||
public class ExportClientConfig extends RestCall<byte[]> {
|
||||
public class ExportClientConfig extends AbstractExportCall {
|
||||
|
||||
protected ExportClientConfig() {
|
||||
super(new TypeKey<>(
|
||||
CallType.UNDEFINED,
|
||||
EntityType.INSTITUTION,
|
||||
EntityType.SEB_CLIENT_CONFIGURATION,
|
||||
new TypeReference<byte[]>() {
|
||||
}),
|
||||
HttpMethod.GET,
|
||||
MediaType.APPLICATION_FORM_URLENCODED,
|
||||
API.SEB_CLIENT_CONFIG_ENDPOINT
|
||||
+ API.SEB_CLIENT_CONFIG_DOWNLOAD_PATH_SEGMENT
|
||||
+ API.MODEL_ID_VAR_PATH_SEGMENT);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Result<byte[]> exchange(final RestCallBuilder builder) {
|
||||
try {
|
||||
final ResponseEntity<byte[]> responseEntity = this.restService
|
||||
.getWebserviceAPIRestTemplate()
|
||||
.exchange(
|
||||
builder.buildURI(),
|
||||
this.httpMethod,
|
||||
builder.buildRequestEntity(),
|
||||
byte[].class,
|
||||
builder.getURIVariables());
|
||||
|
||||
if (responseEntity.getStatusCode() == HttpStatus.OK) {
|
||||
return Result.of(responseEntity.getBody());
|
||||
}
|
||||
|
||||
return Result.ofRuntimeError(
|
||||
"Error while trying to export SEB Config from webservice. Response: " + responseEntity);
|
||||
} catch (final Throwable t) {
|
||||
return Result.ofError(t);
|
||||
}
|
||||
API.SEB_CLIENT_CONFIG_ENDPOINT
|
||||
+ API.SEB_CLIENT_CONFIG_DOWNLOAD_PATH_SEGMENT
|
||||
+ API.MODEL_ID_VAR_PATH_SEGMENT);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -8,8 +8,6 @@
|
|||
|
||||
package ch.ethz.seb.sebserver.gui.service.remote.webservice.api.seb.examconfig;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import org.springframework.context.annotation.Lazy;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.MediaType;
|
||||
|
@ -19,24 +17,25 @@ 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.ConfigurationValue;
|
||||
import ch.ethz.seb.sebserver.gbl.profile.GuiProfile;
|
||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestCall;
|
||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.AbstractExportCall;
|
||||
|
||||
@Lazy
|
||||
@Component
|
||||
@GuiProfile
|
||||
public class GetExamConfigTableRowValues extends RestCall<Collection<ConfigurationValue>> {
|
||||
public class ExportPlainXML extends AbstractExportCall {
|
||||
|
||||
protected GetExamConfigTableRowValues() {
|
||||
protected ExportPlainXML() {
|
||||
super(new TypeKey<>(
|
||||
CallType.GET_LIST,
|
||||
EntityType.CONFIGURATION_VALUE,
|
||||
new TypeReference<Collection<ConfigurationValue>>() {
|
||||
CallType.UNDEFINED,
|
||||
EntityType.CONFIGURATION_NODE,
|
||||
new TypeReference<byte[]>() {
|
||||
}),
|
||||
HttpMethod.GET,
|
||||
MediaType.APPLICATION_FORM_URLENCODED,
|
||||
API.CONFIGURATION_VALUE_ENDPOINT + API.CONFIGURATION_TABLE_ROW_VALUE_PATH_SEGMENT);
|
||||
API.CONFIGURATION_NODE_ENDPOINT
|
||||
+ API.CONFIGURATION_PLAIN_XML_DOWNLOAD_PATH_SEGMENT
|
||||
+ API.MODEL_ID_VAR_PATH_SEGMENT);
|
||||
}
|
||||
|
||||
}
|
|
@ -183,34 +183,46 @@ public class EntityTable<ROW extends Entity> {
|
|||
}
|
||||
|
||||
public void applyFilter() {
|
||||
updateTableRows(
|
||||
this.pageNumber,
|
||||
this.pageSize,
|
||||
this.sortColumn,
|
||||
this.sortOrder);
|
||||
try {
|
||||
updateTableRows(
|
||||
this.pageNumber,
|
||||
this.pageSize,
|
||||
this.sortColumn,
|
||||
this.sortOrder);
|
||||
} catch (final Exception e) {
|
||||
log.error("Unexpected error while trying to apply filter: ", e);
|
||||
}
|
||||
}
|
||||
|
||||
public void applySort(final String columnName) {
|
||||
this.sortColumn = columnName;
|
||||
this.sortOrder = PageSortOrder.ASCENDING;
|
||||
try {
|
||||
this.sortColumn = columnName;
|
||||
this.sortOrder = PageSortOrder.ASCENDING;
|
||||
|
||||
updateTableRows(
|
||||
this.pageNumber,
|
||||
this.pageSize,
|
||||
this.sortColumn,
|
||||
this.sortOrder);
|
||||
updateTableRows(
|
||||
this.pageNumber,
|
||||
this.pageSize,
|
||||
this.sortColumn,
|
||||
this.sortOrder);
|
||||
} catch (final Exception e) {
|
||||
log.error("Unexpected error while trying to apply sort: ", e);
|
||||
}
|
||||
}
|
||||
|
||||
public void changeSortOrder() {
|
||||
this.sortOrder = (this.sortOrder == PageSortOrder.ASCENDING)
|
||||
? PageSortOrder.DESCENDING
|
||||
: PageSortOrder.ASCENDING;
|
||||
try {
|
||||
this.sortOrder = (this.sortOrder == PageSortOrder.ASCENDING)
|
||||
? PageSortOrder.DESCENDING
|
||||
: PageSortOrder.ASCENDING;
|
||||
|
||||
updateTableRows(
|
||||
this.pageNumber,
|
||||
this.pageSize,
|
||||
this.sortColumn,
|
||||
this.sortOrder);
|
||||
updateTableRows(
|
||||
this.pageNumber,
|
||||
this.pageSize,
|
||||
this.sortColumn,
|
||||
this.sortOrder);
|
||||
} catch (final Exception e) {
|
||||
log.error("Unexpected error while trying to apply sort: ", e);
|
||||
}
|
||||
}
|
||||
|
||||
public EntityKey getSingleSelection() {
|
||||
|
|
|
@ -292,6 +292,7 @@ public class TableFilter<ROW extends Entity> {
|
|||
}
|
||||
}
|
||||
|
||||
// NOTE: SWT DateTime month-number starting with 0 and joda DateTime with 1!
|
||||
private class Date extends FilterComponent {
|
||||
|
||||
private DateTime selector;
|
||||
|
@ -310,7 +311,8 @@ public class TableFilter<ROW extends Entity> {
|
|||
@Override
|
||||
FilterComponent reset() {
|
||||
final org.joda.time.DateTime now = org.joda.time.DateTime.now(DateTimeZone.UTC);
|
||||
this.selector.setDate(now.getYear(), now.getMonthOfYear(), now.getDayOfMonth());
|
||||
|
||||
this.selector.setDate(now.getYear(), now.getMonthOfYear() - 1, now.getDayOfMonth());
|
||||
return this;
|
||||
}
|
||||
|
||||
|
@ -319,8 +321,9 @@ public class TableFilter<ROW extends Entity> {
|
|||
if (this.selector != null) {
|
||||
final org.joda.time.DateTime date = org.joda.time.DateTime.now(DateTimeZone.UTC)
|
||||
.withYear(this.selector.getYear())
|
||||
.withMonthOfYear(this.selector.getMonth())
|
||||
.withDayOfMonth(this.selector.getDay());
|
||||
.withMonthOfYear(this.selector.getMonth() + 1)
|
||||
.withDayOfMonth(this.selector.getDay())
|
||||
.withTimeAtStartOfDay();
|
||||
|
||||
return date.toString(Constants.STANDARD_DATE_TIME_FORMATTER);
|
||||
} else {
|
||||
|
|
|
@ -75,6 +75,7 @@ public class WidgetFactory {
|
|||
YES("yes.png"),
|
||||
NO("no.png"),
|
||||
SAVE("save.png"),
|
||||
EXPORT("export.png"),
|
||||
NEW("new.png"),
|
||||
DELETE("delete.png"),
|
||||
SEARCH("lens.png"),
|
||||
|
|
|
@ -8,8 +8,13 @@
|
|||
|
||||
package ch.ethz.seb.sebserver.webservice.servicelayer.dao;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationAttribute;
|
||||
import ch.ethz.seb.sebserver.gbl.util.Result;
|
||||
|
||||
public interface ConfigurationAttributeDAO extends EntityDAO<ConfigurationAttribute, ConfigurationAttribute> {
|
||||
|
||||
Result<Collection<ConfigurationAttribute>> getAllRootAttributes();
|
||||
|
||||
}
|
||||
|
|
|
@ -42,6 +42,10 @@ public interface ConfigurationDAO extends EntityDAO<Configuration, Configuration
|
|||
* @return the new follow-up Configuration model */
|
||||
Result<Configuration> saveToHistory(Long configurationNodeId);
|
||||
|
||||
/** Can be used to reset the current follow-up configuration back to the last saved version in the history
|
||||
*
|
||||
* @param configurationNodeId ConfigurationNode identifier to apply the undo on
|
||||
* @return the current and reseted follow-up version */
|
||||
Result<Configuration> undo(Long configurationNodeId);
|
||||
|
||||
/** Restores the current follow-up Configuration to the values of a given Configuration
|
||||
|
@ -52,6 +56,10 @@ public interface ConfigurationDAO extends EntityDAO<Configuration, Configuration
|
|||
* @return the follow-up Configuration with restored values */
|
||||
Result<Configuration> restoreToVersion(Long configurationNodeId, Long configId);
|
||||
|
||||
Result<Configuration> getFollowupConfiguration(String configNodeId);
|
||||
/** Use this to get the follow-up configuration for a specified configuration node.
|
||||
*
|
||||
* @param configNodeId ConfigurationNode identifier to get the current follow-up configuration from
|
||||
* @return the current follow-up configuration */
|
||||
Result<Configuration> getFollowupConfiguration(Long configNodeId);
|
||||
|
||||
}
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
package ch.ethz.seb.sebserver.webservice.servicelayer.dao;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import ch.ethz.seb.sebserver.gbl.model.EntityKey;
|
||||
|
@ -23,20 +24,48 @@ public interface ConfigurationValueDAO extends EntityDAO<ConfigurationValue, Con
|
|||
@Override
|
||||
default Result<Collection<EntityKey>> delete(final Set<EntityKey> all) {
|
||||
throw new UnsupportedOperationException(
|
||||
"Deletion is not supported for ConfigurationValue. A ConfigurationValue get automatically deleted on deletion of a Configuration");
|
||||
"Deletion is not supported for ConfigurationValue. A ConfigurationValue get "
|
||||
+ "automatically deleted on deletion of a Configuration");
|
||||
}
|
||||
|
||||
Result<ConfigurationTableValues> getTableValues(
|
||||
final Long institutionId,
|
||||
final Long configurationId,
|
||||
final Long attributeId);
|
||||
/** Use this to get all ConfigurationValue for a specific configuration and for a all
|
||||
* root attributes that are not child attributes.
|
||||
*
|
||||
* @param institutionId the institution identifier
|
||||
* @param configurationId the configuration identifier
|
||||
* @return all ConfigurationValue from given configuration and all root attributes */
|
||||
Result<Collection<ConfigurationValue>> allRootAttributeValues(
|
||||
Long institutionId,
|
||||
Long configurationId);
|
||||
|
||||
/** Use this to get ConfigurationTableValues for a specified table attribute and configuration.
|
||||
*
|
||||
* @param institutionId the institution identifier
|
||||
* @param configurationId the configuration identifier
|
||||
* @param attributeId the table attribute to get all values for
|
||||
* @return ConfigurationTableValues containing all values of specified table attribute and configuration on the
|
||||
* TableValue format */
|
||||
Result<ConfigurationTableValues> getTableValues(
|
||||
Long institutionId,
|
||||
Long configurationId,
|
||||
Long attributeId);
|
||||
|
||||
/** Use this to get an ordered list of all ConfigurationValue of a table attribute on a specified configuration.
|
||||
* The list is ordered within the row/list index
|
||||
*
|
||||
* @param institutionId the institution identifier
|
||||
* @param configurationId the configuration identifier
|
||||
* @param attributeId the table attribute to get all values for
|
||||
* @return an ordered list of all ConfigurationValue of a table attribute on a specified configuration */
|
||||
Result<List<List<ConfigurationValue>>> getOrderedTableValues(
|
||||
Long institutionId,
|
||||
Long configurationId,
|
||||
Long attributeId);
|
||||
|
||||
/** Use this to save all values of a table attribute.
|
||||
*
|
||||
* @param value the ConfigurationTableValues instance containing all actual table attribute and value information
|
||||
* @return the saved table values of the attribute and configuration */
|
||||
Result<ConfigurationTableValues> saveTableValues(ConfigurationTableValues value);
|
||||
|
||||
Result<Collection<ConfigurationValue>> getTableRowValues(
|
||||
final Long institutionId,
|
||||
final Long configurationId,
|
||||
final Long attributeId,
|
||||
final Integer rowIndex);
|
||||
|
||||
}
|
||||
|
|
|
@ -24,6 +24,7 @@ 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.Orientation;
|
||||
import ch.ethz.seb.sebserver.gbl.model.sebconfig.SebClientConfig;
|
||||
import ch.ethz.seb.sebserver.gbl.model.user.UserInfo;
|
||||
import ch.ethz.seb.sebserver.gbl.util.Utils;
|
||||
|
||||
|
@ -88,7 +89,7 @@ public class FilterMap extends POSTMapper {
|
|||
}
|
||||
|
||||
public DateTime getSebClientConfigFromTime() {
|
||||
return getQuizFromTime();
|
||||
return Utils.toDateTime(getString(SebClientConfig.FILTER_ATTR_CREATION_DATE));
|
||||
}
|
||||
|
||||
public Long getLmsSetupId() {
|
||||
|
|
|
@ -110,6 +110,22 @@ public class ConfigurationAttributeDAOImpl implements ConfigurationAttributeDAO
|
|||
.collect(Collectors.toList()));
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(readOnly = true)
|
||||
public Result<Collection<ConfigurationAttribute>> getAllRootAttributes() {
|
||||
return Result.tryCatch(() -> this.configurationAttributeRecordMapper
|
||||
.selectByExample()
|
||||
.where(
|
||||
ConfigurationAttributeRecordDynamicSqlSupport.parentId,
|
||||
SqlBuilder.isNull())
|
||||
.build()
|
||||
.execute()
|
||||
.stream()
|
||||
.map(ConfigurationAttributeDAOImpl::toDomainModel)
|
||||
.flatMap(DAOLoggingSupport::logAndSkipOnError)
|
||||
.collect(Collectors.toList()));
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public Result<ConfigurationAttribute> createNew(final ConfigurationAttribute data) {
|
||||
|
|
|
@ -120,12 +120,12 @@ public class ConfigurationDAOImpl implements ConfigurationDAO {
|
|||
|
||||
@Override
|
||||
@Transactional(readOnly = true)
|
||||
public Result<Configuration> getFollowupConfiguration(final String configNodeId) {
|
||||
public Result<Configuration> getFollowupConfiguration(final Long configNodeId) {
|
||||
return Result.tryCatch(() -> {
|
||||
return this.configurationRecordMapper.selectByExample()
|
||||
.where(
|
||||
ConfigurationRecordDynamicSqlSupport.configurationNodeId,
|
||||
isEqualTo(Long.parseLong(configNodeId)))
|
||||
isEqualTo(configNodeId))
|
||||
.and(
|
||||
ConfigurationRecordDynamicSqlSupport.followup,
|
||||
isEqualTo(BooleanUtils.toInteger(true)))
|
||||
|
|
|
@ -13,7 +13,7 @@ import static org.mybatis.dynamic.sql.SqlBuilder.isIn;
|
|||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
@ -121,6 +121,35 @@ public class ConfigurationValueDAOImpl implements ConfigurationValueDAO {
|
|||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(readOnly = true)
|
||||
public Result<Collection<ConfigurationValue>> allRootAttributeValues(
|
||||
final Long institutionId,
|
||||
final Long configurationId) {
|
||||
|
||||
return Result.tryCatch(() -> this.configurationValueRecordMapper
|
||||
.selectByExample()
|
||||
.join(ConfigurationAttributeRecordDynamicSqlSupport.configurationAttributeRecord)
|
||||
.on(
|
||||
ConfigurationAttributeRecordDynamicSqlSupport.id,
|
||||
SqlBuilder.equalTo(ConfigurationValueRecordDynamicSqlSupport.configurationAttributeId))
|
||||
.where(
|
||||
ConfigurationValueRecordDynamicSqlSupport.institutionId,
|
||||
SqlBuilder.isEqualToWhenPresent(institutionId))
|
||||
.and(
|
||||
ConfigurationValueRecordDynamicSqlSupport.configurationId,
|
||||
SqlBuilder.isEqualTo(configurationId))
|
||||
.and(
|
||||
ConfigurationAttributeRecordDynamicSqlSupport.parentId,
|
||||
SqlBuilder.isNull())
|
||||
.build()
|
||||
.execute()
|
||||
.stream()
|
||||
.map(ConfigurationValueDAOImpl::toDomainModel)
|
||||
.flatMap(DAOLoggingSupport::logAndSkipOnError)
|
||||
.collect(Collectors.toList()));
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public Result<ConfigurationValue> createNew(final ConfigurationValue data) {
|
||||
|
@ -230,41 +259,72 @@ public class ConfigurationValueDAOImpl implements ConfigurationValueDAO {
|
|||
}
|
||||
|
||||
@Override
|
||||
public Result<Collection<ConfigurationValue>> getTableRowValues(
|
||||
@Transactional(readOnly = true)
|
||||
public Result<List<List<ConfigurationValue>>> getOrderedTableValues(
|
||||
final Long institutionId,
|
||||
final Long configurationId,
|
||||
final Long attributeId,
|
||||
final Integer rowIndex) {
|
||||
final Long attributeId) {
|
||||
|
||||
return attributeRecordById(attributeId)
|
||||
.flatMap(this::getAttributeMapping)
|
||||
.map(attributeMapping -> {
|
||||
return Result.tryCatch(() -> {
|
||||
|
||||
if (attributeMapping == null || attributeMapping.isEmpty()) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
final List<ConfigurationAttributeRecord> attributes = this.configurationAttributeRecordMapper
|
||||
.selectByExample()
|
||||
.where(
|
||||
ConfigurationAttributeRecordDynamicSqlSupport.parentId,
|
||||
SqlBuilder.isEqualTo(attributeId))
|
||||
.build()
|
||||
.execute()
|
||||
.stream()
|
||||
.sorted((r1, r2) -> r1.getName().compareToIgnoreCase(r2.getName()))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
// get all values of the table for specified row
|
||||
return this.configurationValueRecordMapper.selectByExample()
|
||||
.where(
|
||||
ConfigurationValueRecordDynamicSqlSupport.institutionId,
|
||||
isEqualTo(institutionId))
|
||||
.and(
|
||||
ConfigurationValueRecordDynamicSqlSupport.configurationId,
|
||||
isEqualTo(configurationId))
|
||||
.and(
|
||||
ConfigurationValueRecordDynamicSqlSupport.listIndex,
|
||||
isEqualTo(rowIndex))
|
||||
.and(
|
||||
ConfigurationValueRecordDynamicSqlSupport.configurationAttributeId,
|
||||
SqlBuilder.isIn(new ArrayList<>(attributeMapping.keySet())))
|
||||
.build()
|
||||
.execute()
|
||||
.stream()
|
||||
.map(ConfigurationValueDAOImpl::toDomainModel)
|
||||
.flatMap(DAOLoggingSupport::logAndSkipOnError)
|
||||
.collect(Collectors.toList());
|
||||
});
|
||||
final Map<Integer, Map<Long, ConfigurationValue>> indexMapping = new HashMap<>();
|
||||
this.configurationValueRecordMapper
|
||||
.selectByExample()
|
||||
.join(ConfigurationAttributeRecordDynamicSqlSupport.configurationAttributeRecord)
|
||||
.on(
|
||||
ConfigurationAttributeRecordDynamicSqlSupport.id,
|
||||
SqlBuilder.equalTo(ConfigurationValueRecordDynamicSqlSupport.configurationAttributeId))
|
||||
.where(
|
||||
ConfigurationValueRecordDynamicSqlSupport.institutionId,
|
||||
isEqualTo(institutionId))
|
||||
.and(
|
||||
ConfigurationValueRecordDynamicSqlSupport.configurationId,
|
||||
isEqualTo(configurationId))
|
||||
.and(
|
||||
ConfigurationAttributeRecordDynamicSqlSupport.parentId,
|
||||
SqlBuilder.isEqualTo(attributeId))
|
||||
.build()
|
||||
.execute()
|
||||
.stream()
|
||||
.forEach(rec -> {
|
||||
final Map<Long, ConfigurationValue> rowValues = indexMapping.computeIfAbsent(
|
||||
rec.getListIndex(),
|
||||
key -> new HashMap<>());
|
||||
rowValues.put(
|
||||
rec.getConfigurationAttributeId(),
|
||||
ConfigurationValueDAOImpl
|
||||
.toDomainModel(rec)
|
||||
.getOrThrow());
|
||||
});
|
||||
|
||||
final List<List<ConfigurationValue>> result = new ArrayList<>();
|
||||
final List<Integer> rows = new ArrayList<>(indexMapping.keySet());
|
||||
rows.sort((i1, i2) -> i1.compareTo(i2));
|
||||
rows
|
||||
.stream()
|
||||
.forEach(i -> {
|
||||
|
||||
final Map<Long, ConfigurationValue> rowValuesMapping = indexMapping.get(i);
|
||||
final List<ConfigurationValue> rowValues = attributes
|
||||
.stream()
|
||||
.map(attr -> rowValuesMapping.get(attr.getId()))
|
||||
.collect(Collectors.toList());
|
||||
result.add(rowValues);
|
||||
});
|
||||
|
||||
return result;
|
||||
});
|
||||
}
|
||||
|
||||
// get all attributes of the table (columns) mapped to attribute id
|
||||
|
|
|
@ -24,7 +24,7 @@ public interface ConfigurationValueValidator {
|
|||
|
||||
default void throwValidationError(
|
||||
final ConfigurationValue value,
|
||||
final ConfigurationAttribute attribute) {
|
||||
final ConfigurationAttribute attribute) throws FieldValidationException {
|
||||
|
||||
throw new FieldValidationException(
|
||||
attribute.name,
|
||||
|
|
|
@ -16,12 +16,11 @@ import ch.ethz.seb.sebserver.gbl.util.Result;
|
|||
public interface SebClientConfigService {
|
||||
|
||||
static String SEB_CLIENT_CONFIG_EXAMPLE_XML =
|
||||
"<!DOCTYPE plist PUBLIC \"-//Apple Computer//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\r\n"
|
||||
+
|
||||
"<plist version=\"1.0\">\r\n" +
|
||||
" <dict>\r\n" +
|
||||
" <dict>\r\n" +
|
||||
" <key>sebMode</key>\r\n" +
|
||||
" <integer>1</integer>\r\n" +
|
||||
" <key>sebConfigPurpose</key>\r\n" +
|
||||
" <integer>1</integer>" +
|
||||
" <key>sebServerFallback</key>\r\n" +
|
||||
" <true />\r\n" +
|
||||
" <key>sebServerURL</key>\r\n" +
|
||||
|
@ -37,8 +36,7 @@ public interface SebClientConfigService {
|
|||
" <key>apiDiscovery</key>\r\n" +
|
||||
" <string>%s</string>\r\n" +
|
||||
" </dict>\r\n" +
|
||||
" </dict>\r\n" +
|
||||
"</plist>";
|
||||
" </dict>\r\n";
|
||||
|
||||
String getServerURL();
|
||||
|
||||
|
|
|
@ -10,17 +10,21 @@ package ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig;
|
|||
|
||||
import java.io.OutputStream;
|
||||
|
||||
import ch.ethz.seb.sebserver.gbl.api.APIMessage.FieldValidationException;
|
||||
import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationTableValues;
|
||||
import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationValue;
|
||||
|
||||
/** The base interface and service for all SEB Exam Configuration related functionality. */
|
||||
public interface SebExamConfigService {
|
||||
|
||||
void validate(ConfigurationValue value);
|
||||
void validate(ConfigurationValue value) throws FieldValidationException;
|
||||
|
||||
void validate(ConfigurationTableValues tableValue);
|
||||
void validate(ConfigurationTableValues tableValue) throws FieldValidationException;
|
||||
|
||||
void exportXML(OutputStream out, Long configurationNodeId);
|
||||
void exportPlainXML(OutputStream out, Long institutionId, Long configurationNodeId);
|
||||
|
||||
void exportForExam(OutputStream out, Long configExamMappingId);
|
||||
|
||||
String generateConfigKey(Long configurationNodeId);
|
||||
|
||||
}
|
||||
|
|
|
@ -25,12 +25,13 @@ public interface XMLValueConverter {
|
|||
void convertToXML(
|
||||
OutputStream out,
|
||||
ConfigurationAttribute attribute,
|
||||
ConfigurationValue value) throws IOException;
|
||||
ConfigurationValue value,
|
||||
XMLValueConverterService xmlValueConverterService) throws IOException;
|
||||
|
||||
default String extractName(final ConfigurationAttribute attribute) {
|
||||
final int lastIndexOf = attribute.name.lastIndexOf('.');
|
||||
if (lastIndexOf > 0) {
|
||||
return attribute.name.substring(lastIndexOf, attribute.name.length() - 1);
|
||||
return attribute.name.substring(lastIndexOf + 1, attribute.name.length());
|
||||
} else {
|
||||
return attribute.name;
|
||||
}
|
||||
|
|
|
@ -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.webservice.servicelayer.sebconfig;
|
||||
|
||||
import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationAttribute;
|
||||
|
||||
public interface XMLValueConverterService {
|
||||
|
||||
XMLValueConverter getXMLConverter(ConfigurationAttribute attribute);
|
||||
|
||||
}
|
|
@ -13,6 +13,7 @@ import java.io.InputStream;
|
|||
import java.io.OutputStream;
|
||||
import java.io.PipedInputStream;
|
||||
import java.io.PipedOutputStream;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Collection;
|
||||
import java.util.UUID;
|
||||
|
||||
|
@ -33,6 +34,7 @@ import org.springframework.web.context.request.ServletRequestAttributes;
|
|||
import org.springframework.web.util.UriComponentsBuilder;
|
||||
|
||||
import ch.ethz.seb.sebserver.WebSecurityConfig;
|
||||
import ch.ethz.seb.sebserver.gbl.Constants;
|
||||
import ch.ethz.seb.sebserver.gbl.api.API;
|
||||
import ch.ethz.seb.sebserver.gbl.model.institution.Institution;
|
||||
import ch.ethz.seb.sebserver.gbl.model.sebconfig.SebClientConfig;
|
||||
|
@ -66,6 +68,7 @@ public class SebClientConfigServiceImpl implements SebClientConfigService {
|
|||
private final String httpScheme;
|
||||
private final String serverAddress;
|
||||
private final String serverPort;
|
||||
private final String discoveryEndpoint;
|
||||
|
||||
protected SebClientConfigServiceImpl(
|
||||
final InstitutionDAO institutionDAO,
|
||||
|
@ -75,7 +78,8 @@ public class SebClientConfigServiceImpl implements SebClientConfigService {
|
|||
final ZipService zipService,
|
||||
@Value("${sebserver.webservice.http.scheme}") final String httpScheme,
|
||||
@Value("${server.address}") final String serverAddress,
|
||||
@Value("${server.port}") final String serverPort) {
|
||||
@Value("${server.port}") final String serverPort,
|
||||
@Value("${sebserver.webservice.api.exam.endpoint.discovery}") final String discoveryEndpoint) {
|
||||
|
||||
this.institutionDAO = institutionDAO;
|
||||
this.sebClientConfigDAO = sebClientConfigDAO;
|
||||
|
@ -85,6 +89,7 @@ public class SebClientConfigServiceImpl implements SebClientConfigService {
|
|||
this.httpScheme = httpScheme;
|
||||
this.serverAddress = serverAddress;
|
||||
this.serverPort = serverPort;
|
||||
this.discoveryEndpoint = discoveryEndpoint;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -182,14 +187,22 @@ public class SebClientConfigServiceImpl implements SebClientConfigService {
|
|||
String.valueOf(config.institutionId),
|
||||
plainClientId,
|
||||
plainClientSecret,
|
||||
"TODO:/exam-api/discovery");
|
||||
this.getServerURL() + this.discoveryEndpoint);
|
||||
|
||||
PipedOutputStream pOut = null;
|
||||
PipedInputStream pIn = null;
|
||||
|
||||
try {
|
||||
|
||||
// zip the plain text
|
||||
final InputStream plainIn = IOUtils.toInputStream(plainTextConfig, "UTF-8");
|
||||
final InputStream plainIn = IOUtils.toInputStream(
|
||||
Constants.XML_VERSION_HEADER +
|
||||
Constants.XML_DOCTYPE_HEADER +
|
||||
Constants.XML_PLIST_START_V1 +
|
||||
plainTextConfig +
|
||||
Constants.XML_PLIST_END,
|
||||
StandardCharsets.UTF_8.name());
|
||||
|
||||
pOut = new PipedOutputStream();
|
||||
pIn = new PipedInputStream(pOut);
|
||||
|
||||
|
|
|
@ -8,46 +8,95 @@
|
|||
|
||||
package ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.impl;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
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.apache.commons.lang3.StringUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.context.annotation.Lazy;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import ch.ethz.seb.sebserver.gbl.Constants;
|
||||
import ch.ethz.seb.sebserver.gbl.api.APIMessage.FieldValidationException;
|
||||
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.ConfigurationTableValues;
|
||||
import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationValue;
|
||||
import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.ConfigurationAttributeDAO;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.ConfigurationDAO;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.ConfigurationValueDAO;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.ConfigurationValueValidator;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.SebExamConfigService;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.XMLValueConverter;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.XMLValueConverterService;
|
||||
|
||||
@Lazy
|
||||
@Service
|
||||
@WebServiceProfile
|
||||
public class SebExamConfigServiceImpl implements SebExamConfigService {
|
||||
public class SebExamConfigServiceImpl implements SebExamConfigService, XMLValueConverterService {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(SebExamConfigServiceImpl.class);
|
||||
|
||||
private final ConfigurationAttributeDAO configurationAttributeDAO;
|
||||
private final ConfigurationValueDAO configurationValueDAO;
|
||||
private final ConfigurationDAO configurationDAO;
|
||||
private final Collection<ConfigurationValueValidator> validators;
|
||||
private final Collection<XMLValueConverter> converters;
|
||||
private final Map<String, XMLValueConverter> convertersByAttributeName;
|
||||
private final Map<AttributeType, XMLValueConverter> convertersByAttributeType;
|
||||
|
||||
protected SebExamConfigServiceImpl(
|
||||
final ConfigurationAttributeDAO configurationAttributeDAO,
|
||||
final ConfigurationValueDAO configurationValueDAO,
|
||||
final ConfigurationDAO configurationDAO,
|
||||
final Collection<ConfigurationValueValidator> validators,
|
||||
final Collection<XMLValueConverter> converters) {
|
||||
|
||||
this.configurationAttributeDAO = configurationAttributeDAO;
|
||||
this.configurationValueDAO = configurationValueDAO;
|
||||
this.configurationDAO = configurationDAO;
|
||||
this.validators = validators;
|
||||
this.converters = converters;
|
||||
this.convertersByAttributeName = new HashMap<>();
|
||||
this.convertersByAttributeType = new HashMap<>();
|
||||
for (final XMLValueConverter converter : converters) {
|
||||
if (StringUtils.isNoneBlank(converter.name())) {
|
||||
this.convertersByAttributeName.put(converter.name(), converter);
|
||||
}
|
||||
|
||||
for (final AttributeType aType : converter.types()) {
|
||||
if (this.convertersByAttributeType.containsKey(aType)) {
|
||||
log.warn(
|
||||
"Unexpected state in inititalization: A XMLValueConverter for AttributeType {} exists already: {}",
|
||||
aType,
|
||||
converter);
|
||||
}
|
||||
this.convertersByAttributeType.put(aType, converter);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void validate(final ConfigurationValue value) {
|
||||
public XMLValueConverter getXMLConverter(final ConfigurationAttribute attribute) {
|
||||
if (this.convertersByAttributeName.containsKey(attribute.name)) {
|
||||
return this.convertersByAttributeName.get(attribute.name);
|
||||
}
|
||||
|
||||
if (this.convertersByAttributeType.containsKey(attribute.type)) {
|
||||
return this.convertersByAttributeType.get(attribute.type);
|
||||
}
|
||||
|
||||
throw new IllegalStateException("No XMLValueConverter found for attribute: " + attribute);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void validate(final ConfigurationValue value) throws FieldValidationException {
|
||||
if (value == null) {
|
||||
log.warn("Validate called with null reference. Ignore this and skip validation");
|
||||
return;
|
||||
|
@ -65,15 +114,79 @@ public class SebExamConfigServiceImpl implements SebExamConfigService {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void validate(final ConfigurationTableValues tableValue) {
|
||||
public void validate(final ConfigurationTableValues tableValue) throws FieldValidationException {
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
throw new UnsupportedOperationException("TODO");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exportXML(final OutputStream out, final Long configurationNodeId) {
|
||||
// TODO Auto-generated method stub
|
||||
public void exportPlainXML(
|
||||
final OutputStream out,
|
||||
final Long institutionId,
|
||||
final Long configurationNodeId) {
|
||||
|
||||
// get all defined root configuration attributes
|
||||
final Map<Long, ConfigurationAttribute> attributes = this.configurationAttributeDAO.getAllRootAttributes()
|
||||
.getOrThrow()
|
||||
.stream()
|
||||
.collect(Collectors.toMap(
|
||||
ConfigurationAttribute::getId,
|
||||
Function.identity()));
|
||||
|
||||
final List<ConfigurationAttribute> sortedAttributes = attributes
|
||||
.values()
|
||||
.stream()
|
||||
.sorted()
|
||||
.collect(Collectors.toList());
|
||||
|
||||
// get follow-up configurationId for given configurationNodeId
|
||||
final Long configurationId = this.configurationDAO
|
||||
.getFollowupConfiguration(configurationNodeId)
|
||||
.getOrThrow().id;
|
||||
|
||||
// get all values for that attributes for given configurationId
|
||||
final Map<Long, ConfigurationValue> values = this.configurationValueDAO
|
||||
.allRootAttributeValues(institutionId, configurationId)
|
||||
.getOrThrow()
|
||||
.stream()
|
||||
.collect(Collectors.toMap(
|
||||
ConfigurationValue::getAttributeId,
|
||||
Function.identity()));
|
||||
|
||||
try {
|
||||
// write headers
|
||||
out.write(Constants.XML_VERSION_HEADER_UTF_8);
|
||||
out.write(Constants.XML_DOCTYPE_HEADER_UTF_8);
|
||||
|
||||
// plist open
|
||||
out.write(Constants.XML_PLIST_START_V1_UTF_8);
|
||||
out.write(Constants.XML_DICT_START_UTF_8);
|
||||
|
||||
// write attributes
|
||||
for (final ConfigurationAttribute attribute : sortedAttributes) {
|
||||
final ConfigurationValue configurationValue = values.get(attribute.id);
|
||||
if (configurationValue != null) {
|
||||
this.getXMLConverter(attribute).convertToXML(
|
||||
out,
|
||||
attribute,
|
||||
configurationValue,
|
||||
this);
|
||||
}
|
||||
}
|
||||
|
||||
// plist close
|
||||
out.write(Constants.XML_DICT_END_UTF_8);
|
||||
out.write(Constants.XML_PLIST_END_UTF_8);
|
||||
out.flush();
|
||||
|
||||
} catch (final IOException e) {
|
||||
log.error("Unexpected error while trying to write SEB Exam Configuration XML to output stream: ", e);
|
||||
try {
|
||||
out.flush();
|
||||
} catch (final IOException e1) {
|
||||
log.error("Unable to flush output stream after error");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -82,4 +195,10 @@ public class SebExamConfigServiceImpl implements SebExamConfigService {
|
|||
|
||||
}
|
||||
|
||||
@Override
|
||||
public String generateConfigKey(final Long configurationNodeId) {
|
||||
// TODO https://www.safeexambrowser.org/developer/seb-config-key.html
|
||||
throw new UnsupportedOperationException("TODO");
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,79 @@
|
|||
/*
|
||||
* 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.servicelayer.sebconfig.impl.converter;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.context.annotation.Lazy;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import ch.ethz.seb.sebserver.gbl.Constants;
|
||||
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.ConfigurationValue;
|
||||
import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile;
|
||||
import ch.ethz.seb.sebserver.gbl.util.Utils;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.XMLValueConverter;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.XMLValueConverterService;
|
||||
|
||||
@Lazy
|
||||
@Component
|
||||
@WebServiceProfile
|
||||
public class ArrayOfStringConverter implements XMLValueConverter {
|
||||
|
||||
public static final String ATTRIBUTE_NAME = "ExceptionsList";
|
||||
|
||||
public static final Set<AttributeType> SUPPORTED_TYPES = Collections.unmodifiableSet(
|
||||
new HashSet<>(Arrays.asList(
|
||||
AttributeType.MULTI_CHECKBOX_SELECTION,
|
||||
AttributeType.MULTI_SELECTION)));
|
||||
|
||||
private static final String TEMPLATE = "<key>%s</key><array>";
|
||||
private static final String TEMPLATE_ENTRY = "<string>%s</string>";
|
||||
private static final String TEMPLATE_EMPTY = "<key>%s</key><array></array>";
|
||||
|
||||
@Override
|
||||
public Set<AttributeType> types() {
|
||||
return SUPPORTED_TYPES;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String name() {
|
||||
return ATTRIBUTE_NAME;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void convertToXML(
|
||||
final OutputStream out,
|
||||
final ConfigurationAttribute attribute,
|
||||
final ConfigurationValue value,
|
||||
final XMLValueConverterService xmlValueConverterService) throws IOException {
|
||||
|
||||
final String val = (value.value != null) ? value.value : attribute.getDefaultValue();
|
||||
if (StringUtils.isNoneBlank(val)) {
|
||||
final String[] values = StringUtils.split(val, Constants.LIST_SEPARATOR);
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
sb.append(String.format(TEMPLATE, extractName(attribute)));
|
||||
for (final String v : values) {
|
||||
sb.append(String.format(TEMPLATE_ENTRY, v));
|
||||
}
|
||||
sb.append("</array>");
|
||||
out.write(Utils.toByteArray(sb.toString()));
|
||||
} else {
|
||||
out.write(Utils.toByteArray(String.format(TEMPLATE_EMPTY, extractName(attribute))));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -16,20 +16,28 @@ import java.util.HashSet;
|
|||
import java.util.Set;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.context.annotation.Lazy;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import ch.ethz.seb.sebserver.gbl.Constants;
|
||||
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.ConfigurationValue;
|
||||
import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile;
|
||||
import ch.ethz.seb.sebserver.gbl.util.Utils;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.XMLValueConverter;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.XMLValueConverterService;
|
||||
|
||||
@Lazy
|
||||
@Component
|
||||
@WebServiceProfile
|
||||
public class BooleanConverter implements XMLValueConverter {
|
||||
|
||||
public static final Set<AttributeType> SUPPORTED_TYPES = Collections.unmodifiableSet(
|
||||
new HashSet<>(Arrays.asList(
|
||||
AttributeType.CHECKBOX)));
|
||||
|
||||
private static final StringBuilder BUILDER = new StringBuilder();
|
||||
private static final String TEMPLATE = "<key>%s</key><%s />";
|
||||
|
||||
@Override
|
||||
public String name() {
|
||||
|
@ -45,14 +53,15 @@ public class BooleanConverter implements XMLValueConverter {
|
|||
public void convertToXML(
|
||||
final OutputStream out,
|
||||
final ConfigurationAttribute attribute,
|
||||
final ConfigurationValue value) throws IOException {
|
||||
final ConfigurationValue value,
|
||||
final XMLValueConverterService xmlValueConverterService) throws IOException {
|
||||
|
||||
out.write(Utils.toByteArray(
|
||||
String.format(
|
||||
TEMPLATE,
|
||||
extractName(attribute),
|
||||
(value.value != null) ? value.value : Constants.FALSE_STRING)));
|
||||
|
||||
BUILDER.setLength(0);
|
||||
out.write(Utils.toByteArray(BUILDER.append("<key>")
|
||||
.append(extractName(attribute))
|
||||
.append("<")
|
||||
.append((value.value != null) ? value.value : "false")
|
||||
.append(" />")));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,71 @@
|
|||
/*
|
||||
* 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.servicelayer.sebconfig.impl.converter;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
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.ConfigurationValue;
|
||||
import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile;
|
||||
import ch.ethz.seb.sebserver.gbl.util.Utils;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.XMLValueConverter;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.XMLValueConverterService;
|
||||
|
||||
@Lazy
|
||||
@Component
|
||||
@WebServiceProfile
|
||||
public class IntegerConverter implements XMLValueConverter {
|
||||
|
||||
public static final Set<AttributeType> SUPPORTED_TYPES = Collections.unmodifiableSet(
|
||||
new HashSet<>(Arrays.asList(
|
||||
AttributeType.INTEGER,
|
||||
AttributeType.SLIDER,
|
||||
AttributeType.SINGLE_SELECTION,
|
||||
AttributeType.RADIO_SELECTION)));
|
||||
|
||||
private static final String TEMPLATE = "<key>%s</key><integer>%s</integer>";
|
||||
private static final String TEMPLATE_EMPTY = "<key>%s</key><integer />";
|
||||
|
||||
@Override
|
||||
public Set<AttributeType> types() {
|
||||
return SUPPORTED_TYPES;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String name() {
|
||||
return StringUtils.EMPTY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void convertToXML(
|
||||
final OutputStream out,
|
||||
final ConfigurationAttribute attribute,
|
||||
final ConfigurationValue value,
|
||||
final XMLValueConverterService xmlValueConverterService) throws IOException {
|
||||
|
||||
final String val = (value.value != null) ? value.value : attribute.getDefaultValue();
|
||||
if (StringUtils.isNoneBlank(val)) {
|
||||
out.write(Utils.toByteArray(String.format(TEMPLATE, extractName(attribute), val)));
|
||||
} else {
|
||||
out.write(Utils.toByteArray(String.format(TEMPLATE_EMPTY, extractName(attribute))));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -8,6 +8,7 @@
|
|||
|
||||
package ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.impl.converter;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.util.Collections;
|
||||
import java.util.Set;
|
||||
|
@ -15,11 +16,14 @@ import java.util.Set;
|
|||
import org.springframework.context.annotation.Lazy;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import ch.ethz.seb.sebserver.gbl.Constants;
|
||||
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.ConfigurationValue;
|
||||
import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile;
|
||||
import ch.ethz.seb.sebserver.gbl.util.Utils;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.XMLValueConverter;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.XMLValueConverterService;
|
||||
|
||||
@Lazy
|
||||
@Component
|
||||
|
@ -28,6 +32,8 @@ public class KioskModeConverter implements XMLValueConverter {
|
|||
|
||||
public static final String NAME = "kioskMode";
|
||||
|
||||
private static final String TEMPLATE = "<key>createNewDesktop</key><%s /><key>killExplorerShell</key><%s />";
|
||||
|
||||
@Override
|
||||
public String name() {
|
||||
return NAME;
|
||||
|
@ -42,10 +48,15 @@ public class KioskModeConverter implements XMLValueConverter {
|
|||
public void convertToXML(
|
||||
final OutputStream out,
|
||||
final ConfigurationAttribute attribute,
|
||||
final ConfigurationValue value) {
|
||||
|
||||
// TODO Auto-generated method stub
|
||||
final ConfigurationValue value,
|
||||
final XMLValueConverterService xmlValueConverterService) throws IOException {
|
||||
|
||||
final String val = value.getValue();
|
||||
out.write(Utils.toByteArray(
|
||||
String.format(
|
||||
TEMPLATE,
|
||||
(val != null == "0".equals(val)) ? Constants.TRUE_STRING : Constants.FALSE_STRING,
|
||||
(val != null == "1".equals(val)) ? Constants.TRUE_STRING : Constants.FALSE_STRING)));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,72 @@
|
|||
/*
|
||||
* 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.servicelayer.sebconfig.impl.converter;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
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.ConfigurationValue;
|
||||
import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile;
|
||||
import ch.ethz.seb.sebserver.gbl.util.Utils;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.XMLValueConverter;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.XMLValueConverterService;
|
||||
|
||||
@Lazy
|
||||
@Component
|
||||
@WebServiceProfile
|
||||
public class StringConverter implements XMLValueConverter {
|
||||
|
||||
public static final Set<AttributeType> SUPPORTED_TYPES = Collections.unmodifiableSet(
|
||||
new HashSet<>(Arrays.asList(
|
||||
AttributeType.TEXT_FIELD,
|
||||
AttributeType.TEXT_AREA,
|
||||
AttributeType.PASSWORD_FIELD,
|
||||
AttributeType.DECIMAL,
|
||||
AttributeType.COMBO_SELECTION)));
|
||||
|
||||
private static final String TEMPLATE = "<key>%s</key><string>%s</string>";
|
||||
private static final String TEMPLATE_EMPTY = "<key>%s</key><string />";
|
||||
|
||||
@Override
|
||||
public Set<AttributeType> types() {
|
||||
return SUPPORTED_TYPES;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String name() {
|
||||
return StringUtils.EMPTY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void convertToXML(
|
||||
final OutputStream out,
|
||||
final ConfigurationAttribute attribute,
|
||||
final ConfigurationValue value,
|
||||
final XMLValueConverterService xmlValueConverterService) throws IOException {
|
||||
|
||||
final String val = (value.value != null) ? value.value : attribute.getDefaultValue();
|
||||
if (StringUtils.isNoneBlank(val)) {
|
||||
out.write(Utils.toByteArray(String.format(TEMPLATE, extractName(attribute), val)));
|
||||
} else {
|
||||
out.write(Utils.toByteArray(String.format(TEMPLATE_EMPTY, extractName(attribute))));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,144 @@
|
|||
/*
|
||||
* 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.servicelayer.sebconfig.impl.converter;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
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.ConfigurationValue;
|
||||
import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile;
|
||||
import ch.ethz.seb.sebserver.gbl.util.Utils;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.ConfigurationAttributeDAO;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.ConfigurationValueDAO;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.FilterMap;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.XMLValueConverter;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.XMLValueConverterService;
|
||||
|
||||
@Lazy
|
||||
@Component
|
||||
@WebServiceProfile
|
||||
public class TableConverter implements XMLValueConverter {
|
||||
|
||||
public static final Set<AttributeType> SUPPORTED_TYPES = Collections.unmodifiableSet(
|
||||
new HashSet<>(Arrays.asList(
|
||||
AttributeType.TABLE,
|
||||
AttributeType.INLINE_TABLE,
|
||||
AttributeType.COMPOSITE_TABLE)));
|
||||
|
||||
private static final String KEY_TEMPLATE = "<key>%s</key>";
|
||||
private static final byte[] ARRAY_START = Utils.toByteArray("<array>");
|
||||
private static final byte[] ARRAY_END = Utils.toByteArray("</array>");
|
||||
private static final byte[] DICT_START = Utils.toByteArray("<dict>");
|
||||
private static final byte[] DICT_END = Utils.toByteArray("</dict>");
|
||||
private static final byte[] EMPTY_ARRAY = Utils.toByteArray("<array />");
|
||||
|
||||
private final ConfigurationAttributeDAO configurationAttributeDAO;
|
||||
private final ConfigurationValueDAO configurationValueDAO;
|
||||
|
||||
public TableConverter(
|
||||
final ConfigurationAttributeDAO configurationAttributeDAO,
|
||||
final ConfigurationValueDAO configurationValueDAO) {
|
||||
|
||||
this.configurationAttributeDAO = configurationAttributeDAO;
|
||||
this.configurationValueDAO = configurationValueDAO;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<AttributeType> types() {
|
||||
return SUPPORTED_TYPES;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String name() {
|
||||
return StringUtils.EMPTY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void convertToXML(
|
||||
final OutputStream out,
|
||||
final ConfigurationAttribute attribute,
|
||||
final ConfigurationValue value,
|
||||
final XMLValueConverterService xmlValueConverterService) throws IOException {
|
||||
|
||||
out.write(Utils.toByteArray(String.format(KEY_TEMPLATE, extractName(attribute))));
|
||||
|
||||
final List<List<ConfigurationValue>> values = this.configurationValueDAO.getOrderedTableValues(
|
||||
value.institutionId,
|
||||
value.configurationId,
|
||||
attribute.id).getOrThrow();
|
||||
|
||||
if (values == null || values.isEmpty()) {
|
||||
out.write(EMPTY_ARRAY);
|
||||
out.flush();
|
||||
return;
|
||||
}
|
||||
|
||||
if (attribute.type != AttributeType.COMPOSITE_TABLE) {
|
||||
out.write(ARRAY_START);
|
||||
}
|
||||
|
||||
writeRows(
|
||||
out,
|
||||
getAttributes(attribute),
|
||||
values,
|
||||
xmlValueConverterService);
|
||||
|
||||
if (attribute.type != AttributeType.COMPOSITE_TABLE) {
|
||||
out.write(ARRAY_END);
|
||||
}
|
||||
|
||||
out.flush();
|
||||
}
|
||||
|
||||
private Map<Long, ConfigurationAttribute> getAttributes(final ConfigurationAttribute attribute) {
|
||||
return this.configurationAttributeDAO
|
||||
.allMatching(new FilterMap().putIfAbsent(
|
||||
ConfigurationAttribute.FILTER_ATTR_PARENT_ID,
|
||||
attribute.getModelId()))
|
||||
.getOrThrow()
|
||||
.stream()
|
||||
.collect(Collectors.toMap(
|
||||
attr -> attr.id,
|
||||
Function.identity()));
|
||||
|
||||
}
|
||||
|
||||
private void writeRows(
|
||||
final OutputStream out,
|
||||
final Map<Long, ConfigurationAttribute> attributeMap,
|
||||
final List<List<ConfigurationValue>> values,
|
||||
final XMLValueConverterService xmlValueConverterService) throws IOException {
|
||||
|
||||
for (final List<ConfigurationValue> rowValues : values) {
|
||||
out.write(DICT_START);
|
||||
for (final ConfigurationValue value : rowValues) {
|
||||
final ConfigurationAttribute attr = attributeMap.get(value.attributeId);
|
||||
final XMLValueConverter converter = xmlValueConverterService.getXMLConverter(attr);
|
||||
converter.convertToXML(out, attr, value, xmlValueConverterService);
|
||||
}
|
||||
out.write(DICT_END);
|
||||
out.flush();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -9,11 +9,15 @@
|
|||
package ch.ethz.seb.sebserver.webservice.weblayer.api;
|
||||
|
||||
import org.mybatis.dynamic.sql.SqlTable;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMethod;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBody;
|
||||
|
||||
import ch.ethz.seb.sebserver.gbl.api.API;
|
||||
import ch.ethz.seb.sebserver.gbl.api.POSTMapper;
|
||||
|
@ -25,10 +29,12 @@ import ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.ConfigurationNode
|
|||
import ch.ethz.seb.sebserver.webservice.servicelayer.PaginationService;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.authorization.AuthorizationService;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.authorization.SEBServerUser;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.authorization.UserService;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.bulkaction.BulkActionService;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.ConfigurationDAO;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.ConfigurationNodeDAO;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.UserActivityLogDAO;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.SebExamConfigService;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.validation.BeanValidationService;
|
||||
|
||||
@WebServiceProfile
|
||||
|
@ -37,6 +43,7 @@ import ch.ethz.seb.sebserver.webservice.servicelayer.validation.BeanValidationSe
|
|||
public class ConfigurationNodeController extends EntityController<ConfigurationNode, ConfigurationNode> {
|
||||
|
||||
private final ConfigurationDAO configurationDAO;
|
||||
private final SebExamConfigService sebExamConfigService;
|
||||
|
||||
protected ConfigurationNodeController(
|
||||
final AuthorizationService authorization,
|
||||
|
@ -45,7 +52,8 @@ public class ConfigurationNodeController extends EntityController<ConfigurationN
|
|||
final UserActivityLogDAO userActivityLogDAO,
|
||||
final PaginationService paginationService,
|
||||
final BeanValidationService beanValidationService,
|
||||
final ConfigurationDAO configurationDAO) {
|
||||
final ConfigurationDAO configurationDAO,
|
||||
final SebExamConfigService sebExamConfigService) {
|
||||
|
||||
super(authorization,
|
||||
bulkActionService,
|
||||
|
@ -55,6 +63,7 @@ public class ConfigurationNodeController extends EntityController<ConfigurationN
|
|||
beanValidationService);
|
||||
|
||||
this.configurationDAO = configurationDAO;
|
||||
this.sebExamConfigService = sebExamConfigService;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -75,16 +84,36 @@ public class ConfigurationNodeController extends EntityController<ConfigurationN
|
|||
method = RequestMethod.GET,
|
||||
consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE,
|
||||
produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
|
||||
public Configuration getFollowup(@PathVariable final String modelId) {
|
||||
public Configuration getFollowup(@PathVariable final Long configNodeId) {
|
||||
|
||||
this.entityDAO
|
||||
.byModelId(modelId)
|
||||
.byPK(configNodeId)
|
||||
.flatMap(this::checkModifyAccess)
|
||||
.getOrThrow();
|
||||
|
||||
return this.configurationDAO
|
||||
.getFollowupConfiguration(modelId)
|
||||
.getFollowupConfiguration(configNodeId)
|
||||
.getOrThrow();
|
||||
}
|
||||
|
||||
@RequestMapping(
|
||||
path = API.CONFIGURATION_PLAIN_XML_DOWNLOAD_PATH_SEGMENT + API.MODEL_ID_VAR_PATH_SEGMENT,
|
||||
method = RequestMethod.GET,
|
||||
produces = MediaType.APPLICATION_OCTET_STREAM_VALUE)
|
||||
public ResponseEntity<StreamingResponseBody> downloadPlainXMLConfig(
|
||||
@PathVariable final Long modelId,
|
||||
@RequestParam(
|
||||
name = API.PARAM_INSTITUTION_ID,
|
||||
required = true,
|
||||
defaultValue = UserService.USERS_INSTITUTION_AS_DEFAULT) final Long institutionId) {
|
||||
|
||||
this.entityDAO.byPK(modelId)
|
||||
.map(this.authorization::checkRead);
|
||||
|
||||
final StreamingResponseBody stream = out -> this.sebExamConfigService
|
||||
.exportPlainXML(out, institutionId, modelId);
|
||||
|
||||
return new ResponseEntity<>(stream, HttpStatus.OK);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -8,7 +8,6 @@
|
|||
|
||||
package ch.ethz.seb.sebserver.webservice.weblayer.api;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Objects;
|
||||
|
||||
import javax.validation.Valid;
|
||||
|
@ -108,32 +107,6 @@ public class ConfigurationValueController extends EntityController<Configuration
|
|||
.getOrThrow();
|
||||
}
|
||||
|
||||
@RequestMapping(
|
||||
path = API.CONFIGURATION_TABLE_ROW_VALUE_PATH_SEGMENT,
|
||||
method = RequestMethod.GET,
|
||||
consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE,
|
||||
produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
|
||||
public Collection<ConfigurationValue> getTableRowValueBy(
|
||||
@RequestParam(
|
||||
name = Domain.CONFIGURATION_VALUE.ATTR_CONFIGURATION_ATTRIBUTE_ID,
|
||||
required = true) final Long attributeId,
|
||||
@RequestParam(
|
||||
name = Domain.CONFIGURATION_VALUE.ATTR_CONFIGURATION_ID,
|
||||
required = true) final Long configurationId,
|
||||
@RequestParam(
|
||||
name = Domain.CONFIGURATION_VALUE.ATTR_LIST_INDEX,
|
||||
required = true) final Integer rowIndex) {
|
||||
|
||||
return this.configurationDAO.byPK(configurationId)
|
||||
.flatMap(this.authorization::checkRead)
|
||||
.flatMap(config -> this.configurationValueDAO.getTableRowValues(
|
||||
config.institutionId,
|
||||
configurationId,
|
||||
attributeId,
|
||||
rowIndex))
|
||||
.getOrThrow();
|
||||
}
|
||||
|
||||
@RequestMapping(
|
||||
path = API.CONFIGURATION_TABLE_VALUE_PATH_SEGMENT,
|
||||
method = RequestMethod.PUT,
|
||||
|
|
|
@ -25,8 +25,6 @@ sebserver.webservice.api.pagination.maxPageSize=500
|
|||
# comma separated list of known possible OpenEdX API access token request endpoints
|
||||
sebserver.lms.openedix.api.token.request.paths=/oauth2/access_token
|
||||
|
||||
|
||||
|
||||
sebserver.gui.entrypoint=/gui
|
||||
sebserver.gui.webservice.protocol=http
|
||||
sebserver.gui.webservice.address=0.0.0.0
|
||||
|
@ -36,7 +34,10 @@ sebserver.gui.theme=css/sebserver.css
|
|||
sebserver.gui.list.page.size=20
|
||||
sebserver.gui.date.displayformat=MM/dd/yyyy HH:mm
|
||||
sebserver.gui.date.displayformat.timezone=|ZZ
|
||||
sebserver.gui.multilingual=true
|
||||
sebserver.gui.multilingual=false
|
||||
sebserver.gui.languages=en
|
||||
|
||||
sebserver.gui.seb.client.config.download.filename=SebClientSettings.seb
|
||||
sebserver.gui.seb.exam.config.download.filename=SebClientSettings.seb
|
||||
|
||||
|
||||
|
|
|
@ -19,4 +19,5 @@ sebserver.gui.date.displayformat.timezone=|ZZ
|
|||
sebserver.gui.multilingual=true
|
||||
sebserver.gui.languages=en,de
|
||||
|
||||
sebserver.gui.seb.client.config.download.filename=SebClientSettings.seb
|
||||
sebserver.gui.seb.client.config.download.filename=SebClientSettings.seb
|
||||
sebserver.gui.seb.exam.config.download.filename=SebClientSettings.seb
|
|
@ -391,6 +391,7 @@ sebserver.examconfig.action.saveToHistory=Save In History
|
|||
sebserver.examconfig.action.saveToHistory.success=Successfully saved in history
|
||||
sebserver.examconfig.action.undo=Undo
|
||||
sebserver.examconfig.action.undo.success=Successfully reverted to last saved state
|
||||
sebserver.examconfig.action.export.plainxml=Export XML
|
||||
|
||||
sebserver.examconfig.form.title.new=New Exam Configuration
|
||||
sebserver.examconfig.form.title=Exam Configuration
|
||||
|
|
BIN
src/main/resources/static/images/export.png
Normal file
BIN
src/main/resources/static/images/export.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 180 B |
Loading…
Reference in a new issue