diff --git a/src/main/java/ch/ethz/seb/sebserver/gbl/Constants.java b/src/main/java/ch/ethz/seb/sebserver/gbl/Constants.java index 9a35fbd7..b6116cdb 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gbl/Constants.java +++ b/src/main/java/ch/ethz/seb/sebserver/gbl/Constants.java @@ -8,7 +8,11 @@ package ch.ethz.seb.sebserver.gbl; +import java.text.Collator; +import java.util.Arrays; import java.util.Collection; +import java.util.List; +import java.util.Locale; import org.eclipse.swt.graphics.RGB; import org.eclipse.swt.graphics.RGBA; @@ -19,6 +23,7 @@ import org.springframework.core.ParameterizedTypeReference; import com.fasterxml.jackson.core.type.TypeReference; import ch.ethz.seb.sebserver.gbl.api.APIMessage; +import ch.ethz.seb.sebserver.gbl.api.EntityType; import ch.ethz.seb.sebserver.gbl.api.authorization.Privilege; /** Global Constants used in SEB Server web-service as well as in web-gui component */ @@ -155,6 +160,23 @@ public final class Constants { public static final RGB BLACK_RGB = new RGB(0, 0, 0); public static final RGBA GREY_DISABLED = new RGBA(150, 150, 150, 50); + public static final Collator DEFAULT_ENGLISH_COLLATOR = Collator.getInstance(Locale.ENGLISH); + + public static final List ENTITY_TYPE_HIRARCHIE = Arrays.asList( + EntityType.INSTITUTION, + EntityType.USER, + EntityType.USER_ACTIVITY_LOG, + EntityType.CERTIFICATE, + EntityType.LMS_SETUP, + EntityType.SEB_CLIENT_CONFIGURATION, + EntityType.EXAM_TEMPLATE, + EntityType.EXAM, + EntityType.INDICATOR, + EntityType.EXAM_CONFIGURATION_MAP, + EntityType.CONFIGURATION_NODE, + EntityType.CLIENT_CONNECTION, + EntityType.CLIENT_EVENT); + public static final String IMPORTED_PASSWORD_MARKER = "_IMPORTED_PASSWORD"; public static final TypeReference> TYPE_REFERENCE_API_MESSAGE = diff --git a/src/main/java/ch/ethz/seb/sebserver/gbl/model/EntityDependency.java b/src/main/java/ch/ethz/seb/sebserver/gbl/model/EntityDependency.java index 9125ed78..90170c50 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gbl/model/EntityDependency.java +++ b/src/main/java/ch/ethz/seb/sebserver/gbl/model/EntityDependency.java @@ -11,6 +11,8 @@ package ch.ethz.seb.sebserver.gbl.model; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonProperty; +import ch.ethz.seb.sebserver.gbl.Constants; + @JsonIgnoreProperties(ignoreUnknown = true) public class EntityDependency implements Comparable, ModelIdAware { @@ -113,9 +115,16 @@ public class EntityDependency implements Comparable, ModelIdAw return -1; } - final int compareTo = this.self.entityType.name().compareTo(other.self.entityType.name()); + final int compareTo = Integer.compare( + Constants.ENTITY_TYPE_HIRARCHIE.indexOf(this.self.entityType), + Constants.ENTITY_TYPE_HIRARCHIE.indexOf(other.self.entityType)); + //this.self.entityType.name().compareTo(other.self.entityType.name()); if (compareTo == 0) { - return this.self.modelId.compareTo(other.self.modelId); + if (this.name != null) { + return this.name.compareTo(other.name); + } else { + return this.self.modelId.compareTo(other.self.modelId); + } } else { return compareTo; } diff --git a/src/main/java/ch/ethz/seb/sebserver/gbl/model/EntityKey.java b/src/main/java/ch/ethz/seb/sebserver/gbl/model/EntityKey.java index cb3c0fef..bb0622b1 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gbl/model/EntityKey.java +++ b/src/main/java/ch/ethz/seb/sebserver/gbl/model/EntityKey.java @@ -16,6 +16,7 @@ import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonProperty; +import ch.ethz.seb.sebserver.gbl.Constants; import ch.ethz.seb.sebserver.gbl.api.EntityType; /** An EntityKey uniquely identifies a domain entity within the SEB Server's domain model. @@ -128,7 +129,11 @@ public class EntityKey implements ModelIdAware, Serializable, Comparable deleteWizardFunction(final PageContext pageContext) { + return action -> { + + final ModalInputWizard wizard = + new ModalInputWizard( + action.pageContext().getParent().getShell(), + this.pageService.getWidgetFactory()) + .setVeryLargeDialogWidth(); + + final String page1Id = "DELETE_PAGE"; + final Predicate callback = pc -> doDelete(this.pageService, pc); + final BiFunction> composePage1 = + (prefPageContext, content) -> composeDeleteDialog(content, + (prefPageContext != null) ? prefPageContext : pageContext); + + final WizardPage page1 = new WizardPage<>( + page1Id, + true, + composePage1, + new WizardAction<>(ACTION_DELETE, callback)); + + wizard.open(FORM_TITLE, Utils.EMPTY_EXECUTION, page1); + + return action; + }; + } + + private boolean doDelete( + final PageService pageService, + final PageContext pageContext) { + + try { + final EntityKey entityKey = pageContext.getEntityKey(); + final Institution toDelete = this.pageService + .getRestService() + .getBuilder(GetInstitution.class) + .withURIVariable(API.PARAM_MODEL_ID, entityKey.modelId) + .call() + .getOrThrow(); + + final RestCall.RestCallBuilder restCallBuilder = this.pageService.getRestService() + .getBuilder(DeleteInstitution.class) + .withURIVariable(API.PARAM_MODEL_ID, entityKey.modelId) + .withQueryParam(API.PARAM_BULK_ACTION_TYPE, BulkActionType.HARD_DELETE.name()); + + final Result deleteCall = restCallBuilder.call(); + if (deleteCall.hasError()) { + final Exception error = deleteCall.getError(); + if (error instanceof RestCallError) { + final APIMessage message = ((RestCallError) error) + .getAPIMessages() + .stream() + .findFirst() + .orElse(null); + if (message != null && ErrorMessage.INTEGRITY_VALIDATION.isOf(message)) { + pageContext.publishPageMessage(new PageMessageException(DELETE_ERROR_CONSISTENCY)); + return false; + } + } + } + + final EntityProcessingReport report = deleteCall.getOrThrow(); + + final PageAction action = this.pageService.pageActionBuilder(pageContext) + .newAction(ActionDefinition.INSTITUTION_VIEW_LIST) + .create(); + + this.pageService.firePageEvent( + new ActionEvent(action), + action.pageContext()); + + final List dependencies = report.results.stream() + .filter(key -> !key.equals(entityKey)) + .collect(Collectors.toList()); + pageContext.publishPageMessage( + DELETE_CONFIRM_TITLE, + new LocTextKey( + "sebserver.institution.delete.confirm.message", + toDelete.toName().name, + dependencies.size(), + (report.errors.isEmpty()) ? "no" : String.valueOf((report.errors.size())))); + return true; + } catch (final Exception e) { + log.error("Unexpected error while trying to delete Institution:", e); + pageContext.notifyUnexpectedError(e); + return false; + } + } + + private Supplier composeDeleteDialog( + final Composite parent, + final PageContext pageContext) { + + final I18nSupport i18nSupport = this.pageService.getI18nSupport(); + final Composite grid = this.pageService.getWidgetFactory() + .createPopupScrollComposite(parent); + + final Label title = this.pageService.getWidgetFactory() + .labelLocalized(grid, CustomVariant.TEXT_H3, FORM_INFO); + final GridData gridData = new GridData(); + gridData.horizontalIndent = 10; + gridData.verticalIndent = 10; + title.setLayoutData(gridData); + + final Label titleReport = this.pageService.getWidgetFactory() + .labelLocalized(grid, CustomVariant.TEXT_H3, FORM_REPORT_INFO); + final GridData gridDataReport = new GridData(); + gridDataReport.horizontalIndent = 10; + gridDataReport.verticalIndent = 10; + titleReport.setLayoutData(gridDataReport); + + try { + + // get dependencies + final EntityKey entityKey = pageContext.getEntityKey(); + final RestCall>.RestCallBuilder restCallBuilder = this.pageService.getRestService() + .getBuilder(GetInstitutionDependency.class) + .withURIVariable(API.PARAM_MODEL_ID, entityKey.modelId) + .withQueryParam(API.PARAM_BULK_ACTION_TYPE, BulkActionType.HARD_DELETE.name()); + + final Set dependencies = restCallBuilder + .call() + .getOrThrow(); + final List list = dependencies + .stream() + .sorted() + .collect(Collectors.toList()); + + this.pageService. staticListTableBuilder(list, null) + .withEmptyMessage(FORM_REPORT_NONE) + .withColumn(new ColumnDefinition<>( + "FORM_REPORT_LIST_TYPE", + FORM_REPORT_LIST_TYPE, + dep -> i18nSupport + .getText("sebserver.overall.types.entityType." + dep.self.entityType.name()))) + .withColumn(new ColumnDefinition<>( + "FORM_REPORT_LIST_NAME", + FORM_REPORT_LIST_NAME, + dep -> dep.name)) + .withColumn(new ColumnDefinition( + "FORM_REPORT_LIST_DESC", + FORM_REPORT_LIST_DESC, + dep -> dep.description)) + .compose(pageContext.copyOf(grid)); + + return () -> pageContext; + } catch (final Exception e) { + log.error("Error while trying to compose Institution delete report page: ", e); + pageContext.notifyUnexpectedError(e); + throw e; + } + } + +} diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/content/admin/InstitutionForm.java b/src/main/java/ch/ethz/seb/sebserver/gui/content/admin/InstitutionForm.java index 321e74ac..cd1b7ad0 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/content/admin/InstitutionForm.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/content/admin/InstitutionForm.java @@ -56,12 +56,16 @@ public class InstitutionForm implements TemplateComposer { private final PageService pageService; private final RestService restService; private final CurrentUser currentUser; + private final InstitutionDeletePopup institutionDeletePopup; - protected InstitutionForm(final PageService pageService) { + protected InstitutionForm( + final PageService pageService, + final InstitutionDeletePopup institutionDeletePopup) { this.pageService = pageService; this.restService = pageService.getRestService(); this.currentUser = pageService.getCurrentUser(); + this.institutionDeletePopup = institutionDeletePopup; } @Override @@ -132,6 +136,11 @@ public class InstitutionForm implements TemplateComposer { .withEntityKey(entityKey) .publishIf(() -> modifyGrant && isReadonly) + .newAction(ActionDefinition.INSTITUTION_DELETE) + .withEntityKey(entityKey) + .withExec(this.institutionDeletePopup.deleteWizardFunction(pageContext)) + .publishIf(() -> writeGrant && isReadonly) + .newAction(ActionDefinition.INSTITUTION_DEACTIVATE) .withEntityKey(entityKey) .withSimpleRestCall(this.restService, DeactivateInstitution.class) diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/content/exam/ExamForm.java b/src/main/java/ch/ethz/seb/sebserver/gui/content/exam/ExamForm.java index 742e150e..175dd8cb 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/content/exam/ExamForm.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/content/exam/ExamForm.java @@ -240,9 +240,9 @@ public class ExamForm implements TemplateComposer { final BooleanSupplier isNew = () -> importFromQuizData; final BooleanSupplier isNotNew = () -> !isNew.getAsBoolean(); - final EntityGrantCheck userGrantCheck = currentUser.entityGrantCheck(exam); - final boolean modifyGrant = userGrantCheck.m(); - final boolean writeGrant = userGrantCheck.w(); + final EntityGrantCheck entityGrantCheck = currentUser.entityGrantCheck(exam); + final boolean modifyGrant = entityGrantCheck.m(); + final boolean writeGrant = entityGrantCheck.w(); final ExamStatus examStatus = exam.getStatus(); final boolean editable = modifyGrant && (examStatus == ExamStatus.UP_COMING || examStatus == ExamStatus.RUNNING); @@ -471,7 +471,7 @@ public class ExamForm implements TemplateComposer { this.examFormConfigs.compose( formContext .copyOf(content) - .withAttribute(ATTR_READ_GRANT, String.valueOf(userGrantCheck.r())) + .withAttribute(ATTR_READ_GRANT, String.valueOf(entityGrantCheck.r())) .withAttribute(ATTR_EDITABLE, String.valueOf(editable)) .withAttribute(ATTR_EXAM_STATUS, examStatus.name())); @@ -479,7 +479,7 @@ public class ExamForm implements TemplateComposer { this.examFormIndicators.compose( formContext .copyOf(content) - .withAttribute(ATTR_READ_GRANT, String.valueOf(userGrantCheck.r())) + .withAttribute(ATTR_READ_GRANT, String.valueOf(entityGrantCheck.r())) .withAttribute(ATTR_EDITABLE, String.valueOf(editable)) .withAttribute(ATTR_EXAM_STATUS, examStatus.name())); } diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/content/exam/ExamTemplateForm.java b/src/main/java/ch/ethz/seb/sebserver/gui/content/exam/ExamTemplateForm.java index 51ffa987..c8980166 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/content/exam/ExamTemplateForm.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/content/exam/ExamTemplateForm.java @@ -46,7 +46,6 @@ import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.NewExamTempl import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.SaveExamTemplate; import ch.ethz.seb.sebserver.gui.service.remote.webservice.auth.CurrentUser; import ch.ethz.seb.sebserver.gui.service.remote.webservice.auth.CurrentUser.EntityGrantCheck; -import ch.ethz.seb.sebserver.gui.service.remote.webservice.auth.CurrentUser.GrantCheck; import ch.ethz.seb.sebserver.gui.table.ColumnDefinition; import ch.ethz.seb.sebserver.gui.table.EntityTable; import ch.ethz.seb.sebserver.gui.widget.WidgetFactory; @@ -199,7 +198,6 @@ public class ExamTemplateForm implements TemplateComposer { .map(ProctoringServiceSettings::getEnableProctoring) .getOr(false); - final GrantCheck userGrant = currentUser.grantCheck(EntityType.EXAM_TEMPLATE); final EntityGrantCheck userGrantCheck = currentUser.entityGrantCheck(examTemplate); // propagate content actions to action-pane this.pageService.pageActionBuilder(formContext.clearEntityKeys()) diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/content/exam/LmsSetupDeletePopup.java b/src/main/java/ch/ethz/seb/sebserver/gui/content/exam/LmsSetupDeletePopup.java new file mode 100644 index 00000000..a9eb7177 --- /dev/null +++ b/src/main/java/ch/ethz/seb/sebserver/gui/content/exam/LmsSetupDeletePopup.java @@ -0,0 +1,246 @@ +/* + * Copyright (c) 2020 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.exam; + +import java.util.List; +import java.util.Set; +import java.util.function.BiFunction; +import java.util.function.Function; +import java.util.function.Predicate; +import java.util.function.Supplier; +import java.util.stream.Collectors; + +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Label; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.context.annotation.Lazy; +import org.springframework.stereotype.Component; + +import ch.ethz.seb.sebserver.gbl.api.API; +import ch.ethz.seb.sebserver.gbl.api.API.BulkActionType; +import ch.ethz.seb.sebserver.gbl.api.APIMessage; +import ch.ethz.seb.sebserver.gbl.api.APIMessage.ErrorMessage; +import ch.ethz.seb.sebserver.gbl.model.EntityDependency; +import ch.ethz.seb.sebserver.gbl.model.EntityKey; +import ch.ethz.seb.sebserver.gbl.model.EntityProcessingReport; +import ch.ethz.seb.sebserver.gbl.model.institution.LmsSetup; +import ch.ethz.seb.sebserver.gbl.profile.GuiProfile; +import ch.ethz.seb.sebserver.gbl.util.Result; +import ch.ethz.seb.sebserver.gbl.util.Utils; +import ch.ethz.seb.sebserver.gui.content.action.ActionDefinition; +import ch.ethz.seb.sebserver.gui.service.i18n.I18nSupport; +import ch.ethz.seb.sebserver.gui.service.i18n.LocTextKey; +import ch.ethz.seb.sebserver.gui.service.page.PageContext; +import ch.ethz.seb.sebserver.gui.service.page.PageMessageException; +import ch.ethz.seb.sebserver.gui.service.page.PageService; +import ch.ethz.seb.sebserver.gui.service.page.event.ActionEvent; +import ch.ethz.seb.sebserver.gui.service.page.impl.ModalInputWizard; +import ch.ethz.seb.sebserver.gui.service.page.impl.ModalInputWizard.WizardAction; +import ch.ethz.seb.sebserver.gui.service.page.impl.ModalInputWizard.WizardPage; +import ch.ethz.seb.sebserver.gui.service.page.impl.PageAction; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestCall; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestCallError; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.lmssetup.DeleteLmsSetup; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.lmssetup.GetLmsSetup; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.lmssetup.GetLmsSetupDependencies; +import ch.ethz.seb.sebserver.gui.table.ColumnDefinition; +import ch.ethz.seb.sebserver.gui.widget.WidgetFactory.CustomVariant; + +@Lazy +@Component +@GuiProfile +public class LmsSetupDeletePopup { + + private static final Logger log = LoggerFactory.getLogger(LmsSetupDeletePopup.class); + + private final static LocTextKey FORM_TITLE = + new LocTextKey("sebserver.lmssetup.delete.form.title"); + private final static LocTextKey FORM_INFO = + new LocTextKey("sebserver.lmssetup.delete.form.info"); + private final static LocTextKey FORM_REPORT_INFO = + new LocTextKey("sebserver.lmssetup.delete.report.info"); + private final static LocTextKey FORM_REPORT_LIST_TYPE = + new LocTextKey("sebserver.lmssetup.delete.report.list.type"); + private final static LocTextKey FORM_REPORT_LIST_NAME = + new LocTextKey("sebserver.lmssetup.delete.report.list.name"); + private final static LocTextKey FORM_REPORT_LIST_DESC = + new LocTextKey("sebserver.lmssetup.delete.report.list.description"); + private final static LocTextKey FORM_REPORT_NONE = + new LocTextKey("sebserver.lmssetup.delete.report.list.empty"); + + private final static LocTextKey ACTION_DELETE = + new LocTextKey("sebserver.lmssetup.delete.action.delete"); + + private final static LocTextKey DELETE_CONFIRM_TITLE = + new LocTextKey("sebserver.lmssetup.delete.confirm.title"); + private final static LocTextKey DELETE_ERROR_CONSISTENCY = + new LocTextKey("sebserver.lmssetup.action.delete.consistency.error"); + + private final PageService pageService; + + protected LmsSetupDeletePopup(final PageService pageService) { + this.pageService = pageService; + } + + public Function deleteWizardFunction(final PageContext pageContext) { + return action -> { + + final ModalInputWizard wizard = + new ModalInputWizard( + action.pageContext().getParent().getShell(), + this.pageService.getWidgetFactory()) + .setVeryLargeDialogWidth(); + + final String page1Id = "DELETE_PAGE"; + final Predicate callback = pc -> doDelete(this.pageService, pc); + final BiFunction> composePage1 = + (prefPageContext, content) -> composeDeleteDialog(content, + (prefPageContext != null) ? prefPageContext : pageContext); + + final WizardPage page1 = new WizardPage<>( + page1Id, + true, + composePage1, + new WizardAction<>(ACTION_DELETE, callback)); + + wizard.open(FORM_TITLE, Utils.EMPTY_EXECUTION, page1); + + return action; + }; + } + + private boolean doDelete( + final PageService pageService, + final PageContext pageContext) { + + try { + final EntityKey entityKey = pageContext.getEntityKey(); + final LmsSetup toDelete = this.pageService + .getRestService() + .getBuilder(GetLmsSetup.class) + .withURIVariable(API.PARAM_MODEL_ID, entityKey.modelId) + .call() + .getOrThrow(); + + final RestCall.RestCallBuilder restCallBuilder = this.pageService.getRestService() + .getBuilder(DeleteLmsSetup.class) + .withURIVariable(API.PARAM_MODEL_ID, entityKey.modelId) + .withQueryParam(API.PARAM_BULK_ACTION_TYPE, BulkActionType.HARD_DELETE.name()); + + final Result deleteCall = restCallBuilder.call(); + if (deleteCall.hasError()) { + final Exception error = deleteCall.getError(); + if (error instanceof RestCallError) { + final APIMessage message = ((RestCallError) error) + .getAPIMessages() + .stream() + .findFirst() + .orElse(null); + if (message != null && ErrorMessage.INTEGRITY_VALIDATION.isOf(message)) { + pageContext.publishPageMessage(new PageMessageException(DELETE_ERROR_CONSISTENCY)); + return false; + } + } + } + + final EntityProcessingReport report = deleteCall.getOrThrow(); + + final PageAction action = this.pageService.pageActionBuilder(pageContext) + .newAction(ActionDefinition.LMS_SETUP_VIEW_LIST) + .create(); + + this.pageService.firePageEvent( + new ActionEvent(action), + action.pageContext()); + + final List dependencies = report.results.stream() + .filter(key -> !key.equals(entityKey)) + .collect(Collectors.toList()); + pageContext.publishPageMessage( + DELETE_CONFIRM_TITLE, + new LocTextKey( + "sebserver.lmssetup.delete.confirm.message", + toDelete.toName().name, + dependencies.size(), + (report.errors.isEmpty()) ? "no" : String.valueOf((report.errors.size())))); + return true; + } catch (final Exception e) { + log.error("Unexpected error while trying to delete LMS Setup:", e); + pageContext.notifyUnexpectedError(e); + return false; + } + } + + private Supplier composeDeleteDialog( + final Composite parent, + final PageContext pageContext) { + + final I18nSupport i18nSupport = this.pageService.getI18nSupport(); + final Composite grid = this.pageService.getWidgetFactory() + .createPopupScrollComposite(parent); + + final Label title = this.pageService.getWidgetFactory() + .labelLocalized(grid, CustomVariant.TEXT_H3, FORM_INFO); + final GridData gridData = new GridData(); + gridData.horizontalIndent = 10; + gridData.verticalIndent = 10; + title.setLayoutData(gridData); + + final Label titleReport = this.pageService.getWidgetFactory() + .labelLocalized(grid, CustomVariant.TEXT_H3, FORM_REPORT_INFO); + final GridData gridDataReport = new GridData(); + gridDataReport.horizontalIndent = 10; + gridDataReport.verticalIndent = 10; + titleReport.setLayoutData(gridDataReport); + + try { + + // get dependencies + final EntityKey entityKey = pageContext.getEntityKey(); + final RestCall>.RestCallBuilder restCallBuilder = this.pageService.getRestService() + .getBuilder(GetLmsSetupDependencies.class) + .withURIVariable(API.PARAM_MODEL_ID, entityKey.modelId) + .withQueryParam(API.PARAM_BULK_ACTION_TYPE, BulkActionType.HARD_DELETE.name()); + + final Set dependencies = restCallBuilder + .call() + .getOrThrow(); + final List list = dependencies + .stream() + .sorted() + .collect(Collectors.toList()); + + this.pageService. staticListTableBuilder(list, null) + .withEmptyMessage(FORM_REPORT_NONE) + .withColumn(new ColumnDefinition<>( + "FORM_REPORT_LIST_TYPE", + FORM_REPORT_LIST_TYPE, + dep -> i18nSupport + .getText("sebserver.overall.types.entityType." + dep.self.entityType.name()))) + .withColumn(new ColumnDefinition<>( + "FORM_REPORT_LIST_NAME", + FORM_REPORT_LIST_NAME, + dep -> dep.name)) + .withColumn(new ColumnDefinition( + "FORM_REPORT_LIST_DESC", + FORM_REPORT_LIST_DESC, + dep -> dep.description)) + .compose(pageContext.copyOf(grid)); + + return () -> pageContext; + } catch (final Exception e) { + log.error("Error while trying to compose LMS Setup delete report page: ", e); + pageContext.notifyUnexpectedError(e); + throw e; + } + } + +} diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/content/exam/LmsSetupForm.java b/src/main/java/ch/ethz/seb/sebserver/gui/content/exam/LmsSetupForm.java index 3691cb72..8b7325aa 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/content/exam/LmsSetupForm.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/content/exam/LmsSetupForm.java @@ -105,11 +105,15 @@ public class LmsSetupForm implements TemplateComposer { private final PageService pageService; private final ResourceService resourceService; + private final LmsSetupDeletePopup lmsSetupDeletePopup; - protected LmsSetupForm(final PageService pageService) { + protected LmsSetupForm( + final PageService pageService, + final LmsSetupDeletePopup lmsSetupDeletePopup) { this.pageService = pageService; this.resourceService = pageService.getResourceService(); + this.lmsSetupDeletePopup = lmsSetupDeletePopup; } @Override @@ -305,6 +309,11 @@ public class LmsSetupForm implements TemplateComposer { .withEntityKey(entityKey) .publishIf(() -> modifyGrant && readonly && institutionActive) + .newAction(ActionDefinition.LMS_SETUP_DELETE) + .withEntityKey(entityKey) + .withExec(this.lmsSetupDeletePopup.deleteWizardFunction(pageContext)) + .publishIf(() -> writeGrant && readonly) + .newAction(ActionDefinition.LMS_SETUP_TEST) .withEntityKey(entityKey) .withExec(action -> LmsSetupForm.testLmsSetup(action, formHandle, restService)) diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/service/remote/webservice/api/institution/DeleteInstitution.java b/src/main/java/ch/ethz/seb/sebserver/gui/service/remote/webservice/api/institution/DeleteInstitution.java new file mode 100644 index 00000000..89f90ade --- /dev/null +++ b/src/main/java/ch/ethz/seb/sebserver/gui/service/remote/webservice/api/institution/DeleteInstitution.java @@ -0,0 +1,40 @@ +/* + * 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.service.remote.webservice.api.institution; + +import org.springframework.context.annotation.Lazy; +import org.springframework.http.HttpMethod; +import org.springframework.http.MediaType; +import org.springframework.stereotype.Component; + +import com.fasterxml.jackson.core.type.TypeReference; + +import ch.ethz.seb.sebserver.gbl.api.API; +import ch.ethz.seb.sebserver.gbl.api.EntityType; +import ch.ethz.seb.sebserver.gbl.model.EntityProcessingReport; +import ch.ethz.seb.sebserver.gbl.profile.GuiProfile; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestCall; + +@Lazy +@Component +@GuiProfile +public class DeleteInstitution extends RestCall { + + public DeleteInstitution() { + super(new TypeKey<>( + CallType.DELETE, + EntityType.EXAM, + new TypeReference() { + }), + HttpMethod.DELETE, + MediaType.APPLICATION_FORM_URLENCODED, + API.INSTITUTION_ENDPOINT + API.MODEL_ID_VAR_PATH_SEGMENT); + } + +} diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/service/remote/webservice/api/lmssetup/DeleteLmsSetup.java b/src/main/java/ch/ethz/seb/sebserver/gui/service/remote/webservice/api/lmssetup/DeleteLmsSetup.java new file mode 100644 index 00000000..10ef99e0 --- /dev/null +++ b/src/main/java/ch/ethz/seb/sebserver/gui/service/remote/webservice/api/lmssetup/DeleteLmsSetup.java @@ -0,0 +1,40 @@ +/* + * 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.service.remote.webservice.api.lmssetup; + +import org.springframework.context.annotation.Lazy; +import org.springframework.http.HttpMethod; +import org.springframework.http.MediaType; +import org.springframework.stereotype.Component; + +import com.fasterxml.jackson.core.type.TypeReference; + +import ch.ethz.seb.sebserver.gbl.api.API; +import ch.ethz.seb.sebserver.gbl.api.EntityType; +import ch.ethz.seb.sebserver.gbl.model.EntityProcessingReport; +import ch.ethz.seb.sebserver.gbl.profile.GuiProfile; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestCall; + +@Lazy +@Component +@GuiProfile +public class DeleteLmsSetup extends RestCall { + + public DeleteLmsSetup() { + super(new TypeKey<>( + CallType.DELETE, + EntityType.EXAM, + new TypeReference() { + }), + HttpMethod.DELETE, + MediaType.APPLICATION_FORM_URLENCODED, + API.LMS_SETUP_ENDPOINT + API.MODEL_ID_VAR_PATH_SEGMENT); + } + +} diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/bulkaction/BulkActionSupportDAO.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/bulkaction/BulkActionSupportDAO.java index 0d66dad2..cd01814b 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/bulkaction/BulkActionSupportDAO.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/bulkaction/BulkActionSupportDAO.java @@ -25,7 +25,6 @@ import ch.ethz.seb.sebserver.gbl.util.Result; import ch.ethz.seb.sebserver.webservice.servicelayer.bulkaction.impl.BulkAction; import ch.ethz.seb.sebserver.webservice.servicelayer.dao.ActivatableEntityDAO; import ch.ethz.seb.sebserver.webservice.servicelayer.dao.DAOLoggingSupport; -import ch.ethz.seb.sebserver.webservice.servicelayer.dao.EntityDAO; /** Defines overall DAO support for bulk-actions like activate, deactivate, delete... * @@ -71,17 +70,24 @@ public interface BulkActionSupportDAO { .get(error -> handleBulkActionError(error, all)) : Collections.emptyList(); case HARD_DELETE: - return (this instanceof EntityDAO) - ? ((EntityDAO) this).delete(all) - .map(BulkActionSupportDAO::transformResult) - .get(error -> handleBulkActionError(error, all)) - : Collections.emptyList(); + return delete(all) + .map(BulkActionSupportDAO::transformResult) + .get(error -> handleBulkActionError(error, all)); } // should never happen throw new UnsupportedOperationException("Unsupported Bulk Action: " + bulkAction); } + /** Use this to delete all entities defined by a set of EntityKey + * NOTE: the Set of EntityKey may contain EntityKey of other entity types like the concrete type of the DAO + * use extractPKsFromKeys to get a list of concrete primary keys for entities to delete + * + * @param all The Collection of EntityKey to delete + * @return Result referring a collection of all entities that has been deleted or refer to an error if + * happened */ + Result> delete(Set all); + /** This creates a collection of Results refer the given entity keys. * * @param keys Collection of entity keys to create Results from diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/bulkaction/impl/BulkActionServiceImpl.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/bulkaction/impl/BulkActionServiceImpl.java index b165d44c..ae9aa095 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/bulkaction/impl/BulkActionServiceImpl.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/bulkaction/impl/BulkActionServiceImpl.java @@ -255,7 +255,10 @@ public class BulkActionServiceImpl implements BulkActionService { case INSTITUTION: return Arrays.asList( this.supporter.get(EntityType.LMS_SETUP), + this.supporter.get(EntityType.CERTIFICATE), + this.supporter.get(EntityType.BATCH_ACTION), this.supporter.get(EntityType.USER), + this.supporter.get(EntityType.EXAM_TEMPLATE), this.supporter.get(EntityType.EXAM), this.supporter.get(EntityType.INDICATOR), this.supporter.get(EntityType.SEB_CLIENT_CONFIGURATION), diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/BatchActionDAO.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/BatchActionDAO.java index 88a5a242..79783268 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/BatchActionDAO.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/BatchActionDAO.java @@ -10,8 +10,9 @@ package ch.ethz.seb.sebserver.webservice.servicelayer.dao; import ch.ethz.seb.sebserver.gbl.model.BatchAction; import ch.ethz.seb.sebserver.gbl.util.Result; +import ch.ethz.seb.sebserver.webservice.servicelayer.bulkaction.BulkActionSupportDAO; -public interface BatchActionDAO extends EntityDAO { +public interface BatchActionDAO extends EntityDAO, BulkActionSupportDAO { /** This checks if there is a pending batch action to process next. * If so this reserves the pending batch action and mark it to be processed diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/CertificateDAO.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/CertificateDAO.java index d6cde6b2..964c08e4 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/CertificateDAO.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/CertificateDAO.java @@ -17,9 +17,10 @@ import ch.ethz.seb.sebserver.gbl.model.EntityKey; import ch.ethz.seb.sebserver.gbl.model.sebconfig.CertificateInfo; import ch.ethz.seb.sebserver.gbl.model.sebconfig.Certificates; import ch.ethz.seb.sebserver.gbl.util.Result; +import ch.ethz.seb.sebserver.webservice.servicelayer.bulkaction.BulkActionSupportDAO; /** Concrete EntityDAO interface of Certificate entities */ -public interface CertificateDAO { +public interface CertificateDAO extends BulkActionSupportDAO { Result getCertificate(final Long institutionId, String alias); diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/EntityDAO.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/EntityDAO.java index d213eb17..c6743587 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/EntityDAO.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/EntityDAO.java @@ -180,22 +180,7 @@ public interface EntityDAO { * @param keys Collection of EntityKey of various types * @return Set of id's (PK's) from the given key collection that match the concrete EntityType */ default Set extractPKsFromKeys(final Collection keys) { - try { - - if (keys == null) { - return Collections.emptySet(); - } - - final EntityType entityType = entityType(); - return keys - .stream() - .filter(key -> key.entityType == entityType) - .map(key -> Long.valueOf(key.modelId)) - .collect(Collectors.toSet()); - } catch (final Exception e) { - log.error("unexpected error while trying to extract PK's from EntityKey's : ", e); - return Collections.emptySet(); - } + return extractPKsFromKeys(keys, entityType()); } /** Context based utility method to extract a set of id's (PK) from a collection of various EntityKey @@ -211,4 +196,32 @@ public interface EntityDAO { return new ArrayList<>(extractPKsFromKeys(keys)); } + /** Context based utility method to extract a set of id's (PK) from a collection of various EntityKey + * This uses the EntityType defined by this instance to filter all EntityKey by the given type and + * convert the matching EntityKey's to id's (PK's) + * + * Use this if you need to transform a Collection of EntityKey into a extracted Set of id's of a specified + * EntityType + * + * @param keys Collection of EntityKey of various types + * @param entityType the entity type of the keys to extract + * @return Set of id's (PK's) from the given key collection that match the concrete EntityType */ + static Set extractPKsFromKeys(final Collection keys, final EntityType entityType) { + try { + + if (keys == null) { + return Collections.emptySet(); + } + + return keys + .stream() + .filter(key -> key.entityType == entityType) + .map(key -> Long.valueOf(key.modelId)) + .collect(Collectors.toSet()); + } catch (final Exception e) { + log.error("unexpected error while trying to extract PK's from EntityKey's : ", e); + return Collections.emptySet(); + } + } + } diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/BatchActionDAOImpl.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/BatchActionDAOImpl.java index 2b04a8e5..b3c23cec 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/BatchActionDAOImpl.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/BatchActionDAOImpl.java @@ -35,6 +35,7 @@ import ch.ethz.seb.sebserver.gbl.api.EntityType; import ch.ethz.seb.sebserver.gbl.api.JSONMapper; import ch.ethz.seb.sebserver.gbl.model.BatchAction; import ch.ethz.seb.sebserver.gbl.model.Domain; +import ch.ethz.seb.sebserver.gbl.model.EntityDependency; import ch.ethz.seb.sebserver.gbl.model.EntityKey; import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile; import ch.ethz.seb.sebserver.gbl.util.Result; @@ -43,6 +44,7 @@ import ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.BatchActionRecord import ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.BatchActionRecordMapper; import ch.ethz.seb.sebserver.webservice.datalayer.batis.model.AdditionalAttributeRecord; import ch.ethz.seb.sebserver.webservice.datalayer.batis.model.BatchActionRecord; +import ch.ethz.seb.sebserver.webservice.servicelayer.bulkaction.impl.BulkAction; import ch.ethz.seb.sebserver.webservice.servicelayer.dao.AdditionalAttributesDAO; import ch.ethz.seb.sebserver.webservice.servicelayer.dao.BatchActionDAO; import ch.ethz.seb.sebserver.webservice.servicelayer.dao.DAOLoggingSupport; @@ -322,6 +324,17 @@ public class BatchActionDAOImpl implements BatchActionDAO { .onError(TransactionHandler::rollback); } + @Override + @Transactional(readOnly = true) + public Set getDependencies(final BulkAction bulkAction) { + // all of institution + if (bulkAction.sourceType == EntityType.INSTITUTION) { + return getDependencies(bulkAction, this::allIdsOfInstitution); + } + + return Collections.emptySet(); + } + @Override @Transactional public Result> delete(final Set all) { @@ -419,4 +432,19 @@ public class BatchActionDAOImpl implements BatchActionDAO { } } + private Result> allIdsOfInstitution(final EntityKey institutionKey) { + return Result.tryCatch(() -> this.batchActionRecordMapper.selectByExample() + .where(BatchActionRecordDynamicSqlSupport.institutionId, + isEqualTo(Long.valueOf(institutionKey.modelId))) + .build() + .execute() + .stream() + .map(rec -> new EntityDependency( + institutionKey, + new EntityKey(rec.getId(), EntityType.BATCH_ACTION), + rec.getActionType(), + rec.getOwner())) + .collect(Collectors.toList())); + } + } diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/CertificateDAOImpl.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/CertificateDAOImpl.java index 1b80ef9e..52aeacec 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/CertificateDAOImpl.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/CertificateDAOImpl.java @@ -8,6 +8,9 @@ package ch.ethz.seb.sebserver.webservice.servicelayer.dao.impl; +import static org.mybatis.dynamic.sql.SqlBuilder.isEqualTo; +import static org.mybatis.dynamic.sql.SqlBuilder.isIn; + import java.io.ByteArrayInputStream; import java.security.KeyStoreException; import java.security.PrivateKey; @@ -21,6 +24,7 @@ import java.util.Collections; import java.util.EnumSet; import java.util.List; import java.util.NoSuchElementException; +import java.util.Set; import java.util.stream.Collectors; import org.apache.commons.io.IOUtils; @@ -43,6 +47,7 @@ import org.springframework.transaction.annotation.Transactional; import ch.ethz.seb.sebserver.gbl.Constants; import ch.ethz.seb.sebserver.gbl.api.APIMessage.FieldValidationException; import ch.ethz.seb.sebserver.gbl.api.EntityType; +import ch.ethz.seb.sebserver.gbl.model.EntityDependency; import ch.ethz.seb.sebserver.gbl.model.EntityKey; import ch.ethz.seb.sebserver.gbl.model.sebconfig.CertificateInfo; import ch.ethz.seb.sebserver.gbl.model.sebconfig.CertificateInfo.CertificateType; @@ -54,7 +59,9 @@ import ch.ethz.seb.sebserver.gbl.util.Utils; import ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.CertificateRecordDynamicSqlSupport; import ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.CertificateRecordMapper; import ch.ethz.seb.sebserver.webservice.datalayer.batis.model.CertificateRecord; +import ch.ethz.seb.sebserver.webservice.servicelayer.bulkaction.impl.BulkAction; import ch.ethz.seb.sebserver.webservice.servicelayer.dao.CertificateDAO; +import ch.ethz.seb.sebserver.webservice.servicelayer.dao.EntityDAO; import ch.ethz.seb.sebserver.webservice.servicelayer.dao.ResourceNotFoundException; import ch.ethz.seb.sebserver.webservice.servicelayer.dao.TransactionHandler; @@ -76,6 +83,11 @@ public class CertificateDAOImpl implements CertificateDAO { this.cryptor = cryptor; } + @Override + public EntityType entityType() { + return EntityType.CERTIFICATE; + } + @Override @Transactional(readOnly = true) public Result getCertificate(final Long institutionId, final String alias) { @@ -135,6 +147,17 @@ public class CertificateDAOImpl implements CertificateDAO { .onError(TransactionHandler::rollback); } + @Override + @Transactional(readOnly = true) + public Set getDependencies(final BulkAction bulkAction) { + // all of institution + if (bulkAction.sourceType == EntityType.INSTITUTION) { + return getDependencies(bulkAction, this::allIdsOfInstitution); + } + + return Collections.emptySet(); + } + @Override @Transactional(readOnly = true) public Result> getAllIdentityAlias(final Long institutionId) { @@ -177,6 +200,28 @@ public class CertificateDAOImpl implements CertificateDAO { .collect(Collectors.toList())); } + @Override + @Transactional + public Result> delete(final Set all) { + return Result.tryCatch(() -> { + + final List ids = new ArrayList<>(EntityDAO.extractPKsFromKeys(all, EntityType.CERTIFICATE)); + + if (ids.isEmpty()) { + return Collections.emptyList(); + } + + this.certificateRecordMapper.deleteByExample() + .where(CertificateRecordDynamicSqlSupport.id, isIn(ids)) + .build() + .execute(); + + return ids.stream() + .map(id -> new EntityKey(id, EntityType.CERTIFICATE)) + .collect(Collectors.toList()); + }); + } + @Override public String extractAlias(final X509Certificate certificate, final String alias) { if (StringUtils.isNotBlank(alias)) { @@ -415,4 +460,19 @@ public class CertificateDAOImpl implements CertificateDAO { .flatMap(input -> this.cryptor.loadKeyStore(input)); } + private Result> allIdsOfInstitution(final EntityKey institutionKey) { + return Result.tryCatch(() -> this.certificateRecordMapper.selectByExample() + .where(CertificateRecordDynamicSqlSupport.institutionId, + isEqualTo(Long.valueOf(institutionKey.modelId))) + .build() + .execute() + .stream() + .map(rec -> new EntityDependency( + institutionKey, + new EntityKey(rec.getId(), EntityType.CERTIFICATE), + rec.getAliases(), + rec.getAliases())) + .collect(Collectors.toList())); + } + } diff --git a/src/main/resources/messages.properties b/src/main/resources/messages.properties index d73992df..9c0a9821 100644 --- a/src/main/resources/messages.properties +++ b/src/main/resources/messages.properties @@ -196,6 +196,18 @@ sebserver.institution.form.logoImage.tooltip=The Image that is shown as a logo i sebserver.institution.form.logoImage.unsupportedFileType=The selected file is not supported. Supported are: PNG and JPG +sebserver.institution.action.delete=Delete Institution +sebserver.institution.delete.form.title=Delete Institution +sebserver.institution.delete.form.info=Please Note:
    This deletes the institution and all related object like LMS Setup, exams and local import of a course
    or quiz in SEB Server that belongs to this institution
    This will not delete any course or quiz on a Learning Management System (LMS). +sebserver.institution.delete.report.info=The following dependencies will be deleted within this institution deletion.
Please check them carefully before delete. +sebserver.institution.delete.report.list.type=Type +sebserver.institution.delete.report.list.name=Name +sebserver.institution.delete.report.list.description=Description +sebserver.institution.delete.action.delete=Delete All +sebserver.institution.delete.confirm.title=Deletion Successful +sebserver.institution.delete.confirm.message=The institution ({0}) was successfully deleted.
Also the following number of dependencies where successfully deleted: {1}.

And there where {2} errors. +sebserver.institution.delete.report.list.empty=No dependencies will be deleted. + ################################ # User Account ################################ @@ -368,6 +380,18 @@ sebserver.lmssetup.form.proxy.password=Proxy Password sebserver.lmssetup.form.proxy.password.tooltip=Proxy authentication password, needed if the proxy requests authentication sebserver.lmssetup.form.proxy.auth-credentials.tooltip=The proxy authentication credentials (name and password)
to authenticate the connection within the proxy server +sebserver.lmssetup.action.delete=Delete LMS Setup +sebserver.lmssetup.delete.form.title=Delete LMS Setup +sebserver.lmssetup.delete.form.info=Please Note:
    This deletes the LMS Setup and all exams and local import of a
     course or quiz in SEB Server that belongs to this LMS Setup
    This will not delete any course or quiz on a Learning Management System (LMS). +sebserver.lmssetup.delete.report.info=The following dependencies will be deleted within this LMS Setup deletion.
Please check them carefully before delete. +sebserver.lmssetup.delete.report.list.type=Type +sebserver.lmssetup.delete.report.list.name=Name +sebserver.lmssetup.delete.report.list.description=Description +sebserver.lmssetup.delete.action.delete=Delete All +sebserver.lmssetup.delete.confirm.title=Deletion Successful +sebserver.lmssetup.delete.confirm.message=The LMS Setup ({0}) was successfully deleted.
Also the following number of dependencies where successfully deleted: {1}.

And there where {2} errors. +sebserver.lmssetup.delete.report.list.empty=No dependencies will be deleted. + ################################ #LMS Exam ################################ diff --git a/src/test/java/ch/ethz/seb/sebserver/gui/integration/UseCasesIntegrationTest.java b/src/test/java/ch/ethz/seb/sebserver/gui/integration/UseCasesIntegrationTest.java index db194e55..ddca14d0 100644 --- a/src/test/java/ch/ethz/seb/sebserver/gui/integration/UseCasesIntegrationTest.java +++ b/src/test/java/ch/ethz/seb/sebserver/gui/integration/UseCasesIntegrationTest.java @@ -2384,18 +2384,18 @@ public class UseCasesIntegrationTest extends GuiIntegrationTest { .collect(Collectors.toList()); assertEquals( - "[CLIENT_CONNECTION, " - + "CLIENT_CONNECTION, " - + "CLIENT_CONNECTION, " - + "CLIENT_CONNECTION, " - + "CONFIGURATION_NODE, " - + "CONFIGURATION_NODE, " - + "CONFIGURATION_NODE, " - + "CONFIGURATION_NODE, " - + "EXAM, " - + "EXAM_CONFIGURATION_MAP, " + "[EXAM, " + "INDICATOR, " - + "INDICATOR]", + + "INDICATOR, " + + "EXAM_CONFIGURATION_MAP, " + + "CONFIGURATION_NODE, " + + "CONFIGURATION_NODE, " + + "CONFIGURATION_NODE, " + + "CONFIGURATION_NODE, " + + "CLIENT_CONNECTION, " + + "CLIENT_CONNECTION, " + + "CLIENT_CONNECTION, " + + "CLIENT_CONNECTION]", dependencies.stream().map(dep -> dep.self.entityType).collect(Collectors.toList()).toString()); // check that the user is owner of all depending exams and configurations @@ -2433,14 +2433,14 @@ public class UseCasesIntegrationTest extends GuiIntegrationTest { .collect(Collectors.toList()); assertEquals( - "[CLIENT_CONNECTION, " - + "CLIENT_CONNECTION, " - + "CLIENT_CONNECTION, " - + "CLIENT_CONNECTION, " - + "EXAM, " - + "EXAM_CONFIGURATION_MAP, " + "[EXAM, " + "INDICATOR, " - + "INDICATOR]", + + "INDICATOR, " + + "EXAM_CONFIGURATION_MAP, " + + "CLIENT_CONNECTION, " + + "CLIENT_CONNECTION, " + + "CLIENT_CONNECTION, " + + "CLIENT_CONNECTION]", dependencies.stream().map(dep -> dep.self.entityType).collect(Collectors.toList()).toString()); // only with configuration dependencies @@ -2455,11 +2455,7 @@ public class UseCasesIntegrationTest extends GuiIntegrationTest { .collect(Collectors.toList()); assertEquals( - "[CONFIGURATION_NODE, " - + "CONFIGURATION_NODE, " - + "CONFIGURATION_NODE, " - + "CONFIGURATION_NODE, " - + "EXAM_CONFIGURATION_MAP]", + "[EXAM_CONFIGURATION_MAP, CONFIGURATION_NODE, CONFIGURATION_NODE, CONFIGURATION_NODE, CONFIGURATION_NODE]", dependencies.stream().map(dep -> dep.self.entityType).collect(Collectors.toList()).toString()); // only with exam and configuration dependencies @@ -2475,18 +2471,18 @@ public class UseCasesIntegrationTest extends GuiIntegrationTest { .collect(Collectors.toList()); assertEquals( - "[CLIENT_CONNECTION, " - + "CLIENT_CONNECTION, " - + "CLIENT_CONNECTION, " - + "CLIENT_CONNECTION, " - + "CONFIGURATION_NODE, " - + "CONFIGURATION_NODE, " - + "CONFIGURATION_NODE, " - + "CONFIGURATION_NODE, " - + "EXAM, " - + "EXAM_CONFIGURATION_MAP, " + "[EXAM, " + "INDICATOR, " - + "INDICATOR]", + + "INDICATOR, " + + "EXAM_CONFIGURATION_MAP, " + + "CONFIGURATION_NODE, " + + "CONFIGURATION_NODE, " + + "CONFIGURATION_NODE, " + + "CONFIGURATION_NODE, " + + "CLIENT_CONNECTION, " + + "CLIENT_CONNECTION, " + + "CLIENT_CONNECTION, " + + "CLIENT_CONNECTION]", dependencies.stream().map(dep -> dep.self.entityType).collect(Collectors.toList()).toString()); }