added Config-Key export (not yet correct Config-Key)

This commit is contained in:
anhefti 2019-07-29 16:15:37 +02:00
parent 58c8b12ebe
commit d84ec117c1
23 changed files with 509 additions and 91 deletions

View file

@ -108,6 +108,7 @@ public final class API {
public static final String CONFIGURATION_NODE_ENDPOINT = "/configuration_node"; public static final String CONFIGURATION_NODE_ENDPOINT = "/configuration_node";
public static final String CONFIGURATION_FOLLOWUP_PATH_SEGMENT = "/followup"; public static final String CONFIGURATION_FOLLOWUP_PATH_SEGMENT = "/followup";
public static final String CONFIGURATION_CONFIG_KEY_PATH_SEGMENT = "/configkey";
public static final String CONFIGURATION_ENDPOINT = "/configuration"; public static final String CONFIGURATION_ENDPOINT = "/configuration";
public static final String CONFIGURATION_SAVE_TO_HISTORY_PATH_SEGMENT = "/save_to_history"; 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_UNDO_PATH_SEGMENT = "/undo";

View file

@ -0,0 +1,64 @@
/*
* 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.gbl.model.sebconfig;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
public final class ConfigKey {
public static final String ATTR_KEY = "key";
@JsonProperty(ATTR_KEY)
public final String key;
@JsonCreator
public ConfigKey(@JsonProperty(ATTR_KEY) final String key) {
this.key = key;
}
public String getKey() {
return this.key;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((this.key == null) ? 0 : this.key.hashCode());
return result;
}
@Override
public boolean equals(final Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
final ConfigKey other = (ConfigKey) obj;
if (this.key == null) {
if (other.key != null)
return false;
} else if (!this.key.equals(other.key))
return false;
return true;
}
@Override
public String toString() {
final StringBuilder builder = new StringBuilder();
builder.append("ConfigKey [key=");
builder.append(this.key);
builder.append("]");
return builder.toString();
}
}

View file

@ -385,7 +385,13 @@ public class ExamForm implements TemplateComposer {
indicatorTable::getSelection, indicatorTable::getSelection,
this::deleteSelectedIndicator, this::deleteSelectedIndicator,
INDICATOR_EMPTY_SELECTION_TEXT_KEY) INDICATOR_EMPTY_SELECTION_TEXT_KEY)
.publishIf(() -> modifyGrant && indicatorTable.hasAnyContent() && editable); .publishIf(() -> modifyGrant && indicatorTable.hasAnyContent() && editable)
.newAction(ActionDefinition.SEB_EXAM_CONFIG_GET_CONFIG_KEY)
.withEntityKey(entityKey)
.withExec(SebExamConfigPropForm.getConfigKeyFunction(this.pageService))
.publishIf(() -> userGrantCheck.r());
;
} }
} }

View file

@ -8,9 +8,13 @@
package ch.ethz.seb.sebserver.gui.content; package ch.ethz.seb.sebserver.gui.content;
import java.util.function.Function;
import org.eclipse.rap.rwt.RWT; import org.eclipse.rap.rwt.RWT;
import org.eclipse.rap.rwt.client.service.UrlLauncher; import org.eclipse.rap.rwt.client.service.UrlLauncher;
import org.eclipse.swt.SWT;
import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Text;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.annotation.Value;
@ -20,6 +24,7 @@ import org.springframework.stereotype.Component;
import ch.ethz.seb.sebserver.gbl.api.API; import ch.ethz.seb.sebserver.gbl.api.API;
import ch.ethz.seb.sebserver.gbl.model.Domain; import ch.ethz.seb.sebserver.gbl.model.Domain;
import ch.ethz.seb.sebserver.gbl.model.EntityKey; import ch.ethz.seb.sebserver.gbl.model.EntityKey;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigKey;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationNode; import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationNode;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationNode.ConfigurationType; import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationNode.ConfigurationType;
import ch.ethz.seb.sebserver.gbl.model.user.UserInfo; import ch.ethz.seb.sebserver.gbl.model.user.UserInfo;
@ -32,15 +37,19 @@ 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.PageContext;
import ch.ethz.seb.sebserver.gui.service.page.PageService; 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.TemplateComposer;
import ch.ethz.seb.sebserver.gui.service.page.impl.ModalInputDialog;
import ch.ethz.seb.sebserver.gui.service.page.impl.PageAction;
import ch.ethz.seb.sebserver.gui.service.remote.DownloadService; 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.SebExamConfigDownload;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestService; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestService;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.seb.examconfig.ExportConfigKey;
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.GetExamConfigNode;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.seb.examconfig.NewExamConfig; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.seb.examconfig.NewExamConfig;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.seb.examconfig.SaveExamConfig; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.seb.examconfig.SaveExamConfig;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.auth.CurrentUser; import ch.ethz.seb.sebserver.gui.service.remote.webservice.auth.CurrentUser;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.auth.CurrentUser.EntityGrantCheck; import ch.ethz.seb.sebserver.gui.service.remote.webservice.auth.CurrentUser.EntityGrantCheck;
import ch.ethz.seb.sebserver.gui.widget.WidgetFactory; import ch.ethz.seb.sebserver.gui.widget.WidgetFactory;
import ch.ethz.seb.sebserver.gui.widget.WidgetFactory.CustomVariant;
@Lazy @Lazy
@Component @Component
@ -60,6 +69,9 @@ public class SebExamConfigPropForm implements TemplateComposer {
private static final LocTextKey FORM_STATUS_TEXT_KEY = private static final LocTextKey FORM_STATUS_TEXT_KEY =
new LocTextKey("sebserver.examconfig.form.status"); new LocTextKey("sebserver.examconfig.form.status");
private static final LocTextKey CONFIG_KEY_TITLE_TEXT_KEY =
new LocTextKey("sebserver.examconfig.form.config-key.title");
private final PageService pageService; private final PageService pageService;
private final RestService restService; private final RestService restService;
private final CurrentUser currentUser; private final CurrentUser currentUser;
@ -183,6 +195,11 @@ public class SebExamConfigPropForm implements TemplateComposer {
}) })
.publishIf(() -> readGrant && isReadonly) .publishIf(() -> readGrant && isReadonly)
.newAction(ActionDefinition.SEB_EXAM_CONFIG_GET_CONFIG_KEY)
.withEntityKey(entityKey)
.withExec(SebExamConfigPropForm.getConfigKeyFunction(this.pageService))
.publishIf(() -> readGrant && isReadonly)
.newAction(ActionDefinition.SEB_EXAM_CONFIG_SAVE) .newAction(ActionDefinition.SEB_EXAM_CONFIG_SAVE)
.withEntityKey(entityKey) .withEntityKey(entityKey)
.withExec(formHandle::processFormSave) .withExec(formHandle::processFormSave)
@ -198,4 +215,37 @@ public class SebExamConfigPropForm implements TemplateComposer {
} }
public static Function<PageAction, PageAction> getConfigKeyFunction(final PageService pageService) {
final RestService restService = pageService.getResourceService().getRestService();
return action -> {
final ConfigKey configKey = restService.getBuilder(ExportConfigKey.class)
.withURIVariable(API.PARAM_MODEL_ID, action.getEntityKey().modelId)
.call()
.getOrThrow();
final WidgetFactory widgetFactory = pageService.getWidgetFactory();
final ModalInputDialog<Void> dialog = new ModalInputDialog<>(
action.pageContext().getParent().getShell(),
widgetFactory);
dialog.open(
CONFIG_KEY_TITLE_TEXT_KEY,
action.pageContext(),
pc -> {
final Composite content = widgetFactory.defaultPageLayout(
pc.getParent());
widgetFactory.labelLocalized(
content,
CustomVariant.TEXT_H3,
CONFIG_KEY_TITLE_TEXT_KEY);
final Text text = new Text(content, SWT.NONE);
text.setEditable(false);
text.setText(configKey.key);
});
return action;
};
}
} }

View file

@ -167,6 +167,10 @@ public class SebExamConfigSettingsForm implements TemplateComposer {
.withSuccess(KEY_UNDO_SUCCESS) .withSuccess(KEY_UNDO_SUCCESS)
.publishIf(() -> examConfigGrant.iw()) .publishIf(() -> examConfigGrant.iw())
.newAction(ActionDefinition.SEB_EXAM_CONFIG_VIEW_PROP)
.withEntityKey(entityKey)
.publish()
; ;
} catch (final Exception e) { } catch (final Exception e) {

View file

@ -177,7 +177,6 @@ public enum ActionDefinition {
QUIZ_DISCOVERY_SHOW_DETAILS( QUIZ_DISCOVERY_SHOW_DETAILS(
new LocTextKey("sebserver.quizdiscovery.action.details"), new LocTextKey("sebserver.quizdiscovery.action.details"),
ImageIcon.SHOW, ImageIcon.SHOW,
null,
ActionCategory.QUIZ_LIST), ActionCategory.QUIZ_LIST),
QUIZ_DISCOVERY_EXAM_IMPORT( QUIZ_DISCOVERY_EXAM_IMPORT(
new LocTextKey("sebserver.quizdiscovery.action.import"), new LocTextKey("sebserver.quizdiscovery.action.import"),
@ -340,6 +339,12 @@ public enum ActionDefinition {
ImageIcon.SHOW, ImageIcon.SHOW,
PageStateDefinition.SEB_EXAM_CONFIG_VIEW, PageStateDefinition.SEB_EXAM_CONFIG_VIEW,
ActionCategory.SEB_EXAM_CONFIG_LIST), ActionCategory.SEB_EXAM_CONFIG_LIST),
SEB_EXAM_CONFIG_VIEW_PROP(
new LocTextKey("sebserver.examconfig.action.view"),
ImageIcon.SHOW,
PageStateDefinition.SEB_EXAM_CONFIG_VIEW,
ActionCategory.FORM),
SEB_EXAM_CONFIG_MODIFY_PROP_FROM_LIST( SEB_EXAM_CONFIG_MODIFY_PROP_FROM_LIST(
new LocTextKey("sebserver.examconfig.action.list.modify.properties"), new LocTextKey("sebserver.examconfig.action.list.modify.properties"),
ImageIcon.EDIT, ImageIcon.EDIT,
@ -370,6 +375,10 @@ public enum ActionDefinition {
ImageIcon.EXPORT, ImageIcon.EXPORT,
PageStateDefinition.SEB_EXAM_CONFIG_VIEW, PageStateDefinition.SEB_EXAM_CONFIG_VIEW,
ActionCategory.FORM), ActionCategory.FORM),
SEB_EXAM_CONFIG_GET_CONFIG_KEY(
new LocTextKey("sebserver.examconfig.action.get-config-key"),
ImageIcon.SECURE,
ActionCategory.FORM),
SEB_EXAM_CONFIG_MODIFY_FROM_LIST( SEB_EXAM_CONFIG_MODIFY_FROM_LIST(
new LocTextKey("sebserver.examconfig.action.list.modify"), new LocTextKey("sebserver.examconfig.action.list.modify"),
@ -381,12 +390,12 @@ public enum ActionDefinition {
new LocTextKey("sebserver.examconfig.action.saveToHistory"), new LocTextKey("sebserver.examconfig.action.saveToHistory"),
ImageIcon.SAVE, ImageIcon.SAVE,
PageStateDefinition.SEB_EXAM_CONFIG_EDIT, PageStateDefinition.SEB_EXAM_CONFIG_EDIT,
ActionCategory.SEB_EXAM_CONFIG_LIST), ActionCategory.FORM),
SEB_EXAM_CONFIG_UNDO( SEB_EXAM_CONFIG_UNDO(
new LocTextKey("sebserver.examconfig.action.undo"), new LocTextKey("sebserver.examconfig.action.undo"),
ImageIcon.UNDO, ImageIcon.UNDO,
PageStateDefinition.SEB_EXAM_CONFIG_EDIT, PageStateDefinition.SEB_EXAM_CONFIG_EDIT,
ActionCategory.SEB_EXAM_CONFIG_LIST), ActionCategory.FORM),
RUNNING_EXAM_VIEW_LIST( RUNNING_EXAM_VIEW_LIST(
new LocTextKey("sebserver.monitoring.action.list"), new LocTextKey("sebserver.monitoring.action.list"),
@ -422,6 +431,14 @@ public enum ActionDefinition {
this(title, icon, targetState, ActionCategory.VARIA); this(title, icon, targetState, ActionCategory.VARIA);
} }
private ActionDefinition(
final LocTextKey title,
final ImageIcon icon,
final ActionCategory category) {
this(title, icon, null, category);
}
private ActionDefinition( private ActionDefinition(
final LocTextKey title, final LocTextKey title,
final ImageIcon icon, final ImageIcon icon,

View file

@ -0,0 +1,42 @@
/*
* Copyright (c) 2019 ETH Zürich, Educational Development and Technology (LET)
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package ch.ethz.seb.sebserver.gui.service.remote.webservice.api.seb.examconfig;
import org.springframework.context.annotation.Lazy;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import com.fasterxml.jackson.core.type.TypeReference;
import ch.ethz.seb.sebserver.gbl.api.API;
import ch.ethz.seb.sebserver.gbl.api.EntityType;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigKey;
import ch.ethz.seb.sebserver.gbl.profile.GuiProfile;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestCall;
@Lazy
@Component
@GuiProfile
public class ExportConfigKey extends RestCall<ConfigKey> {
public ExportConfigKey() {
super(new TypeKey<>(
CallType.UNDEFINED,
EntityType.CONFIGURATION_NODE,
new TypeReference<ConfigKey>() {
}),
HttpMethod.GET,
MediaType.APPLICATION_FORM_URLENCODED,
API.CONFIGURATION_NODE_ENDPOINT
+ API.MODEL_ID_VAR_PATH_SEGMENT
+ API.CONFIGURATION_CONFIG_KEY_PATH_SEGMENT);
}
}

View file

@ -34,8 +34,8 @@ public class ExportPlainXML extends AbstractExportCall {
HttpMethod.GET, HttpMethod.GET,
MediaType.APPLICATION_FORM_URLENCODED, MediaType.APPLICATION_FORM_URLENCODED,
API.CONFIGURATION_NODE_ENDPOINT API.CONFIGURATION_NODE_ENDPOINT
+ API.CONFIGURATION_PLAIN_XML_DOWNLOAD_PATH_SEGMENT + API.MODEL_ID_VAR_PATH_SEGMENT
+ API.MODEL_ID_VAR_PATH_SEGMENT); + API.CONFIGURATION_PLAIN_XML_DOWNLOAD_PATH_SEGMENT);
} }
} }

View file

@ -78,6 +78,7 @@ public class WidgetFactory {
NO("no.png"), NO("no.png"),
SAVE("save.png"), SAVE("save.png"),
EXPORT("export.png"), EXPORT("export.png"),
SECURE("secure.png"),
NEW("new.png"), NEW("new.png"),
DELETE("delete.png"), DELETE("delete.png"),
SEARCH("lens.png"), SEARCH("lens.png"),

View file

@ -0,0 +1,14 @@
/*
* 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;
public enum ConfigurationFormat {
XML,
JSON
}

View file

@ -13,6 +13,7 @@ import java.io.OutputStream;
import ch.ethz.seb.sebserver.gbl.api.APIMessage.FieldValidationException; 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.ConfigurationTableValues;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationValue; import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationValue;
import ch.ethz.seb.sebserver.gbl.util.Result;
/** The base interface and service for all SEB Exam Configuration related functionality. */ /** The base interface and service for all SEB Exam Configuration related functionality. */
public interface SebExamConfigService { public interface SebExamConfigService {
@ -40,13 +41,25 @@ public interface SebExamConfigService {
* @param configurationNodeId the identifier of the ConfigurationNode to export */ * @param configurationNodeId the identifier of the ConfigurationNode to export */
void exportPlainXML(OutputStream out, Long institutionId, Long configurationNodeId); void exportPlainXML(OutputStream out, Long institutionId, Long configurationNodeId);
/** Used to export a specified SEB Exam Configuration as plain JSON
* This exports the values of the follow-up configuration defined by a given
* ConfigurationNode (configurationNodeId) and sorts the attributes recording to
* the SEB configuration JSON specification to create a Config-Key as
* described here: https://www.safeexambrowser.org/developer/seb-config-key.html
*
* @param out The output stream to write the plain JSON text to.
* @param institutionId The identifier of the institution of the requesting user
* @param configurationNodeId the identifier of the ConfigurationNode to export */
void exportPlainJSON(OutputStream out, Long institutionId, Long configurationNodeId);
/** Used to export the default SEB Exam Configuration for a given exam identifier. /** Used to export the default SEB Exam Configuration for a given exam identifier.
* either with encryption if defined or as plain text within the SEB Configuration format * either with encryption if defined or as plain text within the SEB Configuration format
* as described here: https://www.safeexambrowser.org/developer/seb-file-format.html * as described here: https://www.safeexambrowser.org/developer/seb-file-format.html
* *
* @param out The output stream to write the export data to * @param out The output stream to write the export data to
* @param institutionId The identifier of the institution of the requesting user * @param institutionId The identifier of the institution of the requesting user
* @param examId the exam identifier */ * @param examId the exam identifier
* @return The configuration node identifier (PK) */
default Long exportForExam(final OutputStream out, final Long institutionId, final Long examId) { default Long exportForExam(final OutputStream out, final Long institutionId, final Long examId) {
return exportForExam(out, institutionId, examId, null); return exportForExam(out, institutionId, examId, null);
} }
@ -58,10 +71,11 @@ public interface SebExamConfigService {
* @param out The output stream to write the export data to * @param out The output stream to write the export data to
* @param institutionId The identifier of the institution of the requesting user * @param institutionId The identifier of the institution of the requesting user
* @param examId the exam identifier * @param examId the exam identifier
* @param userId the user identifier if a specific user based configuration shall be exported */ * @param userId the user identifier if a specific user based configuration shall be exported
* @return The configuration node identifier (PK) */
Long exportForExam(OutputStream out, Long institutionId, Long examId, String userId); Long exportForExam(OutputStream out, Long institutionId, Long examId, String userId);
/** TODO */ /** TODO */
String generateConfigKey(Long configurationNodeId); Result<String> generateConfigKey(Long institutionId, Long configurationNodeId);
} }

View file

@ -11,9 +11,11 @@ package ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.impl;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.function.Function; import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import java.util.stream.Stream; import java.util.stream.Stream;
@ -35,6 +37,7 @@ 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.dao.ConfigurationValueDAO;
import ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.AttributeValueConverter; import ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.AttributeValueConverter;
import ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.AttributeValueConverterService; import ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.AttributeValueConverterService;
import ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.ConfigurationFormat;
@Lazy @Lazy
@Component @Component
@ -49,6 +52,9 @@ public class ExamConfigIO {
private static final byte[] XML_PLIST_END_UTF_8 = Utils.toByteArray(Constants.XML_PLIST_END); private static final byte[] XML_PLIST_END_UTF_8 = Utils.toByteArray(Constants.XML_PLIST_END);
private static final byte[] XML_DICT_START_UTF_8 = Utils.toByteArray(Constants.XML_DICT_START); private static final byte[] XML_DICT_START_UTF_8 = Utils.toByteArray(Constants.XML_DICT_START);
private static final byte[] XML_DICT_END_UTF_8 = Utils.toByteArray(Constants.XML_DICT_END); private static final byte[] XML_DICT_END_UTF_8 = Utils.toByteArray(Constants.XML_DICT_END);
private static final byte[] JSON_START = Utils.toByteArray("{");
private static final byte[] JSON_END = Utils.toByteArray("}");
private static final byte[] JSON_SEPARATOR = Utils.toByteArray(Constants.LIST_SEPARATOR);
private final ConfigurationAttributeDAO configurationAttributeDAO; private final ConfigurationAttributeDAO configurationAttributeDAO;
private final ConfigurationValueDAO configurationValueDAO; private final ConfigurationValueDAO configurationValueDAO;
@ -68,7 +74,8 @@ public class ExamConfigIO {
} }
@Async(AsyncServiceSpringConfig.EXECUTOR_BEAN_NAME) @Async(AsyncServiceSpringConfig.EXECUTOR_BEAN_NAME)
void exportPlainXML( void exportPlain(
final ConfigurationFormat exportFormat,
final OutputStream out, final OutputStream out,
final Long institutionId, final Long institutionId,
final Long configurationNodeId) { final Long configurationNodeId) {
@ -82,6 +89,7 @@ public class ExamConfigIO {
.getOrThrow() .getOrThrow()
.stream() .stream()
.flatMap(this::convertAttribute) .flatMap(this::convertAttribute)
.filter(exportFormatBasedAttributeFilter(exportFormat))
.sorted() .sorted()
.collect(Collectors.toList()); .collect(Collectors.toList());
@ -94,25 +102,39 @@ public class ExamConfigIO {
getConfigurationValueSupplier(institutionId, configurationId); getConfigurationValueSupplier(institutionId, configurationId);
try { try {
// write headers
out.write(XML_VERSION_HEADER_UTF_8);
out.write(XML_DOCTYPE_HEADER_UTF_8);
// plist open writeHeader(exportFormat, out);
out.write(XML_PLIST_START_V1_UTF_8);
out.write(XML_DICT_START_UTF_8);
// write attributes // write attributes
for (final ConfigurationAttribute attribute : sortedAttributes) { final Iterator<ConfigurationAttribute> iterator = sortedAttributes.iterator();
this.attributeValueConverterService.getAttributeValueConverter(attribute).convertToXML( while (iterator.hasNext()) {
out,
attribute, final ConfigurationAttribute attribute = iterator.next();
configurationValueSupplier); final AttributeValueConverter attributeValueConverter =
this.attributeValueConverterService.getAttributeValueConverter(attribute);
switch (exportFormat) {
case XML: {
attributeValueConverter.convertToXML(
out,
attribute,
configurationValueSupplier);
break;
}
case JSON: {
attributeValueConverter.convertToJSON(
out,
attribute,
configurationValueSupplier);
if (iterator.hasNext()) {
out.write(JSON_SEPARATOR);
}
break;
}
}
} }
// plist close writeFooter(exportFormat, out);
out.write(XML_DICT_END_UTF_8);
out.write(XML_PLIST_END_UTF_8);
out.flush(); out.flush();
if (log.isDebugEnabled()) { if (log.isDebugEnabled()) {
@ -131,6 +153,49 @@ public class ExamConfigIO {
} }
} }
private Predicate<ConfigurationAttribute> exportFormatBasedAttributeFilter(final ConfigurationFormat format) {
// Filter originatorVersion according to: https://www.safeexambrowser.org/developer/seb-config-key.html
return attr -> !("originatorVersion".equals(attr.getName()) && format == ConfigurationFormat.JSON);
}
private void writeFooter(
final ConfigurationFormat exportFormat,
final OutputStream out) throws IOException {
if (exportFormat == ConfigurationFormat.XML) {
// plist close
out.write(XML_DICT_END_UTF_8);
out.write(XML_PLIST_END_UTF_8);
} else {
out.write(JSON_END);
}
}
private void writeHeader(
final ConfigurationFormat exportFormat,
final OutputStream out) throws IOException {
if (exportFormat == ConfigurationFormat.XML) {
writeXMLHeaderInformation(out);
} else {
writeJSONHeaderInformation(out);
}
}
private void writeJSONHeaderInformation(final OutputStream out) throws IOException {
out.write(JSON_START);
}
private void writeXMLHeaderInformation(final OutputStream out) throws IOException {
// write headers
out.write(XML_VERSION_HEADER_UTF_8);
out.write(XML_DOCTYPE_HEADER_UTF_8);
// plist open
out.write(XML_PLIST_START_V1_UTF_8);
out.write(XML_DICT_START_UTF_8);
}
private Stream<ConfigurationAttribute> convertAttribute(final ConfigurationAttribute attr) { private Stream<ConfigurationAttribute> convertAttribute(final ConfigurationAttribute attr) {
final AttributeValueConverter attributeValueConverter = final AttributeValueConverter attributeValueConverter =
this.attributeValueConverterService.getAttributeValueConverter(attr); this.attributeValueConverterService.getAttributeValueConverter(attr);

View file

@ -13,7 +13,11 @@ import java.io.OutputStream;
import java.io.PipedInputStream; import java.io.PipedInputStream;
import java.io.PipedOutputStream; import java.io.PipedOutputStream;
import java.util.Collection; import java.util.Collection;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.io.IOUtils; import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger; import org.slf4j.Logger;
@ -21,6 +25,8 @@ import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Lazy; import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import ch.ethz.seb.sebserver.gbl.api.APIMessage;
import ch.ethz.seb.sebserver.gbl.api.APIMessage.APIMessageException;
import ch.ethz.seb.sebserver.gbl.api.APIMessage.FieldValidationException; import ch.ethz.seb.sebserver.gbl.api.APIMessage.FieldValidationException;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationAttribute; 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.ConfigurationTableValues;
@ -29,6 +35,7 @@ import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile;
import ch.ethz.seb.sebserver.gbl.util.Result; import ch.ethz.seb.sebserver.gbl.util.Result;
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.ConfigurationAttributeDAO; import ch.ethz.seb.sebserver.webservice.servicelayer.dao.ConfigurationAttributeDAO;
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.ExamConfigurationMapDAO; import ch.ethz.seb.sebserver.webservice.servicelayer.dao.ExamConfigurationMapDAO;
import ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.ConfigurationFormat;
import ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.ConfigurationValueValidator; 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.SebExamConfigService;
@ -77,8 +84,28 @@ public class SebExamConfigServiceImpl implements SebExamConfigService {
@Override @Override
public void validate(final ConfigurationTableValues tableValue) throws FieldValidationException { public void validate(final ConfigurationTableValues tableValue) throws FieldValidationException {
// TODO Auto-generated method stub final List<APIMessage> errors = tableValue.values.stream()
throw new UnsupportedOperationException("TODO"); .map(tv -> new ConfigurationValue(
null,
tableValue.institutionId,
tableValue.configurationId,
tv.attributeId,
tv.listIndex,
tv.value))
.flatMap(cv -> {
try {
validate(cv);
return Stream.empty();
} catch (final FieldValidationException fve) {
return Stream.of(fve);
}
})
.map(fve -> fve.apiMessage)
.collect(Collectors.toList());
if (!errors.isEmpty()) {
throw new APIMessageException(errors);
}
} }
@Override @Override
@ -87,48 +114,16 @@ public class SebExamConfigServiceImpl implements SebExamConfigService {
final Long institutionId, final Long institutionId,
final Long configurationNodeId) { final Long configurationNodeId) {
if (log.isDebugEnabled()) { this.exportPlain(ConfigurationFormat.XML, out, institutionId, configurationNodeId);
log.debug("Start to stream plain text SEB clonfiguration data"); }
}
PipedOutputStream pout = null; @Override
PipedInputStream pin = null; public void exportPlainJSON(
try { final OutputStream out,
pout = new PipedOutputStream(); final Long institutionId,
pin = new PipedInputStream(pout); final Long configurationNodeId) {
this.examConfigIO.exportPlainXML(
pout,
institutionId,
configurationNodeId);
IOUtils.copyLarge(pin, out);
pout.flush();
pout.close();
pin.close();
} catch (final IOException e) {
log.error("Error while stream plain text SEB clonfiguration data: ", e);
} finally {
try {
if (pin != null)
pin.close();
} catch (final IOException e1) {
log.error("Failed to close PipedInputStream: ", e1);
}
try {
if (pout != null)
pout.close();
} catch (final IOException e1) {
log.error("Failed to close PipedOutputStream: ", e1);
}
if (log.isDebugEnabled()) {
log.debug("Finished to stream plain text SEB clonfiguration data");
}
}
this.exportPlain(ConfigurationFormat.JSON, out, institutionId, configurationNodeId);
} }
public Result<Long> getDefaultConfigurationIdForExam(final Long examId) { public Result<Long> getDefaultConfigurationIdForExam(final Long examId) {
@ -160,12 +155,125 @@ public class SebExamConfigServiceImpl implements SebExamConfigService {
} }
@Override @Override
public String generateConfigKey(final Long configurationNodeId) { public Result<String> generateConfigKey(
final Long institutionId,
final Long configurationNodeId) {
//DigestUtils.sha1Hex(data) if (log.isDebugEnabled()) {
log.debug("Start to stream plain JSON SEB clonfiguration data for Config-Key generation");
}
// TODO https://www.safeexambrowser.org/developer/seb-config-key.html if (log.isTraceEnabled()) {
throw new UnsupportedOperationException("TODO"); PipedOutputStream pout = null;
PipedInputStream pin = null;
try {
pout = new PipedOutputStream();
pin = new PipedInputStream(pout);
this.examConfigIO.exportPlain(
ConfigurationFormat.JSON,
pout,
institutionId,
configurationNodeId);
final String json = IOUtils.toString(pin, "UTF-8");
log.trace("SEB Configuration JSON to create Config-Key: {}", json);
} catch (final Exception e) {
log.error("Failed to trace SEB Configuration JSON: ", e);
}
}
PipedOutputStream pout = null;
PipedInputStream pin = null;
try {
pout = new PipedOutputStream();
pin = new PipedInputStream(pout);
this.examConfigIO.exportPlain(
ConfigurationFormat.JSON,
pout,
institutionId,
configurationNodeId);
final String configKey = DigestUtils.sha256Hex(pin);
pout.flush();
pout.close();
pin.close();
return Result.of(configKey);
} catch (final IOException e) {
log.error("Error while stream plain JSON SEB clonfiguration data for Config-Key generation: ", e);
return Result.ofError(e);
} finally {
try {
if (pin != null)
pin.close();
} catch (final IOException e1) {
log.error("Failed to close PipedInputStream: ", e1);
}
try {
if (pout != null)
pout.close();
} catch (final IOException e1) {
log.error("Failed to close PipedOutputStream: ", e1);
}
if (log.isDebugEnabled()) {
log.debug("Finished to stream plain JSON SEB clonfiguration data for Config-Key generation");
}
}
}
private void exportPlain(
final ConfigurationFormat exportFormat,
final OutputStream out,
final Long institutionId,
final Long configurationNodeId) {
if (log.isDebugEnabled()) {
log.debug("Start to stream plain text SEB clonfiguration data");
}
PipedOutputStream pout = null;
PipedInputStream pin = null;
try {
pout = new PipedOutputStream();
pin = new PipedInputStream(pout);
this.examConfigIO.exportPlain(
exportFormat,
pout,
institutionId,
configurationNodeId);
IOUtils.copyLarge(pin, out);
pout.flush();
pout.close();
pin.close();
} catch (final IOException e) {
log.error("Error while stream plain text SEB clonfiguration data: ", e);
} finally {
try {
if (pin != null)
pin.close();
} catch (final IOException e1) {
log.error("Failed to close PipedInputStream: ", e1);
}
try {
if (pout != null)
pout.close();
} catch (final IOException e1) {
log.error("Failed to close PipedOutputStream: ", e1);
}
if (log.isDebugEnabled()) {
log.debug("Finished to stream plain text SEB clonfiguration data");
}
}
} }
} }

View file

@ -10,9 +10,6 @@ package ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.impl.converter;
import java.io.IOException; import java.io.IOException;
import java.io.OutputStream; import java.io.OutputStream;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set; import java.util.Set;
import java.util.function.Function; import java.util.function.Function;
@ -35,16 +32,24 @@ public class IntegerConverter implements AttributeValueConverter {
private static final Logger log = LoggerFactory.getLogger(IntegerConverter.class); private static final Logger log = LoggerFactory.getLogger(IntegerConverter.class);
public static final Set<AttributeType> SUPPORTED_TYPES = Collections.unmodifiableSet( public static final Set<String> SUPPORTED_ATTR_NAMES = Utils.immutableSetOf(
new HashSet<>(Arrays.asList( "taskBarHeight",
AttributeType.INTEGER, "allowedDisplaysMaxNumber");
AttributeType.SLIDER,
AttributeType.SINGLE_SELECTION, public static final Set<AttributeType> SUPPORTED_TYPES = Utils.immutableSetOf(
AttributeType.RADIO_SELECTION))); AttributeType.INTEGER,
AttributeType.SLIDER,
AttributeType.SINGLE_SELECTION,
AttributeType.RADIO_SELECTION);
private static final String XML_TEMPLATE = "<key>%s</key><integer>%s</integer>"; private static final String XML_TEMPLATE = "<key>%s</key><integer>%s</integer>";
private static final String JSON_TEMPLATE = "\"%s\":%s"; private static final String JSON_TEMPLATE = "\"%s\":%s";
@Override
public Set<String> names() {
return SUPPORTED_ATTR_NAMES;
}
@Override @Override
public Set<AttributeType> types() { public Set<AttributeType> types() {
return SUPPORTED_TYPES; return SUPPORTED_TYPES;

View file

@ -44,7 +44,7 @@ public class StringConverter implements AttributeValueConverter {
private static final String XML_TEMPLATE_EMPTY = "<key>%s</key><string />"; private static final String XML_TEMPLATE_EMPTY = "<key>%s</key><string />";
private static final String JSON_TEMPLATE = "\"%s\":\"%s\""; private static final String JSON_TEMPLATE = "\"%s\":\"%s\"";
private static final String JSON_TEMPLATE_EMPTY = "\"%s\":"; private static final String JSON_TEMPLATE_EMPTY = "\"%s\":\"\"";
@Override @Override
public Set<AttributeType> types() { public Set<AttributeType> types() {

View file

@ -78,7 +78,7 @@ public class APIExceptionHandler extends ResponseEntityExceptionHandler {
} }
@ExceptionHandler(OAuth2Exception.class) @ExceptionHandler(OAuth2Exception.class)
public ResponseEntity<Object> handleBeanValidationException( public ResponseEntity<Object> handleOAuth2Exception(
final OAuth2Exception ex, final OAuth2Exception ex,
final WebRequest request) { final WebRequest request) {

View file

@ -22,6 +22,7 @@ import org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBo
import ch.ethz.seb.sebserver.gbl.api.API; import ch.ethz.seb.sebserver.gbl.api.API;
import ch.ethz.seb.sebserver.gbl.api.POSTMapper; import ch.ethz.seb.sebserver.gbl.api.POSTMapper;
import ch.ethz.seb.sebserver.gbl.model.Domain.EXAM; import ch.ethz.seb.sebserver.gbl.model.Domain.EXAM;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigKey;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.Configuration; import ch.ethz.seb.sebserver.gbl.model.sebconfig.Configuration;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationNode; import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationNode;
import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile; import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile;
@ -84,20 +85,42 @@ public class ConfigurationNodeController extends EntityController<ConfigurationN
method = RequestMethod.GET, method = RequestMethod.GET,
consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE, consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE,
produces = MediaType.APPLICATION_JSON_UTF8_VALUE) produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
public Configuration getFollowup(@PathVariable final Long configNodeId) { public Configuration getFollowup(@PathVariable final Long modelId) {
this.entityDAO this.entityDAO
.byPK(configNodeId) .byPK(modelId)
.flatMap(this::checkModifyAccess) .flatMap(this::checkModifyAccess)
.getOrThrow(); .getOrThrow();
return this.configurationDAO return this.configurationDAO
.getFollowupConfiguration(configNodeId) .getFollowupConfiguration(modelId)
.getOrThrow(); .getOrThrow();
} }
@RequestMapping( @RequestMapping(
path = API.CONFIGURATION_PLAIN_XML_DOWNLOAD_PATH_SEGMENT + API.MODEL_ID_VAR_PATH_SEGMENT, path = API.MODEL_ID_VAR_PATH_SEGMENT + API.CONFIGURATION_CONFIG_KEY_PATH_SEGMENT,
method = RequestMethod.GET,
consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE,
produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
public ConfigKey getConfigKey(
@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)
.flatMap(this.authorization::checkRead);
final String configKey = this.sebExamConfigService
.generateConfigKey(institutionId, modelId)
.getOrThrow();
return new ConfigKey(configKey);
}
@RequestMapping(
path = API.MODEL_ID_VAR_PATH_SEGMENT + API.CONFIGURATION_PLAIN_XML_DOWNLOAD_PATH_SEGMENT,
method = RequestMethod.GET, method = RequestMethod.GET,
produces = MediaType.APPLICATION_OCTET_STREAM_VALUE) produces = MediaType.APPLICATION_OCTET_STREAM_VALUE)
public ResponseEntity<StreamingResponseBody> downloadPlainXMLConfig( public ResponseEntity<StreamingResponseBody> downloadPlainXMLConfig(
@ -108,7 +131,7 @@ public class ConfigurationNodeController extends EntityController<ConfigurationN
defaultValue = UserService.USERS_INSTITUTION_AS_DEFAULT) final Long institutionId) { defaultValue = UserService.USERS_INSTITUTION_AS_DEFAULT) final Long institutionId) {
this.entityDAO.byPK(modelId) this.entityDAO.byPK(modelId)
.map(this.authorization::checkRead); .flatMap(this.authorization::checkRead);
final StreamingResponseBody stream = out -> this.sebExamConfigService final StreamingResponseBody stream = out -> this.sebExamConfigService
.exportPlainXML(out, institutionId, modelId); .exportPlainXML(out, institutionId, modelId);

View file

@ -215,7 +215,7 @@ INSERT IGNORE INTO configuration_attribute VALUES
(259, 'SOCKSPassword', 'TEXT_FIELD', 220, null, null, 'groupId=socks,createDefaultValue=true', null), (259, 'SOCKSPassword', 'TEXT_FIELD', 220, null, null, 'groupId=socks,createDefaultValue=true', null),
(260, 'RTSPEnable', 'CHECKBOX', 220, null, null, 'groupId=rtsp,createDefaultValue=true', 'false'), (260, 'RTSPEnable', 'CHECKBOX', 220, null, null, 'groupId=rtsp,createDefaultValue=true', 'false'),
(261, 'RTSPProxy', 'TEXT_FIELD', 220, null, null, 'groupId=rtsp,createDefaultValue=true', null), (261, 'RTSPProxy', 'TEXT_FIELD', 220, null, null, 'groupId=rtsp,createDefaultValue=true', null),
(262, 'RTSPPort', 'INTEGER', 220, null, null, 'groupId=rtsp,createDefaultValue=true', '1080'), (262, 'RTSPPort', 'INTEGER', 220, null, null, 'groupId=rtsp,createDefaultValue=true', '554'),
(263, 'RTSPRequiresPassword', 'CHECKBOX', 220, null, null, 'groupId=rtsp,createDefaultValue=true', 'false'), (263, 'RTSPRequiresPassword', 'CHECKBOX', 220, null, null, 'groupId=rtsp,createDefaultValue=true', 'false'),
(264, 'RTSPUsername', 'TEXT_FIELD', 220, null, null, 'groupId=rtsp,createDefaultValue=true', null), (264, 'RTSPUsername', 'TEXT_FIELD', 220, null, null, 'groupId=rtsp,createDefaultValue=true', null),
(265, 'RTSPPassword', 'TEXT_FIELD', 220, null, null, 'groupId=rtsp,createDefaultValue=true', null), (265, 'RTSPPassword', 'TEXT_FIELD', 220, null, null, 'groupId=rtsp,createDefaultValue=true', null),
@ -228,7 +228,7 @@ INSERT IGNORE INTO configuration_attribute VALUES
(304, 'enablePrivateClipboard', 'CHECKBOX', null, null, null, null, 'true'), (304, 'enablePrivateClipboard', 'CHECKBOX', null, null, null, null, 'true'),
(305, 'enableLogging', 'CHECKBOX', null, null, null, null, 'false'), (305, 'enableLogging', 'CHECKBOX', null, null, null, null, 'false'),
(306, 'logDirectoryWin', 'TEXT_FIELD', null, null, null, null, ''), (306, 'logDirectoryWin', 'TEXT_FIELD', null, null, null, null, ''),
(307, 'logDirectoryOSX', 'TEXT_FIELD', null, null, null, null, '~/Documents'), (307, 'logDirectoryOSX', 'TEXT_FIELD', null, null, null, null, 'NSTemporaryDirectory'),
(308, 'minMacOSVersion', 'SINGLE_SELECTION', null, '0,1,2,3,4,5,6,7', null, null, '0'), (308, 'minMacOSVersion', 'SINGLE_SELECTION', null, '0,1,2,3,4,5,6,7', null, null, '0'),
(309, 'enableAppSwitcherCheck', 'CHECKBOX', null, null, null, null, 'true'), (309, 'enableAppSwitcherCheck', 'CHECKBOX', null, null, null, null, 'true'),
(310, 'forceAppFolderInstall', 'CHECKBOX', null, null, null, null, 'true'), (310, 'forceAppFolderInstall', 'CHECKBOX', null, null, null, null, 'true'),

View file

@ -33,6 +33,7 @@
<Logger name="org.springframework.security.oauth2" level="INFO" additivity="true" /> <Logger name="org.springframework.security.oauth2" level="INFO" additivity="true" />
<Logger name="ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.impl.SebClientConfigServiceImpl" level="DEBUG" additivity="true" /> <Logger name="ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.impl.SebClientConfigServiceImpl" level="DEBUG" additivity="true" />
<Logger name="ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.impl.SebExamConfigServiceImpl" level="TRACE" additivity="true" />
</springProfile> </springProfile>

View file

@ -392,6 +392,7 @@ sebserver.examconfig.action.list.new=New Exam Configuration
sebserver.examconfig.action.list.view=View Configuration sebserver.examconfig.action.list.view=View Configuration
sebserver.examconfig.action.list.modify=Edit Settings sebserver.examconfig.action.list.modify=Edit Settings
sebserver.examconfig.action.list.modify.properties=Edit Configuration sebserver.examconfig.action.list.modify.properties=Edit Configuration
sebserver.examconfig.action.view=View Configuration
sebserver.examconfig.action.modify=Edit Settings sebserver.examconfig.action.modify=Edit Settings
sebserver.examconfig.action.modify.properties=Edit Configuration sebserver.examconfig.action.modify.properties=Edit Configuration
sebserver.examconfig.action.save=Save sebserver.examconfig.action.save=Save
@ -400,12 +401,14 @@ sebserver.examconfig.action.saveToHistory.success=Successfully saved in history
sebserver.examconfig.action.undo=Undo sebserver.examconfig.action.undo=Undo
sebserver.examconfig.action.undo.success=Successfully reverted to last saved state sebserver.examconfig.action.undo.success=Successfully reverted to last saved state
sebserver.examconfig.action.export.plainxml=Export XML sebserver.examconfig.action.export.plainxml=Export XML
sebserver.examconfig.action.get-config-key=Export Config-Key
sebserver.examconfig.form.title.new=New Exam Configuration sebserver.examconfig.form.title.new=New Exam Configuration
sebserver.examconfig.form.title=Exam Configuration sebserver.examconfig.form.title=Exam Configuration
sebserver.examconfig.form.name=Name sebserver.examconfig.form.name=Name
sebserver.examconfig.form.description=Description sebserver.examconfig.form.description=Description
sebserver.examconfig.form.status=Status sebserver.examconfig.form.status=Status
sebserver.examconfig.form.config-key.title=Config Key
sebserver.examconfig.status.CONSTRUCTION=Under Construction sebserver.examconfig.status.CONSTRUCTION=Under Construction
sebserver.examconfig.status.READY_TO_USE=Ready To Use sebserver.examconfig.status.READY_TO_USE=Ready To Use

Binary file not shown.

After

Width:  |  Height:  |  Size: 190 B

View file

@ -183,7 +183,7 @@ INSERT IGNORE INTO configuration_attribute VALUES
(259, 'SOCKSPassword', 'TEXT_FIELD', 220, null, null, 'groupId=socks,createDefaultValue=true', null), (259, 'SOCKSPassword', 'TEXT_FIELD', 220, null, null, 'groupId=socks,createDefaultValue=true', null),
(260, 'RTSPEnable', 'CHECKBOX', 220, null, null, 'groupId=rtsp,createDefaultValue=true', 'false'), (260, 'RTSPEnable', 'CHECKBOX', 220, null, null, 'groupId=rtsp,createDefaultValue=true', 'false'),
(261, 'RTSPProxy', 'TEXT_FIELD', 220, null, null, 'groupId=rtsp,createDefaultValue=true', null), (261, 'RTSPProxy', 'TEXT_FIELD', 220, null, null, 'groupId=rtsp,createDefaultValue=true', null),
(262, 'RTSPPort', 'INTEGER', 220, null, null, 'groupId=rtsp,createDefaultValue=true', '1080'), (262, 'RTSPPort', 'INTEGER', 220, null, null, 'groupId=rtsp,createDefaultValue=true', '554'),
(263, 'RTSPRequiresPassword', 'CHECKBOX', 220, null, null, 'groupId=rtsp,createDefaultValue=true', 'false'), (263, 'RTSPRequiresPassword', 'CHECKBOX', 220, null, null, 'groupId=rtsp,createDefaultValue=true', 'false'),
(264, 'RTSPUsername', 'TEXT_FIELD', 220, null, null, 'groupId=rtsp,createDefaultValue=true', null), (264, 'RTSPUsername', 'TEXT_FIELD', 220, null, null, 'groupId=rtsp,createDefaultValue=true', null),
(265, 'RTSPPassword', 'TEXT_FIELD', 220, null, null, 'groupId=rtsp,createDefaultValue=true', null), (265, 'RTSPPassword', 'TEXT_FIELD', 220, null, null, 'groupId=rtsp,createDefaultValue=true', null),
@ -196,7 +196,7 @@ INSERT IGNORE INTO configuration_attribute VALUES
(304, 'enablePrivateClipboard', 'CHECKBOX', null, null, null, null, 'true'), (304, 'enablePrivateClipboard', 'CHECKBOX', null, null, null, null, 'true'),
(305, 'enableLogging', 'CHECKBOX', null, null, null, null, 'false'), (305, 'enableLogging', 'CHECKBOX', null, null, null, null, 'false'),
(306, 'logDirectoryWin', 'TEXT_FIELD', null, null, null, null, ''), (306, 'logDirectoryWin', 'TEXT_FIELD', null, null, null, null, ''),
(307, 'logDirectoryOSX', 'TEXT_FIELD', null, null, null, null, '~/Documents'), (307, 'logDirectoryOSX', 'TEXT_FIELD', null, null, null, null, 'NSTemporaryDirectory'),
(308, 'minMacOSVersion', 'SINGLE_SELECTION', null, '0,1,2,3,4,5,6,7', null, null, '0'), (308, 'minMacOSVersion', 'SINGLE_SELECTION', null, '0,1,2,3,4,5,6,7', null, null, '0'),
(309, 'enableAppSwitcherCheck', 'CHECKBOX', null, null, null, null, 'true'), (309, 'enableAppSwitcherCheck', 'CHECKBOX', null, null, null, null, 'true'),
(310, 'forceAppFolderInstall', 'CHECKBOX', null, null, null, null, 'true'), (310, 'forceAppFolderInstall', 'CHECKBOX', null, null, null, null, 'true'),