added Config-Key export (not yet correct Config-Key)
This commit is contained in:
parent
58c8b12ebe
commit
d84ec117c1
23 changed files with 509 additions and 91 deletions
|
@ -108,6 +108,7 @@ public final class API {
|
|||
|
||||
public static final String CONFIGURATION_NODE_ENDPOINT = "/configuration_node";
|
||||
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_SAVE_TO_HISTORY_PATH_SEGMENT = "/save_to_history";
|
||||
public static final String CONFIGURATION_UNDO_PATH_SEGMENT = "/undo";
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
|
@ -385,7 +385,13 @@ public class ExamForm implements TemplateComposer {
|
|||
indicatorTable::getSelection,
|
||||
this::deleteSelectedIndicator,
|
||||
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());
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -8,9 +8,13 @@
|
|||
|
||||
package ch.ethz.seb.sebserver.gui.content;
|
||||
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.eclipse.rap.rwt.RWT;
|
||||
import org.eclipse.rap.rwt.client.service.UrlLauncher;
|
||||
import org.eclipse.swt.SWT;
|
||||
import org.eclipse.swt.widgets.Composite;
|
||||
import org.eclipse.swt.widgets.Text;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
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.model.Domain;
|
||||
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.ConfigurationType;
|
||||
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.PageService;
|
||||
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.SebExamConfigDownload;
|
||||
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.NewExamConfig;
|
||||
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.EntityGrantCheck;
|
||||
import ch.ethz.seb.sebserver.gui.widget.WidgetFactory;
|
||||
import ch.ethz.seb.sebserver.gui.widget.WidgetFactory.CustomVariant;
|
||||
|
||||
@Lazy
|
||||
@Component
|
||||
|
@ -60,6 +69,9 @@ public class SebExamConfigPropForm implements TemplateComposer {
|
|||
private static final LocTextKey FORM_STATUS_TEXT_KEY =
|
||||
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 RestService restService;
|
||||
private final CurrentUser currentUser;
|
||||
|
@ -183,6 +195,11 @@ public class SebExamConfigPropForm implements TemplateComposer {
|
|||
})
|
||||
.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)
|
||||
.withEntityKey(entityKey)
|
||||
.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;
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -167,6 +167,10 @@ public class SebExamConfigSettingsForm implements TemplateComposer {
|
|||
.withSuccess(KEY_UNDO_SUCCESS)
|
||||
.publishIf(() -> examConfigGrant.iw())
|
||||
|
||||
.newAction(ActionDefinition.SEB_EXAM_CONFIG_VIEW_PROP)
|
||||
.withEntityKey(entityKey)
|
||||
.publish()
|
||||
|
||||
;
|
||||
|
||||
} catch (final Exception e) {
|
||||
|
|
|
@ -177,7 +177,6 @@ public enum ActionDefinition {
|
|||
QUIZ_DISCOVERY_SHOW_DETAILS(
|
||||
new LocTextKey("sebserver.quizdiscovery.action.details"),
|
||||
ImageIcon.SHOW,
|
||||
null,
|
||||
ActionCategory.QUIZ_LIST),
|
||||
QUIZ_DISCOVERY_EXAM_IMPORT(
|
||||
new LocTextKey("sebserver.quizdiscovery.action.import"),
|
||||
|
@ -340,6 +339,12 @@ public enum ActionDefinition {
|
|||
ImageIcon.SHOW,
|
||||
PageStateDefinition.SEB_EXAM_CONFIG_VIEW,
|
||||
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(
|
||||
new LocTextKey("sebserver.examconfig.action.list.modify.properties"),
|
||||
ImageIcon.EDIT,
|
||||
|
@ -370,6 +375,10 @@ public enum ActionDefinition {
|
|||
ImageIcon.EXPORT,
|
||||
PageStateDefinition.SEB_EXAM_CONFIG_VIEW,
|
||||
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(
|
||||
new LocTextKey("sebserver.examconfig.action.list.modify"),
|
||||
|
@ -381,12 +390,12 @@ public enum ActionDefinition {
|
|||
new LocTextKey("sebserver.examconfig.action.saveToHistory"),
|
||||
ImageIcon.SAVE,
|
||||
PageStateDefinition.SEB_EXAM_CONFIG_EDIT,
|
||||
ActionCategory.SEB_EXAM_CONFIG_LIST),
|
||||
ActionCategory.FORM),
|
||||
SEB_EXAM_CONFIG_UNDO(
|
||||
new LocTextKey("sebserver.examconfig.action.undo"),
|
||||
ImageIcon.UNDO,
|
||||
PageStateDefinition.SEB_EXAM_CONFIG_EDIT,
|
||||
ActionCategory.SEB_EXAM_CONFIG_LIST),
|
||||
ActionCategory.FORM),
|
||||
|
||||
RUNNING_EXAM_VIEW_LIST(
|
||||
new LocTextKey("sebserver.monitoring.action.list"),
|
||||
|
@ -422,6 +431,14 @@ public enum ActionDefinition {
|
|||
this(title, icon, targetState, ActionCategory.VARIA);
|
||||
}
|
||||
|
||||
private ActionDefinition(
|
||||
final LocTextKey title,
|
||||
final ImageIcon icon,
|
||||
final ActionCategory category) {
|
||||
|
||||
this(title, icon, null, category);
|
||||
}
|
||||
|
||||
private ActionDefinition(
|
||||
final LocTextKey title,
|
||||
final ImageIcon icon,
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
|
@ -34,8 +34,8 @@ public class ExportPlainXML extends AbstractExportCall {
|
|||
HttpMethod.GET,
|
||||
MediaType.APPLICATION_FORM_URLENCODED,
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -78,6 +78,7 @@ public class WidgetFactory {
|
|||
NO("no.png"),
|
||||
SAVE("save.png"),
|
||||
EXPORT("export.png"),
|
||||
SECURE("secure.png"),
|
||||
NEW("new.png"),
|
||||
DELETE("delete.png"),
|
||||
SEARCH("lens.png"),
|
||||
|
|
|
@ -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
|
||||
}
|
|
@ -13,6 +13,7 @@ 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;
|
||||
import ch.ethz.seb.sebserver.gbl.util.Result;
|
||||
|
||||
/** The base interface and service for all SEB Exam Configuration related functionality. */
|
||||
public interface SebExamConfigService {
|
||||
|
@ -40,13 +41,25 @@ public interface SebExamConfigService {
|
|||
* @param configurationNodeId the identifier of the ConfigurationNode to export */
|
||||
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.
|
||||
* 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
|
||||
*
|
||||
* @param out The output stream to write the export data to
|
||||
* @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) {
|
||||
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 institutionId The identifier of the institution of the requesting user
|
||||
* @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);
|
||||
|
||||
/** TODO */
|
||||
String generateConfigKey(Long configurationNodeId);
|
||||
Result<String> generateConfigKey(Long institutionId, Long configurationNodeId);
|
||||
|
||||
}
|
||||
|
|
|
@ -11,9 +11,11 @@ package ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.impl;
|
|||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.Collectors;
|
||||
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.sebconfig.AttributeValueConverter;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.AttributeValueConverterService;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.ConfigurationFormat;
|
||||
|
||||
@Lazy
|
||||
@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_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[] 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 ConfigurationValueDAO configurationValueDAO;
|
||||
|
@ -68,7 +74,8 @@ public class ExamConfigIO {
|
|||
}
|
||||
|
||||
@Async(AsyncServiceSpringConfig.EXECUTOR_BEAN_NAME)
|
||||
void exportPlainXML(
|
||||
void exportPlain(
|
||||
final ConfigurationFormat exportFormat,
|
||||
final OutputStream out,
|
||||
final Long institutionId,
|
||||
final Long configurationNodeId) {
|
||||
|
@ -82,6 +89,7 @@ public class ExamConfigIO {
|
|||
.getOrThrow()
|
||||
.stream()
|
||||
.flatMap(this::convertAttribute)
|
||||
.filter(exportFormatBasedAttributeFilter(exportFormat))
|
||||
.sorted()
|
||||
.collect(Collectors.toList());
|
||||
|
||||
|
@ -94,25 +102,39 @@ public class ExamConfigIO {
|
|||
getConfigurationValueSupplier(institutionId, configurationId);
|
||||
|
||||
try {
|
||||
// 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);
|
||||
writeHeader(exportFormat, out);
|
||||
|
||||
// write attributes
|
||||
for (final ConfigurationAttribute attribute : sortedAttributes) {
|
||||
this.attributeValueConverterService.getAttributeValueConverter(attribute).convertToXML(
|
||||
out,
|
||||
attribute,
|
||||
configurationValueSupplier);
|
||||
final Iterator<ConfigurationAttribute> iterator = sortedAttributes.iterator();
|
||||
while (iterator.hasNext()) {
|
||||
|
||||
final ConfigurationAttribute attribute = iterator.next();
|
||||
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
|
||||
out.write(XML_DICT_END_UTF_8);
|
||||
out.write(XML_PLIST_END_UTF_8);
|
||||
writeFooter(exportFormat, out);
|
||||
out.flush();
|
||||
|
||||
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) {
|
||||
final AttributeValueConverter attributeValueConverter =
|
||||
this.attributeValueConverterService.getAttributeValueConverter(attr);
|
||||
|
|
|
@ -13,7 +13,11 @@ import java.io.OutputStream;
|
|||
import java.io.PipedInputStream;
|
||||
import java.io.PipedOutputStream;
|
||||
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.lang3.StringUtils;
|
||||
import org.slf4j.Logger;
|
||||
|
@ -21,6 +25,8 @@ import org.slf4j.LoggerFactory;
|
|||
import org.springframework.context.annotation.Lazy;
|
||||
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.model.sebconfig.ConfigurationAttribute;
|
||||
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.webservice.servicelayer.dao.ConfigurationAttributeDAO;
|
||||
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.SebExamConfigService;
|
||||
|
||||
|
@ -77,8 +84,28 @@ public class SebExamConfigServiceImpl implements SebExamConfigService {
|
|||
|
||||
@Override
|
||||
public void validate(final ConfigurationTableValues tableValue) throws FieldValidationException {
|
||||
// TODO Auto-generated method stub
|
||||
throw new UnsupportedOperationException("TODO");
|
||||
final List<APIMessage> errors = tableValue.values.stream()
|
||||
.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
|
||||
|
@ -87,48 +114,16 @@ public class SebExamConfigServiceImpl implements SebExamConfigService {
|
|||
final Long institutionId,
|
||||
final Long configurationNodeId) {
|
||||
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("Start to stream plain text SEB clonfiguration data");
|
||||
}
|
||||
this.exportPlain(ConfigurationFormat.XML, out, institutionId, configurationNodeId);
|
||||
}
|
||||
|
||||
PipedOutputStream pout = null;
|
||||
PipedInputStream pin = null;
|
||||
try {
|
||||
pout = new PipedOutputStream();
|
||||
pin = new PipedInputStream(pout);
|
||||
|
||||
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");
|
||||
}
|
||||
}
|
||||
@Override
|
||||
public void exportPlainJSON(
|
||||
final OutputStream out,
|
||||
final Long institutionId,
|
||||
final Long configurationNodeId) {
|
||||
|
||||
this.exportPlain(ConfigurationFormat.JSON, out, institutionId, configurationNodeId);
|
||||
}
|
||||
|
||||
public Result<Long> getDefaultConfigurationIdForExam(final Long examId) {
|
||||
|
@ -160,12 +155,125 @@ public class SebExamConfigServiceImpl implements SebExamConfigService {
|
|||
}
|
||||
|
||||
@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
|
||||
throw new UnsupportedOperationException("TODO");
|
||||
if (log.isTraceEnabled()) {
|
||||
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");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -10,9 +10,6 @@ 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 java.util.function.Function;
|
||||
|
||||
|
@ -35,16 +32,24 @@ public class IntegerConverter implements AttributeValueConverter {
|
|||
|
||||
private static final Logger log = LoggerFactory.getLogger(IntegerConverter.class);
|
||||
|
||||
public static final Set<AttributeType> SUPPORTED_TYPES = Collections.unmodifiableSet(
|
||||
new HashSet<>(Arrays.asList(
|
||||
AttributeType.INTEGER,
|
||||
AttributeType.SLIDER,
|
||||
AttributeType.SINGLE_SELECTION,
|
||||
AttributeType.RADIO_SELECTION)));
|
||||
public static final Set<String> SUPPORTED_ATTR_NAMES = Utils.immutableSetOf(
|
||||
"taskBarHeight",
|
||||
"allowedDisplaysMaxNumber");
|
||||
|
||||
public static final Set<AttributeType> SUPPORTED_TYPES = Utils.immutableSetOf(
|
||||
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 JSON_TEMPLATE = "\"%s\":%s";
|
||||
|
||||
@Override
|
||||
public Set<String> names() {
|
||||
return SUPPORTED_ATTR_NAMES;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<AttributeType> types() {
|
||||
return SUPPORTED_TYPES;
|
||||
|
|
|
@ -44,7 +44,7 @@ public class StringConverter implements AttributeValueConverter {
|
|||
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_EMPTY = "\"%s\":";
|
||||
private static final String JSON_TEMPLATE_EMPTY = "\"%s\":\"\"";
|
||||
|
||||
@Override
|
||||
public Set<AttributeType> types() {
|
||||
|
|
|
@ -78,7 +78,7 @@ public class APIExceptionHandler extends ResponseEntityExceptionHandler {
|
|||
}
|
||||
|
||||
@ExceptionHandler(OAuth2Exception.class)
|
||||
public ResponseEntity<Object> handleBeanValidationException(
|
||||
public ResponseEntity<Object> handleOAuth2Exception(
|
||||
final OAuth2Exception ex,
|
||||
final WebRequest request) {
|
||||
|
||||
|
|
|
@ -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.POSTMapper;
|
||||
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.ConfigurationNode;
|
||||
import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile;
|
||||
|
@ -84,20 +85,42 @@ 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 Long configNodeId) {
|
||||
public Configuration getFollowup(@PathVariable final Long modelId) {
|
||||
|
||||
this.entityDAO
|
||||
.byPK(configNodeId)
|
||||
.byPK(modelId)
|
||||
.flatMap(this::checkModifyAccess)
|
||||
.getOrThrow();
|
||||
|
||||
return this.configurationDAO
|
||||
.getFollowupConfiguration(configNodeId)
|
||||
.getFollowupConfiguration(modelId)
|
||||
.getOrThrow();
|
||||
}
|
||||
|
||||
@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,
|
||||
produces = MediaType.APPLICATION_OCTET_STREAM_VALUE)
|
||||
public ResponseEntity<StreamingResponseBody> downloadPlainXMLConfig(
|
||||
|
@ -108,7 +131,7 @@ public class ConfigurationNodeController extends EntityController<ConfigurationN
|
|||
defaultValue = UserService.USERS_INSTITUTION_AS_DEFAULT) final Long institutionId) {
|
||||
|
||||
this.entityDAO.byPK(modelId)
|
||||
.map(this.authorization::checkRead);
|
||||
.flatMap(this.authorization::checkRead);
|
||||
|
||||
final StreamingResponseBody stream = out -> this.sebExamConfigService
|
||||
.exportPlainXML(out, institutionId, modelId);
|
||||
|
|
|
@ -215,7 +215,7 @@ INSERT IGNORE INTO configuration_attribute VALUES
|
|||
(259, 'SOCKSPassword', 'TEXT_FIELD', 220, null, null, 'groupId=socks,createDefaultValue=true', null),
|
||||
(260, 'RTSPEnable', 'CHECKBOX', 220, null, null, 'groupId=rtsp,createDefaultValue=true', 'false'),
|
||||
(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'),
|
||||
(264, 'RTSPUsername', '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'),
|
||||
(305, 'enableLogging', 'CHECKBOX', null, null, null, null, 'false'),
|
||||
(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'),
|
||||
(309, 'enableAppSwitcherCheck', 'CHECKBOX', null, null, null, null, 'true'),
|
||||
(310, 'forceAppFolderInstall', 'CHECKBOX', null, null, null, null, 'true'),
|
||||
|
|
|
@ -33,6 +33,7 @@
|
|||
<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.SebExamConfigServiceImpl" level="TRACE" additivity="true" />
|
||||
|
||||
</springProfile>
|
||||
|
||||
|
|
|
@ -392,6 +392,7 @@ sebserver.examconfig.action.list.new=New Exam Configuration
|
|||
sebserver.examconfig.action.list.view=View Configuration
|
||||
sebserver.examconfig.action.list.modify=Edit Settings
|
||||
sebserver.examconfig.action.list.modify.properties=Edit Configuration
|
||||
sebserver.examconfig.action.view=View Configuration
|
||||
sebserver.examconfig.action.modify=Edit Settings
|
||||
sebserver.examconfig.action.modify.properties=Edit Configuration
|
||||
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.success=Successfully reverted to last saved state
|
||||
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=Exam Configuration
|
||||
sebserver.examconfig.form.name=Name
|
||||
sebserver.examconfig.form.description=Description
|
||||
sebserver.examconfig.form.status=Status
|
||||
sebserver.examconfig.form.config-key.title=Config Key
|
||||
|
||||
sebserver.examconfig.status.CONSTRUCTION=Under Construction
|
||||
sebserver.examconfig.status.READY_TO_USE=Ready To Use
|
||||
|
|
BIN
src/main/resources/static/images/secure.png
Normal file
BIN
src/main/resources/static/images/secure.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 190 B |
File diff suppressed because one or more lines are too long
|
@ -183,7 +183,7 @@ INSERT IGNORE INTO configuration_attribute VALUES
|
|||
(259, 'SOCKSPassword', 'TEXT_FIELD', 220, null, null, 'groupId=socks,createDefaultValue=true', null),
|
||||
(260, 'RTSPEnable', 'CHECKBOX', 220, null, null, 'groupId=rtsp,createDefaultValue=true', 'false'),
|
||||
(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'),
|
||||
(264, 'RTSPUsername', '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'),
|
||||
(305, 'enableLogging', 'CHECKBOX', null, null, null, null, 'false'),
|
||||
(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'),
|
||||
(309, 'enableAppSwitcherCheck', 'CHECKBOX', null, null, null, null, 'true'),
|
||||
(310, 'forceAppFolderInstall', 'CHECKBOX', null, null, null, null, 'true'),
|
||||
|
|
Loading…
Reference in a new issue