SEBSERV-45 finished rest of views with some missing attributes so far

This commit is contained in:
anhefti 2019-05-22 15:31:17 +02:00
parent fc07b5b80c
commit eaf34ebeed
27 changed files with 480 additions and 112 deletions

View file

@ -67,6 +67,7 @@ public final class API {
public static final String CONFIGURATION_FOLLOWUP_PATH_SEGMENT = "/followup";
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";
public static final String CONFIGURATION_RESTORE_FROM_HISTORY_PATH_SEGMENT = "/restore";
public static final String CONFIGURATION_VALUE_ENDPOINT = "/configuration_value";

View file

@ -23,12 +23,14 @@ import org.springframework.stereotype.Component;
import ch.ethz.seb.sebserver.gbl.Constants;
import ch.ethz.seb.sebserver.gbl.api.API;
import ch.ethz.seb.sebserver.gbl.api.EntityType;
import ch.ethz.seb.sebserver.gbl.model.EntityKey;
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.View;
import ch.ethz.seb.sebserver.gbl.profile.GuiProfile;
import ch.ethz.seb.sebserver.gbl.util.Utils;
import ch.ethz.seb.sebserver.gui.content.action.ActionDefinition;
import ch.ethz.seb.sebserver.gui.service.examconfig.ExamConfigurationService;
import ch.ethz.seb.sebserver.gui.service.examconfig.impl.AttributeMapping;
import ch.ethz.seb.sebserver.gui.service.examconfig.impl.ViewContext;
@ -39,7 +41,10 @@ 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.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.SaveExamConfigHistory;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.seb.examconfig.SebExamConfigUndo;
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.widget.WidgetFactory;
@Lazy
@ -50,7 +55,10 @@ public class SebExamConfigForm implements TemplateComposer {
private static final Logger log = LoggerFactory.getLogger(SebExamConfigForm.class);
private static final String VIEW_TEXT_KEY_PREFIX = "sebserver.examconfig.props.form.views.";
private static final String VIEW_TOOLTIP_TEXT_KEY_SUFFIX = ".tooltip";
private static final String KEY_SAVE_TO_HISTORY_SUCCESS =
"sebserver.examconfig.action.saveToHistory.success";
private static final String KEY_UNDO_SUCCESS =
"sebserver.examconfig.action.undo.success";
private static final LocTextKey TITLE_TEXT_KEY =
new LocTextKey("sebserver.examconfig.props.from.title");
@ -77,7 +85,6 @@ public class SebExamConfigForm implements TemplateComposer {
final WidgetFactory widgetFactory = this.pageService.getWidgetFactory();
final EntityKey entityKey = pageContext.getEntityKey();
final EntityKey parentEntityKey = pageContext.getParentEntityKey();
final Composite content = widgetFactory.defaultPageLayout(
pageContext.getParent(),
@ -131,6 +138,36 @@ public class SebExamConfigForm implements TemplateComposer {
this.examConfigurationService.initInputFieldValues(configuration.id, viewContexts);
final GrantCheck examConfigGrant = this.currentUser.grantCheck(EntityType.CONFIGURATION_NODE);
this.pageService.pageActionBuilder(pageContext.clearEntityKeys())
.newAction(ActionDefinition.SEB_EXAM_CONFIG_SAVE_TO_HISTORY)
.withEntityKey(entityKey)
.withExec(action -> {
this.restService.getBuilder(SaveExamConfigHistory.class)
.withURIVariable(API.PARAM_MODEL_ID, configuration.getModelId())
.call()
.onError(pageContext::notifyError)
.getOrThrow();
return action;
})
.withSuccess(KEY_SAVE_TO_HISTORY_SUCCESS)
.publishIf(() -> examConfigGrant.iw())
.newAction(ActionDefinition.SEB_EXAM_CONFIG_UNDO)
.withEntityKey(entityKey)
.withExec(action -> {
this.restService.getBuilder(SebExamConfigUndo.class)
.withURIVariable(API.PARAM_MODEL_ID, configuration.getModelId())
.call()
.onError(pageContext::notifyError)
.getOrThrow();
return action;
})
.withSuccess(KEY_UNDO_SUCCESS)
.publishIf(() -> examConfigGrant.iw())
;
} catch (final Exception e) {
log.error("Unexpected error while trying to fetch exam configuration data and create views", e);
pageContext.notifyError(e);

View file

@ -345,6 +345,17 @@ public enum ActionDefinition {
PageStateDefinition.SEB_EXAM_CONFIG_EDIT,
ActionCategory.SEB_EXAM_CONFIG_LIST),
SEB_EXAM_CONFIG_SAVE_TO_HISTORY(
new LocTextKey("sebserver.examconfig.action.saveToHistory"),
ImageIcon.SAVE,
PageStateDefinition.SEB_EXAM_CONFIG_EDIT,
ActionCategory.SEB_EXAM_CONFIG_LIST),
SEB_EXAM_CONFIG_UNDO(
new LocTextKey("sebserver.examconfig.action.undo"),
ImageIcon.SAVE,
PageStateDefinition.SEB_EXAM_CONFIG_EDIT,
ActionCategory.SEB_EXAM_CONFIG_LIST),
;
public final LocTextKey title;

View file

@ -8,8 +8,6 @@
package ch.ethz.seb.sebserver.gui.service.examconfig.impl;
import static ch.ethz.seb.sebserver.gui.service.examconfig.impl.CellFieldBuilderAdapter.dummyBuilderAdapter;
import java.util.Collection;
import org.eclipse.swt.SWT;
@ -28,23 +26,20 @@ import ch.ethz.seb.sebserver.gui.widget.WidgetFactory;
interface CellFieldBuilderAdapter {
static CellFieldBuilderAdapter DUMMY_BUILDER_ADAPTER = new CellFieldBuilderAdapter() {
@Override
public void createCell(final ViewGridBuilder builder) {
}
@Override
public String toString() {
return "[DUMMY]";
}
};
void createCell(ViewGridBuilder builder);
default void balanceGrid(final CellFieldBuilderAdapter[][] grid, final int x, final int y) {
}
static CellFieldBuilderAdapter dummyBuilderAdapter() {
return new CellFieldBuilderAdapter() {
@Override
public void createCell(final ViewGridBuilder builder) {
}
@Override
public String toString() {
return "[DUMMY]";
}
};
}
static CellFieldBuilderAdapter fieldBuilderAdapter(
@ -127,7 +122,7 @@ interface CellFieldBuilderAdapter {
int xpos = x - 1;
while (xpos >= 0 && grid[y][xpos] == null && span < orientation.width) {
grid[y][xpos] = this;
grid[y][xpos + 1] = dummyBuilderAdapter();
grid[y][xpos + 1] = DUMMY_BUILDER_ADAPTER;
this.span++;
xpos--;
}
@ -198,13 +193,20 @@ interface CellFieldBuilderAdapter {
final WidgetFactory widgetFactory = builder.examConfigurationService.getWidgetFactory();
final Orientation o = this.orientationsOfGroup.stream().findFirst().get();
final LocTextKey groupLabelKey = new LocTextKey(
ExamConfigurationService.GROUP_LABEL_LOC_TEXT_PREFIX + o.groupId,
ExamConfigurationService.GROUP_LABEL_LOC_TEXT_PREFIX +
o.groupId,
o.groupId);
final LocTextKey groupTooltipKey = new LocTextKey(
ExamConfigurationService.GROUP_LABEL_LOC_TEXT_PREFIX +
o.groupId +
ExamConfigurationService.TOOL_TIP_SUFFIX,
o.groupId);
final Group group = widgetFactory.groupLocalized(
builder.parent,
this.width,
groupLabelKey);
groupLabelKey,
groupTooltipKey);
group.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false, this.width, this.height));
final ViewGridBuilder groupBuilder = new ViewGridBuilder(

View file

@ -20,7 +20,6 @@ import ch.ethz.seb.sebserver.gbl.model.sebconfig.Orientation;
import ch.ethz.seb.sebserver.gbl.profile.GuiProfile;
import ch.ethz.seb.sebserver.gui.service.examconfig.InputField;
import ch.ethz.seb.sebserver.gui.service.examconfig.InputFieldBuilder;
import ch.ethz.seb.sebserver.gui.service.i18n.I18nSupport;
import ch.ethz.seb.sebserver.gui.widget.MultiSelectionCheckbox;
import ch.ethz.seb.sebserver.gui.widget.Selection;
import ch.ethz.seb.sebserver.gui.widget.WidgetFactory;
@ -50,7 +49,6 @@ public class MultiCheckboxSelection extends SelectionFieldBuilder implements Inp
final ConfigurationAttribute attribute,
final ViewContext viewContext) {
final I18nSupport i18nSupport = this.widgetFactory.getI18nSupport();
final Orientation orientation = viewContext
.getOrientation(attribute.id);
final Composite innerGrid = InputFieldBuilder

View file

@ -21,7 +21,6 @@ import ch.ethz.seb.sebserver.gbl.model.sebconfig.Orientation;
import ch.ethz.seb.sebserver.gbl.profile.GuiProfile;
import ch.ethz.seb.sebserver.gui.service.examconfig.InputField;
import ch.ethz.seb.sebserver.gui.service.examconfig.InputFieldBuilder;
import ch.ethz.seb.sebserver.gui.service.i18n.I18nSupport;
import ch.ethz.seb.sebserver.gui.widget.RadioSelection;
import ch.ethz.seb.sebserver.gui.widget.Selection;
import ch.ethz.seb.sebserver.gui.widget.WidgetFactory;
@ -52,7 +51,6 @@ public class RadioSelectionFieldBuilder extends SelectionFieldBuilder implements
final ConfigurationAttribute attribute,
final ViewContext viewContext) {
final I18nSupport i18nSupport = this.widgetFactory.getI18nSupport();
final Orientation orientation = viewContext
.getOrientation(attribute.id);
final Composite innerGrid = InputFieldBuilder

View file

@ -24,6 +24,7 @@ import ch.ethz.seb.sebserver.gbl.model.Domain;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationAttribute;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationTableValues.TableValue;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.Orientation;
import ch.ethz.seb.sebserver.gui.service.ResourceService;
import ch.ethz.seb.sebserver.gui.service.examconfig.ExamConfigurationService;
import ch.ethz.seb.sebserver.gui.service.examconfig.InputField;
import ch.ethz.seb.sebserver.gui.service.examconfig.InputFieldBuilder;
@ -191,12 +192,10 @@ public class TableContext {
switch (attribute.type) {
case CHECKBOX: {
return BooleanUtils.toBoolean(tableValue.value)
? "Active"
: "Inactive";
? this.viewContext.i18nSupport.getText(ResourceService.ACTIVE_TEXT_KEY)
: this.viewContext.i18nSupport.getText(ResourceService.INACTIVE_TEXT_KEY);
}
case SINGLE_SELECTION: {
final ConfigurationAttribute tableAttr =
this.viewContext.attributeMapping.getAttribute(attribute.parentId);
final String key = ExamConfigurationService.ATTRIBUTE_LABEL_LOC_TEXT_PREFIX +
attribute.getName() + "." +
tableValue.value;

View file

@ -278,7 +278,7 @@ public class TableFieldBuilder implements InputFieldBuilder {
}
private void addTableRow(final Map<Long, TableValue> rowValues) {
final TableItem tableItem = new TableItem(this.control, SWT.NONE);
new TableItem(this.control, SWT.NONE);
applyTableRowValues(this.values.size() - 1);
// TODO try to add delete button within table row?

View file

@ -83,10 +83,10 @@ public class TableRowFormBuilder implements ModalInputDialogComposer<Map<Long, T
final Composite parent,
final ConfigurationAttribute attribute) {
// if (attribute.type == AttributeType.TABLE) {
// throw new UnsupportedOperationException(
// "Table type is currently not supported within a table row form view!");
// }
if (attribute.type == AttributeType.TABLE) {
throw new UnsupportedOperationException(
"Table type is currently not supported within a table row form view!");
}
final Orientation orientation = this.tableContext
.getOrientation(attribute.id);

View file

@ -161,8 +161,8 @@ public class ViewGridBuilder {
}
void compose() {
if (log.isDebugEnabled()) {
log.debug("Compose grid view: \n" + gridToString());
if (log.isTraceEnabled()) {
log.trace("Compose grid view: \n" + gridToString());
}
// balance grid (optimize span and grab empty spaces for labels where applicable)
@ -181,7 +181,7 @@ public class ViewGridBuilder {
final GridData gridData = new GridData(SWT.FILL, SWT.FILL, true, false);
gridData.verticalIndent = 8;
empty.setLayoutData(gridData);
empty.setText("" /* "empty " + x + " " + y */);
empty.setText(StringUtils.EMPTY /* "empty " + x + " " + y */);
} else {
this.grid[y][x].createCell(this);
}
@ -194,7 +194,11 @@ public class ViewGridBuilder {
final int upperBoundY = y + height;
for (int _y = y; _y < upperBoundY; _y++) {
for (int _x = x; _x < upperBoundX; _x++) {
this.grid[_y][_x] = CellFieldBuilderAdapter.dummyBuilderAdapter();
if (_y < 0 || _x < 0 || _y >= this.grid.length || _x >= this.grid[_y].length) {
log.warn("Out of bounds: {} {}", _x, _y);
continue;
}
this.grid[_y][_x] = CellFieldBuilderAdapter.DUMMY_BUILDER_ADAPTER;
}
}
}

View file

@ -41,6 +41,15 @@ public interface I18nSupport {
* @return date/time column title suffix for current user */
String getUsersTimeZoneTitleSuffix();
/** Get localized text of specified key for currently set Locale.
*
* @param key LocTextKey instance
* @param def default text
* @return the text in current language parsed from localized text */
default String getText(final LocTextKey key, final String def) {
return getText(key.name, def, key.args);
}
/** Get localized text of specified key for currently set Locale.
*
* @param key LocTextKey instance

View file

@ -31,6 +31,8 @@ public final class PageAction {
private static final Logger log = LoggerFactory.getLogger(PageAction.class);
public static final LocTextKey SUCCESS_MSG_TITLE = new LocTextKey("sebserver.page.message");
public final ActionDefinition definition;
private final Supplier<LocTextKey> confirm;
final LocTextKey successMessage;
@ -126,7 +128,11 @@ public final class PageAction {
private Result<PageAction> exec() {
try {
return Result.of(this.exec.apply(this));
final PageAction apply = this.exec.apply(this);
if (this.successMessage != null) {
apply.pageContext.publishPageMessage(SUCCESS_MSG_TITLE, this.successMessage);
}
return Result.of(apply);
} catch (final PageMessageException pme) {
PageAction.this.pageContext.publishPageMessage(pme);

View file

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

View file

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

View file

@ -60,7 +60,6 @@ public class EntityTable<ROW extends Entity> {
final I18nSupport i18nSupport;
final List<ColumnDefinition<ROW>> columns;
final List<TableRowAction> actions;
final LocTextKey emptyMessage;
final Composite composite;
@ -82,7 +81,6 @@ public class EntityTable<ROW extends Entity> {
final Function<RestCall<Page<ROW>>.RestCallBuilder, RestCall<Page<ROW>>.RestCallBuilder> restCallAdapter,
final PageService pageService,
final List<ColumnDefinition<ROW>> columns,
final List<TableRowAction> actions,
final int pageSize,
final LocTextKey emptyMessage,
final Function<EntityTable<ROW>, PageAction> defaultActionFunction,
@ -95,7 +93,6 @@ public class EntityTable<ROW extends Entity> {
this.restCall = restCall;
this.restCallAdapter = (restCallAdapter != null) ? restCallAdapter : Function.identity();
this.columns = Utils.immutableListOf(columns);
this.actions = Utils.immutableListOf(actions);
this.emptyMessage = emptyMessage;
this.hideNavigation = hideNavigation;
@ -324,9 +321,6 @@ public class EntityTable<ROW extends Entity> {
setValueToCell(item, index, column.valueSupplier.apply(row));
index++;
}
if (this.actions != null) {
// TODO??
}
}
return page;

View file

@ -29,13 +29,12 @@ public class TableBuilder<ROW extends Entity> {
private final PageService pageService;
final RestCall<Page<ROW>> restCall;
final List<ColumnDefinition<ROW>> columns = new ArrayList<>();
final List<TableRowAction> actions = new ArrayList<>();
LocTextKey emptyMessage;
private Function<EntityTable<ROW>, PageAction> defaultActionFunction;
private int pageSize = -1;
private int type = SWT.NONE;
private boolean hideNavigation = false;
private Function<RestCall<Page<ROW>>.RestCallBuilder, RestCall<Page<ROW>>.RestCallBuilder> restCallAdapter;;
private Function<RestCall<Page<ROW>>.RestCallBuilder, RestCall<Page<ROW>>.RestCallBuilder> restCallAdapter;
public TableBuilder(
final PageService pageService,
@ -75,11 +74,6 @@ public class TableBuilder<ROW extends Entity> {
return this;
}
public TableBuilder<ROW> withAction(final TableRowAction action) {
this.actions.add(action);
return this;
}
public TableBuilder<ROW> withRestCallAdapter(
final Function<RestCall<Page<ROW>>.RestCallBuilder, RestCall<Page<ROW>>.RestCallBuilder> adapter) {
this.restCallAdapter = adapter;
@ -120,7 +114,6 @@ public class TableBuilder<ROW extends Entity> {
this.restCallAdapter,
this.pageService,
this.columns,
this.actions,
this.pageSize,
this.emptyMessage,
this.defaultActionFunction,

View file

@ -1,13 +0,0 @@
/*
* 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.table;
public class TableRowAction {
}

View file

@ -17,6 +17,7 @@ import java.util.Locale;
import java.util.function.Consumer;
import java.util.function.Supplier;
import org.apache.commons.lang3.StringUtils;
import org.eclipse.rap.rwt.RWT;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Device;
@ -309,6 +310,15 @@ public class WidgetFactory {
final int columns,
final LocTextKey locTextKey) {
return groupLocalized(parent, columns, locTextKey, null);
}
public Group groupLocalized(
final Composite parent,
final int columns,
final LocTextKey locTextKey,
final LocTextKey locTooltipKey) {
final Group group = new Group(parent, SWT.NONE);
final GridLayout gridLayout = new GridLayout(columns, true);
gridLayout.verticalSpacing = 0;
@ -316,7 +326,7 @@ public class WidgetFactory {
gridLayout.marginHeight = 0;
group.setLayout(gridLayout);
this.injectI18n(group, locTextKey);
this.injectI18n(group, locTextKey, locTooltipKey);
return group;
}
@ -521,8 +531,8 @@ public class WidgetFactory {
labelFunction.accept(label);
}
public void injectI18n(final Group group, final LocTextKey locTextKey) {
final Consumer<Group> groupFunction = groupFunction(locTextKey, null, this.i18nSupport);
public void injectI18n(final Group group, final LocTextKey locTextKey, final LocTextKey locTooltipKey) {
final Consumer<Group> groupFunction = groupFunction(locTextKey, locTooltipKey, this.i18nSupport);
group.setData(POLYGLOT_WIDGET_FUNCTION_KEY, groupFunction);
groupFunction.accept(group);
}
@ -656,7 +666,7 @@ public class WidgetFactory {
group.setText(i18nSupport.getText(locTextKey));
}
if (locToolTipKey != null) {
group.setToolTipText(i18nSupport.getText(locToolTipKey));
group.setToolTipText(i18nSupport.getText(locToolTipKey, StringUtils.EMPTY));
}
};
}

View file

@ -42,6 +42,8 @@ public interface ConfigurationDAO extends EntityDAO<Configuration, Configuration
* @return the new follow-up Configuration model */
Result<Configuration> saveToHistory(Long configurationNodeId);
Result<Configuration> undo(Long configurationNodeId);
/** Restores the current follow-up Configuration to the values of a given Configuration
* in the history of the specified ConfigurationNode.
*

View file

@ -165,18 +165,7 @@ public class ConfigurationDAOImpl implements ConfigurationDAO {
return Result.tryCatch(() -> {
// get follow-up configuration...
final ConfigurationRecord followupConfig = this.configurationRecordMapper
.selectByExample()
.where(
ConfigurationRecordDynamicSqlSupport.configurationNodeId,
isEqualTo(configurationNodeId))
.and(
ConfigurationRecordDynamicSqlSupport.followup,
isEqualTo(BooleanUtils.toInteger(true)))
.build()
.execute()
.stream()
.collect(Utils.toSingleton());
final ConfigurationRecord followupConfig = getFollowupConfigurationRecord(configurationNodeId);
// with actual attribute values
final List<ConfigurationValueRecord> allValues = this.configurationValueRecordMapper
@ -239,6 +228,41 @@ public class ConfigurationDAOImpl implements ConfigurationDAO {
.onError(TransactionHandler::rollback);
}
private ConfigurationRecord getFollowupConfigurationRecord(final Long configurationNodeId) {
return this.configurationRecordMapper
.selectByExample()
.where(
ConfigurationRecordDynamicSqlSupport.configurationNodeId,
isEqualTo(configurationNodeId))
.and(
ConfigurationRecordDynamicSqlSupport.followup,
isEqualTo(BooleanUtils.toInteger(true)))
.build()
.execute()
.stream()
.collect(Utils.toSingleton());
}
@Override
@Transactional
public Result<Configuration> undo(final Long configurationNodeId) {
return Result.tryCatch(() -> {
// get all configurations of the node
final List<ConfigurationRecord> configs = this.configurationRecordMapper
.selectByExample()
.where(
ConfigurationRecordDynamicSqlSupport.configurationNodeId,
isEqualTo(configurationNodeId))
.orderBy(ConfigurationRecordDynamicSqlSupport.versionDate)
.build()
.execute();
return configs.get(configs.size() - 1);
})
.flatMap(rec -> restoreToVersion(configurationNodeId, rec.getId()))
.onError(TransactionHandler::rollback);
}
@Override
@Transactional
public Result<Configuration> restoreToVersion(final Long configurationNodeId, final Long configId) {
@ -268,28 +292,17 @@ public class ConfigurationDAOImpl implements ConfigurationDAO {
.execute();
// get follow-up configuration id
final ConfigurationRecord followup = this.configurationRecordMapper
.selectByExample()
.where(
ConfigurationRecordDynamicSqlSupport.configurationNodeId,
isEqualTo(configurationNodeId))
.and(
ConfigurationRecordDynamicSqlSupport.followup,
isEqualTo(BooleanUtils.toInteger(true)))
.build()
.execute()
.stream()
.collect(Utils.toSingleton());
final ConfigurationRecord followup = getFollowupConfigurationRecord(configurationNodeId);
// restore all current values of the follow-up with historic values
// TODO batch here for better performance
historicValues.stream()
.map(historicValRec -> new ConfigurationValueRecord(
null,
null,
null,
followup.getInstitutionId(),
followup.getId(),
historicValRec.getConfigurationAttributeId(),
null,
historicValRec.getListIndex(),
historicValRec.getValue(),
historicValRec.getText()))
.forEach(newValRec -> this.configurationValueRecordMapper

View file

@ -8,6 +8,8 @@
package ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig;
import java.io.OutputStream;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationTableValues;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationValue;
@ -17,4 +19,8 @@ public interface SebExamConfigService {
void validate(ConfigurationTableValues tableValue);
void exportXML(OutputStream out, Long configurationNodeId);
void exportForExam(OutputStream out, Long configExamMappingId);
}

View file

@ -0,0 +1,25 @@
/*
* Copyright (c) 2019 ETH Zürich, Educational Development and Technology (LET)
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig;
import java.io.OutputStream;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationAttribute;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationValue;
public interface XMLValueConverter {
String name();
void convertToXML(
OutputStream out,
ConfigurationAttribute attribute,
ConfigurationValue value);
}

View file

@ -8,6 +8,7 @@
package ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.impl;
import java.io.OutputStream;
import java.util.Collection;
import org.slf4j.Logger;
@ -22,6 +23,7 @@ import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile;
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.ConfigurationAttributeDAO;
import ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.ConfigurationValueValidator;
import ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.SebExamConfigService;
import ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.XMLValueConverter;
@Lazy
@Service
@ -32,13 +34,16 @@ public class SebExamConfigServiceImpl implements SebExamConfigService {
private final ConfigurationAttributeDAO configurationAttributeDAO;
private final Collection<ConfigurationValueValidator> validators;
private final Collection<XMLValueConverter> converters;
protected SebExamConfigServiceImpl(
final ConfigurationAttributeDAO configurationAttributeDAO,
final Collection<ConfigurationValueValidator> validators) {
final Collection<ConfigurationValueValidator> validators,
final Collection<XMLValueConverter> converters) {
this.configurationAttributeDAO = configurationAttributeDAO;
this.validators = validators;
this.converters = converters;
}
@Override
@ -65,4 +70,16 @@ public class SebExamConfigServiceImpl implements SebExamConfigService {
}
@Override
public void exportXML(final OutputStream out, final Long configurationNodeId) {
// TODO Auto-generated method stub
}
@Override
public void exportForExam(final OutputStream out, final Long configExamMappingId) {
// TODO Auto-generated method stub
}
}

View file

@ -0,0 +1,43 @@
/*
* Copyright (c) 2019 ETH Zürich, Educational Development and Technology (LET)
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.impl.converter;
import java.io.OutputStream;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationAttribute;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationValue;
import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile;
import ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.XMLValueConverter;
@Lazy
@Component
@WebServiceProfile
public class KioskModeConverter implements XMLValueConverter {
public static final String NAME = "KioskModeConverter";
@Override
public String name() {
return NAME;
}
@Override
public void convertToXML(
final OutputStream out,
final ConfigurationAttribute attribute,
final ConfigurationValue value) {
// TODO Auto-generated method stub
}
}

View file

@ -64,24 +64,37 @@ public class ConfigurationController extends EntityController<Configuration, Con
method = RequestMethod.POST,
consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE,
produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
public Configuration saveToHistory(@PathVariable final String configId) {
public Configuration saveToHistory(@PathVariable final String modelId) {
return this.entityDAO.byModelId(configId)
return this.entityDAO.byModelId(modelId)
.flatMap(this::checkModifyAccess)
.flatMap(config -> this.configurationDAO.saveToHistory(config.configurationNodeId))
.getOrThrow();
}
@RequestMapping(
path = API.CONFIGURATION_UNDO_PATH_SEGMENT + API.MODEL_ID_VAR_PATH_SEGMENT,
method = RequestMethod.POST,
consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE,
produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
public Configuration undo(@PathVariable final String modelId) {
return this.entityDAO.byModelId(modelId)
.flatMap(this::checkModifyAccess)
.flatMap(config -> this.configurationDAO.undo(config.configurationNodeId))
.getOrThrow();
}
@RequestMapping(
path = API.CONFIGURATION_RESTORE_FROM_HISTORY_PATH_SEGMENT + API.MODEL_ID_VAR_PATH_SEGMENT,
method = RequestMethod.POST,
consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE,
produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
public Configuration restoreFormHistory(
@PathVariable final String configId,
@PathVariable final String modelId,
@RequestParam(name = API.PARAM_PARENT_MODEL_ID, required = true) final Long configurationNodeId) {
return this.entityDAO.byModelId(configId)
return this.entityDAO.byModelId(modelId)
.flatMap(this::checkModifyAccess)
.flatMap(config -> this.configurationDAO.restoreToVersion(configurationNodeId, config.getId()))
.getOrThrow();

View file

@ -360,6 +360,10 @@ sebserver.examconfig.action.list.modify.properties=Edit Properties
sebserver.examconfig.action.modify=Edit
sebserver.examconfig.action.modify.properties=Edit Properties
sebserver.examconfig.action.save=Save
sebserver.examconfig.action.saveToHistory=Save In History
sebserver.examconfig.action.saveToHistory.success=Successfully saved in history
sebserver.examconfig.action.undo=Undo
sebserver.examconfig.action.undo.success=Successfully reverted to last saved state
sebserver.examconfig.form.title.new=New Exam Configuration
sebserver.examconfig.form.title=Exam Configuration
@ -378,6 +382,11 @@ sebserver.examconfig.props.form.views.browser=Browser
sebserver.examconfig.props.form.views.down_upload=Down/Uploads
sebserver.examconfig.props.form.views.exam=Exam
sebserver.examconfig.props.form.views.applications=Applications
sebserver.examconfig.props.form.views.resources=Additional Resources
sebserver.examconfig.props.form.views.network=Network
sebserver.examconfig.props.form.views.security=Security
sebserver.examconfig.props.form.views.registry=Registry
sebserver.examconfig.props.form.views.hooked_keys=Hooked Keys
sebserver.examconfig.props.label.hashedAdminPassword=Administrator password
sebserver.examconfig.props.label.hashedAdminPassword.confirm=Confirm password
@ -570,6 +579,122 @@ sebserver.examconfig.props.label.prohibitedProcesses.originalName=Original Name
sebserver.examconfig.props.label.prohibitedProcesses.identifier=Identifier
sebserver.examconfig.props.label.prohibitedProcesses.strongKill=Force quit (risk of data loss)
sebserver.examconfig.props.label.URLFilterEnable=Activate URL Filtering
sebserver.examconfig.props.label.URLFilterEnableContentFilter=Filter also embedded content
sebserver.examconfig.props.label.URLFilterRules=Filter
sebserver.examconfig.props.label.URLFilterRules.active=Activity
sebserver.examconfig.props.label.URLFilterRules.regex=Regex
sebserver.examconfig.props.label.URLFilterRules.expression=Expression
sebserver.examconfig.props.label.URLFilterRules.action=Action
sebserver.examconfig.props.label.URLFilterRules.action.0=Block
sebserver.examconfig.props.label.URLFilterRules.action.1=Allow
sebserver.examconfig.props.group.servicePolicy=SEB Service policy
sebserver.examconfig.props.label.sebServicePolicy.0=allow to run SEB without service
sebserver.examconfig.props.label.sebServicePolicy.1=display warning when service is not running
sebserver.examconfig.props.label.sebServicePolicy.2=allow to use SEB only with service
sebserver.examconfig.props.label.sebServicePolicy.tooltip=Policy that applies when an exam client doesn't have the SEB client running
sebserver.examconfig.props.group.kioskMode=Kiosk Mode
sebserver.examconfig.props.label.kioskMode.tooltip=The kiosk mode setting reflects how the computer is locked down into SEB.
sebserver.examconfig.props.label.kioskMode.0=Create new desktop
sebserver.examconfig.props.label.kioskMode.0.tooltip=This kiosk mode may prevent specific third party software to run correctly together with SEB, like some screen recording software or the Windows onscreen keyboard.
sebserver.examconfig.props.label.kioskMode.1=Disable explorer Shell
sebserver.examconfig.props.label.kioskMode.1.tooltip=This kiosk mode is compatible with some screen recording/proctoring software and the Windows onscreen keyboard.
sebserver.examconfig.props.label.kioskMode.2=None (for debugging only)
sebserver.examconfig.props.label.kioskMode.2.tooltip=SEB runs without kiosk mode, switching to other applications is possible. Use this for debugging purposes only.
sebserver.examconfig.props.label.allowVirtualMachine=Allow to run inside virtual machine
sebserver.examconfig.props.label.allowVirtualMachine.tooltip=Indicates if SEB is allowed to run in a virtual machine or not (in order to prevent potential manipulation).
sebserver.examconfig.props.label.allowScreenSharing=Allow remote session/screen sharing
sebserver.examconfig.props.label.allowScreenSharing.tootlip=Allows Windows remote sessions and macOS screen sharing to be used
sebserver.examconfig.props.label.enablePrivateClipboard=Use private clipboard (Mac)
sebserver.examconfig.props.label.enablePrivateClipboard.tooltip=Private clipboard should always be used beside when working with third party application in managed/virtual machine
sebserver.examconfig.props.group.logging=Logging
sebserver.examconfig.props.label.enableLogging=Enable logging
sebserver.examconfig.props.label.enableLogging.tooltip=The log can help debugging SEB (send it to the developers) and to find out about possible manipulations
sebserver.examconfig.props.label.logDirectoryWin=Log file directory on Windows
sebserver.examconfig.props.label.logDirectoryOSX=Log file directory on Mac
sebserver.examconfig.props.group.macSettings=macOS specific settings
sebserver.examconfig.props.label.minMacOSVersion=Enforce minimal macOS version:
sebserver.examconfig.props.label.minMacOSVersion.0=OS X 10.7 Lion
sebserver.examconfig.props.label.minMacOSVersion.1=OS X 10.8 Mountain Lion
sebserver.examconfig.props.label.minMacOSVersion.2=OS X 10.9 Mavericks
sebserver.examconfig.props.label.minMacOSVersion.3=OS X 10.10 Yosemite
sebserver.examconfig.props.label.minMacOSVersion.4=OS X 10.11 El Capitan
sebserver.examconfig.props.label.minMacOSVersion.5=OS X 10.12 Sierra
sebserver.examconfig.props.label.minMacOSVersion.6=OS X 10.13 Hight Sierra
sebserver.examconfig.props.label.minMacOSVersion.7=OS X 10.14 Mojave
sebserver.examconfig.props.label.enableAppSwitcherCheck=Disable app switcher when starting
sebserver.examconfig.props.label.enableAppSwitcherCheck.tooltip=SEB checks for the command key being held down while SEB is starting up. This prevents using the application switcher to mess with SEB\'s kiosk mode
sebserver.examconfig.props.label.forceAppFolderInstall=Force installation in Applications folder
sebserver.examconfig.props.label.forceAppFolderInstall.tooltip=SEB enforces to be installed in an Applications folder (/Applications or ~/Applications)
sebserver.examconfig.props.label.allowUserAppFolderInstall=Allow also user's ~/Applications folder
sebserver.examconfig.props.label.allowUserAppFolderInstall.tooltip=SEB can also be installed in the Applications folder of the current user (~/Applications)
sebserver.examconfig.props.label.allowSiri=Allow to use Siri
sebserver.examconfig.props.label.allowSiri.tooltip=If enabled, Siri can be used by tapping th emenu bar icon, Touch Bar icon or shortcut set in System Preferences/Siri (default: hold command space). The Siri window won't be displayed though
sebserver.examconfig.props.label.detectStoppedProcess=Detect when SEB process was stopped
sebserver.examconfig.props.label.detectStoppedProcess.tooltip=SEB displays a lock screen (requiring to enter the quit/unlock password) if it detects its process was stopped, which can indicate manipulation
sebserver.examconfig.props.label.allowDisplayMirroring=Allow display mirroring (affects also AirPlay Display)
sebserver.examconfig.props.label.allowDisplayMirroring.tooltip=If not selected, SEB prevents to mirror the main display to another
sebserver.examconfig.props.label.allowedDisplaysMaxNumber=Maximum allowed number of connected displays
sebserver.examconfig.props.label.allowedDisplaysMaxNumber.tooltip=If more displays are connected, this are blanked with an orange full screen window
sebserver.examconfig.props.label.allowedDisplayBuiltin=Use built-in display
sebserver.examconfig.props.label.allowedDisplayBuiltin.tooltip=Use the built-in display (if available) when only one display is allowed or when switching off display mirroring
sebserver.examconfig.props.group.registry=While running SEB
sebserver.examconfig.props.group.registry.tooltip=Options in the Windows Security Screen invoked by Ctrl-Alt-Del
sebserver.examconfig.props.label.insideSebEnableSwitchUser=Enable Switch User
sebserver.examconfig.props.label.insideSebEnableSwitchUser.tooltip=Activates the button "Switch User"
sebserver.examconfig.props.label.insideSebEnableLockThisComputer=Enable Lock this computer
sebserver.examconfig.props.label.insideSebEnableLockThisComputer.tooltip=Activates the button "Lock this computer"
sebserver.examconfig.props.label.insideSebEnableChangeAPassword=Enable Change a password
sebserver.examconfig.props.label.insideSebEnableChangeAPassword.tooltip=Activates the button "Change a password..."
sebserver.examconfig.props.label.insideSebEnableStartTaskManager=Enable Start Task Manager
sebserver.examconfig.props.label.insideSebEnableStartTaskManager.tooltip=Activates the button "Start Task Manager"
sebserver.examconfig.props.label.insideSebEnableLogOff=Enable Log off
sebserver.examconfig.props.label.insideSebEnableLogOff.tooltip=Activates the button "Log off"
sebserver.examconfig.props.label.insideSebEnableShutDown=Enable Shut down
sebserver.examconfig.props.label.insideSebEnableShutDown.tooltip=Activates the button "Shutdown"
sebserver.examconfig.props.label.insideSebEnableEaseOfAccess=Enable Ease of Access
sebserver.examconfig.props.label.insideSebEnableEaseOfAccess.tooltip=Shows options when the button "Ease of Access" in the lower left corner is clicked,\nwhich offers help e.g. to visually or aurally handicapped persons, like the Magnifier Glass.
sebserver.examconfig.props.label.insideSebEnableVmWareClientShade=Enable VMware Client Shade
sebserver.examconfig.props.label.insideSebEnableVmWareClientShade.tooltip=Activates the "Shade" bar at the upper edge of a virtual desktop, if existent. If you're not using VMware, this setting doesn't have any effect.
sebserver.examconfig.props.label.insideSebEnableNetworkConnectionSelector=Enable network connection selector
sebserver.examconfig.props.label.insideSebEnableNetworkConnectionSelector.tooltip=Activates the button which allows to connect to WiFi networks, introduces in Windows 10.
sebserver.examconfig.props.group.specialKeys=Special Keys
sebserver.examconfig.props.group.specialKeys.tooltip=Settings to enable or block (hook) keys, key combinations and mouse buttons.
sebserver.examconfig.props.label.enableEsc=Enable Esc
sebserver.examconfig.props.label.enablePrintScreen=Enable Print Screen
sebserver.examconfig.props.label.enablePrintScreen.tooltip=Controls Print Screen and OS X screen capture, corresponds with Enable screen capture in Security settings.
sebserver.examconfig.props.label.enableCtrlEsc=Enable Ctrl-Esc
sebserver.examconfig.props.label.enableAltEsc=Enable Alt-Esc
sebserver.examconfig.props.label.enableAltTab=Enable Alt-Tap
sebserver.examconfig.props.label.enableAltF4=Enable Alt-F4
sebserver.examconfig.props.label.enableStartMenu=Enable Start Menu
sebserver.examconfig.props.label.enableRightMouse=Enable Right Mouse
sebserver.examconfig.props.label.enableAltMouseWheel=Enable Alt-Mousewheel
sebserver.examconfig.props.label.enableAltMouseWheel.tooltip=Corresponds to 'Allow browsing back/forward' in Browser pane. Disabling browsing to previously visited pages may increase security, because browsing back might allow to leave an exam
sebserver.examconfig.props.group.functionKeys=Function Keys
sebserver.examconfig.props.group.functionKeys.tooltip=Enable or block function keys. This doesn't have any effect on the SEB exit sequence. Depending on specific keyboards some function keys cannot be blocked.
sebserver.examconfig.props.label.enableF1=Enable F1
sebserver.examconfig.props.label.enableF2=Enable F2
sebserver.examconfig.props.label.enableF3=Enable F3
sebserver.examconfig.props.label.enableF4=Enable F4
sebserver.examconfig.props.label.enableF5=Enable F5
sebserver.examconfig.props.label.enableF6=Enable F6
sebserver.examconfig.props.label.enableF7=Enable F7
sebserver.examconfig.props.label.enableF8=Enable F8
sebserver.examconfig.props.label.enableF9=Enable F9
sebserver.examconfig.props.label.enableF10=Enable F10
sebserver.examconfig.props.label.enableF11=Enable F11
sebserver.examconfig.props.label.enableF12=Enable F12
sebserver.examconfig.props.validation.password.confirm=Please enter correct confirm password
sebserver.examconfig.props.validation.unexpected=Unexpected error happened. Value was not set correctly

View file

@ -604,7 +604,7 @@ TabItem {
text-decoration: none;
text-shadow: none;
background-image: none;
margin: 1px 0px 0px -1px;
margin: 1px 0px 0px 0px;
border: 1px solid #bdbdbd;
border-bottom: none;
border-left: none;
@ -734,15 +734,6 @@ Widget-ToolTip-Pointer {
background-image: none;
}
/* Table default theme */
Table {
font: 12px Arial, Helvetica, sans-serif;