SEBSERV-160 implemented reset from template
This commit is contained in:
parent
0921b713f5
commit
55baa2d518
45 changed files with 446 additions and 89 deletions
|
@ -20,7 +20,7 @@ public final class API {
|
||||||
|
|
||||||
public enum BatchActionType {
|
public enum BatchActionType {
|
||||||
EXAM_CONFIG_STATE_CHANGE(EntityType.CONFIGURATION_NODE),
|
EXAM_CONFIG_STATE_CHANGE(EntityType.CONFIGURATION_NODE),
|
||||||
EXAM_CONFIG_APPLY_TEMPLATE_VALUES(EntityType.CONFIGURATION_NODE);
|
EXAM_CONFIG_REST_TEMPLATE_SETTINGS(EntityType.CONFIGURATION_NODE);
|
||||||
|
|
||||||
public final EntityType entityType;
|
public final EntityType entityType;
|
||||||
|
|
||||||
|
|
|
@ -246,7 +246,7 @@ public class APIMessage implements Serializable {
|
||||||
}
|
}
|
||||||
|
|
||||||
public APIMessageException(final APIMessage apiMessage) {
|
public APIMessageException(final APIMessage apiMessage) {
|
||||||
super();
|
super(apiMessage.systemMessage + " " + apiMessage.details);
|
||||||
this.apiMessages = Arrays.asList(apiMessage);
|
this.apiMessages = Arrays.asList(apiMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -18,6 +18,7 @@ public enum UserLogActivityType {
|
||||||
PASSWORD_CHANGE,
|
PASSWORD_CHANGE,
|
||||||
DEACTIVATE,
|
DEACTIVATE,
|
||||||
ACTIVATE,
|
ACTIVATE,
|
||||||
|
FINISHED,
|
||||||
DELETE,
|
DELETE,
|
||||||
LOGIN,
|
LOGIN,
|
||||||
LOGOUT
|
LOGOUT
|
||||||
|
|
|
@ -533,6 +533,12 @@ public enum ActionDefinition {
|
||||||
PageStateDefinitionImpl.SEB_EXAM_CONFIG_LIST,
|
PageStateDefinitionImpl.SEB_EXAM_CONFIG_LIST,
|
||||||
ActionCategory.SEB_EXAM_CONFIG_LIST),
|
ActionCategory.SEB_EXAM_CONFIG_LIST),
|
||||||
|
|
||||||
|
SEB_EXAM_CONFIG_BULK_RESET_TO_TEMPLATE(
|
||||||
|
new LocTextKey("sebserver.examconfig.list.action.reset"),
|
||||||
|
ImageIcon.EXPORT,
|
||||||
|
PageStateDefinitionImpl.SEB_EXAM_CONFIG_LIST,
|
||||||
|
ActionCategory.SEB_EXAM_CONFIG_LIST),
|
||||||
|
|
||||||
SEB_EXAM_CONFIG_MODIFY_PROP_FROM_LIST(
|
SEB_EXAM_CONFIG_MODIFY_PROP_FROM_LIST(
|
||||||
new LocTextKey("sebserver.examconfig.action.list.modify.properties"),
|
new LocTextKey("sebserver.examconfig.action.list.modify.properties"),
|
||||||
ImageIcon.EDIT,
|
ImageIcon.EDIT,
|
||||||
|
|
|
@ -138,14 +138,14 @@ public class InstitutionList implements TemplateComposer {
|
||||||
|
|
||||||
.newAction(ActionDefinition.INSTITUTION_VIEW_FROM_LIST)
|
.newAction(ActionDefinition.INSTITUTION_VIEW_FROM_LIST)
|
||||||
.withSelect(
|
.withSelect(
|
||||||
table::getSelection,
|
table::getMultiSelection,
|
||||||
PageAction::applySingleSelectionAsEntityKey,
|
PageAction::applySingleSelectionAsEntityKey,
|
||||||
EMPTY_SELECTION_TEXT_KEY)
|
EMPTY_SELECTION_TEXT_KEY)
|
||||||
.publish(false)
|
.publish(false)
|
||||||
|
|
||||||
.newAction(ActionDefinition.INSTITUTION_MODIFY_FROM_LIST)
|
.newAction(ActionDefinition.INSTITUTION_MODIFY_FROM_LIST)
|
||||||
.withSelect(
|
.withSelect(
|
||||||
table::getSelection,
|
table::getMultiSelection,
|
||||||
PageAction::applySingleSelectionAsEntityKey,
|
PageAction::applySingleSelectionAsEntityKey,
|
||||||
EMPTY_SELECTION_TEXT_KEY)
|
EMPTY_SELECTION_TEXT_KEY)
|
||||||
.publishIf(() -> instGrant.m(), false)
|
.publishIf(() -> instGrant.m(), false)
|
||||||
|
|
|
@ -222,11 +222,12 @@ public class UserAccountList implements TemplateComposer {
|
||||||
.publishIf(userGrant::iw)
|
.publishIf(userGrant::iw)
|
||||||
|
|
||||||
.newAction(ActionDefinition.USER_ACCOUNT_VIEW_FROM_LIST)
|
.newAction(ActionDefinition.USER_ACCOUNT_VIEW_FROM_LIST)
|
||||||
.withSelect(table::getSelection, PageAction::applySingleSelectionAsEntityKey, EMPTY_SELECTION_TEXT_KEY)
|
.withSelect(table::getMultiSelection, PageAction::applySingleSelectionAsEntityKey,
|
||||||
|
EMPTY_SELECTION_TEXT_KEY)
|
||||||
.publish(false)
|
.publish(false)
|
||||||
|
|
||||||
.newAction(ActionDefinition.USER_ACCOUNT_MODIFY_FROM_LIST)
|
.newAction(ActionDefinition.USER_ACCOUNT_MODIFY_FROM_LIST)
|
||||||
.withSelect(table::getSelection, this::editAction, EMPTY_SELECTION_TEXT_KEY)
|
.withSelect(table::getMultiSelection, this::editAction, EMPTY_SELECTION_TEXT_KEY)
|
||||||
.publishIf(() -> userGrant.im(), false)
|
.publishIf(() -> userGrant.im(), false)
|
||||||
|
|
||||||
.newAction(ActionDefinition.USER_ACCOUNT_TOGGLE_ACTIVITY)
|
.newAction(ActionDefinition.USER_ACCOUNT_TOGGLE_ACTIVITY)
|
||||||
|
|
|
@ -244,7 +244,7 @@ public class UserActivityLogs implements TemplateComposer {
|
||||||
actionBuilder
|
actionBuilder
|
||||||
.newAction(ActionDefinition.LOGS_USER_ACTIVITY_SHOW_DETAILS)
|
.newAction(ActionDefinition.LOGS_USER_ACTIVITY_SHOW_DETAILS)
|
||||||
.withSelect(
|
.withSelect(
|
||||||
table::getSelection,
|
table::getMultiSelection,
|
||||||
action -> this.showDetails(action, table.getSingleSelectedROWData()),
|
action -> this.showDetails(action, table.getSingleSelectedROWData()),
|
||||||
EMPTY_SELECTION_TEXT)
|
EMPTY_SELECTION_TEXT)
|
||||||
.noEventPropagation()
|
.noEventPropagation()
|
||||||
|
|
|
@ -159,7 +159,7 @@ public class CertificateList implements TemplateComposer {
|
||||||
.newAction(ActionDefinition.SEB_CERTIFICATE_REMOVE)
|
.newAction(ActionDefinition.SEB_CERTIFICATE_REMOVE)
|
||||||
.withConfirm(() -> FORM_ACTION_MESSAGE_REMOVE_CONFIRM_TEXT_KEY)
|
.withConfirm(() -> FORM_ACTION_MESSAGE_REMOVE_CONFIRM_TEXT_KEY)
|
||||||
.withSelect(
|
.withSelect(
|
||||||
table::getSelection,
|
table::getMultiSelection,
|
||||||
this::removeCertificate,
|
this::removeCertificate,
|
||||||
EMPTY_SELECTION_TEXT_KEY)
|
EMPTY_SELECTION_TEXT_KEY)
|
||||||
.publishIf(() -> grantCheck.iw(), false);
|
.publishIf(() -> grantCheck.iw(), false);
|
||||||
|
|
|
@ -249,7 +249,7 @@ public class ConfigTemplateForm implements TemplateComposer {
|
||||||
.newAction(ActionDefinition.SEB_EXAM_CONFIG_TEMPLATE_ATTR_EDIT)
|
.newAction(ActionDefinition.SEB_EXAM_CONFIG_TEMPLATE_ATTR_EDIT)
|
||||||
.withParentEntityKey(entityKey)
|
.withParentEntityKey(entityKey)
|
||||||
.withSelect(
|
.withSelect(
|
||||||
attrTable::getSelection,
|
attrTable::getMultiSelection,
|
||||||
PageAction::applySingleSelectionAsEntityKey,
|
PageAction::applySingleSelectionAsEntityKey,
|
||||||
EMPTY_ATTRIBUTE_SELECTION_TEXT_KEY)
|
EMPTY_ATTRIBUTE_SELECTION_TEXT_KEY)
|
||||||
.publishIf(() -> modifyGrant, false)
|
.publishIf(() -> modifyGrant, false)
|
||||||
|
@ -257,7 +257,7 @@ public class ConfigTemplateForm implements TemplateComposer {
|
||||||
.newAction(ActionDefinition.SEB_EXAM_CONFIG_TEMPLATE_ATTR_SET_DEFAULT)
|
.newAction(ActionDefinition.SEB_EXAM_CONFIG_TEMPLATE_ATTR_SET_DEFAULT)
|
||||||
.withParentEntityKey(entityKey)
|
.withParentEntityKey(entityKey)
|
||||||
.withSelect(
|
.withSelect(
|
||||||
attrTable::getSelection,
|
attrTable::getMultiSelection,
|
||||||
action -> this.resetToDefaults(action, attrTable),
|
action -> this.resetToDefaults(action, attrTable),
|
||||||
EMPTY_ATTRIBUTE_SELECTION_TEXT_KEY)
|
EMPTY_ATTRIBUTE_SELECTION_TEXT_KEY)
|
||||||
.noEventPropagation()
|
.noEventPropagation()
|
||||||
|
@ -266,7 +266,7 @@ public class ConfigTemplateForm implements TemplateComposer {
|
||||||
.newAction(ActionDefinition.SEB_EXAM_CONFIG_TEMPLATE_ATTR_LIST_REMOVE_VIEW)
|
.newAction(ActionDefinition.SEB_EXAM_CONFIG_TEMPLATE_ATTR_LIST_REMOVE_VIEW)
|
||||||
.withParentEntityKey(entityKey)
|
.withParentEntityKey(entityKey)
|
||||||
.withSelect(
|
.withSelect(
|
||||||
attrTable::getSelection,
|
attrTable::getMultiSelection,
|
||||||
action -> this.removeFormView(action, attrTable),
|
action -> this.removeFormView(action, attrTable),
|
||||||
EMPTY_ATTRIBUTE_SELECTION_TEXT_KEY)
|
EMPTY_ATTRIBUTE_SELECTION_TEXT_KEY)
|
||||||
.noEventPropagation()
|
.noEventPropagation()
|
||||||
|
@ -275,7 +275,7 @@ public class ConfigTemplateForm implements TemplateComposer {
|
||||||
.newAction(ActionDefinition.SEB_EXAM_CONFIG_TEMPLATE_ATTR_LIST_ATTACH_DEFAULT_VIEW)
|
.newAction(ActionDefinition.SEB_EXAM_CONFIG_TEMPLATE_ATTR_LIST_ATTACH_DEFAULT_VIEW)
|
||||||
.withParentEntityKey(entityKey)
|
.withParentEntityKey(entityKey)
|
||||||
.withSelect(
|
.withSelect(
|
||||||
attrTable::getSelection,
|
attrTable::getMultiSelection,
|
||||||
action -> this.attachView(action, attrTable),
|
action -> this.attachView(action, attrTable),
|
||||||
EMPTY_ATTRIBUTE_SELECTION_TEXT_KEY)
|
EMPTY_ATTRIBUTE_SELECTION_TEXT_KEY)
|
||||||
.noEventPropagation()
|
.noEventPropagation()
|
||||||
|
|
|
@ -147,7 +147,7 @@ public class ConfigTemplateList implements TemplateComposer {
|
||||||
.publishIf(examConfigGrant::iw)
|
.publishIf(examConfigGrant::iw)
|
||||||
|
|
||||||
.newAction(ActionDefinition.SEB_EXAM_CONFIG_TEMPLATE_VIEW_FROM_LIST)
|
.newAction(ActionDefinition.SEB_EXAM_CONFIG_TEMPLATE_VIEW_FROM_LIST)
|
||||||
.withSelect(templateTable::getSelection, PageAction::applySingleSelectionAsEntityKey,
|
.withSelect(templateTable::getMultiSelection, PageAction::applySingleSelectionAsEntityKey,
|
||||||
EMPTY_TEMPLATE_SELECTION_TEXT_KEY)
|
EMPTY_TEMPLATE_SELECTION_TEXT_KEY)
|
||||||
.publish(false)
|
.publish(false)
|
||||||
|
|
||||||
|
|
|
@ -173,7 +173,8 @@ public class SEBClientConfigList implements TemplateComposer {
|
||||||
.publishIf(clientConfigGrant::iw)
|
.publishIf(clientConfigGrant::iw)
|
||||||
|
|
||||||
.newAction(ActionDefinition.SEB_CLIENT_CONFIG_VIEW_FROM_LIST)
|
.newAction(ActionDefinition.SEB_CLIENT_CONFIG_VIEW_FROM_LIST)
|
||||||
.withSelect(table::getSelection, PageAction::applySingleSelectionAsEntityKey, EMPTY_SELECTION_TEXT_KEY)
|
.withSelect(table::getMultiSelection, PageAction::applySingleSelectionAsEntityKey,
|
||||||
|
EMPTY_SELECTION_TEXT_KEY)
|
||||||
.publish(false)
|
.publish(false)
|
||||||
|
|
||||||
.newAction(ActionDefinition.SEB_CLIENT_CONFIG_MODIFY_FROM_LIST)
|
.newAction(ActionDefinition.SEB_CLIENT_CONFIG_MODIFY_FROM_LIST)
|
||||||
|
|
|
@ -0,0 +1,95 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2022 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.content.configs;
|
||||||
|
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
|
import org.springframework.context.annotation.Lazy;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import ch.ethz.seb.sebserver.gbl.api.API.BatchActionType;
|
||||||
|
import ch.ethz.seb.sebserver.gbl.model.BatchAction;
|
||||||
|
import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationNode;
|
||||||
|
import ch.ethz.seb.sebserver.gbl.profile.GuiProfile;
|
||||||
|
import ch.ethz.seb.sebserver.gui.form.FormBuilder;
|
||||||
|
import ch.ethz.seb.sebserver.gui.form.FormHandle;
|
||||||
|
import ch.ethz.seb.sebserver.gui.service.i18n.LocTextKey;
|
||||||
|
import ch.ethz.seb.sebserver.gui.service.page.AbstractBatchActionWizard;
|
||||||
|
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.push.ServerPushService;
|
||||||
|
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestCall;
|
||||||
|
|
||||||
|
@Lazy
|
||||||
|
@Component
|
||||||
|
@GuiProfile
|
||||||
|
public class SEBExamConfigBatchResetToTemplatePopup extends AbstractBatchActionWizard {
|
||||||
|
|
||||||
|
private final static LocTextKey FORM_TITLE =
|
||||||
|
new LocTextKey("sebserver.examconfig.list.batch.reset.title");
|
||||||
|
private final static LocTextKey ACTION_DO_RESET =
|
||||||
|
new LocTextKey("sebserver.examconfig.list.batch.action.reset");
|
||||||
|
private final static LocTextKey FORM_INFO =
|
||||||
|
new LocTextKey("sebserver.examconfig.list.batch.action.reset.info");
|
||||||
|
|
||||||
|
protected SEBExamConfigBatchResetToTemplatePopup(
|
||||||
|
final PageService pageService,
|
||||||
|
final ServerPushService serverPushService) {
|
||||||
|
|
||||||
|
super(pageService, serverPushService);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected LocTextKey getTitle() {
|
||||||
|
return FORM_TITLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected LocTextKey getBatchActionInfo() {
|
||||||
|
return FORM_INFO;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected LocTextKey getBatchActionTitle() {
|
||||||
|
return ACTION_DO_RESET;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected BatchActionType getBatchActionType() {
|
||||||
|
return BatchActionType.EXAM_CONFIG_REST_TEMPLATE_SETTINGS;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Supplier<PageContext> createResultPageSupplier(
|
||||||
|
final PageContext pageContext,
|
||||||
|
final FormHandle<ConfigurationNode> formHandle) {
|
||||||
|
|
||||||
|
// No specific fields for this action
|
||||||
|
return () -> pageContext;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void extendBatchActionRequest(
|
||||||
|
final PageContext pageContext,
|
||||||
|
final RestCall<BatchAction>.RestCallBuilder batchActionRequestBuilder) {
|
||||||
|
|
||||||
|
// Nothing to do here
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected FormBuilder buildSpecificFormFields(
|
||||||
|
final PageContext formContext,
|
||||||
|
final FormBuilder formHead,
|
||||||
|
final boolean readonly) {
|
||||||
|
|
||||||
|
// No specific fields for this action
|
||||||
|
return formHead;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -32,7 +32,7 @@ import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestCall;
|
||||||
@Lazy
|
@Lazy
|
||||||
@Component
|
@Component
|
||||||
@GuiProfile
|
@GuiProfile
|
||||||
public class SEBExamConfigStateChangePopup extends AbstractBatchActionWizard {
|
public class SEBExamConfigBatchStateChangePopup extends AbstractBatchActionWizard {
|
||||||
|
|
||||||
private static final String ATTR_SELECTED_TARGET_STATE = "selectedTargetState";
|
private static final String ATTR_SELECTED_TARGET_STATE = "selectedTargetState";
|
||||||
|
|
||||||
|
@ -45,7 +45,7 @@ public class SEBExamConfigStateChangePopup extends AbstractBatchActionWizard {
|
||||||
private final static LocTextKey FORM_STATUS_TEXT_KEY =
|
private final static LocTextKey FORM_STATUS_TEXT_KEY =
|
||||||
new LocTextKey("sebserver.examconfig.list.batch.action.status");
|
new LocTextKey("sebserver.examconfig.list.batch.action.status");
|
||||||
|
|
||||||
protected SEBExamConfigStateChangePopup(
|
protected SEBExamConfigBatchStateChangePopup(
|
||||||
final PageService pageService,
|
final PageService pageService,
|
||||||
final ServerPushService serverPushService) {
|
final ServerPushService serverPushService) {
|
||||||
|
|
|
@ -70,7 +70,8 @@ public class SEBExamConfigList implements TemplateComposer {
|
||||||
private final PageService pageService;
|
private final PageService pageService;
|
||||||
private final SEBExamConfigImportPopup sebExamConfigImportPopup;
|
private final SEBExamConfigImportPopup sebExamConfigImportPopup;
|
||||||
private final SEBExamConfigCreationPopup sebExamConfigCreationPopup;
|
private final SEBExamConfigCreationPopup sebExamConfigCreationPopup;
|
||||||
private final SEBExamConfigStateChangePopup sebExamConfigStateChangePopup;
|
private final SEBExamConfigBatchStateChangePopup sebExamConfigBatchStateChangePopup;
|
||||||
|
private final SEBExamConfigBatchResetToTemplatePopup sebExamConfigBatchResetToTemplatePopup;
|
||||||
private final CurrentUser currentUser;
|
private final CurrentUser currentUser;
|
||||||
private final ResourceService resourceService;
|
private final ResourceService resourceService;
|
||||||
private final int pageSize;
|
private final int pageSize;
|
||||||
|
@ -79,13 +80,15 @@ public class SEBExamConfigList implements TemplateComposer {
|
||||||
final PageService pageService,
|
final PageService pageService,
|
||||||
final SEBExamConfigImportPopup sebExamConfigImportPopup,
|
final SEBExamConfigImportPopup sebExamConfigImportPopup,
|
||||||
final SEBExamConfigCreationPopup sebExamConfigCreationPopup,
|
final SEBExamConfigCreationPopup sebExamConfigCreationPopup,
|
||||||
final SEBExamConfigStateChangePopup sebExamConfigStateChangePopup,
|
final SEBExamConfigBatchStateChangePopup sebExamConfigBatchStateChangePopup,
|
||||||
|
final SEBExamConfigBatchResetToTemplatePopup sebExamConfigBatchResetToTemplatePopup,
|
||||||
@Value("${sebserver.gui.list.page.size:20}") final Integer pageSize) {
|
@Value("${sebserver.gui.list.page.size:20}") final Integer pageSize) {
|
||||||
|
|
||||||
this.pageService = pageService;
|
this.pageService = pageService;
|
||||||
this.sebExamConfigImportPopup = sebExamConfigImportPopup;
|
this.sebExamConfigImportPopup = sebExamConfigImportPopup;
|
||||||
this.sebExamConfigCreationPopup = sebExamConfigCreationPopup;
|
this.sebExamConfigCreationPopup = sebExamConfigCreationPopup;
|
||||||
this.sebExamConfigStateChangePopup = sebExamConfigStateChangePopup;
|
this.sebExamConfigBatchStateChangePopup = sebExamConfigBatchStateChangePopup;
|
||||||
|
this.sebExamConfigBatchResetToTemplatePopup = sebExamConfigBatchResetToTemplatePopup;
|
||||||
this.currentUser = pageService.getCurrentUser();
|
this.currentUser = pageService.getCurrentUser();
|
||||||
this.resourceService = pageService.getResourceService();
|
this.resourceService = pageService.getResourceService();
|
||||||
this.pageSize = pageSize;
|
this.pageSize = pageSize;
|
||||||
|
@ -159,7 +162,8 @@ public class SEBExamConfigList implements TemplateComposer {
|
||||||
ActionDefinition.SEB_EXAM_CONFIG_VIEW_PROP_FROM_LIST,
|
ActionDefinition.SEB_EXAM_CONFIG_VIEW_PROP_FROM_LIST,
|
||||||
ActionDefinition.SEB_EXAM_CONFIG_MODIFY_PROP_FROM_LIST,
|
ActionDefinition.SEB_EXAM_CONFIG_MODIFY_PROP_FROM_LIST,
|
||||||
ActionDefinition.SEB_EXAM_CONFIG_COPY_CONFIG_FROM_LIST,
|
ActionDefinition.SEB_EXAM_CONFIG_COPY_CONFIG_FROM_LIST,
|
||||||
ActionDefinition.SEB_EXAM_CONFIG_BULK_STATE_CHANGE))
|
ActionDefinition.SEB_EXAM_CONFIG_BULK_STATE_CHANGE,
|
||||||
|
ActionDefinition.SEB_EXAM_CONFIG_BULK_RESET_TO_TEMPLATE))
|
||||||
|
|
||||||
.compose(pageContext.copyOf(content));
|
.compose(pageContext.copyOf(content));
|
||||||
|
|
||||||
|
@ -171,7 +175,7 @@ public class SEBExamConfigList implements TemplateComposer {
|
||||||
|
|
||||||
.newAction(ActionDefinition.SEB_EXAM_CONFIG_VIEW_PROP_FROM_LIST)
|
.newAction(ActionDefinition.SEB_EXAM_CONFIG_VIEW_PROP_FROM_LIST)
|
||||||
.withSelect(
|
.withSelect(
|
||||||
configTable::getSelection,
|
configTable::getMultiSelection,
|
||||||
PageAction::applySingleSelectionAsEntityKey,
|
PageAction::applySingleSelectionAsEntityKey,
|
||||||
EMPTY_SELECTION_TEXT_KEY)
|
EMPTY_SELECTION_TEXT_KEY)
|
||||||
.publish(false)
|
.publish(false)
|
||||||
|
@ -205,8 +209,16 @@ public class SEBExamConfigList implements TemplateComposer {
|
||||||
|
|
||||||
.newAction(ActionDefinition.SEB_EXAM_CONFIG_BULK_STATE_CHANGE)
|
.newAction(ActionDefinition.SEB_EXAM_CONFIG_BULK_STATE_CHANGE)
|
||||||
.withSelect(
|
.withSelect(
|
||||||
configTable.getGrantedSelection(this.currentUser, NO_MODIFY_PRIVILEGE_ON_OTHER_INSTITUTION),
|
configTable::getMultiSelection,
|
||||||
this.sebExamConfigStateChangePopup.popupCreationFunction(pageContext),
|
this.sebExamConfigBatchStateChangePopup.popupCreationFunction(pageContext),
|
||||||
|
EMPTY_SELECTION_TEXT_KEY)
|
||||||
|
.noEventPropagation()
|
||||||
|
.publishIf(() -> examConfigGrant.im(), false)
|
||||||
|
|
||||||
|
.newAction(ActionDefinition.SEB_EXAM_CONFIG_BULK_RESET_TO_TEMPLATE)
|
||||||
|
.withSelect(
|
||||||
|
configTable::getMultiSelection,
|
||||||
|
this.sebExamConfigBatchResetToTemplatePopup.popupCreationFunction(pageContext),
|
||||||
EMPTY_SELECTION_TEXT_KEY)
|
EMPTY_SELECTION_TEXT_KEY)
|
||||||
.noEventPropagation()
|
.noEventPropagation()
|
||||||
.publishIf(() -> examConfigGrant.im(), false)
|
.publishIf(() -> examConfigGrant.im(), false)
|
||||||
|
|
|
@ -133,7 +133,7 @@ public class ExamFormIndicators implements TemplateComposer {
|
||||||
.newAction(ActionDefinition.EXAM_INDICATOR_MODIFY_FROM_LIST)
|
.newAction(ActionDefinition.EXAM_INDICATOR_MODIFY_FROM_LIST)
|
||||||
.withParentEntityKey(entityKey)
|
.withParentEntityKey(entityKey)
|
||||||
.withSelect(
|
.withSelect(
|
||||||
indicatorTable::getSelection,
|
indicatorTable::getMultiSelection,
|
||||||
PageAction::applySingleSelectionAsEntityKey,
|
PageAction::applySingleSelectionAsEntityKey,
|
||||||
INDICATOR_EMPTY_SELECTION_TEXT_KEY)
|
INDICATOR_EMPTY_SELECTION_TEXT_KEY)
|
||||||
.publishIf(() -> editable && indicatorTable.hasAnyContent(), false)
|
.publishIf(() -> editable && indicatorTable.hasAnyContent(), false)
|
||||||
|
@ -141,7 +141,7 @@ public class ExamFormIndicators implements TemplateComposer {
|
||||||
.newAction(ActionDefinition.EXAM_INDICATOR_DELETE_FROM_LIST)
|
.newAction(ActionDefinition.EXAM_INDICATOR_DELETE_FROM_LIST)
|
||||||
.withEntityKey(entityKey)
|
.withEntityKey(entityKey)
|
||||||
.withSelect(
|
.withSelect(
|
||||||
indicatorTable::getSelection,
|
indicatorTable::getMultiSelection,
|
||||||
this::deleteSelectedIndicator,
|
this::deleteSelectedIndicator,
|
||||||
INDICATOR_EMPTY_SELECTION_TEXT_KEY)
|
INDICATOR_EMPTY_SELECTION_TEXT_KEY)
|
||||||
.publishIf(() -> editable && indicatorTable.hasAnyContent(), false)
|
.publishIf(() -> editable && indicatorTable.hasAnyContent(), false)
|
||||||
|
|
|
@ -210,7 +210,8 @@ public class ExamList implements TemplateComposer {
|
||||||
final GrantCheck userGrant = currentUser.grantCheck(EntityType.EXAM);
|
final GrantCheck userGrant = currentUser.grantCheck(EntityType.EXAM);
|
||||||
actionBuilder
|
actionBuilder
|
||||||
.newAction(ActionDefinition.EXAM_VIEW_FROM_LIST)
|
.newAction(ActionDefinition.EXAM_VIEW_FROM_LIST)
|
||||||
.withSelect(table::getSelection, PageAction::applySingleSelectionAsEntityKey, EMPTY_SELECTION_TEXT_KEY)
|
.withSelect(table::getMultiSelection, PageAction::applySingleSelectionAsEntityKey,
|
||||||
|
EMPTY_SELECTION_TEXT_KEY)
|
||||||
.publish(false)
|
.publish(false)
|
||||||
|
|
||||||
.newAction(ActionDefinition.EXAM_MODIFY_FROM_LIST)
|
.newAction(ActionDefinition.EXAM_MODIFY_FROM_LIST)
|
||||||
|
|
|
@ -268,7 +268,7 @@ public class ExamTemplateForm implements TemplateComposer {
|
||||||
.newAction(ActionDefinition.INDICATOR_TEMPLATE_MODIFY_FROM_LIST)
|
.newAction(ActionDefinition.INDICATOR_TEMPLATE_MODIFY_FROM_LIST)
|
||||||
.withParentEntityKey(entityKey)
|
.withParentEntityKey(entityKey)
|
||||||
.withSelect(
|
.withSelect(
|
||||||
indicatorTable::getSelection,
|
indicatorTable::getMultiSelection,
|
||||||
PageAction::applySingleSelectionAsEntityKey,
|
PageAction::applySingleSelectionAsEntityKey,
|
||||||
INDICATOR_EMPTY_SELECTION_TEXT_KEY)
|
INDICATOR_EMPTY_SELECTION_TEXT_KEY)
|
||||||
.publishIf(() -> userGrant.im() && indicatorTable.hasAnyContent(), false)
|
.publishIf(() -> userGrant.im() && indicatorTable.hasAnyContent(), false)
|
||||||
|
@ -276,7 +276,7 @@ public class ExamTemplateForm implements TemplateComposer {
|
||||||
.newAction(ActionDefinition.INDICATOR_TEMPLATE_DELETE_FROM_LIST)
|
.newAction(ActionDefinition.INDICATOR_TEMPLATE_DELETE_FROM_LIST)
|
||||||
.withEntityKey(entityKey)
|
.withEntityKey(entityKey)
|
||||||
.withSelect(
|
.withSelect(
|
||||||
indicatorTable::getSelection,
|
indicatorTable::getMultiSelection,
|
||||||
this::deleteSelectedIndicator,
|
this::deleteSelectedIndicator,
|
||||||
INDICATOR_EMPTY_SELECTION_TEXT_KEY)
|
INDICATOR_EMPTY_SELECTION_TEXT_KEY)
|
||||||
.publishIf(() -> userGrant.im() && indicatorTable.hasAnyContent(), false)
|
.publishIf(() -> userGrant.im() && indicatorTable.hasAnyContent(), false)
|
||||||
|
|
|
@ -175,11 +175,13 @@ public class ExamTemplateList implements TemplateComposer {
|
||||||
.publishIf(userGrant::iw)
|
.publishIf(userGrant::iw)
|
||||||
|
|
||||||
.newAction(ActionDefinition.EXAM_TEMPLATE_VIEW_FROM_LIST)
|
.newAction(ActionDefinition.EXAM_TEMPLATE_VIEW_FROM_LIST)
|
||||||
.withSelect(table::getSelection, PageAction::applySingleSelectionAsEntityKey, EMPTY_SELECTION_TEXT_KEY)
|
.withSelect(table::getMultiSelection, PageAction::applySingleSelectionAsEntityKey,
|
||||||
|
EMPTY_SELECTION_TEXT_KEY)
|
||||||
.publish(false)
|
.publish(false)
|
||||||
|
|
||||||
.newAction(ActionDefinition.EXAM_TEMPLATE_MODIFY_FROM_LIST)
|
.newAction(ActionDefinition.EXAM_TEMPLATE_MODIFY_FROM_LIST)
|
||||||
.withSelect(table::getSelection, PageAction::applySingleSelectionAsEntityKey, EMPTY_SELECTION_TEXT_KEY)
|
.withSelect(table::getMultiSelection, PageAction::applySingleSelectionAsEntityKey,
|
||||||
|
EMPTY_SELECTION_TEXT_KEY)
|
||||||
.publishIf(() -> userGrant.im(), false);
|
.publishIf(() -> userGrant.im(), false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -169,7 +169,10 @@ public class LmsSetupList implements TemplateComposer {
|
||||||
.publishIf(userGrant::iw)
|
.publishIf(userGrant::iw)
|
||||||
|
|
||||||
.newAction(ActionDefinition.LMS_SETUP_VIEW_FROM_LIST)
|
.newAction(ActionDefinition.LMS_SETUP_VIEW_FROM_LIST)
|
||||||
.withSelect(table::getSelection, PageAction::applySingleSelectionAsEntityKey, EMPTY_SELECTION_TEXT_KEY)
|
.withSelect(
|
||||||
|
table::getMultiSelection,
|
||||||
|
PageAction::applySingleSelectionAsEntityKey,
|
||||||
|
EMPTY_SELECTION_TEXT_KEY)
|
||||||
.publish(false)
|
.publish(false)
|
||||||
|
|
||||||
.newAction(ActionDefinition.LMS_SETUP_MODIFY_FROM_LIST)
|
.newAction(ActionDefinition.LMS_SETUP_MODIFY_FROM_LIST)
|
||||||
|
|
|
@ -236,7 +236,7 @@ public class QuizLookupList implements TemplateComposer {
|
||||||
actionBuilder
|
actionBuilder
|
||||||
.newAction(ActionDefinition.QUIZ_DISCOVERY_SHOW_DETAILS)
|
.newAction(ActionDefinition.QUIZ_DISCOVERY_SHOW_DETAILS)
|
||||||
.withSelect(
|
.withSelect(
|
||||||
table::getSelection,
|
table::getMultiSelection,
|
||||||
action -> this.showDetails(
|
action -> this.showDetails(
|
||||||
action,
|
action,
|
||||||
table.getSingleSelectedROWData(),
|
table.getSingleSelectedROWData(),
|
||||||
|
|
|
@ -170,7 +170,10 @@ public class FinishedExam implements TemplateComposer {
|
||||||
|
|
||||||
.newAction(ActionDefinition.VIEW_FINISHED_EXAM_CLIENT_CONNECTION)
|
.newAction(ActionDefinition.VIEW_FINISHED_EXAM_CLIENT_CONNECTION)
|
||||||
.withParentEntityKey(examKey)
|
.withParentEntityKey(examKey)
|
||||||
.withSelect(table::getSelection, PageAction::applySingleSelectionAsEntityKey, EMPTY_SELECTION_TEXT_KEY)
|
.withSelect(
|
||||||
|
table::getMultiSelection,
|
||||||
|
PageAction::applySingleSelectionAsEntityKey,
|
||||||
|
EMPTY_SELECTION_TEXT_KEY)
|
||||||
.publishIf(isExamSupporter, false);
|
.publishIf(isExamSupporter, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -141,7 +141,10 @@ public class FinishedExamList implements TemplateComposer {
|
||||||
actionBuilder
|
actionBuilder
|
||||||
|
|
||||||
.newAction(ActionDefinition.VIEW_FINISHED_EXAM_FROM_LIST)
|
.newAction(ActionDefinition.VIEW_FINISHED_EXAM_FROM_LIST)
|
||||||
.withSelect(table::getSelection, PageAction::applySingleSelectionAsEntityKey, EMPTY_SELECTION_TEXT_KEY)
|
.withSelect(
|
||||||
|
table::getMultiSelection,
|
||||||
|
PageAction::applySingleSelectionAsEntityKey,
|
||||||
|
EMPTY_SELECTION_TEXT_KEY)
|
||||||
.publishIf(() -> currentUser.get().hasRole(UserRole.EXAM_SUPPORTER), false);
|
.publishIf(() -> currentUser.get().hasRole(UserRole.EXAM_SUPPORTER), false);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -264,7 +264,7 @@ public class MonitoringClientConnection implements TemplateComposer {
|
||||||
.withParentEntityKey(parentEntityKey)
|
.withParentEntityKey(parentEntityKey)
|
||||||
.withConfirm(() -> NOTIFICATION_LIST_CONFIRM_TEXT_KEY)
|
.withConfirm(() -> NOTIFICATION_LIST_CONFIRM_TEXT_KEY)
|
||||||
.withSelect(
|
.withSelect(
|
||||||
() -> notificationTable.getSelection(),
|
() -> notificationTable.getMultiSelection(),
|
||||||
action -> this.confirmNotification(action, connectionData, notificationTable),
|
action -> this.confirmNotification(action, connectionData, notificationTable),
|
||||||
|
|
||||||
NOTIFICATION_LIST_NO_SELECTION_KEY)
|
NOTIFICATION_LIST_NO_SELECTION_KEY)
|
||||||
|
|
|
@ -13,6 +13,7 @@ import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.function.BooleanSupplier;
|
import java.util.function.BooleanSupplier;
|
||||||
|
import java.util.function.Consumer;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
|
|
||||||
import org.eclipse.swt.SWT;
|
import org.eclipse.swt.SWT;
|
||||||
|
@ -172,7 +173,7 @@ public class MonitoringRunningExam implements TemplateComposer {
|
||||||
.withParentEntityKey(entityKey)
|
.withParentEntityKey(entityKey)
|
||||||
.create(),
|
.create(),
|
||||||
this.pageService)
|
this.pageService)
|
||||||
.withSelectionListener(this.pageService.getSelectionPublisher(
|
.withSelectionListener(this.getSelectionPublisherClientConnectionTable(
|
||||||
pageContext,
|
pageContext,
|
||||||
ActionDefinition.MONITOR_EXAM_CLIENT_CONNECTION,
|
ActionDefinition.MONITOR_EXAM_CLIENT_CONNECTION,
|
||||||
ActionDefinition.MONITOR_EXAM_QUIT_SELECTED,
|
ActionDefinition.MONITOR_EXAM_QUIT_SELECTED,
|
||||||
|
@ -505,4 +506,13 @@ public class MonitoringRunningExam implements TemplateComposer {
|
||||||
return action;
|
return action;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Consumer<ClientConnectionTable> getSelectionPublisherClientConnectionTable(
|
||||||
|
final PageContext pageContext,
|
||||||
|
final ActionDefinition... actionDefinitions) {
|
||||||
|
|
||||||
|
return table -> this.pageService.firePageEvent(
|
||||||
|
new ActionActivationEvent(table.getSingleSelection() != null, actionDefinitions),
|
||||||
|
pageContext);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -141,7 +141,10 @@ public class MonitoringRunningExamList implements TemplateComposer {
|
||||||
actionBuilder
|
actionBuilder
|
||||||
|
|
||||||
.newAction(ActionDefinition.MONITOR_EXAM_FROM_LIST)
|
.newAction(ActionDefinition.MONITOR_EXAM_FROM_LIST)
|
||||||
.withSelect(table::getSelection, PageAction::applySingleSelectionAsEntityKey, EMPTY_SELECTION_TEXT_KEY)
|
.withSelect(
|
||||||
|
table::getMultiSelection,
|
||||||
|
PageAction::applySingleSelectionAsEntityKey,
|
||||||
|
EMPTY_SELECTION_TEXT_KEY)
|
||||||
.publishIf(() -> currentUser.get().hasRole(UserRole.EXAM_SUPPORTER), false);
|
.publishIf(() -> currentUser.get().hasRole(UserRole.EXAM_SUPPORTER), false);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -227,7 +227,7 @@ public class SEBClientEvents implements TemplateComposer {
|
||||||
actionBuilder
|
actionBuilder
|
||||||
.newAction(ActionDefinition.LOGS_SEB_CLIENT_SHOW_DETAILS)
|
.newAction(ActionDefinition.LOGS_SEB_CLIENT_SHOW_DETAILS)
|
||||||
.withSelect(
|
.withSelect(
|
||||||
table::getSelection,
|
table::getMultiSelection,
|
||||||
action -> this.sebClientEventDetailsPopup.showDetails(action, table.getSingleSelectedROWData()),
|
action -> this.sebClientEventDetailsPopup.showDetails(action, table.getSingleSelectedROWData()),
|
||||||
EMPTY_SELECTION_TEXT)
|
EMPTY_SELECTION_TEXT)
|
||||||
.noEventPropagation()
|
.noEventPropagation()
|
||||||
|
|
|
@ -172,7 +172,7 @@ public interface PageService {
|
||||||
* @return a message supplier to notify deactivation dependencies to the user */
|
* @return a message supplier to notify deactivation dependencies to the user */
|
||||||
default <T extends Entity & Activatable> Supplier<LocTextKey> confirmDeactivation(final EntityTable<T> table) {
|
default <T extends Entity & Activatable> Supplier<LocTextKey> confirmDeactivation(final EntityTable<T> table) {
|
||||||
return () -> {
|
return () -> {
|
||||||
final List<EntityKey> multiSelection = table.getMultiSelection();
|
final Set<EntityKey> multiSelection = table.getMultiSelection();
|
||||||
if (multiSelection.size() > 1) {
|
if (multiSelection.size() > 1) {
|
||||||
throw new PageMessageException(MESSAGE_NO_MULTISELECTION);
|
throw new PageMessageException(MESSAGE_NO_MULTISELECTION);
|
||||||
}
|
}
|
||||||
|
@ -204,12 +204,12 @@ public interface PageService {
|
||||||
* @param pageContext the current PageContext
|
* @param pageContext the current PageContext
|
||||||
* @param actionDefinitions list of action definitions that activity should be toggled on table selection
|
* @param actionDefinitions list of action definitions that activity should be toggled on table selection
|
||||||
* @return the selection publisher that handles this defines action activation on table selection */
|
* @return the selection publisher that handles this defines action activation on table selection */
|
||||||
default <T> Consumer<Set<T>> getSelectionPublisher(
|
default <T extends ModelIdAware> Consumer<EntityTable<T>> getSelectionPublisher(
|
||||||
final PageContext pageContext,
|
final PageContext pageContext,
|
||||||
final ActionDefinition... actionDefinitions) {
|
final ActionDefinition... actionDefinitions) {
|
||||||
|
|
||||||
return rows -> firePageEvent(
|
return table -> firePageEvent(
|
||||||
new ActionActivationEvent(!rows.isEmpty(), actionDefinitions),
|
new ActionActivationEvent(table.hasSelection(), actionDefinitions),
|
||||||
pageContext);
|
pageContext);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -225,15 +225,15 @@ public interface PageService {
|
||||||
* @param pageContext the current PageContext
|
* @param pageContext the current PageContext
|
||||||
* @param actionDefinitions list of action definitions that activity should be toggled on table selection
|
* @param actionDefinitions list of action definitions that activity should be toggled on table selection
|
||||||
* @return the selection publisher that handles this defines action activation on table selection */
|
* @return the selection publisher that handles this defines action activation on table selection */
|
||||||
default <T extends Activatable> Consumer<Set<T>> getSelectionPublisher(
|
default <T extends Activatable & ModelIdAware> Consumer<EntityTable<T>> getSelectionPublisher(
|
||||||
final ActionDefinition toggle,
|
final ActionDefinition toggle,
|
||||||
final ActionDefinition activate,
|
final ActionDefinition activate,
|
||||||
final ActionDefinition deactivate,
|
final ActionDefinition deactivate,
|
||||||
final PageContext pageContext,
|
final PageContext pageContext,
|
||||||
final ActionDefinition... actionDefinitions) {
|
final ActionDefinition... actionDefinitions) {
|
||||||
|
|
||||||
return rows -> {
|
return table -> {
|
||||||
|
final Set<T> rows = table.getPageSelectionData();
|
||||||
if (!rows.isEmpty()) {
|
if (!rows.isEmpty()) {
|
||||||
firePageEvent(
|
firePageEvent(
|
||||||
new ActionActivationEvent(
|
new ActionActivationEvent(
|
||||||
|
|
|
@ -171,7 +171,6 @@ public final class PageAction {
|
||||||
// if selection is needed, check selection fist, before confirm dialog
|
// if selection is needed, check selection fist, before confirm dialog
|
||||||
if (this.selectionSupplier != null) {
|
if (this.selectionSupplier != null) {
|
||||||
getMultiSelection();
|
getMultiSelection();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
final LocTextKey confirmMessage = this.confirm.apply(this);
|
final LocTextKey confirmMessage = this.confirm.apply(this);
|
||||||
|
|
|
@ -249,7 +249,7 @@ public class PageServiceImpl implements PageService {
|
||||||
final Function<PageAction, PageAction> testBeforeActivation) {
|
final Function<PageAction, PageAction> testBeforeActivation) {
|
||||||
|
|
||||||
return action -> {
|
return action -> {
|
||||||
final List<EntityKey> multiSelection = table.getMultiSelection();
|
final Set<EntityKey> multiSelection = table.getMultiSelection();
|
||||||
if (multiSelection == null || multiSelection.isEmpty()) {
|
if (multiSelection == null || multiSelection.isEmpty()) {
|
||||||
throw new PageMessageException(noSelectionText);
|
throw new PageMessageException(noSelectionText);
|
||||||
}
|
}
|
||||||
|
|
|
@ -88,7 +88,7 @@ public final class ClientConnectionTable implements FullPageMonitoringGUIUpdate
|
||||||
private final Table table;
|
private final Table table;
|
||||||
private final ColorData colorData;
|
private final ColorData colorData;
|
||||||
private final Function<ClientConnectionData, String> localizedClientConnectionStatusNameFunction;
|
private final Function<ClientConnectionData, String> localizedClientConnectionStatusNameFunction;
|
||||||
private Consumer<Set<EntityKey>> selectionListener;
|
private Consumer<ClientConnectionTable> selectionListener;
|
||||||
|
|
||||||
private int tableWidth;
|
private int tableWidth;
|
||||||
private boolean needsSort = false;
|
private boolean needsSort = false;
|
||||||
|
@ -238,7 +238,7 @@ public final class ClientConnectionTable implements FullPageMonitoringGUIUpdate
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public ClientConnectionTable withSelectionListener(final Consumer<Set<EntityKey>> selectionListener) {
|
public ClientConnectionTable withSelectionListener(final Consumer<ClientConnectionTable> selectionListener) {
|
||||||
this.selectionListener = selectionListener;
|
this.selectionListener = selectionListener;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
@ -402,7 +402,7 @@ public final class ClientConnectionTable implements FullPageMonitoringGUIUpdate
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.selectionListener.accept(this.getSelection());
|
this.selectionListener.accept(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void notifyTableInfoClick(final Event event) {
|
private void notifyTableInfoClick(final Event event) {
|
||||||
|
|
|
@ -95,7 +95,7 @@ public class EntityTable<ROW extends ModelIdAware> {
|
||||||
|
|
||||||
private final MultiValueMap<String, String> staticQueryParams;
|
private final MultiValueMap<String, String> staticQueryParams;
|
||||||
private final BiConsumer<TableItem, ROW> rowDecorator;
|
private final BiConsumer<TableItem, ROW> rowDecorator;
|
||||||
private final Consumer<Set<ROW>> selectionListener;
|
private final Consumer<EntityTable<ROW>> selectionListener;
|
||||||
private final Consumer<Integer> contentChangeListener;
|
private final Consumer<Integer> contentChangeListener;
|
||||||
|
|
||||||
private final Set<String> multiselection;
|
private final Set<String> multiselection;
|
||||||
|
@ -125,7 +125,7 @@ public class EntityTable<ROW extends ModelIdAware> {
|
||||||
final boolean hideNavigation,
|
final boolean hideNavigation,
|
||||||
final MultiValueMap<String, String> staticQueryParams,
|
final MultiValueMap<String, String> staticQueryParams,
|
||||||
final BiConsumer<TableItem, ROW> rowDecorator,
|
final BiConsumer<TableItem, ROW> rowDecorator,
|
||||||
final Consumer<Set<ROW>> selectionListener,
|
final Consumer<EntityTable<ROW>> selectionListener,
|
||||||
final Consumer<Integer> contentChangeListener,
|
final Consumer<Integer> contentChangeListener,
|
||||||
final String defaultSortColumn,
|
final String defaultSortColumn,
|
||||||
final PageSortOrder defaultSortOrder) {
|
final PageSortOrder defaultSortOrder) {
|
||||||
|
@ -263,6 +263,10 @@ public class EntityTable<ROW extends ModelIdAware> {
|
||||||
return this.table.getItemCount() > 0;
|
return this.table.getItemCount() > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean hasSelection() {
|
||||||
|
return (this.multiselection != null && !this.multiselection.isEmpty()) || this.table.getSelectionCount() > 0;
|
||||||
|
}
|
||||||
|
|
||||||
public void setPageSize(final int pageSize) {
|
public void setPageSize(final int pageSize) {
|
||||||
this.pageSize = pageSize;
|
this.pageSize = pageSize;
|
||||||
updateTableRows(
|
updateTableRows(
|
||||||
|
@ -363,15 +367,18 @@ public class EntityTable<ROW extends ModelIdAware> {
|
||||||
return getEntityKey(selection[0]);
|
return getEntityKey(selection[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<EntityKey> getMultiSelection() {
|
public Set<EntityKey> getMultiSelection() {
|
||||||
if (this.multiselection == null) {
|
if (this.multiselection == null) {
|
||||||
return Collections.emptyList();
|
return getPageSelectionData()
|
||||||
|
.stream()
|
||||||
|
.map(row -> new EntityKey(row.getModelId(), getEntityType()))
|
||||||
|
.collect(Collectors.toSet());
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.multiselection
|
return this.multiselection
|
||||||
.stream()
|
.stream()
|
||||||
.map(modelId -> new EntityKey(modelId, getEntityType()))
|
.map(modelId -> new EntityKey(modelId, getEntityType()))
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toSet());
|
||||||
}
|
}
|
||||||
|
|
||||||
public ROW getFirstRowData() {
|
public ROW getFirstRowData() {
|
||||||
|
@ -396,7 +403,7 @@ public class EntityTable<ROW extends ModelIdAware> {
|
||||||
return getRowData(selection[0]);
|
return getRowData(selection[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Set<ROW> getPageSelectionData() {
|
public Set<ROW> getPageSelectionData() {
|
||||||
final TableItem[] selection = this.table.getSelection();
|
final TableItem[] selection = this.table.getSelection();
|
||||||
if (selection == null || selection.length == 0) {
|
if (selection == null || selection.length == 0) {
|
||||||
return Collections.emptySet();
|
return Collections.emptySet();
|
||||||
|
@ -407,10 +414,6 @@ public class EntityTable<ROW extends ModelIdAware> {
|
||||||
.collect(Collectors.toSet());
|
.collect(Collectors.toSet());
|
||||||
}
|
}
|
||||||
|
|
||||||
public Set<EntityKey> getSelection() {
|
|
||||||
return getSelection(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Set<EntityKey> getSelection(final Predicate<ROW> grantCheck) {
|
public Set<EntityKey> getSelection(final Predicate<ROW> grantCheck) {
|
||||||
final TableItem[] selection = this.table.getSelection();
|
final TableItem[] selection = this.table.getSelection();
|
||||||
if (selection == null) {
|
if (selection == null) {
|
||||||
|
@ -685,7 +688,7 @@ public class EntityTable<ROW extends ModelIdAware> {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.selectionListener.accept(this.getPageSelectionData());
|
this.selectionListener.accept(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateCurrentPageAttr() {
|
private void updateCurrentPageAttr() {
|
||||||
|
@ -807,6 +810,9 @@ public class EntityTable<ROW extends ModelIdAware> {
|
||||||
this.multiselection.remove(modelId);
|
this.multiselection.remove(modelId);
|
||||||
} else {
|
} else {
|
||||||
this.multiselection.add(modelId);
|
this.multiselection.add(modelId);
|
||||||
|
Arrays.asList(this.table.getSelection())
|
||||||
|
.stream()
|
||||||
|
.forEach(i -> this.multiselection.add(getModelId(i)));
|
||||||
}
|
}
|
||||||
multiselectFromPage();
|
multiselectFromPage();
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,7 +10,6 @@ package ch.ethz.seb.sebserver.gui.table;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
|
||||||
import java.util.function.BiConsumer;
|
import java.util.function.BiConsumer;
|
||||||
import java.util.function.BooleanSupplier;
|
import java.util.function.BooleanSupplier;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
|
@ -48,7 +47,7 @@ public class TableBuilder<ROW extends ModelIdAware> {
|
||||||
private boolean hideNavigation = false;
|
private boolean hideNavigation = false;
|
||||||
private Function<PageSupplier.Builder<ROW>, PageSupplier.Builder<ROW>> restCallAdapter;
|
private Function<PageSupplier.Builder<ROW>, PageSupplier.Builder<ROW>> restCallAdapter;
|
||||||
private BiConsumer<TableItem, ROW> rowDecorator;
|
private BiConsumer<TableItem, ROW> rowDecorator;
|
||||||
private Consumer<Set<ROW>> selectionListener;
|
private Consumer<EntityTable<ROW>> selectionListener;
|
||||||
private Consumer<Integer> contentChangeListener;
|
private Consumer<Integer> contentChangeListener;
|
||||||
private boolean markupEnabled = false;
|
private boolean markupEnabled = false;
|
||||||
private String defaultSortColumn = null;
|
private String defaultSortColumn = null;
|
||||||
|
@ -152,7 +151,7 @@ public class TableBuilder<ROW extends ModelIdAware> {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public TableBuilder<ROW> withSelectionListener(final Consumer<Set<ROW>> selectionListener) {
|
public TableBuilder<ROW> withSelectionListener(final Consumer<EntityTable<ROW>> selectionListener) {
|
||||||
this.selectionListener = selectionListener;
|
this.selectionListener = selectionListener;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
|
@ -201,6 +201,7 @@ public class PaginationServiceImpl implements PaginationService {
|
||||||
final PageSortOrder sortOrder = PageSortOrder.getSortOrder(sort);
|
final PageSortOrder sortOrder = PageSortOrder.getSortOrder(sort);
|
||||||
final String sortColumnName = verifySortColumnName(sort, sortMappingName);
|
final String sortColumnName = verifySortColumnName(sort, sortMappingName);
|
||||||
if (StringUtils.isNotBlank(sortColumnName)) {
|
if (StringUtils.isNotBlank(sortColumnName)) {
|
||||||
|
startPage.setOrderByOnly(false);
|
||||||
switch (sortOrder) {
|
switch (sortOrder) {
|
||||||
case DESCENDING: {
|
case DESCENDING: {
|
||||||
PageHelper.orderBy(sortColumnName + " DESC");
|
PageHelper.orderBy(sortColumnName + " DESC");
|
||||||
|
@ -211,6 +212,7 @@ public class PaginationServiceImpl implements PaginationService {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
PageHelper.orderBy("id");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -10,6 +10,7 @@ package ch.ethz.seb.sebserver.webservice.servicelayer.authorization;
|
||||||
|
|
||||||
import java.security.Principal;
|
import java.security.Principal;
|
||||||
|
|
||||||
|
import org.springframework.security.core.Authentication;
|
||||||
import org.springframework.web.bind.WebDataBinder;
|
import org.springframework.web.bind.WebDataBinder;
|
||||||
|
|
||||||
import ch.ethz.seb.sebserver.webservice.servicelayer.authorization.impl.SEBServerUser;
|
import ch.ethz.seb.sebserver.webservice.servicelayer.authorization.impl.SEBServerUser;
|
||||||
|
@ -51,4 +52,9 @@ public interface UserService {
|
||||||
* @param binder Springs WebDataBinder is injected on controller side */
|
* @param binder Springs WebDataBinder is injected on controller side */
|
||||||
void addUsersInstitutionDefaultPropertySupport(final WebDataBinder binder);
|
void addUsersInstitutionDefaultPropertySupport(final WebDataBinder binder);
|
||||||
|
|
||||||
|
/** Used to set authentication on different thread.
|
||||||
|
*
|
||||||
|
* @param authentication */
|
||||||
|
void setAuthenticationIfAbsent(Authentication authentication);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,6 +13,7 @@ import java.util.Collections;
|
||||||
import java.util.EnumSet;
|
import java.util.EnumSet;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import org.springframework.security.core.Authentication;
|
||||||
import org.springframework.security.core.CredentialsContainer;
|
import org.springframework.security.core.CredentialsContainer;
|
||||||
import org.springframework.security.core.GrantedAuthority;
|
import org.springframework.security.core.GrantedAuthority;
|
||||||
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
||||||
|
@ -26,7 +27,7 @@ import ch.ethz.seb.sebserver.gbl.model.user.UserRole;
|
||||||
*
|
*
|
||||||
* This implements Spring's UserDetails and CredentialsContainer to act as a principal
|
* This implements Spring's UserDetails and CredentialsContainer to act as a principal
|
||||||
* within internal authentication and authorization processes. */
|
* within internal authentication and authorization processes. */
|
||||||
public final class SEBServerUser implements UserDetails, CredentialsContainer {
|
public final class SEBServerUser implements UserDetails, CredentialsContainer, Authentication {
|
||||||
|
|
||||||
private static final long serialVersionUID = 5726250141482925769L;
|
private static final long serialVersionUID = 5726250141482925769L;
|
||||||
|
|
||||||
|
@ -160,4 +161,34 @@ public final class SEBServerUser implements UserDetails, CredentialsContainer {
|
||||||
return new SEBServerUser(user.id, UserInfo.of(user.userInfo), user.password);
|
return new SEBServerUser(user.id, UserInfo.of(user.userInfo), user.password);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return this.userInfo.username;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object getCredentials() {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object getDetails() {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object getPrincipal() {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isAuthenticated() {
|
||||||
|
return isEnabled();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setAuthenticated(final boolean isAuthenticated) throws IllegalArgumentException {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -94,6 +94,13 @@ public class UserServiceImpl implements UserService {
|
||||||
binder.registerCustomEditor(Long.class, usersInstitutionDefaultEditor);
|
binder.registerCustomEditor(Long.class, usersInstitutionDefaultEditor);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setAuthenticationIfAbsent(final Authentication authentication) {
|
||||||
|
if (SecurityContextHolder.getContext().getAuthentication() == null) {
|
||||||
|
SecurityContextHolder.getContext().setAuthentication(authentication);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 1. OAuth2Authentication strategy
|
// 1. OAuth2Authentication strategy
|
||||||
@Lazy
|
@Lazy
|
||||||
@Component
|
@Component
|
||||||
|
@ -115,6 +122,21 @@ public class UserServiceImpl implements UserService {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 2. Separated thread strategy
|
||||||
|
@Lazy
|
||||||
|
@Component
|
||||||
|
public static class OtherThreadUserExtractStrategy implements ExtractUserFromAuthenticationStrategy {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SEBServerUser extract(final Principal principal) {
|
||||||
|
if (principal instanceof SEBServerUser) {
|
||||||
|
return (SEBServerUser) principal;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static final SEBServerUser ANONYMOUS_USER = new SEBServerUser(
|
private static final SEBServerUser ANONYMOUS_USER = new SEBServerUser(
|
||||||
-1L,
|
-1L,
|
||||||
new UserInfo("SEB_SERVER_ANONYMOUS_USER", -2L, null, "anonymous", "anonymous", "anonymous", null, false,
|
new UserInfo("SEB_SERVER_ANONYMOUS_USER", -2L, null, "anonymous", "anonymous", "anonymous", null, false,
|
||||||
|
|
|
@ -22,6 +22,8 @@ import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import org.springframework.scheduling.TaskScheduler;
|
import org.springframework.scheduling.TaskScheduler;
|
||||||
import org.springframework.scheduling.annotation.Scheduled;
|
import org.springframework.scheduling.annotation.Scheduled;
|
||||||
|
import org.springframework.security.core.Authentication;
|
||||||
|
import org.springframework.security.core.context.SecurityContextHolder;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
import ch.ethz.seb.sebserver.gbl.api.API;
|
import ch.ethz.seb.sebserver.gbl.api.API;
|
||||||
|
@ -38,6 +40,8 @@ import ch.ethz.seb.sebserver.webservice.servicelayer.bulkaction.BatchActionServi
|
||||||
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.BatchActionDAO;
|
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.BatchActionDAO;
|
||||||
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.FilterMap;
|
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.FilterMap;
|
||||||
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.ResourceNotFoundException;
|
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.ResourceNotFoundException;
|
||||||
|
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.UserActivityLogDAO;
|
||||||
|
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.UserDAO;
|
||||||
|
|
||||||
@Service
|
@Service
|
||||||
@WebServiceProfile
|
@WebServiceProfile
|
||||||
|
@ -46,17 +50,23 @@ public class BatchActionServiceImpl implements BatchActionService {
|
||||||
private static final Logger log = LoggerFactory.getLogger(BatchActionServiceImpl.class);
|
private static final Logger log = LoggerFactory.getLogger(BatchActionServiceImpl.class);
|
||||||
|
|
||||||
private final BatchActionDAO batchActionDAO;
|
private final BatchActionDAO batchActionDAO;
|
||||||
|
private final UserDAO userDAO;
|
||||||
private final TaskScheduler taskScheduler;
|
private final TaskScheduler taskScheduler;
|
||||||
|
private final UserActivityLogDAO userActivityLogDAO;
|
||||||
private final EnumMap<BatchActionType, BatchActionExec> batchExecutions;
|
private final EnumMap<BatchActionType, BatchActionExec> batchExecutions;
|
||||||
|
|
||||||
private ScheduledFuture<?> runningBatchProcess = null;
|
private ScheduledFuture<?> runningBatchProcess = null;
|
||||||
|
|
||||||
public BatchActionServiceImpl(
|
public BatchActionServiceImpl(
|
||||||
final BatchActionDAO batchActionDAO,
|
final BatchActionDAO batchActionDAO,
|
||||||
|
final UserDAO userDAO,
|
||||||
|
final UserActivityLogDAO userActivityLogDAO,
|
||||||
final Collection<BatchActionExec> batchExecutions,
|
final Collection<BatchActionExec> batchExecutions,
|
||||||
final TaskScheduler taskScheduler) {
|
final TaskScheduler taskScheduler) {
|
||||||
|
|
||||||
this.batchActionDAO = batchActionDAO;
|
this.batchActionDAO = batchActionDAO;
|
||||||
|
this.userDAO = userDAO;
|
||||||
|
this.userActivityLogDAO = userActivityLogDAO;
|
||||||
this.taskScheduler = taskScheduler;
|
this.taskScheduler = taskScheduler;
|
||||||
|
|
||||||
this.batchExecutions = new EnumMap<>(BatchActionType.class);
|
this.batchExecutions = new EnumMap<>(BatchActionType.class);
|
||||||
|
@ -158,7 +168,8 @@ public class BatchActionServiceImpl implements BatchActionService {
|
||||||
new BatchActionProcess(
|
new BatchActionProcess(
|
||||||
new BatchActionHandlerImpl(action),
|
new BatchActionHandlerImpl(action),
|
||||||
this.batchExecutions.get(action.actionType),
|
this.batchExecutions.get(action.actionType),
|
||||||
action),
|
action,
|
||||||
|
getAuthentication(action)),
|
||||||
Instant.now());
|
Instant.now());
|
||||||
})
|
})
|
||||||
.onError(error -> {
|
.onError(error -> {
|
||||||
|
@ -174,22 +185,43 @@ public class BatchActionServiceImpl implements BatchActionService {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Authentication getAuthentication(final BatchAction action) {
|
||||||
|
try {
|
||||||
|
final Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
|
||||||
|
if (authentication != null) {
|
||||||
|
return authentication;
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.userDAO.byModelId(action.ownerId)
|
||||||
|
.flatMap(userInfo -> this.userDAO.sebServerUserByUsername(userInfo.username))
|
||||||
|
.onError(error -> log.error("Failed to get batch action owner user -> ", error))
|
||||||
|
.getOr(null);
|
||||||
|
|
||||||
|
} catch (final Exception e) {
|
||||||
|
log.error("Failed to get authentication: ", e);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private final static class BatchActionProcess implements Runnable {
|
private final static class BatchActionProcess implements Runnable {
|
||||||
|
|
||||||
private final BatchActionHandler batchActionHandler;
|
private final BatchActionHandler batchActionHandler;
|
||||||
private final BatchActionExec batchActionExec;
|
private final BatchActionExec batchActionExec;
|
||||||
private final BatchAction batchAction;
|
private final BatchAction batchAction;
|
||||||
|
private final Authentication authentication;
|
||||||
|
|
||||||
private Set<String> processingIds;
|
private Set<String> processingIds;
|
||||||
|
|
||||||
public BatchActionProcess(
|
public BatchActionProcess(
|
||||||
final BatchActionHandler batchActionHandler,
|
final BatchActionHandler batchActionHandler,
|
||||||
final BatchActionExec batchActionExec,
|
final BatchActionExec batchActionExec,
|
||||||
final BatchAction batchAction) {
|
final BatchAction batchAction,
|
||||||
|
final Authentication authentication) {
|
||||||
|
|
||||||
this.batchActionHandler = batchActionHandler;
|
this.batchActionHandler = batchActionHandler;
|
||||||
this.batchActionExec = batchActionExec;
|
this.batchActionExec = batchActionExec;
|
||||||
this.batchAction = batchAction;
|
this.batchAction = batchAction;
|
||||||
|
this.authentication = authentication;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -198,6 +230,13 @@ public class BatchActionServiceImpl implements BatchActionService {
|
||||||
|
|
||||||
log.info("Starting or continuing batch action - {}", this.batchAction);
|
log.info("Starting or continuing batch action - {}", this.batchAction);
|
||||||
|
|
||||||
|
if (SecurityContextHolder.getContext().getAuthentication() == null) {
|
||||||
|
if (this.authentication == null) {
|
||||||
|
throw new IllegalStateException("No authentication found within batch context");
|
||||||
|
}
|
||||||
|
SecurityContextHolder.getContext().setAuthentication(this.authentication);
|
||||||
|
}
|
||||||
|
|
||||||
this.processingIds = new HashSet<>(this.batchAction.sourceIds);
|
this.processingIds = new HashSet<>(this.batchAction.sourceIds);
|
||||||
this.processingIds.removeAll(this.batchAction.successful);
|
this.processingIds.removeAll(this.batchAction.successful);
|
||||||
|
|
||||||
|
@ -256,10 +295,10 @@ public class BatchActionServiceImpl implements BatchActionService {
|
||||||
@Override
|
@Override
|
||||||
public void handleError(final String modelId, final Exception error) {
|
public void handleError(final String modelId, final Exception error) {
|
||||||
log.error(
|
log.error(
|
||||||
"Failed to process single entity on batch action. ModelId: {}, action: ",
|
"Failed to process single entity on batch action. ModelId: {}, action: {}, errorMessage: {}",
|
||||||
modelId,
|
modelId,
|
||||||
this.batchAction,
|
this.batchAction,
|
||||||
error);
|
error.getMessage());
|
||||||
|
|
||||||
BatchActionServiceImpl.this.batchActionDAO.setFailure(
|
BatchActionServiceImpl.this.batchActionDAO.setFailure(
|
||||||
this.batchAction.id,
|
this.batchAction.id,
|
||||||
|
@ -276,6 +315,10 @@ public class BatchActionServiceImpl implements BatchActionService {
|
||||||
.onError(error -> log.error(
|
.onError(error -> log.error(
|
||||||
"Failed to mark batch action as finished: {}",
|
"Failed to mark batch action as finished: {}",
|
||||||
this.batchAction, error));
|
this.batchAction, error));
|
||||||
|
|
||||||
|
BatchActionServiceImpl.this.batchActionDAO.byPK(this.batchAction.id)
|
||||||
|
.flatMap(BatchActionServiceImpl.this.userActivityLogDAO::logFinished)
|
||||||
|
.onError(error -> log.error("Failed to put audit log for batch action finish: ", error));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,86 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2022 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.bulkaction.impl;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.springframework.context.annotation.Lazy;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import ch.ethz.seb.sebserver.gbl.api.API.BatchActionType;
|
||||||
|
import ch.ethz.seb.sebserver.gbl.api.APIMessage;
|
||||||
|
import ch.ethz.seb.sebserver.gbl.api.authorization.PrivilegeType;
|
||||||
|
import ch.ethz.seb.sebserver.gbl.model.BatchAction;
|
||||||
|
import ch.ethz.seb.sebserver.gbl.model.Entity;
|
||||||
|
import ch.ethz.seb.sebserver.gbl.model.EntityKey;
|
||||||
|
import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationNode;
|
||||||
|
import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile;
|
||||||
|
import ch.ethz.seb.sebserver.gbl.util.Result;
|
||||||
|
import ch.ethz.seb.sebserver.webservice.servicelayer.authorization.AuthorizationService;
|
||||||
|
import ch.ethz.seb.sebserver.webservice.servicelayer.bulkaction.BatchActionExec;
|
||||||
|
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.ConfigurationNodeDAO;
|
||||||
|
import ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.ExamConfigService;
|
||||||
|
import ch.ethz.seb.sebserver.webservice.servicelayer.session.ExamConfigUpdateService;
|
||||||
|
|
||||||
|
@Lazy
|
||||||
|
@Component
|
||||||
|
@WebServiceProfile
|
||||||
|
public class ExamConfigResetToTemplate implements BatchActionExec {
|
||||||
|
|
||||||
|
private final ConfigurationNodeDAO configurationNodeDAO;
|
||||||
|
private final ExamConfigService sebExamConfigService;
|
||||||
|
private final ExamConfigUpdateService examConfigUpdateService;
|
||||||
|
private final AuthorizationService authorizationService;
|
||||||
|
|
||||||
|
public ExamConfigResetToTemplate(
|
||||||
|
final ConfigurationNodeDAO configurationNodeDAO,
|
||||||
|
final ExamConfigService sebExamConfigService,
|
||||||
|
final ExamConfigUpdateService examConfigUpdateService,
|
||||||
|
final AuthorizationService authorizationService) {
|
||||||
|
|
||||||
|
this.configurationNodeDAO = configurationNodeDAO;
|
||||||
|
this.sebExamConfigService = sebExamConfigService;
|
||||||
|
this.examConfigUpdateService = examConfigUpdateService;
|
||||||
|
this.authorizationService = authorizationService;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BatchActionType actionType() {
|
||||||
|
return BatchActionType.EXAM_CONFIG_REST_TEMPLATE_SETTINGS;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public APIMessage checkConsistency(final Map<String, String> actionAttributes) {
|
||||||
|
// no additional check here
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Result<EntityKey> doSingleAction(final String modelId, final BatchAction batchAction) {
|
||||||
|
|
||||||
|
return this.configurationNodeDAO
|
||||||
|
.byModelId(modelId)
|
||||||
|
.flatMap(node -> this.authorizationService.check(PrivilegeType.MODIFY, node))
|
||||||
|
.map(this::checkConsistency)
|
||||||
|
.flatMap(this.sebExamConfigService::resetToTemplateSettings)
|
||||||
|
.map(node -> {
|
||||||
|
this.examConfigUpdateService
|
||||||
|
.processExamConfigurationChange(node.id)
|
||||||
|
.getOrThrow();
|
||||||
|
return node;
|
||||||
|
})
|
||||||
|
.map(Entity::getEntityKey);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private ConfigurationNode checkConsistency(final ConfigurationNode configurationNode) {
|
||||||
|
return configurationNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -21,36 +21,31 @@ import ch.ethz.seb.sebserver.gbl.model.Entity;
|
||||||
import ch.ethz.seb.sebserver.gbl.model.EntityKey;
|
import ch.ethz.seb.sebserver.gbl.model.EntityKey;
|
||||||
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.user.UserInfo;
|
|
||||||
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.authorization.AuthorizationService;
|
import ch.ethz.seb.sebserver.webservice.servicelayer.authorization.AuthorizationService;
|
||||||
import ch.ethz.seb.sebserver.webservice.servicelayer.bulkaction.BatchActionExec;
|
import ch.ethz.seb.sebserver.webservice.servicelayer.bulkaction.BatchActionExec;
|
||||||
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.ConfigurationNodeDAO;
|
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.ConfigurationNodeDAO;
|
||||||
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.UserDAO;
|
|
||||||
import ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.ExamConfigService;
|
import ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.ExamConfigService;
|
||||||
import io.micrometer.core.instrument.util.StringUtils;
|
import io.micrometer.core.instrument.util.StringUtils;
|
||||||
|
|
||||||
@Lazy
|
@Lazy
|
||||||
@Component
|
@Component
|
||||||
@WebServiceProfile
|
@WebServiceProfile
|
||||||
public class SingleExamConfigStateChange implements BatchActionExec {
|
public class ExamConfigStateChange implements BatchActionExec {
|
||||||
|
|
||||||
private final ExamConfigService sebExamConfigService;
|
private final ExamConfigService sebExamConfigService;
|
||||||
private final ConfigurationNodeDAO configurationNodeDAO;
|
private final ConfigurationNodeDAO configurationNodeDAO;
|
||||||
private final AuthorizationService authorizationService;
|
private final AuthorizationService authorizationService;
|
||||||
private final UserDAO userDAO;
|
|
||||||
|
|
||||||
public SingleExamConfigStateChange(
|
public ExamConfigStateChange(
|
||||||
final ExamConfigService sebExamConfigService,
|
final ExamConfigService sebExamConfigService,
|
||||||
final ConfigurationNodeDAO configurationNodeDAO,
|
final ConfigurationNodeDAO configurationNodeDAO,
|
||||||
final AuthorizationService authorizationService,
|
final AuthorizationService authorizationService) {
|
||||||
final UserDAO userDAO) {
|
|
||||||
|
|
||||||
this.sebExamConfigService = sebExamConfigService;
|
this.sebExamConfigService = sebExamConfigService;
|
||||||
this.configurationNodeDAO = configurationNodeDAO;
|
this.configurationNodeDAO = configurationNodeDAO;
|
||||||
this.authorizationService = authorizationService;
|
this.authorizationService = authorizationService;
|
||||||
this.userDAO = userDAO;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -71,13 +66,9 @@ public class SingleExamConfigStateChange implements BatchActionExec {
|
||||||
@Override
|
@Override
|
||||||
public Result<EntityKey> doSingleAction(final String modelId, final BatchAction batchAction) {
|
public Result<EntityKey> doSingleAction(final String modelId, final BatchAction batchAction) {
|
||||||
|
|
||||||
final UserInfo user = this.userDAO
|
|
||||||
.byModelId(batchAction.ownerId)
|
|
||||||
.getOrThrow();
|
|
||||||
|
|
||||||
return this.configurationNodeDAO
|
return this.configurationNodeDAO
|
||||||
.byModelId(modelId)
|
.byModelId(modelId)
|
||||||
.map(node -> this.authorizationService.check(PrivilegeType.MODIFY, user, node))
|
.flatMap(node -> this.authorizationService.check(PrivilegeType.MODIFY, node))
|
||||||
.map(node -> new ConfigurationNode(
|
.map(node -> new ConfigurationNode(
|
||||||
node.id, null, null, null, null, null, null,
|
node.id, null, null, null, null, null, null,
|
||||||
getTargetState(batchAction.attributes)))
|
getTargetState(batchAction.attributes)))
|
|
@ -77,6 +77,12 @@ public interface UserActivityLogDAO extends
|
||||||
* @return Result of the Entity or referring to an Error if happened */
|
* @return Result of the Entity or referring to an Error if happened */
|
||||||
<E extends Entity> Result<E> logModify(E entity);
|
<E extends Entity> Result<E> logModify(E entity);
|
||||||
|
|
||||||
|
/** Create a user activity log entry for the current user of activity type FINISHED
|
||||||
|
*
|
||||||
|
* @param entity the Entity
|
||||||
|
* @return Result of the Entity or referring to an Error if happened */
|
||||||
|
<E extends Entity> Result<E> logFinished(E entity);
|
||||||
|
|
||||||
/** Create a user activity log entry for the current user of activity type DELETE
|
/** Create a user activity log entry for the current user of activity type DELETE
|
||||||
*
|
*
|
||||||
* @param entity the Entity
|
* @param entity the Entity
|
||||||
|
|
|
@ -160,6 +160,11 @@ public class UserActivityLogDAOImpl implements UserActivityLogDAO {
|
||||||
return log(UserLogActivityType.MODIFY, entity);
|
return log(UserLogActivityType.MODIFY, entity);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <E extends Entity> Result<E> logFinished(final E entity) {
|
||||||
|
return log(UserLogActivityType.FINISHED, entity);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Transactional
|
@Transactional
|
||||||
public <E extends Entity> Result<E> logDelete(final E entity) {
|
public <E extends Entity> Result<E> logDelete(final E entity) {
|
||||||
|
|
|
@ -14,6 +14,7 @@ import java.io.OutputStream;
|
||||||
import java.io.PipedInputStream;
|
import java.io.PipedInputStream;
|
||||||
import java.io.PipedOutputStream;
|
import java.io.PipedOutputStream;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.concurrent.Future;
|
import java.util.concurrent.Future;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
@ -414,6 +415,18 @@ public class ExamConfigServiceImpl implements ExamConfigService {
|
||||||
"The Type of ConfigurationNode cannot change after creation");
|
"The Type of ConfigurationNode cannot change after creation");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// if configuration is in use, "Ready to Use" is not possible
|
||||||
|
if (configurationNode.status == ConfigurationStatus.READY_TO_USE) {
|
||||||
|
if (!this.examConfigurationMapDAO
|
||||||
|
.getExamIdsForConfigNodeId(configurationNode.id)
|
||||||
|
.getOr(Collections.emptyList())
|
||||||
|
.isEmpty()) {
|
||||||
|
throw new APIMessageException(
|
||||||
|
APIMessage.ErrorMessage.INTEGRITY_VALIDATION
|
||||||
|
.of("Exam configuration has references to at least one exam."));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// if changing to archived check possibility
|
// if changing to archived check possibility
|
||||||
if (configurationNode.status == ConfigurationStatus.ARCHIVED) {
|
if (configurationNode.status == ConfigurationStatus.ARCHIVED) {
|
||||||
if (existingNode.status != ConfigurationStatus.ARCHIVED) {
|
if (existingNode.status != ConfigurationStatus.ARCHIVED) {
|
||||||
|
|
|
@ -49,6 +49,7 @@ sebserver.overall.types.activityType.ACTIVATE=Activate
|
||||||
sebserver.overall.types.activityType.DELETE=Delete
|
sebserver.overall.types.activityType.DELETE=Delete
|
||||||
sebserver.overall.types.activityType.LOGIN=Login
|
sebserver.overall.types.activityType.LOGIN=Login
|
||||||
sebserver.overall.types.activityType.LOGOUT=Logout
|
sebserver.overall.types.activityType.LOGOUT=Logout
|
||||||
|
sebserver.overall.types.activityType.FINISHED=Finished
|
||||||
|
|
||||||
sebserver.overall.types.entityType.CONFIGURATION_ATTRIBUTE=Configuration Attribute
|
sebserver.overall.types.entityType.CONFIGURATION_ATTRIBUTE=Configuration Attribute
|
||||||
sebserver.overall.types.entityType.CONFIGURATION_VALUE=Configuration Value
|
sebserver.overall.types.entityType.CONFIGURATION_VALUE=Configuration Value
|
||||||
|
@ -70,6 +71,7 @@ sebserver.overall.types.entityType.EXAM_SEB_RESTRICTION=SEB Exam Restriction
|
||||||
sebserver.overall.types.entityType.CERTIFICATE=Certificate
|
sebserver.overall.types.entityType.CERTIFICATE=Certificate
|
||||||
sebserver.overall.types.entityType.EXAM_TEMPLATE=Exam Template
|
sebserver.overall.types.entityType.EXAM_TEMPLATE=Exam Template
|
||||||
sebserver.overall.types.entityType.EXAM_PROCTOR_DATA=Exam Proctoring Settings
|
sebserver.overall.types.entityType.EXAM_PROCTOR_DATA=Exam Proctoring Settings
|
||||||
|
sebserver.overall.types.entityType.BATCH_ACTION=Batch Action
|
||||||
|
|
||||||
sebserver.overall.activity.title.serveradmin=SEB Server Administration
|
sebserver.overall.activity.title.serveradmin=SEB Server Administration
|
||||||
sebserver.overall.activity.title.sebconfig=Configurations
|
sebserver.overall.activity.title.sebconfig=Configurations
|
||||||
|
@ -814,13 +816,16 @@ sebserver.examconfig.list.action.no.modify.privilege=No Access: An Exam Configur
|
||||||
sebserver.examconfig.action.list.new=Add Exam Configuration
|
sebserver.examconfig.action.list.new=Add Exam Configuration
|
||||||
sebserver.examconfig.action.list.view=View Exam Configuration
|
sebserver.examconfig.action.list.view=View Exam Configuration
|
||||||
sebserver.examconfig.list.action.statechange=State Change
|
sebserver.examconfig.list.action.statechange=State Change
|
||||||
sebserver.examconfig.list.batch.statechange.title=Bulk State Change
|
sebserver.examconfig.list.batch.statechange.title=State Change All
|
||||||
sebserver.examconfig.list.batch.action.statechange=Change States
|
sebserver.examconfig.list.batch.action.statechange=Change States
|
||||||
sebserver.examconfig.list.batch.progress=Bulk State Change in Progress: {0}%
|
sebserver.examconfig.list.batch.progress=Bulk State Change in Progress: {0}%
|
||||||
sebserver.examconfig.list.batch.finished=Bulk State Change finished. Successful: {0}.Failed: {1}
|
sebserver.examconfig.list.batch.finished=Bulk State Change finished. Successful: {0}.Failed: {1}
|
||||||
sebserver.examconfig.list.batch.action.statechange.info=This action changes all selected exam configurations to a defined target state.<br/>Please note that this will be done only for those that can be changed to the defined target state in regard to its current state.
|
sebserver.examconfig.list.batch.action.statechange.info=This action changes all selected exam configurations to a defined target state.<br/>Please note that this will be done only for those that can be changed to the defined target state in regard to its current state.
|
||||||
sebserver.examconfig.list.batch.action.status=Target Status
|
sebserver.examconfig.list.batch.action.status=Target Status
|
||||||
|
sebserver.examconfig.list.action.reset=Reset To Template Settings
|
||||||
|
sebserver.examconfig.list.batch.reset.title=Reset To Template Settings
|
||||||
|
sebserver.examconfig.list.batch.action.reset=Reset All
|
||||||
|
sebserver.examconfig.list.batch.action.reset.info=This action process a reset of the SEB settings to its template settings for all selected configurations.<br/>Please note this will be done only for those that has an origin template and are not in state "Used". <br/>Please note also that this action tries to directly publish the changes and if not possible (active SEB client connections) it will report an error even if the settings has been reseted.
|
||||||
|
|
||||||
sebserver.examconfig.action.list.modify.properties=Edit Exam Configuration
|
sebserver.examconfig.action.list.modify.properties=Edit Exam Configuration
|
||||||
sebserver.examconfig.action.delete=Delete Exam Configuration
|
sebserver.examconfig.action.delete=Delete Exam Configuration
|
||||||
|
|
|
@ -15,6 +15,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
@ -248,11 +249,12 @@ public abstract class AdministrationAPIIntegrationTester {
|
||||||
}
|
}
|
||||||
|
|
||||||
protected String getOrderedUUIDs(final Collection<? extends Entity> list) {
|
protected String getOrderedUUIDs(final Collection<? extends Entity> list) {
|
||||||
return list
|
final List<String> l = list
|
||||||
.stream()
|
.stream()
|
||||||
.map(userInfo -> userInfo.getModelId())
|
.map(userInfo -> userInfo.getModelId())
|
||||||
.collect(Collectors.toList())
|
.collect(Collectors.toList());
|
||||||
.toString();
|
l.sort((s1, s2) -> s1.compareTo(s2));
|
||||||
|
return l.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -268,7 +268,7 @@ public class UserAPITest extends AdministrationAPIIntegrationTester {
|
||||||
assertTrue(userInfos.numberOfPages == 1);
|
assertTrue(userInfos.numberOfPages == 1);
|
||||||
assertNotNull(userInfos.content);
|
assertNotNull(userInfos.content);
|
||||||
assertTrue(userInfos.content.size() == 3);
|
assertTrue(userInfos.content.size() == 3);
|
||||||
assertEquals("[user5, user2, user1]", getOrderedUUIDs(userInfos.content));
|
assertEquals("[user1, user2, user5]", getOrderedUUIDs(userInfos.content));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -347,7 +347,7 @@ public class UserAPITest extends AdministrationAPIIntegrationTester {
|
||||||
assertTrue(userInfos.numberOfPages == 2);
|
assertTrue(userInfos.numberOfPages == 2);
|
||||||
assertNotNull(userInfos.content);
|
assertNotNull(userInfos.content);
|
||||||
assertTrue(userInfos.content.size() == 3);
|
assertTrue(userInfos.content.size() == 3);
|
||||||
assertEquals("[user7, user6, user4]", getOrderedUUIDs(userInfos.content));
|
assertEquals("[user3, user4, user6]", getOrderedUUIDs(userInfos.content));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
Loading…
Reference in a new issue