SEBSERV-97 implementation
This commit is contained in:
parent
eec4392f78
commit
c5008ad5c2
13 changed files with 266 additions and 38 deletions
|
@ -141,6 +141,7 @@ public final class API {
|
||||||
public static final String CONFIGURATION_NODE_ENDPOINT = "/configuration-node";
|
public static final String CONFIGURATION_NODE_ENDPOINT = "/configuration-node";
|
||||||
public static final String CONFIGURATION_FOLLOWUP_PATH_SEGMENT = "/followup";
|
public static final String CONFIGURATION_FOLLOWUP_PATH_SEGMENT = "/followup";
|
||||||
public static final String CONFIGURATION_CONFIG_KEY_PATH_SEGMENT = "/configkey";
|
public static final String CONFIGURATION_CONFIG_KEY_PATH_SEGMENT = "/configkey";
|
||||||
|
public static final String CONFIGURATION_SETTINGS_PUBLISHED_PATH_SEGMENT = "/settings_published";
|
||||||
public static final String CONFIGURATION_ENDPOINT = "/configuration";
|
public static final String CONFIGURATION_ENDPOINT = "/configuration";
|
||||||
public static final String CONFIGURATION_SAVE_TO_HISTORY_PATH_SEGMENT = "/save-to-history";
|
public static final String CONFIGURATION_SAVE_TO_HISTORY_PATH_SEGMENT = "/save-to-history";
|
||||||
public static final String CONFIGURATION_UNDO_PATH_SEGMENT = "/undo";
|
public static final String CONFIGURATION_UNDO_PATH_SEGMENT = "/undo";
|
||||||
|
|
|
@ -0,0 +1,31 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2021 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 javax.validation.constraints.NotNull;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
|
|
||||||
|
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||||
|
public class SettingsPublished {
|
||||||
|
|
||||||
|
public static final String ATTR_SETTINGS_PUBLISHED = "settingsPublished";
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@JsonProperty(ATTR_SETTINGS_PUBLISHED)
|
||||||
|
public final Boolean settingsPublished;
|
||||||
|
|
||||||
|
@JsonCreator
|
||||||
|
public SettingsPublished(@JsonProperty(ATTR_SETTINGS_PUBLISHED) final Boolean settingsPublished) {
|
||||||
|
this.settingsPublished = settingsPublished;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -183,7 +183,7 @@ public class ConfigTemplateAttributeForm implements TemplateComposer {
|
||||||
configuration,
|
configuration,
|
||||||
new View(-1L, "template", 10, 0, templateId),
|
new View(-1L, "template", 10, 0, templateId),
|
||||||
attributeMapping,
|
attributeMapping,
|
||||||
1, false);
|
1, false, null);
|
||||||
|
|
||||||
final InputFieldBuilder inputFieldBuilder = this.examConfigurationService.getInputFieldBuilder(
|
final InputFieldBuilder inputFieldBuilder = this.examConfigurationService.getInputFieldBuilder(
|
||||||
attribute.getConfigAttribute(),
|
attribute.getConfigAttribute(),
|
||||||
|
|
|
@ -14,6 +14,7 @@ import java.util.List;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.eclipse.swt.SWT;
|
import org.eclipse.swt.SWT;
|
||||||
import org.eclipse.swt.layout.GridData;
|
import org.eclipse.swt.layout.GridData;
|
||||||
|
import org.eclipse.swt.layout.GridLayout;
|
||||||
import org.eclipse.swt.widgets.Composite;
|
import org.eclipse.swt.widgets.Composite;
|
||||||
import org.eclipse.swt.widgets.TabFolder;
|
import org.eclipse.swt.widgets.TabFolder;
|
||||||
import org.eclipse.swt.widgets.TabItem;
|
import org.eclipse.swt.widgets.TabItem;
|
||||||
|
@ -46,11 +47,13 @@ import ch.ethz.seb.sebserver.gui.service.page.TemplateComposer;
|
||||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestService;
|
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestService;
|
||||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.seb.examconfig.GetConfigurations;
|
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.seb.examconfig.GetConfigurations;
|
||||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.seb.examconfig.GetExamConfigNode;
|
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.seb.examconfig.GetExamConfigNode;
|
||||||
|
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.seb.examconfig.GetSettingsPublished;
|
||||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.seb.examconfig.SEBExamConfigUndo;
|
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.seb.examconfig.SEBExamConfigUndo;
|
||||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.seb.examconfig.SaveExamConfigHistory;
|
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.seb.examconfig.SaveExamConfigHistory;
|
||||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.auth.CurrentUser;
|
import ch.ethz.seb.sebserver.gui.service.remote.webservice.auth.CurrentUser;
|
||||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.auth.CurrentUser.GrantCheck;
|
import ch.ethz.seb.sebserver.gui.service.remote.webservice.auth.CurrentUser.GrantCheck;
|
||||||
import ch.ethz.seb.sebserver.gui.widget.WidgetFactory;
|
import ch.ethz.seb.sebserver.gui.widget.WidgetFactory;
|
||||||
|
import ch.ethz.seb.sebserver.gui.widget.WidgetFactory.CustomVariant;
|
||||||
|
|
||||||
@Lazy
|
@Lazy
|
||||||
@Component
|
@Component
|
||||||
|
@ -67,6 +70,8 @@ public class SEBSettingsForm implements TemplateComposer {
|
||||||
"sebserver.examconfig.action.undo.success";
|
"sebserver.examconfig.action.undo.success";
|
||||||
private static final LocTextKey TITLE_TEXT_KEY =
|
private static final LocTextKey TITLE_TEXT_KEY =
|
||||||
new LocTextKey("sebserver.examconfig.props.from.title");
|
new LocTextKey("sebserver.examconfig.props.from.title");
|
||||||
|
private static final LocTextKey UNPUBLISHED_MESSAGE_KEY =
|
||||||
|
new LocTextKey("sebserver.examconfig.props.from.unpublished.message");
|
||||||
|
|
||||||
private static final LocTextKey MESSAGE_SAVE_INTEGRITY_VIOLATION =
|
private static final LocTextKey MESSAGE_SAVE_INTEGRITY_VIOLATION =
|
||||||
new LocTextKey("sebserver.examconfig.action.saveToHistory.integrity-violation");
|
new LocTextKey("sebserver.examconfig.action.saveToHistory.integrity-violation");
|
||||||
|
@ -101,6 +106,28 @@ public class SEBSettingsForm implements TemplateComposer {
|
||||||
.onError(error -> pageContext.notifyLoadError(EntityType.CONFIGURATION_NODE, error))
|
.onError(error -> pageContext.notifyLoadError(EntityType.CONFIGURATION_NODE, error))
|
||||||
.getOrThrow();
|
.getOrThrow();
|
||||||
|
|
||||||
|
final boolean settingsPublished = this.restService.getBuilder(GetSettingsPublished.class)
|
||||||
|
.withURIVariable(API.PARAM_MODEL_ID, entityKey.modelId)
|
||||||
|
.call()
|
||||||
|
.onError(error -> log.warn("Failed to verify published settings. Cause: ", error.getMessage()))
|
||||||
|
.map(result -> result.settingsPublished)
|
||||||
|
.getOr(false);
|
||||||
|
|
||||||
|
final boolean readonly = pageContext.isReadonly() || configNode.status == ConfigurationStatus.IN_USE;
|
||||||
|
final Composite warningPanelAnchor = new Composite(pageContext.getParent(), SWT.NONE);
|
||||||
|
final GridData gridData = new GridData(SWT.FILL, SWT.FILL, true, false);
|
||||||
|
warningPanelAnchor.setLayoutData(gridData);
|
||||||
|
final GridLayout gridLayout = new GridLayout(1, true);
|
||||||
|
gridLayout.marginHeight = 0;
|
||||||
|
gridLayout.marginWidth = 0;
|
||||||
|
warningPanelAnchor.setLayout(gridLayout);
|
||||||
|
final Runnable publishedMessagePanelViewCallback = this.publishedMessagePanelViewCallback(
|
||||||
|
warningPanelAnchor,
|
||||||
|
entityKey.modelId);
|
||||||
|
if (!settingsPublished) {
|
||||||
|
publishedMessagePanelViewCallback.run();
|
||||||
|
}
|
||||||
|
|
||||||
final Composite content = widgetFactory.defaultPageLayout(
|
final Composite content = widgetFactory.defaultPageLayout(
|
||||||
pageContext.getParent(),
|
pageContext.getParent(),
|
||||||
new LocTextKey(TITLE_TEXT_KEY.name, Utils.truncateText(configNode.name, 30)));
|
new LocTextKey(TITLE_TEXT_KEY.name, Utils.truncateText(configNode.name, 30)));
|
||||||
|
@ -120,7 +147,6 @@ public class SEBSettingsForm implements TemplateComposer {
|
||||||
.onError(error -> pageContext.notifyLoadError(EntityType.CONFIGURATION_ATTRIBUTE, error))
|
.onError(error -> pageContext.notifyLoadError(EntityType.CONFIGURATION_ATTRIBUTE, error))
|
||||||
.getOrThrow();
|
.getOrThrow();
|
||||||
|
|
||||||
final boolean readonly = pageContext.isReadonly() || configNode.status == ConfigurationStatus.IN_USE;
|
|
||||||
final List<View> views = this.examConfigurationService.getViews(attributes);
|
final List<View> views = this.examConfigurationService.getViews(attributes);
|
||||||
final TabFolder tabFolder = widgetFactory.tabFolderLocalized(content);
|
final TabFolder tabFolder = widgetFactory.tabFolderLocalized(content);
|
||||||
tabFolder.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false));
|
tabFolder.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false));
|
||||||
|
@ -133,7 +159,8 @@ public class SEBSettingsForm implements TemplateComposer {
|
||||||
view,
|
view,
|
||||||
attributes,
|
attributes,
|
||||||
20,
|
20,
|
||||||
readonly);
|
readonly,
|
||||||
|
publishedMessagePanelViewCallback);
|
||||||
viewContexts.add(viewContext);
|
viewContexts.add(viewContext);
|
||||||
|
|
||||||
final Composite viewGrid = this.examConfigurationService.createViewGrid(
|
final Composite viewGrid = this.examConfigurationService.createViewGrid(
|
||||||
|
@ -218,6 +245,32 @@ public class SEBSettingsForm implements TemplateComposer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Runnable publishedMessagePanelViewCallback(final Composite parent, final String nodeId) {
|
||||||
|
return () -> {
|
||||||
|
if (parent.getChildren() != null && parent.getChildren().length > 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final boolean settingsPublished = this.restService.getBuilder(GetSettingsPublished.class)
|
||||||
|
.withURIVariable(API.PARAM_MODEL_ID, nodeId)
|
||||||
|
.call()
|
||||||
|
.onError(error -> log.warn("Failed to verify published settings. Cause: ", error.getMessage()))
|
||||||
|
.map(result -> result.settingsPublished)
|
||||||
|
.getOr(false);
|
||||||
|
|
||||||
|
if (!settingsPublished) {
|
||||||
|
|
||||||
|
final WidgetFactory widgetFactory = this.pageService.getWidgetFactory();
|
||||||
|
final Composite warningPanel = widgetFactory.createWarningPanel(parent);
|
||||||
|
widgetFactory.labelLocalized(
|
||||||
|
warningPanel,
|
||||||
|
CustomVariant.MESSAGE,
|
||||||
|
UNPUBLISHED_MESSAGE_KEY);
|
||||||
|
parent.getParent().layout();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
private void notifyErrorOnSave(final Exception error, final PageContext context) {
|
private void notifyErrorOnSave(final Exception error, final PageContext context) {
|
||||||
if (error instanceof APIMessageError) {
|
if (error instanceof APIMessageError) {
|
||||||
try {
|
try {
|
||||||
|
|
|
@ -70,7 +70,7 @@ public interface ExamConfigurationService {
|
||||||
/** Create to ViewContext to compose a exam configuration property page,
|
/** Create to ViewContext to compose a exam configuration property page,
|
||||||
* The ViewContext is the internal state of a exam configuration property page
|
* The ViewContext is the internal state of a exam configuration property page
|
||||||
* and is passed through all composers while composing the page.
|
* and is passed through all composers while composing the page.
|
||||||
*
|
*
|
||||||
* @param pageContext The original PageContext that holds the state of the overall page.
|
* @param pageContext The original PageContext that holds the state of the overall page.
|
||||||
* @param configuration The configuration on which the exam configuration property page is based on.
|
* @param configuration The configuration on which the exam configuration property page is based on.
|
||||||
* @param view The View of the context
|
* @param view The View of the context
|
||||||
|
@ -84,7 +84,8 @@ public interface ExamConfigurationService {
|
||||||
View view,
|
View view,
|
||||||
AttributeMapping attributeMapping,
|
AttributeMapping attributeMapping,
|
||||||
int rows,
|
int rows,
|
||||||
boolean readonly);
|
boolean readonly,
|
||||||
|
Runnable valueChageCallback);
|
||||||
|
|
||||||
Composite createViewGrid(
|
Composite createViewGrid(
|
||||||
Composite parent,
|
Composite parent,
|
||||||
|
|
|
@ -176,7 +176,8 @@ public class ExamConfigurationServiceImpl implements ExamConfigurationService {
|
||||||
final View view,
|
final View view,
|
||||||
final AttributeMapping attributeMapping,
|
final AttributeMapping attributeMapping,
|
||||||
final int rows,
|
final int rows,
|
||||||
final boolean readonly) {
|
final boolean readonly,
|
||||||
|
final Runnable valueChageCallback) {
|
||||||
|
|
||||||
return new ViewContext(
|
return new ViewContext(
|
||||||
configuration,
|
configuration,
|
||||||
|
@ -187,7 +188,8 @@ public class ExamConfigurationServiceImpl implements ExamConfigurationService {
|
||||||
pageContext,
|
pageContext,
|
||||||
this.restService,
|
this.restService,
|
||||||
this.jsonMapper,
|
this.jsonMapper,
|
||||||
this.valueChangeRules),
|
this.valueChangeRules,
|
||||||
|
valueChageCallback),
|
||||||
this.widgetFactory.getI18nSupport(),
|
this.widgetFactory.getI18nSupport(),
|
||||||
readonly);
|
readonly);
|
||||||
|
|
||||||
|
@ -323,17 +325,20 @@ public class ExamConfigurationServiceImpl implements ExamConfigurationService {
|
||||||
private final RestService restService;
|
private final RestService restService;
|
||||||
private final JSONMapper jsonMapper;
|
private final JSONMapper jsonMapper;
|
||||||
private final Collection<ValueChangeRule> valueChangeRules;
|
private final Collection<ValueChangeRule> valueChangeRules;
|
||||||
|
private final Runnable valueChageCallback;
|
||||||
|
|
||||||
protected ValueChangeListenerImpl(
|
protected ValueChangeListenerImpl(
|
||||||
final PageContext pageContext,
|
final PageContext pageContext,
|
||||||
final RestService restService,
|
final RestService restService,
|
||||||
final JSONMapper jsonMapper,
|
final JSONMapper jsonMapper,
|
||||||
final Collection<ValueChangeRule> valueChangeRules) {
|
final Collection<ValueChangeRule> valueChangeRules,
|
||||||
|
final Runnable valueChageCallback) {
|
||||||
|
|
||||||
this.pageContext = pageContext;
|
this.pageContext = pageContext;
|
||||||
this.restService = restService;
|
this.restService = restService;
|
||||||
this.jsonMapper = jsonMapper;
|
this.jsonMapper = jsonMapper;
|
||||||
this.valueChangeRules = valueChangeRules;
|
this.valueChangeRules = valueChangeRules;
|
||||||
|
this.valueChageCallback = valueChageCallback;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -367,6 +372,10 @@ public class ExamConfigurationServiceImpl implements ExamConfigurationService {
|
||||||
} catch (final Exception e) {
|
} catch (final Exception e) {
|
||||||
this.pageContext.notifySaveError(EntityType.CONFIGURATION_VALUE, e);
|
this.pageContext.notifySaveError(EntityType.CONFIGURATION_VALUE, e);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this.valueChageCallback != null) {
|
||||||
|
this.valueChageCallback.run();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -0,0 +1,42 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2021 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.SettingsPublished;
|
||||||
|
import ch.ethz.seb.sebserver.gbl.profile.GuiProfile;
|
||||||
|
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestCall;
|
||||||
|
|
||||||
|
@Lazy
|
||||||
|
@Component
|
||||||
|
@GuiProfile
|
||||||
|
public class GetSettingsPublished extends RestCall<SettingsPublished> {
|
||||||
|
|
||||||
|
public GetSettingsPublished() {
|
||||||
|
super(new TypeKey<>(
|
||||||
|
CallType.UNDEFINED,
|
||||||
|
EntityType.CONFIGURATION_NODE,
|
||||||
|
new TypeReference<SettingsPublished>() {
|
||||||
|
}),
|
||||||
|
HttpMethod.GET,
|
||||||
|
MediaType.APPLICATION_FORM_URLENCODED,
|
||||||
|
API.CONFIGURATION_NODE_ENDPOINT
|
||||||
|
+ API.MODEL_ID_VAR_PATH_SEGMENT
|
||||||
|
+ API.CONFIGURATION_SETTINGS_PUBLISHED_PATH_SEGMENT);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -330,12 +330,16 @@ public class WidgetFactory {
|
||||||
}
|
}
|
||||||
|
|
||||||
public Composite createWarningPanel(final Composite parent) {
|
public Composite createWarningPanel(final Composite parent) {
|
||||||
|
return createWarningPanel(parent, 20);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Composite createWarningPanel(final Composite parent, final int margin) {
|
||||||
final Composite composite = new Composite(parent, SWT.NONE);
|
final Composite composite = new Composite(parent, SWT.NONE);
|
||||||
final GridData gridData = new GridData(SWT.FILL, SWT.FILL, true, false);
|
final GridData gridData = new GridData(SWT.FILL, SWT.FILL, true, false);
|
||||||
composite.setLayoutData(gridData);
|
composite.setLayoutData(gridData);
|
||||||
final GridLayout gridLayout = new GridLayout(1, true);
|
final GridLayout gridLayout = new GridLayout(1, true);
|
||||||
gridLayout.marginWidth = 20;
|
gridLayout.marginWidth = margin;
|
||||||
gridLayout.marginHeight = 20;
|
gridLayout.marginHeight = margin;
|
||||||
composite.setLayout(gridLayout);
|
composite.setLayout(gridLayout);
|
||||||
composite.setData(RWT.CUSTOM_VARIANT, CustomVariant.WARNING.key);
|
composite.setData(RWT.CUSTOM_VARIANT, CustomVariant.WARNING.key);
|
||||||
return composite;
|
return composite;
|
||||||
|
|
|
@ -124,4 +124,15 @@ public interface ExamConfigService {
|
||||||
* @return The newly created Configuration instance */
|
* @return The newly created Configuration instance */
|
||||||
Result<Configuration> importFromSEBFile(Configuration config, InputStream input, CharSequence password);
|
Result<Configuration> importFromSEBFile(Configuration config, InputStream input, CharSequence password);
|
||||||
|
|
||||||
|
/** Use this to check whether a specified ConfigurationNode has unpublished changes within the settings.
|
||||||
|
*
|
||||||
|
* This uses the Config Key of the actual and the follow-up settings to verify if there are changes made that
|
||||||
|
* are not published yet.
|
||||||
|
*
|
||||||
|
* @param institutionId the institutional id
|
||||||
|
* @param configurationNodeId the id if the ConfigurationNode
|
||||||
|
* @return true if there are unpublished changed in the SEB setting of the follow-up for the specified
|
||||||
|
* ConfigurationNode */
|
||||||
|
Result<Boolean> hasUnpublishedChanged(Long institutionId, Long configurationNodeId);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -98,11 +98,37 @@ public class ExamConfigIO {
|
||||||
final Long institutionId,
|
final Long institutionId,
|
||||||
final Long configurationNodeId) throws Exception {
|
final Long configurationNodeId) throws Exception {
|
||||||
|
|
||||||
|
exportPlain(exportFormat, out, institutionId, configurationNodeId, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Async(AsyncServiceSpringConfig.EXECUTOR_BEAN_NAME)
|
||||||
|
void exportForConfigKeyGeneration(
|
||||||
|
final OutputStream out,
|
||||||
|
final Long institutionId,
|
||||||
|
final Long configurationNodeId,
|
||||||
|
final Long configId) throws Exception {
|
||||||
|
|
||||||
|
exportPlain(ConfigurationFormat.JSON, out, institutionId, configurationNodeId, configId);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void exportPlain(
|
||||||
|
final ConfigurationFormat exportFormat,
|
||||||
|
final OutputStream out,
|
||||||
|
final Long institutionId,
|
||||||
|
final Long configurationNodeId,
|
||||||
|
final Long configId) throws Exception {
|
||||||
|
|
||||||
if (log.isDebugEnabled()) {
|
if (log.isDebugEnabled()) {
|
||||||
log.debug("Start export SEB plain XML configuration asynconously");
|
log.debug("Start export SEB plain XML configuration asynconously");
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
// get configurationId for given configId or configurationNodeId last stable if null
|
||||||
|
final Long configurationId = (configId == null)
|
||||||
|
? this.configurationDAO
|
||||||
|
.getConfigurationLastStableVersion(configurationNodeId)
|
||||||
|
.getOrThrow().id
|
||||||
|
: configId;
|
||||||
|
|
||||||
// get all defined root configuration attributes prepared and sorted
|
// get all defined root configuration attributes prepared and sorted
|
||||||
final List<ConfigurationAttribute> sortedAttributes = this.configurationAttributeDAO.getAllRootAttributes()
|
final List<ConfigurationAttribute> sortedAttributes = this.configurationAttributeDAO.getAllRootAttributes()
|
||||||
|
@ -113,11 +139,6 @@ public class ExamConfigIO {
|
||||||
.sorted()
|
.sorted()
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
// get follow-up configurationId for given configurationNodeId
|
|
||||||
final Long configurationId = this.configurationDAO
|
|
||||||
.getConfigurationLastStableVersion(configurationNodeId)
|
|
||||||
.getOrThrow().id;
|
|
||||||
|
|
||||||
final Function<ConfigurationAttribute, ConfigurationValue> configurationValueSupplier =
|
final Function<ConfigurationAttribute, ConfigurationValue> configurationValueSupplier =
|
||||||
getConfigurationValueSupplier(institutionId, configurationId);
|
getConfigurationValueSupplier(institutionId, configurationId);
|
||||||
|
|
||||||
|
|
|
@ -38,6 +38,7 @@ import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationValue;
|
||||||
import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile;
|
import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile;
|
||||||
import ch.ethz.seb.sebserver.gbl.util.Result;
|
import ch.ethz.seb.sebserver.gbl.util.Result;
|
||||||
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.ConfigurationAttributeDAO;
|
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.ConfigurationAttributeDAO;
|
||||||
|
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.ConfigurationDAO;
|
||||||
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.ExamConfigurationMapDAO;
|
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.ExamConfigurationMapDAO;
|
||||||
import ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.ConfigurationFormat;
|
import ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.ConfigurationFormat;
|
||||||
import ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.ConfigurationValueValidator;
|
import ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.ConfigurationValueValidator;
|
||||||
|
@ -61,6 +62,7 @@ public class ExamConfigServiceImpl implements ExamConfigService {
|
||||||
private final ClientCredentialService clientCredentialService;
|
private final ClientCredentialService clientCredentialService;
|
||||||
private final ZipService zipService;
|
private final ZipService zipService;
|
||||||
private final SEBConfigEncryptionService sebConfigEncryptionService;
|
private final SEBConfigEncryptionService sebConfigEncryptionService;
|
||||||
|
private final ConfigurationDAO configurationDAO;
|
||||||
|
|
||||||
protected ExamConfigServiceImpl(
|
protected ExamConfigServiceImpl(
|
||||||
final ExamConfigIO examConfigIO,
|
final ExamConfigIO examConfigIO,
|
||||||
|
@ -69,7 +71,8 @@ public class ExamConfigServiceImpl implements ExamConfigService {
|
||||||
final Collection<ConfigurationValueValidator> validators,
|
final Collection<ConfigurationValueValidator> validators,
|
||||||
final ClientCredentialService clientCredentialService,
|
final ClientCredentialService clientCredentialService,
|
||||||
final ZipService zipService,
|
final ZipService zipService,
|
||||||
final SEBConfigEncryptionService sebConfigEncryptionService) {
|
final SEBConfigEncryptionService sebConfigEncryptionService,
|
||||||
|
final ConfigurationDAO configurationDAO) {
|
||||||
|
|
||||||
this.examConfigIO = examConfigIO;
|
this.examConfigIO = examConfigIO;
|
||||||
this.configurationAttributeDAO = configurationAttributeDAO;
|
this.configurationAttributeDAO = configurationAttributeDAO;
|
||||||
|
@ -78,6 +81,7 @@ public class ExamConfigServiceImpl implements ExamConfigService {
|
||||||
this.clientCredentialService = clientCredentialService;
|
this.clientCredentialService = clientCredentialService;
|
||||||
this.zipService = zipService;
|
this.zipService = zipService;
|
||||||
this.sebConfigEncryptionService = sebConfigEncryptionService;
|
this.sebConfigEncryptionService = sebConfigEncryptionService;
|
||||||
|
this.configurationDAO = configurationDAO;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -249,29 +253,39 @@ public class ExamConfigServiceImpl implements ExamConfigService {
|
||||||
final Long institutionId,
|
final Long institutionId,
|
||||||
final Long configurationNodeId) {
|
final Long configurationNodeId) {
|
||||||
|
|
||||||
|
return this.configurationDAO
|
||||||
|
.getConfigurationLastStableVersion(configurationNodeId)
|
||||||
|
.flatMap(config -> generateConfigKey(institutionId, configurationNodeId, config.id));
|
||||||
|
}
|
||||||
|
|
||||||
|
private Result<String> generateConfigKey(
|
||||||
|
final Long institutionId,
|
||||||
|
final Long configurationNodeId,
|
||||||
|
final Long configId) {
|
||||||
|
|
||||||
if (log.isDebugEnabled()) {
|
if (log.isDebugEnabled()) {
|
||||||
log.debug("Start to stream plain JSON SEB Configuration data for Config-Key generation");
|
log.debug("Start to stream plain JSON SEB Configuration data for Config-Key generation");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (true) {
|
// if (true) {
|
||||||
PipedOutputStream pout;
|
// PipedOutputStream pout;
|
||||||
PipedInputStream pin;
|
// PipedInputStream pin;
|
||||||
try {
|
// try {
|
||||||
pout = new PipedOutputStream();
|
// pout = new PipedOutputStream();
|
||||||
pin = new PipedInputStream(pout);
|
// pin = new PipedInputStream(pout);
|
||||||
this.examConfigIO.exportPlain(
|
// this.examConfigIO.exportPlain(
|
||||||
ConfigurationFormat.JSON,
|
// ConfigurationFormat.JSON,
|
||||||
pout,
|
// pout,
|
||||||
institutionId,
|
// institutionId,
|
||||||
configurationNodeId);
|
// configurationNodeId);
|
||||||
|
//
|
||||||
final String json = IOUtils.toString(pin, "UTF-8");
|
// final String json = IOUtils.toString(pin, "UTF-8");
|
||||||
|
//
|
||||||
log.trace("SEB Configuration JSON to create Config-Key: {}", json);
|
// log.trace("SEB Configuration JSON to create Config-Key: {}", json);
|
||||||
} catch (final Exception e) {
|
// } catch (final Exception e) {
|
||||||
log.error("Failed to trace SEB Configuration JSON: ", e);
|
// log.error("Failed to trace SEB Configuration JSON: ", e);
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
PipedOutputStream pout = null;
|
PipedOutputStream pout = null;
|
||||||
PipedInputStream pin = null;
|
PipedInputStream pin = null;
|
||||||
|
@ -279,11 +293,11 @@ public class ExamConfigServiceImpl implements ExamConfigService {
|
||||||
pout = new PipedOutputStream();
|
pout = new PipedOutputStream();
|
||||||
pin = new PipedInputStream(pout);
|
pin = new PipedInputStream(pout);
|
||||||
|
|
||||||
this.examConfigIO.exportPlain(
|
this.examConfigIO.exportForConfigKeyGeneration(
|
||||||
ConfigurationFormat.JSON,
|
|
||||||
pout,
|
pout,
|
||||||
institutionId,
|
institutionId,
|
||||||
configurationNodeId);
|
configurationNodeId,
|
||||||
|
configId);
|
||||||
|
|
||||||
final String configKey = DigestUtils.sha256Hex(pin);
|
final String configKey = DigestUtils.sha256Hex(pin);
|
||||||
|
|
||||||
|
@ -380,6 +394,23 @@ public class ExamConfigServiceImpl implements ExamConfigService {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Result<Boolean> hasUnpublishedChanged(final Long institutionId, final Long configurationNodeId) {
|
||||||
|
return Result.tryCatch(() -> {
|
||||||
|
|
||||||
|
final String followupKey = this.configurationDAO
|
||||||
|
.getFollowupConfiguration(configurationNodeId)
|
||||||
|
.flatMap(config -> generateConfigKey(institutionId, configurationNodeId, config.id))
|
||||||
|
.getOrThrow();
|
||||||
|
final String stableKey = this.configurationDAO
|
||||||
|
.getConfigurationLastStableVersion(configurationNodeId)
|
||||||
|
.flatMap(config -> generateConfigKey(institutionId, configurationNodeId, config.id))
|
||||||
|
.getOrThrow();
|
||||||
|
|
||||||
|
return !followupKey.equals(stableKey);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
private void exportPlainOnly(
|
private void exportPlainOnly(
|
||||||
final ConfigurationFormat exportFormat,
|
final ConfigurationFormat exportFormat,
|
||||||
final OutputStream out,
|
final OutputStream out,
|
||||||
|
|
|
@ -50,6 +50,7 @@ import ch.ethz.seb.sebserver.gbl.model.sebconfig.Configuration;
|
||||||
import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationNode;
|
import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationNode;
|
||||||
import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationNode.ConfigurationStatus;
|
import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationNode.ConfigurationStatus;
|
||||||
import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationNode.ConfigurationType;
|
import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationNode.ConfigurationType;
|
||||||
|
import ch.ethz.seb.sebserver.gbl.model.sebconfig.SettingsPublished;
|
||||||
import ch.ethz.seb.sebserver.gbl.model.sebconfig.TemplateAttribute;
|
import ch.ethz.seb.sebserver.gbl.model.sebconfig.TemplateAttribute;
|
||||||
import ch.ethz.seb.sebserver.gbl.model.user.UserLogActivityType;
|
import ch.ethz.seb.sebserver.gbl.model.user.UserLogActivityType;
|
||||||
import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile;
|
import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile;
|
||||||
|
@ -142,6 +143,28 @@ public class ConfigurationNodeController extends EntityController<ConfigurationN
|
||||||
.getOrThrow();
|
.getOrThrow();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@RequestMapping(
|
||||||
|
path = API.MODEL_ID_VAR_PATH_SEGMENT + API.CONFIGURATION_SETTINGS_PUBLISHED_PATH_SEGMENT,
|
||||||
|
method = RequestMethod.GET,
|
||||||
|
consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE,
|
||||||
|
produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
|
||||||
|
public SettingsPublished settingsPublished(
|
||||||
|
@RequestParam(
|
||||||
|
name = API.PARAM_INSTITUTION_ID,
|
||||||
|
required = true,
|
||||||
|
defaultValue = UserService.USERS_INSTITUTION_AS_DEFAULT) final Long institutionId,
|
||||||
|
@PathVariable final Long modelId) {
|
||||||
|
|
||||||
|
this.entityDAO
|
||||||
|
.byPK(modelId)
|
||||||
|
.flatMap(this::checkReadAccess)
|
||||||
|
.getOrThrow();
|
||||||
|
|
||||||
|
return this.sebExamConfigService.hasUnpublishedChanged(institutionId, modelId)
|
||||||
|
.map(flag -> new SettingsPublished(!flag))
|
||||||
|
.getOrThrow();
|
||||||
|
}
|
||||||
|
|
||||||
@RequestMapping(
|
@RequestMapping(
|
||||||
path = API.CONFIGURATION_COPY_PATH_SEGMENT,
|
path = API.CONFIGURATION_COPY_PATH_SEGMENT,
|
||||||
method = RequestMethod.PUT,
|
method = RequestMethod.PUT,
|
||||||
|
|
|
@ -789,6 +789,7 @@ sebserver.examconfig.status.CONSTRUCTION=Under Construction
|
||||||
sebserver.examconfig.status.READY_TO_USE=Ready To Use
|
sebserver.examconfig.status.READY_TO_USE=Ready To Use
|
||||||
sebserver.examconfig.status.IN_USE=In Use
|
sebserver.examconfig.status.IN_USE=In Use
|
||||||
|
|
||||||
|
sebserver.examconfig.props.from.unpublished.message=Note: There are unpublished changes to this Settings. Use 'Save/Publish Settings' to make sure the settings are active.
|
||||||
sebserver.examconfig.props.from.title=Exam Configuration Settings ({0})
|
sebserver.examconfig.props.from.title=Exam Configuration Settings ({0})
|
||||||
sebserver.examconfig.props.from.title.subtitle=
|
sebserver.examconfig.props.from.title.subtitle=
|
||||||
sebserver.examconfig.props.form.views.general=General
|
sebserver.examconfig.props.form.views.general=General
|
||||||
|
|
Loading…
Reference in a new issue