SEBSERV-462 implement force delete
This commit is contained in:
parent
8484e2f6b5
commit
ea94adf29b
11 changed files with 219 additions and 92 deletions
|
@ -81,6 +81,8 @@ public final class API {
|
||||||
public static final String TOGGLE_ACTIVITY_PATH_SEGMENT = "/toggle-activity";
|
public static final String TOGGLE_ACTIVITY_PATH_SEGMENT = "/toggle-activity";
|
||||||
public static final String INACTIVE_PATH_SEGMENT = "/inactive";
|
public static final String INACTIVE_PATH_SEGMENT = "/inactive";
|
||||||
|
|
||||||
|
public static final String FORCE_PATH_SEGMENT = "/force";
|
||||||
|
|
||||||
public static final String DEPENDENCY_PATH_SEGMENT = "/dependency";
|
public static final String DEPENDENCY_PATH_SEGMENT = "/dependency";
|
||||||
|
|
||||||
public static final String PASSWORD_PATH_SEGMENT = "/password";
|
public static final String PASSWORD_PATH_SEGMENT = "/password";
|
||||||
|
|
|
@ -16,6 +16,7 @@ import java.util.function.Predicate;
|
||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.ForceDeleteExam;
|
||||||
import org.apache.commons.text.StringEscapeUtils;
|
import org.apache.commons.text.StringEscapeUtils;
|
||||||
import org.eclipse.swt.layout.GridData;
|
import org.eclipse.swt.layout.GridData;
|
||||||
import org.eclipse.swt.widgets.Composite;
|
import org.eclipse.swt.widgets.Composite;
|
||||||
|
@ -62,28 +63,19 @@ public class ExamDeletePopup {
|
||||||
|
|
||||||
private static final Logger log = LoggerFactory.getLogger(ExamDeletePopup.class);
|
private static final Logger log = LoggerFactory.getLogger(ExamDeletePopup.class);
|
||||||
|
|
||||||
private final static LocTextKey FORM_TITLE =
|
private final static LocTextKey FORM_TITLE = new LocTextKey("sebserver.exam.delete.form.title");
|
||||||
new LocTextKey("sebserver.exam.delete.form.title");
|
private final static LocTextKey FORM_INFO = new LocTextKey("sebserver.exam.delete.form.info");
|
||||||
private final static LocTextKey FORM_INFO =
|
private final static LocTextKey FORM_REPORT_INFO = new LocTextKey("sebserver.exam.delete.report.info");
|
||||||
new LocTextKey("sebserver.exam.delete.form.info");
|
private final static LocTextKey FORM_REPORT_LIST_TYPE = new LocTextKey("sebserver.exam.delete.report.list.type");
|
||||||
private final static LocTextKey FORM_REPORT_INFO =
|
private final static LocTextKey FORM_REPORT_LIST_NAME = new LocTextKey("sebserver.exam.delete.report.list.name");
|
||||||
new LocTextKey("sebserver.exam.delete.report.info");
|
private final static LocTextKey FORM_REPORT_LIST_DESC = new LocTextKey("sebserver.exam.delete.report.list.description");
|
||||||
private final static LocTextKey FORM_REPORT_LIST_TYPE =
|
private final static LocTextKey FORM_REPORT_NONE = new LocTextKey("sebserver.exam.delete.report.list.empty");
|
||||||
new LocTextKey("sebserver.exam.delete.report.list.type");
|
private final static LocTextKey ACTION_DELETE = new LocTextKey("sebserver.exam.delete.action.delete");
|
||||||
private final static LocTextKey FORM_REPORT_LIST_NAME =
|
private final static LocTextKey DELETE_CONFIRM_TITLE = new LocTextKey("sebserver.exam.delete.confirm.title");
|
||||||
new LocTextKey("sebserver.exam.delete.report.list.name");
|
private final static LocTextKey DELETE_ERROR_CONSISTENCY = new LocTextKey("sebserver.exam.action.delete.consistency.error");
|
||||||
private final static LocTextKey FORM_REPORT_LIST_DESC =
|
private final static LocTextKey FORCE_DELETE_CONFIRM_TITLE = new LocTextKey("sebserver.exam.action.delete.force.confirm.title");
|
||||||
new LocTextKey("sebserver.exam.delete.report.list.description");
|
private final static LocTextKey FORCE_DELETE_CONFIRM_ACTION = new LocTextKey("sebserver.exam.action.delete.force.confirm.action");
|
||||||
private final static LocTextKey FORM_REPORT_NONE =
|
private final static LocTextKey FORCE_DELETE_CONFIRM = new LocTextKey("sebserver.exam.action.delete.force.confirm");
|
||||||
new LocTextKey("sebserver.exam.delete.report.list.empty");
|
|
||||||
|
|
||||||
private final static LocTextKey ACTION_DELETE =
|
|
||||||
new LocTextKey("sebserver.exam.delete.action.delete");
|
|
||||||
|
|
||||||
private final static LocTextKey DELETE_CONFIRM_TITLE =
|
|
||||||
new LocTextKey("sebserver.exam.delete.confirm.title");
|
|
||||||
private final static LocTextKey DELETE_ERROR_CONSISTENCY =
|
|
||||||
new LocTextKey("sebserver.exam.action.delete.consistency.error");
|
|
||||||
|
|
||||||
private final PageService pageService;
|
private final PageService pageService;
|
||||||
|
|
||||||
|
@ -131,12 +123,12 @@ public class ExamDeletePopup {
|
||||||
.call()
|
.call()
|
||||||
.getOrThrow();
|
.getOrThrow();
|
||||||
|
|
||||||
final RestCall<EntityProcessingReport>.RestCallBuilder restCallBuilder = this.pageService.getRestService()
|
final Result<EntityProcessingReport> deleteCall = this.pageService.getRestService()
|
||||||
.getBuilder(DeleteExam.class)
|
.getBuilder(DeleteExam.class)
|
||||||
.withURIVariable(API.PARAM_MODEL_ID, entityKey.modelId)
|
.withURIVariable(API.PARAM_MODEL_ID, entityKey.modelId)
|
||||||
.withQueryParam(API.PARAM_BULK_ACTION_TYPE, BulkActionType.HARD_DELETE.name());
|
.withQueryParam(API.PARAM_BULK_ACTION_TYPE, BulkActionType.HARD_DELETE.name())
|
||||||
|
.call();
|
||||||
|
|
||||||
final Result<EntityProcessingReport> deleteCall = restCallBuilder.call();
|
|
||||||
if (deleteCall.hasError()) {
|
if (deleteCall.hasError()) {
|
||||||
final Exception error = deleteCall.getError();
|
final Exception error = deleteCall.getError();
|
||||||
if (error instanceof RestCallError) {
|
if (error instanceof RestCallError) {
|
||||||
|
@ -146,33 +138,33 @@ public class ExamDeletePopup {
|
||||||
.findFirst()
|
.findFirst()
|
||||||
.orElse(null);
|
.orElse(null);
|
||||||
if (message != null && ErrorMessage.INTEGRITY_VALIDATION.isOf(message)) {
|
if (message != null && ErrorMessage.INTEGRITY_VALIDATION.isOf(message)) {
|
||||||
pageContext.publishPageMessage(new PageMessageException(DELETE_ERROR_CONSISTENCY));
|
pageContext.applyConfirmDialog(
|
||||||
return false;
|
FORCE_DELETE_CONFIRM_TITLE,
|
||||||
|
FORCE_DELETE_CONFIRM,
|
||||||
|
FORCE_DELETE_CONFIRM_ACTION,
|
||||||
|
confirm -> {
|
||||||
|
if (confirm) {
|
||||||
|
final Result<EntityProcessingReport> forceDeleteCall = this.pageService.getRestService()
|
||||||
|
.getBuilder(ForceDeleteExam.class)
|
||||||
|
.withURIVariable(API.PARAM_MODEL_ID, entityKey.modelId)
|
||||||
|
.withQueryParam(API.PARAM_BULK_ACTION_TYPE, BulkActionType.HARD_DELETE.name())
|
||||||
|
.call();
|
||||||
|
|
||||||
|
if (forceDeleteCall.hasError()) {
|
||||||
|
pageContext.notifyUnexpectedError(forceDeleteCall.getError());
|
||||||
|
} else {
|
||||||
|
showSuccessDialog(pageContext, examToDelete, forceDeleteCall.getOrThrow(), entityKey);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
final EntityProcessingReport report = deleteCall.getOrThrow();
|
showSuccessDialog(pageContext, examToDelete, deleteCall.getOrThrow(), entityKey);
|
||||||
|
|
||||||
final PageAction action = this.pageService.pageActionBuilder(pageContext)
|
|
||||||
.newAction(ActionDefinition.EXAM_VIEW_LIST)
|
|
||||||
.create();
|
|
||||||
|
|
||||||
this.pageService.firePageEvent(
|
|
||||||
new ActionEvent(action),
|
|
||||||
action.pageContext());
|
|
||||||
|
|
||||||
final String examName = StringEscapeUtils.escapeXml11(examToDelete.toName().name);
|
|
||||||
final List<EntityKey> dependencies = report.results.stream()
|
|
||||||
.filter(key -> !key.equals(entityKey))
|
|
||||||
.collect(Collectors.toList());
|
|
||||||
pageContext.publishPageMessage(
|
|
||||||
DELETE_CONFIRM_TITLE,
|
|
||||||
new LocTextKey(
|
|
||||||
"sebserver.exam.delete.confirm.message",
|
|
||||||
examName,
|
|
||||||
dependencies.size(),
|
|
||||||
(report.errors.isEmpty()) ? "no" : String.valueOf((report.errors.size()))));
|
|
||||||
return true;
|
return true;
|
||||||
} catch (final Exception e) {
|
} catch (final Exception e) {
|
||||||
log.error("Unexpected error while trying to delete Exam:", e);
|
log.error("Unexpected error while trying to delete Exam:", e);
|
||||||
|
@ -181,6 +173,34 @@ public class ExamDeletePopup {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void showSuccessDialog(
|
||||||
|
final PageContext pageContext,
|
||||||
|
final Exam examToDelete,
|
||||||
|
final EntityProcessingReport report,
|
||||||
|
final EntityKey entityKey) {
|
||||||
|
|
||||||
|
final PageAction action = this.pageService.pageActionBuilder(pageContext)
|
||||||
|
.newAction(ActionDefinition.EXAM_VIEW_LIST)
|
||||||
|
.create();
|
||||||
|
|
||||||
|
this.pageService.firePageEvent(
|
||||||
|
new ActionEvent(action),
|
||||||
|
action.pageContext());
|
||||||
|
|
||||||
|
final String examName = StringEscapeUtils.escapeXml11(examToDelete.toName().name);
|
||||||
|
final List<EntityKey> dependencies = report.results.stream()
|
||||||
|
.filter(key -> !key.equals(entityKey))
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
|
pageContext.publishPageMessage(
|
||||||
|
DELETE_CONFIRM_TITLE,
|
||||||
|
new LocTextKey(
|
||||||
|
"sebserver.exam.delete.confirm.message",
|
||||||
|
examName,
|
||||||
|
dependencies.size(),
|
||||||
|
(report.errors.isEmpty()) ? "no" : String.valueOf((report.errors.size()))));
|
||||||
|
}
|
||||||
|
|
||||||
private Supplier<PageContext> composeDeleteDialog(
|
private Supplier<PageContext> composeDeleteDialog(
|
||||||
final Composite parent,
|
final Composite parent,
|
||||||
final PageContext pageContext) {
|
final PageContext pageContext) {
|
||||||
|
|
|
@ -200,6 +200,19 @@ public interface PageContext {
|
||||||
* @param callback callback code block that will be called on users selection */
|
* @param callback callback code block that will be called on users selection */
|
||||||
void applyConfirmDialog(LocTextKey confirmMessage, final Consumer<Boolean> callback);
|
void applyConfirmDialog(LocTextKey confirmMessage, final Consumer<Boolean> callback);
|
||||||
|
|
||||||
|
/** Apply a confirm dialog with a specified confirm message and a callback code
|
||||||
|
* block that will be executed on users OK selection.
|
||||||
|
*
|
||||||
|
* @param confirmTitle the title of the confirm message dialog
|
||||||
|
* @param confirmMessage the localized confirm message key
|
||||||
|
* @param confirmButtonText The text for the confirm button to display
|
||||||
|
* @param callback callback code block that will be called on users selection */
|
||||||
|
void applyConfirmDialog(
|
||||||
|
LocTextKey confirmTitle,
|
||||||
|
LocTextKey confirmMessage,
|
||||||
|
LocTextKey confirmButtonText,
|
||||||
|
Consumer<Boolean> callback);
|
||||||
|
|
||||||
/** This can be used to forward to a defined page.
|
/** This can be used to forward to a defined page.
|
||||||
*
|
*
|
||||||
* @param pageDefinition the defined page */
|
* @param pageDefinition the defined page */
|
||||||
|
|
|
@ -64,18 +64,20 @@ public class ComposerServiceImpl implements ComposerService {
|
||||||
private final Class<? extends PageDefinition> mainPageType = DefaultMainPage.class;
|
private final Class<? extends PageDefinition> mainPageType = DefaultMainPage.class;
|
||||||
|
|
||||||
final AuthorizationContextHolder authorizationContextHolder;
|
final AuthorizationContextHolder authorizationContextHolder;
|
||||||
|
private final PageService pageService;
|
||||||
private final I18nSupport i18nSupport;
|
private final I18nSupport i18nSupport;
|
||||||
private final Map<String, TemplateComposer> composer;
|
private final Map<String, TemplateComposer> composer;
|
||||||
private final Map<String, PageDefinition> pages;
|
private final Map<String, PageDefinition> pages;
|
||||||
|
|
||||||
public ComposerServiceImpl(
|
public ComposerServiceImpl(
|
||||||
final AuthorizationContextHolder authorizationContextHolder,
|
final AuthorizationContextHolder authorizationContextHolder,
|
||||||
final I18nSupport i18nSupport,
|
final PageService pageService,
|
||||||
final Collection<TemplateComposer> composer,
|
final Collection<TemplateComposer> composer,
|
||||||
final Collection<PageDefinition> pageDefinitions) {
|
final Collection<PageDefinition> pageDefinitions) {
|
||||||
|
|
||||||
this.authorizationContextHolder = authorizationContextHolder;
|
this.authorizationContextHolder = authorizationContextHolder;
|
||||||
this.i18nSupport = i18nSupport;
|
this.pageService = pageService;
|
||||||
|
this.i18nSupport = pageService.getI18nSupport();
|
||||||
this.composer = composer
|
this.composer = composer
|
||||||
.stream()
|
.stream()
|
||||||
.collect(Collectors.toMap(
|
.collect(Collectors.toMap(
|
||||||
|
@ -233,7 +235,7 @@ public class ComposerServiceImpl implements ComposerService {
|
||||||
}
|
}
|
||||||
|
|
||||||
private PageContext createPageContext(final Composite root) {
|
private PageContext createPageContext(final Composite root) {
|
||||||
return new PageContextImpl(this.i18nSupport, this, root, root, null);
|
return new PageContextImpl(this.pageService, this, root, root, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -184,10 +184,8 @@ public final class PageAction {
|
||||||
}
|
}
|
||||||
} catch (final PageMessageException pme) {
|
} catch (final PageMessageException pme) {
|
||||||
PageAction.this.pageContext.publishPageMessage(pme);
|
PageAction.this.pageContext.publishPageMessage(pme);
|
||||||
return;
|
|
||||||
} catch (final Exception e) {
|
} catch (final Exception e) {
|
||||||
this.pageContext.notifyUnexpectedError(e);
|
this.pageContext.notifyUnexpectedError(e);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
callback.accept(exec());
|
callback.accept(exec());
|
||||||
|
|
|
@ -14,16 +14,18 @@ import java.util.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.function.BiConsumer;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
|
import java.util.function.Supplier;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import ch.ethz.seb.sebserver.gui.service.page.*;
|
||||||
import org.apache.commons.lang3.BooleanUtils;
|
import org.apache.commons.lang3.BooleanUtils;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.eclipse.rap.rwt.RWT;
|
||||||
import org.eclipse.rap.rwt.widgets.DialogCallback;
|
import org.eclipse.rap.rwt.widgets.DialogCallback;
|
||||||
import org.eclipse.swt.SWT;
|
import org.eclipse.swt.SWT;
|
||||||
import org.eclipse.swt.widgets.Composite;
|
import org.eclipse.swt.widgets.*;
|
||||||
import org.eclipse.swt.widgets.MessageBox;
|
|
||||||
import org.eclipse.swt.widgets.Shell;
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
@ -35,10 +37,6 @@ import ch.ethz.seb.sebserver.gbl.model.EntityKey;
|
||||||
import ch.ethz.seb.sebserver.gbl.util.Utils;
|
import ch.ethz.seb.sebserver.gbl.util.Utils;
|
||||||
import ch.ethz.seb.sebserver.gui.service.i18n.I18nSupport;
|
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.i18n.LocTextKey;
|
||||||
import ch.ethz.seb.sebserver.gui.service.page.ComposerService;
|
|
||||||
import ch.ethz.seb.sebserver.gui.service.page.PageContext;
|
|
||||||
import ch.ethz.seb.sebserver.gui.service.page.PageDefinition;
|
|
||||||
import ch.ethz.seb.sebserver.gui.service.page.PageMessageException;
|
|
||||||
import ch.ethz.seb.sebserver.gui.widget.Message;
|
import ch.ethz.seb.sebserver.gui.widget.Message;
|
||||||
|
|
||||||
public class PageContextImpl implements PageContext {
|
public class PageContextImpl implements PageContext {
|
||||||
|
@ -48,6 +46,7 @@ public class PageContextImpl implements PageContext {
|
||||||
private static final LocTextKey UNEXPECTED_ERROR = new LocTextKey("sebserver.error.unexpected");
|
private static final LocTextKey UNEXPECTED_ERROR = new LocTextKey("sebserver.error.unexpected");
|
||||||
private static final String ENTITY_LIST_TYPE = null;
|
private static final String ENTITY_LIST_TYPE = null;
|
||||||
|
|
||||||
|
private final PageService pageService;
|
||||||
private final I18nSupport i18nSupport;
|
private final I18nSupport i18nSupport;
|
||||||
private final ComposerService composerService;
|
private final ComposerService composerService;
|
||||||
private final Composite root;
|
private final Composite root;
|
||||||
|
@ -55,13 +54,14 @@ public class PageContextImpl implements PageContext {
|
||||||
private final Map<String, String> attributes;
|
private final Map<String, String> attributes;
|
||||||
|
|
||||||
PageContextImpl(
|
PageContextImpl(
|
||||||
final I18nSupport i18nSupport,
|
final PageService pageService,
|
||||||
final ComposerService composerService,
|
final ComposerService composerService,
|
||||||
final Composite root,
|
final Composite root,
|
||||||
final Composite parent,
|
final Composite parent,
|
||||||
final Map<String, String> attributes) {
|
final Map<String, String> attributes) {
|
||||||
|
|
||||||
this.i18nSupport = i18nSupport;
|
this.pageService = pageService;
|
||||||
|
this.i18nSupport = pageService.getI18nSupport();
|
||||||
this.composerService = composerService;
|
this.composerService = composerService;
|
||||||
this.root = root;
|
this.root = root;
|
||||||
this.parent = parent;
|
this.parent = parent;
|
||||||
|
@ -105,7 +105,7 @@ public class PageContextImpl implements PageContext {
|
||||||
@Override
|
@Override
|
||||||
public PageContext copyOf(final Composite parent) {
|
public PageContext copyOf(final Composite parent) {
|
||||||
return new PageContextImpl(
|
return new PageContextImpl(
|
||||||
this.i18nSupport,
|
this.pageService,
|
||||||
this.composerService,
|
this.composerService,
|
||||||
this.root,
|
this.root,
|
||||||
parent,
|
parent,
|
||||||
|
@ -118,7 +118,7 @@ public class PageContextImpl implements PageContext {
|
||||||
attrs.putAll(this.attributes);
|
attrs.putAll(this.attributes);
|
||||||
attrs.putAll(((PageContextImpl) otherContext).attributes);
|
attrs.putAll(((PageContextImpl) otherContext).attributes);
|
||||||
return new PageContextImpl(
|
return new PageContextImpl(
|
||||||
this.i18nSupport,
|
this.pageService,
|
||||||
this.composerService,
|
this.composerService,
|
||||||
this.root,
|
this.root,
|
||||||
this.parent,
|
this.parent,
|
||||||
|
@ -130,7 +130,7 @@ public class PageContextImpl implements PageContext {
|
||||||
final Map<String, String> attrs = new HashMap<>(this.attributes);
|
final Map<String, String> attrs = new HashMap<>(this.attributes);
|
||||||
attrs.put(key, value);
|
attrs.put(key, value);
|
||||||
return new PageContextImpl(
|
return new PageContextImpl(
|
||||||
this.i18nSupport,
|
this.pageService,
|
||||||
this.composerService,
|
this.composerService,
|
||||||
this.root,
|
this.root,
|
||||||
this.parent,
|
this.parent,
|
||||||
|
@ -238,7 +238,7 @@ public class PageContextImpl implements PageContext {
|
||||||
final Map<String, String> attrs = new HashMap<>(this.attributes);
|
final Map<String, String> attrs = new HashMap<>(this.attributes);
|
||||||
attrs.remove(name);
|
attrs.remove(name);
|
||||||
return new PageContextImpl(
|
return new PageContextImpl(
|
||||||
this.i18nSupport,
|
this.pageService,
|
||||||
this.composerService,
|
this.composerService,
|
||||||
this.root,
|
this.root,
|
||||||
this.parent,
|
this.parent,
|
||||||
|
@ -248,7 +248,7 @@ public class PageContextImpl implements PageContext {
|
||||||
@Override
|
@Override
|
||||||
public PageContext clearAttributes() {
|
public PageContext clearAttributes() {
|
||||||
return new PageContextImpl(
|
return new PageContextImpl(
|
||||||
this.i18nSupport,
|
this.pageService,
|
||||||
this.composerService,
|
this.composerService,
|
||||||
this.root,
|
this.root,
|
||||||
this.parent,
|
this.parent,
|
||||||
|
@ -267,6 +267,36 @@ public class PageContextImpl implements PageContext {
|
||||||
messageBox.open(new ConfirmDialogCallback(callback));
|
messageBox.open(new ConfirmDialogCallback(callback));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void applyConfirmDialog(
|
||||||
|
final LocTextKey confirmTitle,
|
||||||
|
final LocTextKey confirmMessage,
|
||||||
|
final LocTextKey confirmButtonText,
|
||||||
|
final Consumer<Boolean> callback) {
|
||||||
|
|
||||||
|
final ModalInputDialog<Void> dialog = new ModalInputDialog<>(
|
||||||
|
this.root.getShell(),
|
||||||
|
this.pageService.getWidgetFactory());
|
||||||
|
dialog.setDialogWidth(500);
|
||||||
|
|
||||||
|
|
||||||
|
final ModalInputDialogComposer<Void> contentComposer = comp -> {
|
||||||
|
this.pageService.getWidgetFactory().labelLocalized(comp, confirmMessage, true);
|
||||||
|
return () -> (Void) null;
|
||||||
|
};
|
||||||
|
final BiConsumer<Composite, Supplier<Void>> actionComposer = (actionsComp, value) -> {
|
||||||
|
final Button confirm = this.pageService.getWidgetFactory()
|
||||||
|
.buttonLocalized(actionsComp, confirmButtonText);
|
||||||
|
|
||||||
|
confirm.addListener(SWT.Selection, event -> {
|
||||||
|
dialog.close();
|
||||||
|
callback.accept(true);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
dialog.openWithActions(confirmTitle, actionComposer, () -> callback.accept(false), contentComposer);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void forwardToPage(final PageDefinition pageDefinition) {
|
public void forwardToPage(final PageDefinition pageDefinition) {
|
||||||
this.composerService.compose(
|
this.composerService.compose(
|
||||||
|
|
|
@ -0,0 +1,30 @@
|
||||||
|
package ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam;
|
||||||
|
|
||||||
|
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;
|
||||||
|
import com.fasterxml.jackson.core.type.TypeReference;
|
||||||
|
import org.springframework.context.annotation.Lazy;
|
||||||
|
import org.springframework.http.HttpMethod;
|
||||||
|
import org.springframework.http.MediaType;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
@Lazy
|
||||||
|
@Component
|
||||||
|
@GuiProfile
|
||||||
|
public class ForceDeleteExam extends RestCall<EntityProcessingReport> {
|
||||||
|
|
||||||
|
public ForceDeleteExam() {
|
||||||
|
super(new TypeKey<>(
|
||||||
|
CallType.DELETE,
|
||||||
|
EntityType.EXAM,
|
||||||
|
new TypeReference<EntityProcessingReport>() {
|
||||||
|
}),
|
||||||
|
HttpMethod.DELETE,
|
||||||
|
MediaType.APPLICATION_FORM_URLENCODED,
|
||||||
|
API.EXAM_ADMINISTRATION_ENDPOINT + API.MODEL_ID_VAR_PATH_SEGMENT + API.FORCE_PATH_SEGMENT);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -439,7 +439,7 @@ public class LmsAPITemplateAdapter implements LmsAPITemplate {
|
||||||
|
|
||||||
if (protectedRun.hasError()) {
|
if (protectedRun.hasError()) {
|
||||||
final Throwable cause = protectedRun.getError().getCause();
|
final Throwable cause = protectedRun.getError().getCause();
|
||||||
if (cause.getMessage().contains("LMS Warnings")) {
|
if (cause != null && cause.getMessage().contains("LMS Warnings")) {
|
||||||
return Result.ofRuntimeError(cause.getMessage());
|
return Result.ofRuntimeError(cause.getMessage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -49,7 +49,6 @@ public class SEBClientEventBatchService {
|
||||||
private static final Logger log = LoggerFactory.getLogger(SEBClientEventBatchService.class);
|
private static final Logger log = LoggerFactory.getLogger(SEBClientEventBatchService.class);
|
||||||
|
|
||||||
private final SEBClientNotificationService sebClientNotificationService;
|
private final SEBClientNotificationService sebClientNotificationService;
|
||||||
private final SqlSessionFactory sqlSessionFactory;
|
|
||||||
private final TransactionTemplate transactionTemplate;
|
private final TransactionTemplate transactionTemplate;
|
||||||
private final ExamSessionCacheService examSessionCacheService;
|
private final ExamSessionCacheService examSessionCacheService;
|
||||||
private final JSONMapper jsonMapper;
|
private final JSONMapper jsonMapper;
|
||||||
|
@ -65,13 +64,12 @@ public class SEBClientEventBatchService {
|
||||||
final JSONMapper jsonMapper) {
|
final JSONMapper jsonMapper) {
|
||||||
|
|
||||||
this.sebClientNotificationService = sebClientNotificationService;
|
this.sebClientNotificationService = sebClientNotificationService;
|
||||||
this.sqlSessionFactory = sqlSessionFactory;
|
|
||||||
this.transactionTemplate = new TransactionTemplate(transactionManager);
|
this.transactionTemplate = new TransactionTemplate(transactionManager);
|
||||||
this.transactionTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
|
this.transactionTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
|
||||||
this.examSessionCacheService = examSessionCacheService;
|
this.examSessionCacheService = examSessionCacheService;
|
||||||
this.jsonMapper = jsonMapper;
|
this.jsonMapper = jsonMapper;
|
||||||
|
|
||||||
this.sqlSessionTemplate = new SqlSessionTemplate(this.sqlSessionFactory, ExecutorType.BATCH);
|
this.sqlSessionTemplate = new SqlSessionTemplate(sqlSessionFactory, ExecutorType.BATCH);
|
||||||
this.clientEventMapper = this.sqlSessionTemplate.getMapper(ClientEventRecordMapper.class);
|
this.clientEventMapper = this.sqlSessionTemplate.getMapper(ClientEventRecordMapper.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -172,7 +170,7 @@ public class SEBClientEventBatchService {
|
||||||
|
|
||||||
this.sqlSessionTemplate.flushStatements();
|
this.sqlSessionTemplate.flushStatements();
|
||||||
|
|
||||||
if (log.isDebugEnabled()) {
|
if (log.isTraceEnabled()) {
|
||||||
log.debug("SEBClientEventBatchService worker {} processes batch of size {} in {} ms",
|
log.debug("SEBClientEventBatchService worker {} processes batch of size {} in {} ms",
|
||||||
workerName,
|
workerName,
|
||||||
size,
|
size,
|
||||||
|
@ -227,7 +225,7 @@ public class SEBClientEventBatchService {
|
||||||
eventData.event.eventType,
|
eventData.event.eventType,
|
||||||
eventData.event.getClientTime(),
|
eventData.event.getClientTime(),
|
||||||
eventData.event.getServerTime(),
|
eventData.event.getServerTime(),
|
||||||
(eventData.event.numValue != null) ? eventData.event.numValue.doubleValue() : null,
|
(eventData.event.numValue != null) ? eventData.event.numValue : null,
|
||||||
typeAndPlainText.b,
|
typeAndPlainText.b,
|
||||||
typeAndPlainText.a);
|
typeAndPlainText.a);
|
||||||
|
|
||||||
|
|
|
@ -18,6 +18,7 @@ import java.util.stream.Collectors;
|
||||||
import javax.servlet.http.HttpServletRequest;
|
import javax.servlet.http.HttpServletRequest;
|
||||||
import javax.validation.Valid;
|
import javax.validation.Valid;
|
||||||
|
|
||||||
|
import ch.ethz.seb.sebserver.gbl.model.*;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.mybatis.dynamic.sql.SqlTable;
|
import org.mybatis.dynamic.sql.SqlTable;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
|
@ -38,13 +39,6 @@ import ch.ethz.seb.sebserver.gbl.api.API.BulkActionType;
|
||||||
import ch.ethz.seb.sebserver.gbl.api.EntityType;
|
import ch.ethz.seb.sebserver.gbl.api.EntityType;
|
||||||
import ch.ethz.seb.sebserver.gbl.api.POSTMapper;
|
import ch.ethz.seb.sebserver.gbl.api.POSTMapper;
|
||||||
import ch.ethz.seb.sebserver.gbl.api.authorization.PrivilegeType;
|
import ch.ethz.seb.sebserver.gbl.api.authorization.PrivilegeType;
|
||||||
import ch.ethz.seb.sebserver.gbl.model.Entity;
|
|
||||||
import ch.ethz.seb.sebserver.gbl.model.EntityDependency;
|
|
||||||
import ch.ethz.seb.sebserver.gbl.model.EntityKey;
|
|
||||||
import ch.ethz.seb.sebserver.gbl.model.EntityName;
|
|
||||||
import ch.ethz.seb.sebserver.gbl.model.EntityProcessingReport;
|
|
||||||
import ch.ethz.seb.sebserver.gbl.model.GrantEntity;
|
|
||||||
import ch.ethz.seb.sebserver.gbl.model.Page;
|
|
||||||
import ch.ethz.seb.sebserver.gbl.util.Pair;
|
import ch.ethz.seb.sebserver.gbl.util.Pair;
|
||||||
import ch.ethz.seb.sebserver.gbl.util.Result;
|
import ch.ethz.seb.sebserver.gbl.util.Result;
|
||||||
import ch.ethz.seb.sebserver.webservice.servicelayer.PaginationService;
|
import ch.ethz.seb.sebserver.webservice.servicelayer.PaginationService;
|
||||||
|
@ -97,8 +91,8 @@ public abstract class EntityController<T extends Entity, M extends Entity> {
|
||||||
|
|
||||||
/** This is called by Spring to initialize the WebDataBinder and is used here to
|
/** This is called by Spring to initialize the WebDataBinder and is used here to
|
||||||
* initialize the default value binding for the institutionId request-parameter
|
* initialize the default value binding for the institutionId request-parameter
|
||||||
* that has the current users insitutionId as default.
|
* that has the current users institutionId as default.
|
||||||
*
|
* <p>
|
||||||
* See also UserService.addUsersInstitutionDefaultPropertySupport */
|
* See also UserService.addUsersInstitutionDefaultPropertySupport */
|
||||||
@InitBinder
|
@InitBinder
|
||||||
public void initBinder(final WebDataBinder binder) {
|
public void initBinder(final WebDataBinder binder) {
|
||||||
|
@ -293,10 +287,10 @@ public abstract class EntityController<T extends Entity, M extends Entity> {
|
||||||
+ "This is the name of the enumeration "),
|
+ "This is the name of the enumeration "),
|
||||||
@Parameter(
|
@Parameter(
|
||||||
name = API.PARAM_BULK_ACTION_ADD_INCLUDES,
|
name = API.PARAM_BULK_ACTION_ADD_INCLUDES,
|
||||||
description = "Indicates if the following 'includes' paramerer shall be processed or not.\n The default is false "),
|
description = "Indicates if the following 'includes' parameter shall be processed or not.\n The default is false "),
|
||||||
@Parameter(
|
@Parameter(
|
||||||
name = API.PARAM_BULK_ACTION_INCLUDES,
|
name = API.PARAM_BULK_ACTION_INCLUDES,
|
||||||
description = "A comma separated list of names of the EntityType enummeration that defines all entity types that shall be included in the result.")
|
description = "A comma separated list of names of the EntityType enumeration that defines all entity types that shall be included in the result.")
|
||||||
})
|
})
|
||||||
@RequestMapping(
|
@RequestMapping(
|
||||||
path = API.MODEL_ID_VAR_PATH_SEGMENT + API.DEPENDENCY_PATH_SEGMENT,
|
path = API.MODEL_ID_VAR_PATH_SEGMENT + API.DEPENDENCY_PATH_SEGMENT,
|
||||||
|
@ -466,7 +460,7 @@ public abstract class EntityController<T extends Entity, M extends Entity> {
|
||||||
summary = "Deletes a single entity (and all its dependencies) by its modelId.",
|
summary = "Deletes a single entity (and all its dependencies) by its modelId.",
|
||||||
description = "To check or report what dependent object also would be deleted for a certain entity object, "
|
description = "To check or report what dependent object also would be deleted for a certain entity object, "
|
||||||
+
|
+
|
||||||
"please use the dependency endpoint to get a report of all dependend entity objects.",
|
"please use the dependency endpoint to get a report of all dependent entity objects.",
|
||||||
requestBody = @io.swagger.v3.oas.annotations.parameters.RequestBody(
|
requestBody = @io.swagger.v3.oas.annotations.parameters.RequestBody(
|
||||||
content = { @Content(mediaType = MediaType.APPLICATION_FORM_URLENCODED_VALUE) }),
|
content = { @Content(mediaType = MediaType.APPLICATION_FORM_URLENCODED_VALUE) }),
|
||||||
parameters = {
|
parameters = {
|
||||||
|
@ -476,10 +470,10 @@ public abstract class EntityController<T extends Entity, M extends Entity> {
|
||||||
in = ParameterIn.PATH),
|
in = ParameterIn.PATH),
|
||||||
@Parameter(
|
@Parameter(
|
||||||
name = API.PARAM_BULK_ACTION_ADD_INCLUDES,
|
name = API.PARAM_BULK_ACTION_ADD_INCLUDES,
|
||||||
description = "Indicates if the following 'includes' paramerer shall be processed or not.\n The default is false "),
|
description = "Indicates if the following 'includes' parameter shall be processed or not.\n The default is false "),
|
||||||
@Parameter(
|
@Parameter(
|
||||||
name = API.PARAM_BULK_ACTION_INCLUDES,
|
name = API.PARAM_BULK_ACTION_INCLUDES,
|
||||||
description = "A comma separated list of names of the EntityType enummeration that defines all entity types that shall be included in the result.")
|
description = "A comma separated list of names of the EntityType enumeration that defines all entity types that shall be included in the result.")
|
||||||
})
|
})
|
||||||
@RequestMapping(
|
@RequestMapping(
|
||||||
path = API.MODEL_ID_VAR_PATH_SEGMENT,
|
path = API.MODEL_ID_VAR_PATH_SEGMENT,
|
||||||
|
@ -500,6 +494,43 @@ public abstract class EntityController<T extends Entity, M extends Entity> {
|
||||||
.getOrThrow();
|
.getOrThrow();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Operation(
|
||||||
|
summary = "Force deletes a single entity (and all its dependencies) by its modelId.",
|
||||||
|
description = "To check or report what dependent object also would be deleted for a certain entity object, "
|
||||||
|
+
|
||||||
|
"please use the dependency endpoint to get a report of all dependent entity objects.",
|
||||||
|
requestBody = @io.swagger.v3.oas.annotations.parameters.RequestBody(
|
||||||
|
content = { @Content(mediaType = MediaType.APPLICATION_FORM_URLENCODED_VALUE) }),
|
||||||
|
parameters = {
|
||||||
|
@Parameter(
|
||||||
|
name = API.PARAM_MODEL_ID,
|
||||||
|
description = "The model identifier of the entity object to get.",
|
||||||
|
in = ParameterIn.PATH),
|
||||||
|
@Parameter(
|
||||||
|
name = API.PARAM_BULK_ACTION_ADD_INCLUDES,
|
||||||
|
description = "Indicates if the following 'includes' parameter shall be processed or not.\n The default is false "),
|
||||||
|
@Parameter(
|
||||||
|
name = API.PARAM_BULK_ACTION_INCLUDES,
|
||||||
|
description = "A comma separated list of names of the EntityType enumeration that defines all entity types that shall be included in the result.")
|
||||||
|
})
|
||||||
|
@RequestMapping(
|
||||||
|
path = API.MODEL_ID_VAR_PATH_SEGMENT + API.FORCE_PATH_SEGMENT,
|
||||||
|
method = RequestMethod.DELETE,
|
||||||
|
produces = MediaType.APPLICATION_JSON_VALUE)
|
||||||
|
public EntityProcessingReport forceHardDelete(
|
||||||
|
@PathVariable final String modelId,
|
||||||
|
@RequestParam(name = API.PARAM_BULK_ACTION_ADD_INCLUDES, defaultValue = "false") final boolean addIncludes,
|
||||||
|
@RequestParam(name = API.PARAM_BULK_ACTION_INCLUDES, required = false) final List<String> includes) {
|
||||||
|
|
||||||
|
return this.entityDAO.byModelId(modelId)
|
||||||
|
.flatMap(this::checkWriteAccess)
|
||||||
|
.flatMap(this::logDelete)
|
||||||
|
.flatMap(entity -> bulkDelete(entity, convertToEntityType(addIncludes, includes)))
|
||||||
|
.flatMap(this::notifyDeleted)
|
||||||
|
.flatMap(pair -> this.logBulkAction(pair.b))
|
||||||
|
.getOrThrow();
|
||||||
|
}
|
||||||
|
|
||||||
// **************************
|
// **************************
|
||||||
// * DELETE ALL (hard-delete)
|
// * DELETE ALL (hard-delete)
|
||||||
// **************************
|
// **************************
|
||||||
|
@ -508,7 +539,7 @@ public abstract class EntityController<T extends Entity, M extends Entity> {
|
||||||
summary = "Deletes all given entity (and all its dependencies) by a given list of model identifiers.",
|
summary = "Deletes all given entity (and all its dependencies) by a given list of model identifiers.",
|
||||||
description = "To check or report what dependent object also would be deleted for a certain entity object, "
|
description = "To check or report what dependent object also would be deleted for a certain entity object, "
|
||||||
+
|
+
|
||||||
"please use the dependency endpoint to get a report of all dependend entity objects.",
|
"please use the dependency endpoint to get a report of all dependent entity objects.",
|
||||||
requestBody = @io.swagger.v3.oas.annotations.parameters.RequestBody(
|
requestBody = @io.swagger.v3.oas.annotations.parameters.RequestBody(
|
||||||
content = { @Content(mediaType = MediaType.APPLICATION_FORM_URLENCODED_VALUE) }),
|
content = { @Content(mediaType = MediaType.APPLICATION_FORM_URLENCODED_VALUE) }),
|
||||||
parameters = {
|
parameters = {
|
||||||
|
@ -521,7 +552,7 @@ public abstract class EntityController<T extends Entity, M extends Entity> {
|
||||||
description = "Indicates if the following 'includes' paramerer shall be processed or not.\n The default is false "),
|
description = "Indicates if the following 'includes' paramerer shall be processed or not.\n The default is false "),
|
||||||
@Parameter(
|
@Parameter(
|
||||||
name = API.PARAM_BULK_ACTION_INCLUDES,
|
name = API.PARAM_BULK_ACTION_INCLUDES,
|
||||||
description = "A comma separated list of names of the EntityType enummeration that defines all entity types that shall be included in the result.")
|
description = "A comma separated list of names of the EntityType enumeration that defines all entity types that shall be included in the result.")
|
||||||
})
|
})
|
||||||
@RequestMapping(
|
@RequestMapping(
|
||||||
method = RequestMethod.DELETE,
|
method = RequestMethod.DELETE,
|
||||||
|
@ -547,11 +578,11 @@ public abstract class EntityController<T extends Entity, M extends Entity> {
|
||||||
final Collection<EntityKey> sources = ids.stream()
|
final Collection<EntityKey> sources = ids.stream()
|
||||||
.map(id -> {
|
.map(id -> {
|
||||||
return this.entityDAO.byModelId(id)
|
return this.entityDAO.byModelId(id)
|
||||||
.flatMap(exam -> this.validForDelete(exam))
|
.flatMap(this::validForDelete)
|
||||||
.getOr(null);
|
.getOr(null);
|
||||||
})
|
})
|
||||||
.filter(Objects::nonNull)
|
.filter(Objects::nonNull)
|
||||||
.map(exam -> exam.getModelId())
|
.map(ModelIdAware::getModelId)
|
||||||
.map(id -> new EntityKey(id, entityType))
|
.map(id -> new EntityKey(id, entityType))
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
|
@ -756,9 +787,9 @@ public abstract class EntityController<T extends Entity, M extends Entity> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Get the EntityType of the GrantEntity that is used for grant checks of the concrete Entity.
|
/** Get the EntityType of the GrantEntity that is used for grant checks of the concrete Entity.
|
||||||
*
|
* <p>
|
||||||
* NOTE: override this if the EntityType of the GrantEntity is not the same as the Entity itself.
|
* NOTE: override this if the EntityType of the GrantEntity is not the same as the Entity itself.
|
||||||
* For example, the Exam is the GrantEntity of a Indicator
|
* For example, the Exam is the GrantEntity of an Indicator
|
||||||
*
|
*
|
||||||
* @return the EntityType of the GrantEntity that is used for grant checks of the concrete Entity */
|
* @return the EntityType of the GrantEntity that is used for grant checks of the concrete Entity */
|
||||||
protected EntityType getGrantEntityType() {
|
protected EntityType getGrantEntityType() {
|
||||||
|
|
|
@ -554,6 +554,9 @@ sebserver.exam.action.activate=Activate Exam
|
||||||
sebserver.exam.action.deactivate=Deactivate Exam
|
sebserver.exam.action.deactivate=Deactivate Exam
|
||||||
sebserver.exam.action.delete=Delete Exam
|
sebserver.exam.action.delete=Delete Exam
|
||||||
sebserver.exam.action.delete.consistency.error=Deletion Failed.<br/>Please make sure there are no active SEB clients connected to the exam before deletion.
|
sebserver.exam.action.delete.consistency.error=Deletion Failed.<br/>Please make sure there are no active SEB clients connected to the exam before deletion.
|
||||||
|
sebserver.exam.action.delete.force.confirm.title=Deletion Failed due to condition violation
|
||||||
|
sebserver.exam.action.delete.force.confirm.action=Force Delete
|
||||||
|
sebserver.exam.action.delete.force.confirm=There are still some active SEB client connections for this exam.<br/><br/>Please use "Force Delete" to delete anyway or "Cancel" to aboard deletion.
|
||||||
sebserver.exam.action.archive=Archive
|
sebserver.exam.action.archive=Archive
|
||||||
sebserver.exam.action.archive.confirm=An archived exam cannot be rerun and will remain in read-only view.<br/><br/>Are you sure to archive the exam?
|
sebserver.exam.action.archive.confirm=An archived exam cannot be rerun and will remain in read-only view.<br/><br/>Are you sure to archive the exam?
|
||||||
sebserver.exam.action.sebrestriction.enable=Apply SEB Lock
|
sebserver.exam.action.sebrestriction.enable=Apply SEB Lock
|
||||||
|
|
Loading…
Reference in a new issue