diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/content/action/ActionDefinition.java b/src/main/java/ch/ethz/seb/sebserver/gui/content/action/ActionDefinition.java index b0416cb0..eca07f00 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/content/action/ActionDefinition.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/content/action/ActionDefinition.java @@ -715,6 +715,12 @@ public enum ActionDefinition { PageStateDefinitionImpl.SEB_EXAM_CONFIG_LIST, ActionCategory.SEB_EXAM_CONFIG_LIST), + SEB_EXAM_CONFIG_BULK_DELETE( + new LocTextKey("sebserver.examconfig.list.batch.action.delete"), + ImageIcon.DELETE, + PageStateDefinitionImpl.SEB_EXAM_CONFIG_LIST, + ActionCategory.SEB_EXAM_CONFIG_LIST), + SEB_EXAM_CONFIG_MODIFY_PROP_FROM_LIST( new LocTextKey("sebserver.examconfig.action.list.modify.properties"), ImageIcon.EDIT, diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/content/configs/SEBExamConfigBatchDeletePopup.java b/src/main/java/ch/ethz/seb/sebserver/gui/content/configs/SEBExamConfigBatchDeletePopup.java new file mode 100644 index 00000000..5270feaf --- /dev/null +++ b/src/main/java/ch/ethz/seb/sebserver/gui/content/configs/SEBExamConfigBatchDeletePopup.java @@ -0,0 +1,130 @@ +package ch.ethz.seb.sebserver.gui.content.configs; + +import ch.ethz.seb.sebserver.gbl.Constants; +import ch.ethz.seb.sebserver.gbl.api.API; +import ch.ethz.seb.sebserver.gbl.api.API.BatchActionType; +import ch.ethz.seb.sebserver.gbl.api.EntityType; +import ch.ethz.seb.sebserver.gbl.model.BatchAction; +import ch.ethz.seb.sebserver.gbl.model.Domain; +import ch.ethz.seb.sebserver.gbl.model.EntityKey; +import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationNode; +import ch.ethz.seb.sebserver.gbl.profile.GuiProfile; +import ch.ethz.seb.sebserver.gui.content.action.ActionDefinition; +import ch.ethz.seb.sebserver.gui.form.FormBuilder; +import ch.ethz.seb.sebserver.gui.form.FormHandle; +import ch.ethz.seb.sebserver.gui.service.ResourceService; +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; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestService; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.seb.examconfig.GetExamConfigNodesByIds; +import ch.ethz.seb.sebserver.gui.table.ColumnDefinition; +import org.apache.tomcat.util.buf.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.context.annotation.Lazy; +import org.springframework.stereotype.Component; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Set; +import java.util.function.Supplier; +import java.util.stream.Collectors; + +@Lazy +@Component +@GuiProfile +public class SEBExamConfigBatchDeletePopup extends AbstractBatchActionWizard { + + private final static LocTextKey FORM_TITLE = new LocTextKey("sebserver.examconfig.list.batch.delete.title"); + private final static LocTextKey FORM_INFO = new LocTextKey("sebserver.examconfig.list.batch.delete.info"); + private final static LocTextKey ACTION_DO_DELETE = new LocTextKey("sebserver.examconfig.list.batch.action.delete.button"); + + protected SEBExamConfigBatchDeletePopup( + 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_DELETE; + } + + @Override + protected BatchActionType getBatchActionType() { + return BatchActionType.EXAM_CONFIG_DELETE; + } + + @Override + protected Supplier createResultPageSupplier(PageContext pageContext, FormHandle formHandle) { + return () -> pageContext; + } + + @Override + protected void extendBatchActionRequest(PageContext pageContext, RestCall.RestCallBuilder batchActionRequestBuilder) { + // Nothing to do here + } + + @Override + protected FormBuilder buildSpecificFormFields(PageContext formContext, FormBuilder formHead, boolean readonly) { + return formHead; + } + + @Override + protected void processUpdateListAction(PageContext formContext) { + this.pageService.executePageAction(this.pageService.pageActionBuilder(formContext) + .newAction(ActionDefinition.SEB_EXAM_CONFIG_LIST) + .create()); + } + + @Override + protected void applySelectionList( + final PageContext formContext, + final Set multiSelection) { + + final ResourceService resourceService = this.pageService.getResourceService(); + + final String ids = StringUtils.join( + multiSelection.stream().map(EntityKey::getModelId).collect(Collectors.toList()), + Constants.LIST_SEPARATOR_CHAR); + + final RestService restService = this.pageService.getRestService(); + final List selected = new ArrayList<>(restService.getBuilder(GetExamConfigNodesByIds.class) + .withQueryParam(API.PARAM_MODEL_ID_LIST, ids) + .call() + .getOr(Collections.emptyList())); + + selected.sort((examConfig1, examConfig2) -> examConfig1.name.compareTo(examConfig2.name)); + + this.pageService.staticListTableBuilder(selected, EntityType.CONFIGURATION_NODE) + .withPaging(10) + .withColumn(new ColumnDefinition<>( + Domain.CONFIGURATION_NODE.ATTR_NAME, + SEBExamConfigList.NAME_TEXT_KEY, + ConfigurationNode::getName)) + + .withColumn(new ColumnDefinition<>( + Domain.CONFIGURATION_NODE.ATTR_STATUS, + SEBExamConfigList.STATUS_TEXT_KEY, + resourceService::localizedExamConfigStatusName) + ) + + .compose(formContext); + } +} \ No newline at end of file diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/content/configs/SEBExamConfigList.java b/src/main/java/ch/ethz/seb/sebserver/gui/content/configs/SEBExamConfigList.java index 4fa3f37c..28d58ff6 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/content/configs/SEBExamConfigList.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/content/configs/SEBExamConfigList.java @@ -53,11 +53,11 @@ public class SEBExamConfigList implements TemplateComposer { new LocTextKey("sebserver.examconfig.list.title"); private static final LocTextKey INSTITUTION_TEXT_KEY = new LocTextKey("sebserver.examconfig.list.column.institution"); - private static final LocTextKey NAME_TEXT_KEY = + public static final LocTextKey NAME_TEXT_KEY = new LocTextKey("sebserver.examconfig.list.column.name"); private static final LocTextKey DESCRIPTION_TEXT_KEY = new LocTextKey("sebserver.examconfig.list.column.description"); - private static final LocTextKey STATUS_TEXT_KEY = + public static final LocTextKey STATUS_TEXT_KEY = new LocTextKey("sebserver.examconfig.list.column.status"); private static final LocTextKey TEMPLATE_TEXT_KEY = new LocTextKey("sebserver.examconfig.list.column.template"); @@ -81,6 +81,8 @@ public class SEBExamConfigList implements TemplateComposer { private final SEBExamConfigCreationPopup sebExamConfigCreationPopup; private final SEBExamConfigBatchStateChangePopup sebExamConfigBatchStateChangePopup; private final SEBExamConfigBatchResetToTemplatePopup sebExamConfigBatchResetToTemplatePopup; + private final SEBExamConfigBatchDeletePopup sebExamConfigBatchDeletePopup; + private final CurrentUser currentUser; private final ResourceService resourceService; private final int pageSize; @@ -91,6 +93,7 @@ public class SEBExamConfigList implements TemplateComposer { final SEBExamConfigCreationPopup sebExamConfigCreationPopup, final SEBExamConfigBatchStateChangePopup sebExamConfigBatchStateChangePopup, final SEBExamConfigBatchResetToTemplatePopup sebExamConfigBatchResetToTemplatePopup, + final SEBExamConfigBatchDeletePopup sebExamConfigBatchDeletePopup, @Value("${sebserver.gui.list.page.size:20}") final Integer pageSize) { this.pageService = pageService; @@ -98,6 +101,7 @@ public class SEBExamConfigList implements TemplateComposer { this.sebExamConfigCreationPopup = sebExamConfigCreationPopup; this.sebExamConfigBatchStateChangePopup = sebExamConfigBatchStateChangePopup; this.sebExamConfigBatchResetToTemplatePopup = sebExamConfigBatchResetToTemplatePopup; + this.sebExamConfigBatchDeletePopup = sebExamConfigBatchDeletePopup; this.currentUser = pageService.getCurrentUser(); this.resourceService = pageService.getResourceService(); this.pageSize = pageSize; @@ -188,7 +192,8 @@ public class SEBExamConfigList implements TemplateComposer { ActionDefinition.SEB_EXAM_CONFIG_MODIFY_PROP_FROM_LIST, ActionDefinition.SEB_EXAM_CONFIG_COPY_CONFIG_FROM_LIST, ActionDefinition.SEB_EXAM_CONFIG_BULK_STATE_CHANGE, - ActionDefinition.SEB_EXAM_CONFIG_BULK_RESET_TO_TEMPLATE)) + ActionDefinition.SEB_EXAM_CONFIG_BULK_RESET_TO_TEMPLATE, + ActionDefinition.SEB_EXAM_CONFIG_BULK_DELETE)) .compose(pageContext.copyOf(content)); @@ -248,6 +253,14 @@ public class SEBExamConfigList implements TemplateComposer { .noEventPropagation() .publishIf(() -> !isLight && examConfigGrant.im(), false) + .newAction(ActionDefinition.SEB_EXAM_CONFIG_BULK_DELETE) + .withSelect( + configTable::getMultiSelection, + this.sebExamConfigBatchDeletePopup.popupCreationFunction(pageContext), + EMPTY_SELECTION_TEXT_KEY) + .noEventPropagation() + .publishIf(() -> !isLight && examConfigGrant.im(), false) + .newAction(ActionDefinition.SEB_EXAM_CONFIG_IMPORT_TO_NEW_CONFIG) .withSelect( configTable.getGrantedSelection(this.currentUser, NO_MODIFY_PRIVILEGE_ON_OTHER_INSTITUTION), diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/service/remote/webservice/api/seb/examconfig/GetExamConfigNodesByIds.java b/src/main/java/ch/ethz/seb/sebserver/gui/service/remote/webservice/api/seb/examconfig/GetExamConfigNodesByIds.java new file mode 100644 index 00000000..2569f357 --- /dev/null +++ b/src/main/java/ch/ethz/seb/sebserver/gui/service/remote/webservice/api/seb/examconfig/GetExamConfigNodesByIds.java @@ -0,0 +1,32 @@ +package ch.ethz.seb.sebserver.gui.service.remote.webservice.api.seb.examconfig; + +import ch.ethz.seb.sebserver.gbl.api.API; +import ch.ethz.seb.sebserver.gbl.api.EntityType; +import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationNode; +import ch.ethz.seb.sebserver.gbl.profile.GuiProfile; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestCall; +import com.fasterxml.jackson.core.type.TypeReference; +import org.springframework.context.annotation.Lazy; +import org.springframework.http.HttpMethod; +import org.springframework.http.MediaType; +import org.springframework.stereotype.Component; + +import java.util.Collection; + +@Lazy +@Component +@GuiProfile +public class GetExamConfigNodesByIds extends RestCall> { + + public GetExamConfigNodesByIds() { + super(new TypeKey<>( + CallType.GET_LIST, + EntityType.CONFIGURATION_NODE, + new TypeReference>() { + }), + HttpMethod.GET, + MediaType.APPLICATION_FORM_URLENCODED, + API.CONFIGURATION_NODE_ENDPOINT + API.LIST_PATH_SEGMENT + ); + } +} diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/bulkaction/impl/ExamConfigDelete.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/bulkaction/impl/ExamConfigDelete.java new file mode 100644 index 00000000..4d213e70 --- /dev/null +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/bulkaction/impl/ExamConfigDelete.java @@ -0,0 +1,82 @@ +package ch.ethz.seb.sebserver.webservice.servicelayer.bulkaction.impl; + +import ch.ethz.seb.sebserver.gbl.api.API.BatchActionType; +import ch.ethz.seb.sebserver.gbl.api.APIMessage; +import ch.ethz.seb.sebserver.gbl.api.EntityType; +import ch.ethz.seb.sebserver.gbl.api.authorization.PrivilegeType; +import ch.ethz.seb.sebserver.gbl.model.BatchAction; +import ch.ethz.seb.sebserver.gbl.api.APIMessage.APIMessageException; +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.dao.ExamConfigurationMapDAO; +import ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.ExamConfigService; +import org.springframework.context.annotation.Lazy; +import org.springframework.stereotype.Component; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.Map; +import java.util.stream.Collectors; + +@Lazy +@Component +@WebServiceProfile +public class ExamConfigDelete implements BatchActionExec { + + private final ConfigurationNodeDAO configurationNodeDAO; + private final AuthorizationService authorizationService; + private final ExamConfigurationMapDAO examConfigurationMapDAO; + + public ExamConfigDelete( + final ExamConfigService sebExamConfigService, + final ConfigurationNodeDAO configurationNodeDAO, + final AuthorizationService authorizationService, + final ExamConfigurationMapDAO examConfigurationMapDAO) { + + this.configurationNodeDAO = configurationNodeDAO; + this.authorizationService = authorizationService; + this.examConfigurationMapDAO = examConfigurationMapDAO; + } + + @Override + public BatchActionType actionType() { + return BatchActionType.EXAM_CONFIG_DELETE; + } + + @Override + public APIMessage checkConsistency(final Map actionAttributes) { + // no additional check here + return null; + } + + @Override + public Result doSingleAction(final String modelId, final BatchAction batchAction) { + return this.configurationNodeDAO + .byModelId(modelId) + .flatMap(examConfig -> this.authorizationService.check(PrivilegeType.MODIFY, examConfig)) + .flatMap(examConfig -> checkDeletionRequirements(examConfig)) + .flatMap(examConfig -> this.configurationNodeDAO.delete(new HashSet<>(Arrays.asList(new EntityKey( + modelId, + EntityType.CONFIGURATION_NODE)))) + ) + .map(res -> res.stream().collect(Collectors.toList()).get(0)); + } + + private Result checkDeletionRequirements(ConfigurationNode examConfig){ + Result isNotActive = this.examConfigurationMapDAO.checkNoActiveExamReferences(examConfig.id); + if(!isNotActive.getOrThrow()){ + return Result.ofError(new APIMessageException( + APIMessage.ErrorMessage.INTEGRITY_VALIDATION + .of("Exam Configuration has active Exam references") + )); + } + + return Result.of(examConfig); + } + +} \ No newline at end of file diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/ConfigurationNodeDAOImpl.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/ConfigurationNodeDAOImpl.java index 5283e262..0d55745e 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/ConfigurationNodeDAOImpl.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/ConfigurationNodeDAOImpl.java @@ -265,7 +265,7 @@ public class ConfigurationNodeDAOImpl implements ConfigurationNodeDAO { @Override @Transactional public Result> delete(final Set all) { - return Result.tryCatch(() -> { + return Result.>tryCatch(() -> { final List ids = extractListOfPKs(all); @@ -306,7 +306,7 @@ public class ConfigurationNodeDAOImpl implements ConfigurationNodeDAO { return ids.stream() .map(id -> new EntityKey(id, EntityType.CONFIGURATION_NODE)) .collect(Collectors.toList()); - }); + }).onError(TransactionHandler::rollback); } private void handleConfigTemplateDeletion(final List configurationIds) { diff --git a/src/main/resources/messages.properties b/src/main/resources/messages.properties index 16744fcc..43cff9a3 100644 --- a/src/main/resources/messages.properties +++ b/src/main/resources/messages.properties @@ -1097,6 +1097,10 @@ 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.delete=Delete Exam Configurations +sebserver.examconfig.list.batch.action.delete.button=Delete +sebserver.examconfig.list.batch.delete.title=Delete Selected Exam Configurations +sebserver.examconfig.list.batch.delete.info=Please Note:
    This deletes the exam configuration in SEB Server
     sebserver.examconfig.list.batch.action.reset.info=This action process a reset of the SEB settings to its template settings for all selected configurations.
Please note this will be done only for those that has an origin template and are not in state "Used".
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