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…
	
	Add table
		
		Reference in a new issue
	
	 anhefti
						anhefti