From cc0f15ab6289d1a3ff36703fe79a9c6025f032d4 Mon Sep 17 00:00:00 2001 From: anhefti Date: Tue, 21 Jul 2020 16:27:08 +0200 Subject: [PATCH] SEBSERV-10 GUI implementation --- .../ch/ethz/seb/sebserver/gbl/api/API.java | 2 + .../gui/content/UserAccountDeletePopup.java | 200 +++++++++++++++--- .../gui/content/action/ActionDefinition.java | 4 +- .../gui/service/page/PageService.java | 29 ++- .../service/page/impl/ModalInputDialog.java | 28 +-- .../service/page/impl/ModelInputWizard.java | 153 ++++++++------ .../service/page/impl/PageServiceImpl.java | 14 +- .../sebserver/gui/table/ColumnDefinition.java | 3 +- .../seb/sebserver/gui/table/EntityTable.java | 13 +- .../gui/table/StaticListPageSupplier.java | 107 ++++++++++ .../seb/sebserver/gui/table/TableBuilder.java | 28 ++- .../seb/sebserver/gui/table/TableFilter.java | 7 +- .../sebserver/gui/widget/WidgetFactory.java | 18 ++ .../dao/impl/ExamConfigurationMapDAOImpl.java | 4 + .../servicelayer/dao/impl/UserDAOImpl.java | 10 +- .../api/ClientConnectionController.java | 1 + .../weblayer/api/ClientEventController.java | 1 + .../weblayer/api/ConfigurationController.java | 1 + .../api/ConfigurationValueController.java | 1 + .../weblayer/api/EntityController.java | 30 +-- .../ExamConfigurationMappingController.java | 3 +- .../api/ReadonlyEntityController.java | 1 + src/main/resources/messages.properties | 11 +- 23 files changed, 520 insertions(+), 149 deletions(-) create mode 100644 src/main/java/ch/ethz/seb/sebserver/gui/table/StaticListPageSupplier.java diff --git a/src/main/java/ch/ethz/seb/sebserver/gbl/api/API.java b/src/main/java/ch/ethz/seb/sebserver/gbl/api/API.java index 33fa38b3..8e943bb5 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gbl/api/API.java +++ b/src/main/java/ch/ethz/seb/sebserver/gbl/api/API.java @@ -26,7 +26,9 @@ public final class API { public static final String PARAM_MODEL_ID_LIST = "modelIds"; public static final String PARAM_PARENT_MODEL_ID = "parentModelId"; public static final String PARAM_ENTITY_TYPE = "entityType"; + public static final String PARAM_BULK_ACTION_TYPE = "bulkActionType"; + public static final String PARAM_BULK_ACTION_ADD_INCLUDES = "bulkActionAddIncludes"; public static final String PARAM_BULK_ACTION_INCLUDES = "bulkActionIncludes"; public static final String PARAM_VIEW_ID = "viewId"; public static final String PARAM_INSTRUCTION_TYPE = "instructionType"; diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/content/UserAccountDeletePopup.java b/src/main/java/ch/ethz/seb/sebserver/gui/content/UserAccountDeletePopup.java index 5f30d5e8..dca6bb1c 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/content/UserAccountDeletePopup.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/content/UserAccountDeletePopup.java @@ -8,43 +8,60 @@ package ch.ethz.seb.sebserver.gui.content; +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.apache.commons.lang3.BooleanUtils; +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.Constants; import ch.ethz.seb.sebserver.gbl.api.API; +import ch.ethz.seb.sebserver.gbl.api.API.BulkActionType; 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.EntityProcessingReport; import ch.ethz.seb.sebserver.gbl.model.user.UserInfo; import ch.ethz.seb.sebserver.gbl.profile.GuiProfile; import ch.ethz.seb.sebserver.gbl.util.Utils; +import ch.ethz.seb.sebserver.gui.content.action.ActionDefinition; import ch.ethz.seb.sebserver.gui.form.Form; import ch.ethz.seb.sebserver.gui.form.FormBuilder; import ch.ethz.seb.sebserver.gui.form.FormHandle; +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.PageService; +import ch.ethz.seb.sebserver.gui.service.page.event.ActionEvent; import ch.ethz.seb.sebserver.gui.service.page.impl.ModelInputWizard; import ch.ethz.seb.sebserver.gui.service.page.impl.ModelInputWizard.WizardAction; import ch.ethz.seb.sebserver.gui.service.page.impl.ModelInputWizard.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.useraccount.DeleteUserAccount; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.useraccount.GetUserAccount; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.useraccount.GetUserDependency; +import ch.ethz.seb.sebserver.gui.table.ColumnDefinition; +import ch.ethz.seb.sebserver.gui.widget.WidgetFactory.CustomVariant; @Lazy @Component @GuiProfile public class UserAccountDeletePopup { + private static final Logger log = LoggerFactory.getLogger(UserAccountDeletePopup.class); + private final static String ARG_WITH_CONFIGS = "WITH_CONFIGS"; private final static String ARG_WITH_EXAMS = "WITH_EXAMS"; @@ -52,6 +69,17 @@ public class UserAccountDeletePopup { new LocTextKey("sebserver.useraccount.delete.form.title"); private final static LocTextKey FORM_INFO = new LocTextKey("sebserver.useraccount.delete.form.info"); + private final static LocTextKey FORM_REPORT_INFO = + new LocTextKey("sebserver.useraccount.delete.form.report.info"); + private final static LocTextKey FORM_REPORT_LIST_TYPE = + new LocTextKey("sebserver.useraccount.delete.form.report.list.type"); + private final static LocTextKey FORM_REPORT_LIST_NAME = + new LocTextKey("sebserver.useraccount.delete.form.report.list.name"); + private final static LocTextKey FORM_REPORT_LIST_DESC = + new LocTextKey("sebserver.useraccount.delete.form.report.list.description"); + + private final static LocTextKey FORM_REPORT_NONE = + new LocTextKey("sebserver.useraccount.delete.form.report.empty"); private final static LocTextKey FORM_NAME = new LocTextKey("sebserver.useraccount.delete.form.accountName"); private final static LocTextKey FORM_CONFIGS = @@ -63,6 +91,9 @@ public class UserAccountDeletePopup { private final static LocTextKey ACTION_REPORT = new LocTextKey("sebserver.useraccount.delete.form.action.report"); + private final static LocTextKey DELETE_CONFIRM_TITLE = + new LocTextKey("sebserver.useraccount.delete.confirm.title"); + private final PageService pageService; protected UserAccountDeletePopup(final PageService pageService) { @@ -76,15 +107,17 @@ public class UserAccountDeletePopup { new ModelInputWizard( action.pageContext().getParent().getShell(), this.pageService.getWidgetFactory()) - .setLargeDialogWidth(); + .setVeryLargeDialogWidth(); final String page1Id = "DELETE_PAGE"; final String page2Id = "REPORT_PAGE"; final Predicate callback = pc -> doDelete(this.pageService, pc); final BiFunction> composePage1 = - (formHandle, content) -> composeDeleteDialog(content, pageContext); + (prefPageContext, content) -> composeDeleteDialog(content, + (prefPageContext != null) ? prefPageContext : pageContext); final BiFunction> composePage2 = - (formHandle, content) -> composeReportDialog(content, pageContext); + (prefPageContext, content) -> composeReportDialog(content, + (prefPageContext != null) ? prefPageContext : pageContext); final WizardPage page1 = new WizardPage<>( page1Id, @@ -109,15 +142,81 @@ public class UserAccountDeletePopup { final PageService pageService, final PageContext pageContext) { - final boolean withConfigs = BooleanUtils.toBoolean(pageContext.getAttribute(ARG_WITH_CONFIGS)); - final boolean withExams = BooleanUtils.toBoolean(pageContext.getAttribute(ARG_WITH_EXAMS)); - return true; + try { + final boolean withConfigs = BooleanUtils.toBoolean(pageContext.getAttribute(ARG_WITH_CONFIGS)); + final boolean withExams = BooleanUtils.toBoolean(pageContext.getAttribute(ARG_WITH_EXAMS)); + final EntityKey entityKey = pageContext.getEntityKey(); + final UserInfo currentUser = pageService.getCurrentUser().get(); + final boolean ownAccount = currentUser.getModelId().equals(entityKey.modelId); + final UserInfo userToDelete = this.pageService.getRestService().getBuilder(GetUserAccount.class) + .withURIVariable(API.PARAM_MODEL_ID, entityKey.modelId) + .call() + .getOrThrow(); + + final RestCall.RestCallBuilder restCallBuilder = this.pageService.getRestService() + .getBuilder(DeleteUserAccount.class) + .withURIVariable(API.PARAM_MODEL_ID, entityKey.modelId) + .withQueryParam(API.PARAM_BULK_ACTION_TYPE, BulkActionType.HARD_DELETE.name()) + .withQueryParam(API.PARAM_BULK_ACTION_ADD_INCLUDES, Constants.TRUE_STRING); + + if (withConfigs) { + restCallBuilder.withQueryParam( + API.PARAM_BULK_ACTION_INCLUDES, + EntityType.CONFIGURATION_NODE.name()); + } + if (withExams) { + restCallBuilder.withQueryParam( + API.PARAM_BULK_ACTION_INCLUDES, + EntityType.EXAM.name()); + } + + final EntityProcessingReport report = restCallBuilder.call().getOrThrow(); + + if (ownAccount) { + pageService.logout(pageContext); + } else { + final PageAction action = this.pageService.pageActionBuilder(pageContext) + .newAction(ActionDefinition.USER_ACCOUNT_VIEW_LIST) + .create(); + + this.pageService.firePageEvent( + new ActionEvent(action), + action.pageContext()); + } + + final String userName = userToDelete.toName().name; + final List dependencies = report.results.stream() + .filter(key -> !key.equals(entityKey)) + .collect(Collectors.toList()); + pageContext.publishPageMessage( + DELETE_CONFIRM_TITLE, + new LocTextKey( + "sebserver.useraccount.delete.confirm.message", + userName, + 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 User Account:", e); + pageContext.notifyUnexpectedError(e); + return false; + } } private Supplier composeDeleteDialog( final Composite parent, final PageContext pageContext) { + 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 EntityKey entityKey = pageContext.getEntityKey(); final UserInfo userInfo = this.pageService.getRestService() .getBuilder(GetUserAccount.class) @@ -126,8 +225,10 @@ public class UserAccountDeletePopup { .get(); final FormHandle formHandle = this.pageService.formBuilder( - pageContext.copyOf(parent)) + pageContext.copyOf(grid)) .readonly(false) + .withDefaultSpanLabel(3) + .withDefaultSpanInput(4) .addField(FormBuilder.text( "USE_NAME", FORM_NAME, @@ -153,36 +254,67 @@ public class UserAccountDeletePopup { final Composite parent, final PageContext pageContext) { - // get selection - final boolean withConfigs = BooleanUtils.toBoolean(pageContext.getAttribute(ARG_WITH_CONFIGS)); - final boolean withExams = BooleanUtils.toBoolean(pageContext.getAttribute(ARG_WITH_EXAMS)); + final Composite grid = this.pageService.getWidgetFactory() + .createPopupScrollCompositeFilled(parent); + final I18nSupport i18nSupport = this.pageService.getI18nSupport(); - // get dependencies - final EntityKey entityKey = pageContext.getEntityKey(); - final RestCall>.RestCallBuilder restCallBuilder = this.pageService.getRestService() - .getBuilder(GetUserDependency.class) - .withURIVariable(API.PARAM_MODEL_ID, entityKey.modelId); + final Label title = this.pageService.getWidgetFactory() + .labelLocalized(grid, CustomVariant.TEXT_H3, FORM_REPORT_INFO); + final GridData gridData = new GridData(); + gridData.horizontalIndent = 10; + gridData.verticalIndent = 10; + title.setLayoutData(gridData); - if (withConfigs) { - restCallBuilder.withQueryParam( - API.PARAM_BULK_ACTION_INCLUDES, - EntityType.CONFIGURATION_NODE.name()); + try { + // get selection + final boolean withConfigs = BooleanUtils.toBoolean(pageContext.getAttribute(ARG_WITH_CONFIGS)); + final boolean withExams = BooleanUtils.toBoolean(pageContext.getAttribute(ARG_WITH_EXAMS)); + + // get dependencies + final EntityKey entityKey = pageContext.getEntityKey(); + final RestCall>.RestCallBuilder restCallBuilder = this.pageService.getRestService() + .getBuilder(GetUserDependency.class) + .withURIVariable(API.PARAM_MODEL_ID, entityKey.modelId) + .withQueryParam(API.PARAM_BULK_ACTION_TYPE, BulkActionType.HARD_DELETE.name()) + .withQueryParam(API.PARAM_BULK_ACTION_ADD_INCLUDES, Constants.TRUE_STRING); + + if (withConfigs) { + restCallBuilder.withQueryParam( + API.PARAM_BULK_ACTION_INCLUDES, + EntityType.CONFIGURATION_NODE.name()); + } + if (withExams) { + restCallBuilder.withQueryParam( + API.PARAM_BULK_ACTION_INCLUDES, + EntityType.EXAM.name()); + } + + final Set dependencies = restCallBuilder.call().getOrThrow(); + final List list = dependencies.stream().sorted().collect(Collectors.toList()); + this.pageService. staticListTableBuilder(list, null) + .withEmptyMessage(FORM_REPORT_NONE) + .withColumn(new ColumnDefinition<>( + "FORM_REPORT_LIST_TYPE", + FORM_REPORT_LIST_TYPE, + dep -> i18nSupport + .getText("sebserver.overall.types.entityType." + dep.self.entityType.name()) + + " (" + dep.self.getModelId() + ")")) + .withColumn(new ColumnDefinition<>( + "FORM_REPORT_LIST_NAME", + FORM_REPORT_LIST_NAME, + dep -> dep.name)) + .withColumn(new ColumnDefinition<>( + "FORM_REPORT_LIST_DESC", + FORM_REPORT_LIST_DESC, + dep -> dep.description)) + .compose(pageContext.copyOf(grid)); + + return () -> pageContext; + } catch (final Exception e) { + log.error("Error while trying to compose User Account delete report page: ", e); + pageContext.notifyUnexpectedError(e); + throw e; } - if (withExams) { - restCallBuilder.withQueryParam( - API.PARAM_BULK_ACTION_INCLUDES, - EntityType.EXAM.name()); - } - -// final EntityTable configTable = -// this.pageService.entityTableBuilder(GetUserDependency.class) -// . - - final Set dependencies = restCallBuilder.call().getOrThrow(); - - // TODO get dependencies in case of selection and show all in a list (type / name) - - return () -> pageContext; } } diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/content/action/ActionDefinition.java b/src/main/java/ch/ethz/seb/sebserver/gui/content/action/ActionDefinition.java index 61c63eff..17218a93 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/content/action/ActionDefinition.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/content/action/ActionDefinition.java @@ -133,8 +133,8 @@ public enum ActionDefinition { USER_ACCOUNT_DELETE( new LocTextKey("sebserver.useraccount.action.delete"), ImageIcon.DELETE, - PageStateDefinitionImpl.USER_ACCOUNT_LIST, - ActionCategory.USER_ACCOUNT_LIST), + PageStateDefinitionImpl.USER_ACCOUNT_VIEW, + ActionCategory.FORM), USER_ACCOUNT_CHANGE_PASSWORD( new LocTextKey("sebserver.useraccount.action.change.password"), diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/service/page/PageService.java b/src/main/java/ch/ethz/seb/sebserver/gui/service/page/PageService.java index b851cf0f..384bc6fa 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/service/page/PageService.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/service/page/PageService.java @@ -10,6 +10,7 @@ package ch.ethz.seb.sebserver.gui.service.page; import java.util.Arrays; import java.util.HashSet; +import java.util.List; import java.util.Set; import java.util.function.BooleanSupplier; import java.util.function.Consumer; @@ -294,7 +295,7 @@ public interface PageService { * @param apiCall the SEB Server API RestCall that feeds the table with data * @param the type of the Entity of the table * @return TableBuilder of specified type */ - default TableBuilder entityTableBuilder(final RestCall> apiCall) { + default TableBuilder entityTableBuilder(final RestCall> apiCall) { return entityTableBuilder(apiCall.getClass().getSimpleName(), apiCall); } @@ -304,10 +305,12 @@ public interface PageService { * @param apiCall the SEB Server API RestCall that feeds the table with data * @param the type of the Entity of the table * @return TableBuilder of specified type */ - TableBuilder entityTableBuilder( + TableBuilder entityTableBuilder( String name, RestCall> apiCall); + TableBuilder staticListTableBuilder(final List staticList, EntityType entityType); + /** Get a new PageActionBuilder for a given PageContext. * * @param pageContext the PageContext that is used by the new PageActionBuilder @@ -347,9 +350,29 @@ public interface PageService { final Composite parent, final Function contentFunction, final boolean showScrollbars) { + return createManagedVScrolledComposite(parent, contentFunction, showScrollbars, false); + } + + /** Creates a ScrolledComposite with content supplied the given content creation function. + * The content creation function is used to create the content Composite as a child of the + * newly created ScrolledComposite. + * Also adds an update function within the ScrolledComposite Data mapping. If a child inside + * the ScrolledComposite changes its dimensions the method updateScrolledComposite must be + * called to update the ScrolledComposite scrolled content. + * + * @param parent the parent Composite of the ScrolledComposite + * @param contentFunction the content creation function + * @param showScrollbars indicates whether the scrollbar shall always be shown + * @param fill indicates if the content shall be vertically filled + * @return the child composite that is scrolled by the newly created ScrolledComposite */ + static Composite createManagedVScrolledComposite( + final Composite parent, + final Function contentFunction, + final boolean showScrollbars, + final boolean fill) { final ScrolledComposite scrolledComposite = new ScrolledComposite(parent, SWT.BORDER | SWT.V_SCROLL); - scrolledComposite.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, true)); + scrolledComposite.setLayoutData(new GridData(SWT.FILL, (fill) ? SWT.FILL : SWT.TOP, true, true)); final Composite content = contentFunction.apply(scrolledComposite); scrolledComposite.setContent(content); diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/service/page/impl/ModalInputDialog.java b/src/main/java/ch/ethz/seb/sebserver/gui/service/page/impl/ModalInputDialog.java index 9a9d591e..fea904a1 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/service/page/impl/ModalInputDialog.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/service/page/impl/ModalInputDialog.java @@ -115,15 +115,15 @@ public class ModalInputDialog extends Dialog { final ModalInputDialogComposer contentComposer) { // Create the selection dialog window - final Shell shell = new Shell(getParent(), getStyle()); - shell.setText(getText()); - shell.setData(RWT.CUSTOM_VARIANT, CustomVariant.MESSAGE.key); - shell.setText(this.widgetFactory.getI18nSupport().getText(title)); - shell.setLayout(new GridLayout(2, true)); + this.shell = new Shell(getParent(), getStyle()); + this.shell.setText(getText()); + this.shell.setData(RWT.CUSTOM_VARIANT, CustomVariant.MESSAGE.key); + this.shell.setText(this.widgetFactory.getI18nSupport().getText(title)); + this.shell.setLayout(new GridLayout(2, true)); final GridData gridData2 = new GridData(SWT.FILL, SWT.TOP, false, false); - shell.setLayoutData(gridData2); + this.shell.setLayoutData(gridData2); - final Composite main = new Composite(shell, SWT.NONE); + final Composite main = new Composite(this.shell, SWT.NONE); main.setLayout(new GridLayout()); final GridData gridData = new GridData(SWT.FILL, SWT.FILL, true, true); gridData.horizontalSpan = 2; @@ -133,7 +133,7 @@ public class ModalInputDialog extends Dialog { final Supplier valueSupplier = contentComposer.compose(main); gridData.heightHint = calcDialogHeight(main); - final Button ok = this.widgetFactory.buttonLocalized(shell, OK_TEXT_KEY); + final Button ok = this.widgetFactory.buttonLocalized(this.shell, OK_TEXT_KEY); GridData data = new GridData(GridData.HORIZONTAL_ALIGN_END); data.widthHint = this.buttonWidth; ok.setLayoutData(data); @@ -141,16 +141,16 @@ public class ModalInputDialog extends Dialog { if (valueSupplier != null) { final T result = valueSupplier.get(); if (callback.test(result)) { - shell.close(); + this.shell.close(); } } else { - shell.close(); + this.shell.close(); } }); - shell.setDefaultButton(ok); + this.shell.setDefaultButton(ok); - final Button cancel = this.widgetFactory.buttonLocalized(shell, CANCEL_TEXT_KEY); + final Button cancel = this.widgetFactory.buttonLocalized(this.shell, CANCEL_TEXT_KEY); data = new GridData(GridData.HORIZONTAL_ALIGN_BEGINNING); data.widthHint = this.buttonWidth; cancel.setLayoutData(data); @@ -158,10 +158,10 @@ public class ModalInputDialog extends Dialog { if (cancelCallback != null) { cancelCallback.run(); } - shell.close(); + this.shell.close(); }); - finishUp(shell); + finishUp(this.shell); } public void open( diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/service/page/impl/ModelInputWizard.java b/src/main/java/ch/ethz/seb/sebserver/gui/service/page/impl/ModelInputWizard.java index c86ba639..c8691bd4 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/service/page/impl/ModelInputWizard.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/service/page/impl/ModelInputWizard.java @@ -25,6 +25,8 @@ import org.eclipse.swt.widgets.Button; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Dialog; import org.eclipse.swt.widgets.Shell; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import ch.ethz.seb.sebserver.gbl.util.Utils; import ch.ethz.seb.sebserver.gui.service.i18n.LocTextKey; @@ -34,6 +36,8 @@ import ch.ethz.seb.sebserver.gui.widget.WidgetFactory.CustomVariant; public class ModelInputWizard extends Dialog { + private static final Logger log = LoggerFactory.getLogger(ModelInputWizard.class); + private static final long serialVersionUID = -3314062148477979319L; private final WidgetFactory widgetFactory; @@ -81,33 +85,92 @@ public class ModelInputWizard extends Dialog { final WizardPage... pages) { // Create the selection dialog window - final Shell shell = new Shell(getParent(), getStyle()); - shell.setText(getText()); - shell.setData(RWT.CUSTOM_VARIANT, CustomVariant.MESSAGE.key); - shell.setText(this.widgetFactory.getI18nSupport().getText(title)); - shell.setLayout(new GridLayout(1, true)); - final GridData gridData2 = new GridData(SWT.FILL, SWT.TOP, false, false); - shell.setLayoutData(gridData2); + this.shell = new Shell(getParent(), getStyle()); + this.shell.setText(getText()); + this.shell.setData(RWT.CUSTOM_VARIANT, CustomVariant.MESSAGE.key); + this.shell.setText(this.widgetFactory.getI18nSupport().getText(title)); + this.shell.setLayout(new GridLayout()); + final GridData gridData2 = new GridData(SWT.FILL, SWT.FILL, true, true); + this.shell.setLayoutData(gridData2); // the content composite - final Composite main = new Composite(shell, SWT.NONE); + final Composite main = new Composite(this.shell, SWT.NONE); main.setLayout(new GridLayout()); final GridData gridData = new GridData(SWT.FILL, SWT.FILL, true, true); gridData.widthHint = this.dialogWidth; main.setLayoutData(gridData); - final Composite actionComp = new Composite(shell, SWT.NONE); + final Composite content = new Composite(main, SWT.NONE); + content.setLayout(new GridLayout()); + final GridData gridDataContent = new GridData(SWT.FILL, SWT.FILL, true, true); + content.setLayoutData(gridDataContent); + content.setData(RWT.CUSTOM_VARIANT, CustomVariant.MESSAGE.key); + + final Composite actionComp = new Composite(main, SWT.NONE); actionComp.setLayout(new GridLayout()); - actionComp.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); + final GridData gridData3 = new GridData(SWT.FILL, SWT.FILL, true, true); + actionComp.setLayoutData(gridData3); final Composite actionsComp = new Composite(actionComp, SWT.NONE); actionsComp.setLayout(new RowLayout(SWT.HORIZONTAL)); - actionComp.setLayoutData(new GridData(SWT.TRAIL, SWT.FILL, true, true)); + final GridData gridData4 = new GridData(SWT.CENTER, SWT.FILL, true, true); + actionComp.setLayoutData(gridData4); final List> pageList = Utils.immutableListOf(pages); - createPage(null, pageList, null, main, actionsComp); + createPage(null, pageList, null, content, actionsComp, cancelCallback); + finishUp(this.shell); + } + + private void createPage( + final String pageId, + final List> pages, + final T valueFromPrevPage, + final Composite contentComp, + final Composite actionsComp, + final Runnable cancelCallback) { + + try { + + final Optional> newPage = (pageId != null) + ? pages.stream() + .filter(p -> pageId.equals(p.id)) + .findFirst() + : pages.stream() + .filter(page -> page.isStart) + .findFirst(); + + if (!newPage.isPresent()) { + return; + } + + final WizardPage page = newPage.get(); + final Supplier valueSupplier = page.contentCompose.apply(valueFromPrevPage, contentComp); + + if (page.actions != null) { + for (final WizardAction action : page.actions) { + final Button actionButton = this.widgetFactory.buttonLocalized(actionsComp, action.name); + final RowData data = new RowData(); + data.width = this.buttonWidth; + actionButton.setLayoutData(data); + + actionButton.addListener(SWT.Selection, event -> { + if (valueSupplier != null) { + final T result = valueSupplier.get(); + if (action.toPage != null) { + PageService.clearComposite(contentComp); + PageService.clearComposite(actionsComp); + createPage(action.toPage, pages, result, contentComp, actionsComp, cancelCallback); + } else { + action.callback.test(result); + this.shell.close(); + } + } else { + this.shell.close(); + } + }); + } + } - if (cancelCallback != null) { final Button cancel = this.widgetFactory.buttonLocalized(actionsComp, ModalInputDialog.CANCEL_TEXT_KEY); final RowData data = new RowData(); data.width = this.buttonWidth; @@ -116,60 +179,24 @@ public class ModelInputWizard extends Dialog { if (cancelCallback != null) { cancelCallback.run(); } - shell.close(); + this.shell.close(); }); - } - gridData.heightHint = calcDialogHeight(main); - finishUp(shell); - } + try { + final Composite parent = contentComp.getParent(); + final int dialogHeight = calcDialogHeight(parent); + ((GridData) parent.getLayoutData()).heightHint = dialogHeight; + ((GridData) contentComp.getLayoutData()).heightHint = dialogHeight - 55; + ((GridData) actionsComp.getLayoutData()).heightHint = 45; - private void createPage( - final String pageId, - final List> pages, - final T valueFromPrevPage, - final Composite contentComp, - final Composite actionsComp) { - - final Optional> newPage = (pageId != null) - ? pages.stream() - .filter(p -> pageId.equals(p.id)) - .findFirst() - : pages.stream() - .filter(page -> page.isStart) - .findFirst(); - - if (!newPage.isPresent()) { - return; - } - - final WizardPage page = newPage.get(); - PageService.clearComposite(contentComp); - PageService.clearComposite(actionsComp); - - final Supplier valueSupplier = page.contentCompose.apply(valueFromPrevPage, contentComp); - - if (page.actions != null) { - for (final WizardAction action : page.actions) { - final Button actionButton = this.widgetFactory.buttonLocalized(actionsComp, action.name); - final RowData data = new RowData(); - data.width = this.buttonWidth; - actionButton.setLayoutData(data); - - actionButton.addListener(SWT.Selection, event -> { - if (valueSupplier != null) { - final T result = valueSupplier.get(); - if (action.toPage != null) { - createPage(action.toPage, pages, result, contentComp, actionsComp); - } else { - action.callback.test(result); - this.shell.close(); - } - } else { - this.shell.close(); - } - }); + contentComp.getShell().layout(true, true); + } catch (final Exception e) { + log.warn("Failed to calculate dialog height: {}", e.getMessage()); } + + } catch (final Exception e) { + log.error("Unexpected error: ", e); + this.shell.close(); } } diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/service/page/impl/PageServiceImpl.java b/src/main/java/ch/ethz/seb/sebserver/gui/service/page/impl/PageServiceImpl.java index e91523b8..0b9cd70e 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/service/page/impl/PageServiceImpl.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/service/page/impl/PageServiceImpl.java @@ -358,13 +358,25 @@ public class PageServiceImpl implements PageService { } @Override - public TableBuilder entityTableBuilder( + public TableBuilder entityTableBuilder( final String name, final RestCall> apiCall) { return new TableBuilder<>(name, this, apiCall); } + @Override + public TableBuilder staticListTableBuilder( + final List staticList, + final EntityType entityType) { + + return new TableBuilder<>( + (entityType != null) + ? entityType.name() + : "", + this, staticList, entityType); + } + @Override public void logout(final PageContext pageContext) { this.clearState(); diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/table/ColumnDefinition.java b/src/main/java/ch/ethz/seb/sebserver/gui/table/ColumnDefinition.java index da4a52b6..90275f35 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/table/ColumnDefinition.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/table/ColumnDefinition.java @@ -12,12 +12,11 @@ import java.util.List; import java.util.function.Function; import java.util.function.Supplier; -import ch.ethz.seb.sebserver.gbl.model.Entity; import ch.ethz.seb.sebserver.gbl.util.Tuple; import ch.ethz.seb.sebserver.gui.service.i18n.LocTextKey; import ch.ethz.seb.sebserver.gui.table.TableFilter.CriteriaType; -public final class ColumnDefinition { +public final class ColumnDefinition { private static final String TOOLTIP_TEXT_KEY_SUFFIX = ".tooltip"; diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/table/EntityTable.java b/src/main/java/ch/ethz/seb/sebserver/gui/table/EntityTable.java index e64c5ca9..03ee9169 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/table/EntityTable.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/table/EntityTable.java @@ -54,13 +54,12 @@ 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.impl.PageAction; -import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestCall; import ch.ethz.seb.sebserver.gui.service.remote.webservice.auth.CurrentUser; import ch.ethz.seb.sebserver.gui.widget.WidgetFactory; import ch.ethz.seb.sebserver.gui.widget.WidgetFactory.ImageIcon; import io.micrometer.core.instrument.util.StringUtils; -public class EntityTable { +public class EntityTable { private static final Logger log = LoggerFactory.getLogger(EntityTable.class); @@ -108,7 +107,7 @@ public class EntityTable { final boolean markupEnabled, final int type, final PageContext pageContext, - final RestCall> restCall, + final PageSupplier pageSupplier, final Function, PageSupplier.Builder> pageSupplierAdapter, final PageService pageService, final List> columns, @@ -132,7 +131,7 @@ public class EntityTable { this.i18nSupport = pageService.getI18nSupport(); this.pageContext = pageContext; this.widgetFactory = pageService.getWidgetFactory(); - this.pageSupplier = new RestCallPageSupplier<>(restCall); + this.pageSupplier = pageSupplier; this.pageSupplierAdapter = (pageSupplierAdapter != null) ? pageSupplierAdapter : Function.identity(); this.columns = Utils.immutableListOf(columns); this.emptyMessage = emptyMessage; @@ -581,7 +580,11 @@ public class EntityTable { } private EntityKey getRowDataId(final TableItem item) { - return getRowData(item).getEntityKey(); + final ROW rowData = getRowData(item); + if (rowData instanceof Entity) { + return ((Entity) rowData).getEntityKey(); + } + return null; } private void updateValues(final EntityTable table) { diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/table/StaticListPageSupplier.java b/src/main/java/ch/ethz/seb/sebserver/gui/table/StaticListPageSupplier.java new file mode 100644 index 00000000..6745ae59 --- /dev/null +++ b/src/main/java/ch/ethz/seb/sebserver/gui/table/StaticListPageSupplier.java @@ -0,0 +1,107 @@ +/* + * 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.table; + +import java.util.ArrayList; +import java.util.List; +import java.util.function.Function; + +import org.springframework.util.MultiValueMap; + +import ch.ethz.seb.sebserver.gbl.api.EntityType; +import ch.ethz.seb.sebserver.gbl.model.Page; +import ch.ethz.seb.sebserver.gbl.model.PageSortOrder; +import ch.ethz.seb.sebserver.gbl.util.Result; + +public class StaticListPageSupplier implements PageSupplier { + + private final EntityType entityType; + private final List list; + + public StaticListPageSupplier(final List list, final EntityType entityType) { + this.list = list; + this.entityType = entityType; + } + + @Override + public EntityType getEntityType() { + return this.entityType; + } + + @Override + public Builder newBuilder() { + return new StaticListTableBuilderAdapter<>(this.list); + } + + public static final class StaticListTableBuilderAdapter implements Builder { + + private final List list; + private int pageNumber; + private int pageSize; + private String column; + private PageSortOrder order; + + private StaticListTableBuilderAdapter(final List list) { + this.list = new ArrayList<>(list); + } + + @Override + public Builder withPaging(final int pageNumber, final int pageSize) { + this.pageNumber = pageNumber; + this.pageSize = pageSize; + return this; + } + + @Override + public Builder withSorting(final String column, final PageSortOrder order) { + this.column = column; + this.order = order; + return this; + } + + @Override + public Builder withQueryParams(final MultiValueMap params) { + return this; + } + + @Override + public Builder withQueryParam(final String name, final String value) { + return this; + } + + @Override + public Builder withURIVariable(final String name, final String id) { + return this; + } + + @Override + public Builder apply(final Function, Builder> f) { + return f.apply(this); + } + + @Override + public Result> getPage() { + return Result.tryCatch(() -> { + if (this.list.isEmpty()) { + return new Page<>(0, this.pageNumber, this.column, this.list); + } + + if (this.pageSize <= 0) { + return new Page<>(1, 1, this.column, this.list); + } + + final int numOfPages = this.list.size() / this.pageSize; + final List subList = this.list.subList(this.pageNumber * this.pageSize, + this.pageNumber * this.pageSize + this.pageSize); + return new Page<>(numOfPages, this.pageNumber, this.column, subList); + }); + } + } + +} diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/table/TableBuilder.java b/src/main/java/ch/ethz/seb/sebserver/gui/table/TableBuilder.java index 20ca1e71..ebec8eea 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/table/TableBuilder.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/table/TableBuilder.java @@ -22,7 +22,7 @@ import org.eclipse.swt.widgets.TableItem; import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.MultiValueMap; -import ch.ethz.seb.sebserver.gbl.model.Entity; +import ch.ethz.seb.sebserver.gbl.api.EntityType; import ch.ethz.seb.sebserver.gbl.model.Page; import ch.ethz.seb.sebserver.gui.service.i18n.LocTextKey; import ch.ethz.seb.sebserver.gui.service.page.PageContext; @@ -30,11 +30,13 @@ import ch.ethz.seb.sebserver.gui.service.page.PageService; import ch.ethz.seb.sebserver.gui.service.page.impl.PageAction; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestCall; -public class TableBuilder { +public class TableBuilder { private final String name; private final PageService pageService; final RestCall> restCall; + final List staticList; + final EntityType entityType; private final MultiValueMap staticQueryParams; final List> columns = new ArrayList<>(); LocTextKey emptyMessage; @@ -55,6 +57,22 @@ public class TableBuilder { this.name = name; this.pageService = pageService; this.restCall = restCall; + this.staticList = null; + this.entityType = null; + this.staticQueryParams = new LinkedMultiValueMap<>(); + } + + public TableBuilder( + final String name, + final PageService pageService, + final List staticList, + final EntityType entityType) { + + this.name = name; + this.pageService = pageService; + this.restCall = null; + this.staticList = staticList; + this.entityType = entityType; this.staticQueryParams = new LinkedMultiValueMap<>(); } @@ -153,12 +171,14 @@ public class TableBuilder { } public EntityTable compose(final PageContext pageContext) { - return new EntityTable<>( + return new EntityTable( this.name, this.markupEnabled, this.type, pageContext, - this.restCall, + (this.restCall != null) + ? new RestCallPageSupplier<>(this.restCall) + : new StaticListPageSupplier<>(this.staticList, this.entityType), this.restCallAdapter, this.pageService, this.columns, diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/table/TableFilter.java b/src/main/java/ch/ethz/seb/sebserver/gui/table/TableFilter.java index 8bfdba10..a1bf0515 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/table/TableFilter.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/table/TableFilter.java @@ -31,7 +31,6 @@ import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.MultiValueMap; import ch.ethz.seb.sebserver.gbl.Constants; -import ch.ethz.seb.sebserver.gbl.model.Entity; import ch.ethz.seb.sebserver.gbl.model.user.UserInfo; import ch.ethz.seb.sebserver.gbl.util.Tuple; import ch.ethz.seb.sebserver.gbl.util.Utils; @@ -41,7 +40,7 @@ import ch.ethz.seb.sebserver.gui.widget.Selection; import ch.ethz.seb.sebserver.gui.widget.WidgetFactory; import ch.ethz.seb.sebserver.gui.widget.WidgetFactory.ImageIcon; -public class TableFilter { +public class TableFilter { private static final Logger log = LoggerFactory.getLogger(TableFilter.class); @@ -365,8 +364,8 @@ public class TableFilter { final Supplier>> _resourceSupplier = resourceSupplier; resourceSupplier = () -> { - List> list = _resourceSupplier.get(); - list.add(new Tuple<>(StringUtils.EMPTY, entityTable.i18nSupport.getText(ALL_TEXT))); + final List> list = _resourceSupplier.get(); + list.add(new Tuple<>(StringUtils.EMPTY, TableFilter.this.entityTable.i18nSupport.getText(ALL_TEXT))); return list; }; diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/widget/WidgetFactory.java b/src/main/java/ch/ethz/seb/sebserver/gui/widget/WidgetFactory.java index c6493e93..4fcb1020 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/widget/WidgetFactory.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/widget/WidgetFactory.java @@ -302,9 +302,27 @@ public class WidgetFactory { g.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false)); return g; }, + false, false); } + /** Use this to create a scrolled Composite for usual popup forms + * + * @param parent The parent Composite + * @return the scrolled Composite to add the form content */ + public Composite createPopupScrollCompositeFilled(final Composite parent) { + return PageService.createManagedVScrolledComposite( + parent, + scrolledComposite -> { + final Composite g = new Composite(scrolledComposite, SWT.NONE); + g.setLayout(new GridLayout()); + g.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false)); + return g; + }, + false, + true); + } + public Composite createWarningPanel(final Composite parent) { final Composite composite = new Composite(parent, SWT.NONE); final GridData gridData = new GridData(SWT.FILL, SWT.FILL, true, false); diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/ExamConfigurationMapDAOImpl.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/ExamConfigurationMapDAOImpl.java index 6d21014e..687ff691 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/ExamConfigurationMapDAOImpl.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/ExamConfigurationMapDAOImpl.java @@ -449,6 +449,10 @@ public class ExamConfigurationMapDAOImpl implements ExamConfigurationMapDAO { .build() .execute(); + if (examsIds.isEmpty()) { + return Collections.emptyList(); + } + return toDependencies( this.examConfigurationMapRecordMapper.selectByExample() .where( diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/UserDAOImpl.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/UserDAOImpl.java index e39baa56..40bef45a 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/UserDAOImpl.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/UserDAOImpl.java @@ -335,6 +335,12 @@ public class UserDAOImpl implements UserDAO { final List ids = extractListOfPKs(all); + // get all user records for later processing + final List users = this.userRecordMapper.selectByExample() + .where(UserRecordDynamicSqlSupport.id, isIn(ids)) + .build() + .execute(); + // first delete assigned user roles this.roleRecordMapper.deleteByExample() .where(RoleRecordDynamicSqlSupport.userId, isIn(ids)) @@ -347,8 +353,8 @@ public class UserDAOImpl implements UserDAO { .build() .execute(); - return ids.stream() - .map(id -> new EntityKey(id, EntityType.USER)) + return users.stream() + .map(rec -> new EntityKey(rec.getUuid(), EntityType.USER)) .collect(Collectors.toList()); }); } diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/ClientConnectionController.java b/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/ClientConnectionController.java index 1b4557d7..6939f806 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/ClientConnectionController.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/ClientConnectionController.java @@ -56,6 +56,7 @@ public class ClientConnectionController extends ReadonlyEntityController getDependencies( final String modelId, final BulkActionType bulkActionType, + final boolean addIncludes, final List includes) { throw new UnsupportedOperationException(); } diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/ClientEventController.java b/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/ClientEventController.java index e7f95998..d89ccb67 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/ClientEventController.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/ClientEventController.java @@ -118,6 +118,7 @@ public class ClientEventController extends ReadonlyEntityController getDependencies( final String modelId, final BulkActionType bulkActionType, + final boolean addIncludes, final List includes) { throw new UnsupportedOperationException(); } diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/ConfigurationController.java b/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/ConfigurationController.java index cc56ea21..40bc1246 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/ConfigurationController.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/ConfigurationController.java @@ -114,6 +114,7 @@ public class ConfigurationController extends ReadonlyEntityController getDependencies( final String modelId, final BulkActionType bulkActionType, + final boolean addIncludes, final List includes) { throw new UnsupportedOperationException(); } diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/ConfigurationValueController.java b/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/ConfigurationValueController.java index 0e50cd9f..d67f9090 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/ConfigurationValueController.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/ConfigurationValueController.java @@ -90,6 +90,7 @@ public class ConfigurationValueController extends EntityController includes) { throw new UnsupportedOperationException(); } diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/EntityController.java b/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/EntityController.java index d0a395bf..43a1c70f 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/EntityController.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/EntityController.java @@ -213,6 +213,7 @@ public abstract class EntityController { public Collection getDependencies( @PathVariable final String modelId, @RequestParam(name = API.PARAM_BULK_ACTION_TYPE, required = true) final BulkActionType bulkActionType, + @RequestParam(name = API.PARAM_BULK_ACTION_ADD_INCLUDES, defaultValue = "false") final boolean addIncludes, @RequestParam(name = API.PARAM_BULK_ACTION_INCLUDES, required = false) final List includes) { this.entityDAO @@ -223,7 +224,7 @@ public abstract class EntityController { bulkActionType, this.entityDAO.entityType(), Arrays.asList(new EntityKey(modelId, this.entityDAO.entityType())), - convertToEntityType(includes)); + convertToEntityType(addIncludes, includes)); this.bulkActionService.collectDependencies(bulkAction); return bulkAction.getDependencies(); @@ -326,29 +327,32 @@ public abstract class EntityController { produces = MediaType.APPLICATION_JSON_UTF8_VALUE) public EntityProcessingReport hardDelete( @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 includes) { return this.entityDAO.byModelId(modelId) .flatMap(this::checkWriteAccess) .flatMap(this::validForDelete) - .flatMap(entity -> bulkDelete(entity, convertToEntityType(includes))) + .flatMap(entity -> bulkDelete(entity, convertToEntityType(addIncludes, includes))) .flatMap(this::notifyDeleted) .flatMap(pair -> this.logBulkAction(pair.b)) .getOrThrow(); } - protected EnumSet convertToEntityType(final List includes) { + protected EnumSet convertToEntityType(final boolean addIncludes, final List includes) { final EnumSet includeDependencies = (includes != null) - ? EnumSet.copyOf(includes.stream().map(name -> { - try { - return EntityType.valueOf(name); - } catch (final Exception e) { - return null; - } - }) - .filter(Objects::nonNull) - .collect(Collectors.toSet())) - : null; + ? (includes.isEmpty()) + ? EnumSet.noneOf(EntityType.class) + : EnumSet.copyOf(includes.stream().map(name -> { + try { + return EntityType.valueOf(name); + } catch (final Exception e) { + return null; + } + }) + .filter(Objects::nonNull) + .collect(Collectors.toSet())) + : (addIncludes) ? EnumSet.noneOf(EntityType.class) : null; return includeDependencies; } diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/ExamConfigurationMappingController.java b/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/ExamConfigurationMappingController.java index c022758f..da107c8c 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/ExamConfigurationMappingController.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/ExamConfigurationMappingController.java @@ -166,13 +166,14 @@ public class ExamConfigurationMappingController extends EntityController includes) { return this.entityDAO.byModelId(modelId) .flatMap(this::checkWriteAccess) .flatMap(entity -> this.examConfigUpdateService.processExamConfigurationMappingChange( entity, - e -> bulkDelete(e, convertToEntityType(includes)))) + e -> bulkDelete(e, convertToEntityType(addIncludes, includes)))) .flatMap(this::notifyDeleted) .flatMap(pair -> this.logBulkAction(pair.b)) .getOrThrow(); diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/ReadonlyEntityController.java b/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/ReadonlyEntityController.java index c81f40ea..a9fdee3a 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/ReadonlyEntityController.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/ReadonlyEntityController.java @@ -63,6 +63,7 @@ public abstract class ReadonlyEntityController includes) { throw new UnsupportedOperationException(ONLY_READ_ACCESS); } diff --git a/src/main/resources/messages.properties b/src/main/resources/messages.properties index ea715e37..ce6b6dad 100644 --- a/src/main/resources/messages.properties +++ b/src/main/resources/messages.properties @@ -57,6 +57,7 @@ sebserver.overall.types.entityType.CONFIGURATION=Exam Configuration History sebserver.overall.types.entityType.CONFIGURATION_NODE=Exam Configuration sebserver.overall.types.entityType.EXAM_CONFIGURATION_MAP=Exam Configuration Mapping sebserver.overall.types.entityType.EXAM=Exam +sebserver.overall.types.entityType.CLIENT_CONNECTION=SEB Client Connection sebserver.overall.types.entityType.INDICATOR=Indicator sebserver.overall.types.entityType.THRESHOLD=Threshold sebserver.overall.types.entityType.INSTITUTION=Institution @@ -256,7 +257,13 @@ sebserver.useraccount.form.password.new.confirm=Confirm New Password sebserver.useraccount.form.password.new.confirm.tooltip=Please confirm the password sebserver.useraccount.delete.form.title=Delete User Account -sebserver.useraccount.delete.form.info=Delete this User Account with all dependencies. +sebserver.useraccount.delete.form.info.title=Please Note +sebserver.useraccount.delete.form.info=Please Note:
    This deletes the particular User Account with all selected dependencies.
    The User Account and selected dependent exams and exam configurations, will be lost after deletion.
    Please use the "Show Report" action below to see a report of all objects that will be
    deleted and check them carefully. +sebserver.useraccount.delete.form.report.info=The following objects will be deleted within this User Account deletion.
Please check them carefully before delete. +sebserver.useraccount.delete.form.report.empty=No dependencies will be deleted. +sebserver.useraccount.delete.form.report.list.type=Type +sebserver.useraccount.delete.form.report.list.name=Name +sebserver.useraccount.delete.form.report.list.description=Description sebserver.useraccount.delete.form.accountName=Name sebserver.useraccount.delete.form.deleteAlsoConfigs=Include all Exam Configurations sebserver.useraccount.delete.form.deleteAlsoConfigs.tooltip=This includes all Exam Configuration which this uses has created and is owner of @@ -264,6 +271,8 @@ sebserver.useraccount.delete.form.deleteAlsoExams=Include all Exams sebserver.useraccount.delete.form.deleteAlsoExams.tooltip=This includes all Exams which the user has imported and is owner of.
This also includes all Client Connections and all SEB Client Logs that where created within an Exam that is part of this deletion. sebserver.useraccount.delete.form.action.delete=Delete sebserver.useraccount.delete.form.action.report=Show Report +sebserver.useraccount.delete.confirm.title=Deletion Successful +sebserver.useraccount.delete.confirm.message=The User Account ({0}) was successfully deleted.
Also the following number dependencies where successfully deleted: {1}.

And there where {2} errors. ################################ # LMS Setup