SEBSERV-133 impl LMS Setup and Institution
This commit is contained in:
parent
060e68bb7b
commit
938dafc0dd
21 changed files with 836 additions and 70 deletions
|
@ -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<EntityType> 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<Collection<APIMessage>> TYPE_REFERENCE_API_MESSAGE =
|
||||
|
|
|
@ -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<EntityDependency>, ModelIdAware {
|
||||
|
||||
|
@ -113,9 +115,16 @@ public class EntityDependency implements Comparable<EntityDependency>, 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;
|
||||
}
|
||||
|
|
|
@ -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<EntityK
|
|||
return -1;
|
||||
}
|
||||
|
||||
final int compareTo = this.entityType.name().compareTo(other.entityType.name());
|
||||
final int compareTo = Constants.DEFAULT_ENGLISH_COLLATOR.compare(
|
||||
this.entityType.name(),
|
||||
other.entityType.name());
|
||||
//this.entityType.name().compareTo(other.entityType.name());
|
||||
|
||||
if (compareTo == 0) {
|
||||
return this.modelId.compareTo(other.modelId);
|
||||
} else {
|
||||
|
|
|
@ -73,6 +73,11 @@ public enum ActionDefinition {
|
|||
ImageIcon.SWITCH,
|
||||
PageStateDefinitionImpl.INSTITUTION_LIST,
|
||||
ActionCategory.INSTITUTION_LIST),
|
||||
INSTITUTION_DELETE(
|
||||
new LocTextKey("sebserver.institution.action.delete"),
|
||||
ImageIcon.DELETE,
|
||||
PageStateDefinitionImpl.INSTITUTION_VIEW,
|
||||
ActionCategory.FORM),
|
||||
|
||||
USER_ACCOUNT_VIEW_LIST(
|
||||
new LocTextKey("sebserver.useraccount.action.list"),
|
||||
|
@ -215,6 +220,11 @@ public enum ActionDefinition {
|
|||
ImageIcon.SWITCH,
|
||||
PageStateDefinitionImpl.LMS_SETUP_LIST,
|
||||
ActionCategory.LMS_SETUP_LIST),
|
||||
LMS_SETUP_DELETE(
|
||||
new LocTextKey("sebserver.lmssetup.action.delete"),
|
||||
ImageIcon.DELETE,
|
||||
PageStateDefinitionImpl.LMS_SETUP_VIEW,
|
||||
ActionCategory.FORM),
|
||||
|
||||
QUIZ_DISCOVERY_VIEW_LIST(
|
||||
new LocTextKey("sebserver.quizdiscovery.action.list"),
|
||||
|
|
|
@ -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.admin;
|
||||
|
||||
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.Institution;
|
||||
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.institution.DeleteInstitution;
|
||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.institution.GetInstitution;
|
||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.institution.GetInstitutionDependency;
|
||||
import ch.ethz.seb.sebserver.gui.table.ColumnDefinition;
|
||||
import ch.ethz.seb.sebserver.gui.widget.WidgetFactory.CustomVariant;
|
||||
|
||||
@Lazy
|
||||
@Component
|
||||
@GuiProfile
|
||||
public class InstitutionDeletePopup {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(InstitutionDeletePopup.class);
|
||||
|
||||
private final static LocTextKey FORM_TITLE =
|
||||
new LocTextKey("sebserver.institution.delete.form.title");
|
||||
private final static LocTextKey FORM_INFO =
|
||||
new LocTextKey("sebserver.institution.delete.form.info");
|
||||
private final static LocTextKey FORM_REPORT_INFO =
|
||||
new LocTextKey("sebserver.institution.delete.report.info");
|
||||
private final static LocTextKey FORM_REPORT_LIST_TYPE =
|
||||
new LocTextKey("sebserver.institution.delete.report.list.type");
|
||||
private final static LocTextKey FORM_REPORT_LIST_NAME =
|
||||
new LocTextKey("sebserver.institution.delete.report.list.name");
|
||||
private final static LocTextKey FORM_REPORT_LIST_DESC =
|
||||
new LocTextKey("sebserver.institution.delete.report.list.description");
|
||||
private final static LocTextKey FORM_REPORT_NONE =
|
||||
new LocTextKey("sebserver.institution.delete.report.list.empty");
|
||||
|
||||
private final static LocTextKey ACTION_DELETE =
|
||||
new LocTextKey("sebserver.institution.delete.action.delete");
|
||||
|
||||
private final static LocTextKey DELETE_CONFIRM_TITLE =
|
||||
new LocTextKey("sebserver.institution.delete.confirm.title");
|
||||
private final static LocTextKey DELETE_ERROR_CONSISTENCY =
|
||||
new LocTextKey("sebserver.institution.action.delete.consistency.error");
|
||||
|
||||
private final PageService pageService;
|
||||
|
||||
protected InstitutionDeletePopup(final PageService pageService) {
|
||||
this.pageService = pageService;
|
||||
}
|
||||
|
||||
public Function<PageAction, PageAction> deleteWizardFunction(final PageContext pageContext) {
|
||||
return action -> {
|
||||
|
||||
final ModalInputWizard<PageContext> wizard =
|
||||
new ModalInputWizard<PageContext>(
|
||||
action.pageContext().getParent().getShell(),
|
||||
this.pageService.getWidgetFactory())
|
||||
.setVeryLargeDialogWidth();
|
||||
|
||||
final String page1Id = "DELETE_PAGE";
|
||||
final Predicate<PageContext> callback = pc -> doDelete(this.pageService, pc);
|
||||
final BiFunction<PageContext, Composite, Supplier<PageContext>> composePage1 =
|
||||
(prefPageContext, content) -> composeDeleteDialog(content,
|
||||
(prefPageContext != null) ? prefPageContext : pageContext);
|
||||
|
||||
final WizardPage<PageContext> 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<EntityProcessingReport>.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<EntityProcessingReport> 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<EntityKey> 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<PageContext> 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<Set<EntityDependency>>.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<EntityDependency> dependencies = restCallBuilder
|
||||
.call()
|
||||
.getOrThrow();
|
||||
final List<EntityDependency> list = dependencies
|
||||
.stream()
|
||||
.sorted()
|
||||
.collect(Collectors.toList());
|
||||
|
||||
this.pageService.<EntityDependency> 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<EntityDependency>(
|
||||
"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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -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)
|
||||
|
|
|
@ -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()));
|
||||
}
|
||||
|
|
|
@ -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())
|
||||
|
|
|
@ -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<PageAction, PageAction> deleteWizardFunction(final PageContext pageContext) {
|
||||
return action -> {
|
||||
|
||||
final ModalInputWizard<PageContext> wizard =
|
||||
new ModalInputWizard<PageContext>(
|
||||
action.pageContext().getParent().getShell(),
|
||||
this.pageService.getWidgetFactory())
|
||||
.setVeryLargeDialogWidth();
|
||||
|
||||
final String page1Id = "DELETE_PAGE";
|
||||
final Predicate<PageContext> callback = pc -> doDelete(this.pageService, pc);
|
||||
final BiFunction<PageContext, Composite, Supplier<PageContext>> composePage1 =
|
||||
(prefPageContext, content) -> composeDeleteDialog(content,
|
||||
(prefPageContext != null) ? prefPageContext : pageContext);
|
||||
|
||||
final WizardPage<PageContext> 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<EntityProcessingReport>.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<EntityProcessingReport> 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<EntityKey> 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<PageContext> 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<Set<EntityDependency>>.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<EntityDependency> dependencies = restCallBuilder
|
||||
.call()
|
||||
.getOrThrow();
|
||||
final List<EntityDependency> list = dependencies
|
||||
.stream()
|
||||
.sorted()
|
||||
.collect(Collectors.toList());
|
||||
|
||||
this.pageService.<EntityDependency> 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<EntityDependency>(
|
||||
"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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -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))
|
||||
|
|
|
@ -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<EntityProcessingReport> {
|
||||
|
||||
public DeleteInstitution() {
|
||||
super(new TypeKey<>(
|
||||
CallType.DELETE,
|
||||
EntityType.EXAM,
|
||||
new TypeReference<EntityProcessingReport>() {
|
||||
}),
|
||||
HttpMethod.DELETE,
|
||||
MediaType.APPLICATION_FORM_URLENCODED,
|
||||
API.INSTITUTION_ENDPOINT + API.MODEL_ID_VAR_PATH_SEGMENT);
|
||||
}
|
||||
|
||||
}
|
|
@ -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<EntityProcessingReport> {
|
||||
|
||||
public DeleteLmsSetup() {
|
||||
super(new TypeKey<>(
|
||||
CallType.DELETE,
|
||||
EntityType.EXAM,
|
||||
new TypeReference<EntityProcessingReport>() {
|
||||
}),
|
||||
HttpMethod.DELETE,
|
||||
MediaType.APPLICATION_FORM_URLENCODED,
|
||||
API.LMS_SETUP_ENDPOINT + API.MODEL_ID_VAR_PATH_SEGMENT);
|
||||
}
|
||||
|
||||
}
|
|
@ -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<T extends Entity> {
|
|||
.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<Collection<EntityKey>> delete(Set<EntityKey> all);
|
||||
|
||||
/** This creates a collection of Results refer the given entity keys.
|
||||
*
|
||||
* @param keys Collection of entity keys to create Results from
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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<BatchAction, BatchAction> {
|
||||
public interface BatchActionDAO extends EntityDAO<BatchAction, BatchAction>, BulkActionSupportDAO<BatchAction> {
|
||||
|
||||
/** 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
|
||||
|
|
|
@ -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<CertificateInfo> {
|
||||
|
||||
Result<Certificate> getCertificate(final Long institutionId, String alias);
|
||||
|
||||
|
|
|
@ -180,22 +180,7 @@ public interface EntityDAO<T extends Entity, M extends ModelIdAware> {
|
|||
* @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<Long> extractPKsFromKeys(final Collection<EntityKey> 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<T extends Entity, M extends ModelIdAware> {
|
|||
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<Long> extractPKsFromKeys(final Collection<EntityKey> 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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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<EntityDependency> getDependencies(final BulkAction bulkAction) {
|
||||
// all of institution
|
||||
if (bulkAction.sourceType == EntityType.INSTITUTION) {
|
||||
return getDependencies(bulkAction, this::allIdsOfInstitution);
|
||||
}
|
||||
|
||||
return Collections.emptySet();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public Result<Collection<EntityKey>> delete(final Set<EntityKey> all) {
|
||||
|
@ -419,4 +432,19 @@ public class BatchActionDAOImpl implements BatchActionDAO {
|
|||
}
|
||||
}
|
||||
|
||||
private Result<Collection<EntityDependency>> 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()));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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<Certificate> 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<EntityDependency> 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<Collection<String>> getAllIdentityAlias(final Long institutionId) {
|
||||
|
@ -177,6 +200,28 @@ public class CertificateDAOImpl implements CertificateDAO {
|
|||
.collect(Collectors.toList()));
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public Result<Collection<EntityKey>> delete(final Set<EntityKey> all) {
|
||||
return Result.tryCatch(() -> {
|
||||
|
||||
final List<Long> 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<Collection<EntityDependency>> 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()));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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:<br/> This deletes the institution and all related object like LMS Setup, exams and local import of a course<br/> or quiz in SEB Server that belongs to this institution<br/> 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.<br/>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.<br/>Also the following number of dependencies where successfully deleted: {1}.<br/><br/>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)<br/>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:<br/> This deletes the LMS Setup and all exams and local import of a<br/> course or quiz in SEB Server that belongs to this LMS Setup<br/> 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.<br/>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.<br/>Also the following number of dependencies where successfully deleted: {1}.<br/><br/>And there where {2} errors.
|
||||
sebserver.lmssetup.delete.report.list.empty=No dependencies will be deleted.
|
||||
|
||||
################################
|
||||
#LMS Exam
|
||||
################################
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue