SEBSERV-10 GUI implementation

This commit is contained in:
anhefti 2020-07-21 16:27:08 +02:00
parent 265624ec4c
commit cc0f15ab62
23 changed files with 520 additions and 149 deletions

View file

@ -26,7 +26,9 @@ public final class API {
public static final String PARAM_MODEL_ID_LIST = "modelIds"; public static final String PARAM_MODEL_ID_LIST = "modelIds";
public static final String PARAM_PARENT_MODEL_ID = "parentModelId"; public static final String PARAM_PARENT_MODEL_ID = "parentModelId";
public static final String PARAM_ENTITY_TYPE = "entityType"; public static final String PARAM_ENTITY_TYPE = "entityType";
public static final String PARAM_BULK_ACTION_TYPE = "bulkActionType"; 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_BULK_ACTION_INCLUDES = "bulkActionIncludes";
public static final String PARAM_VIEW_ID = "viewId"; public static final String PARAM_VIEW_ID = "viewId";
public static final String PARAM_INSTRUCTION_TYPE = "instructionType"; public static final String PARAM_INSTRUCTION_TYPE = "instructionType";

View file

@ -8,43 +8,60 @@
package ch.ethz.seb.sebserver.gui.content; package ch.ethz.seb.sebserver.gui.content;
import java.util.List;
import java.util.Set; import java.util.Set;
import java.util.function.BiFunction; import java.util.function.BiFunction;
import java.util.function.Function; import java.util.function.Function;
import java.util.function.Predicate; import java.util.function.Predicate;
import java.util.function.Supplier; import java.util.function.Supplier;
import java.util.stream.Collectors;
import org.apache.commons.lang3.BooleanUtils; import org.apache.commons.lang3.BooleanUtils;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.widgets.Composite; 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.context.annotation.Lazy;
import org.springframework.stereotype.Component; 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;
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.model.EntityDependency; import ch.ethz.seb.sebserver.gbl.model.EntityDependency;
import ch.ethz.seb.sebserver.gbl.model.EntityKey; 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.model.user.UserInfo;
import ch.ethz.seb.sebserver.gbl.profile.GuiProfile; import ch.ethz.seb.sebserver.gbl.profile.GuiProfile;
import ch.ethz.seb.sebserver.gbl.util.Utils; 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.Form;
import ch.ethz.seb.sebserver.gui.form.FormBuilder; import ch.ethz.seb.sebserver.gui.form.FormBuilder;
import ch.ethz.seb.sebserver.gui.form.FormHandle; 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.i18n.LocTextKey;
import ch.ethz.seb.sebserver.gui.service.page.PageContext; 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.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;
import ch.ethz.seb.sebserver.gui.service.page.impl.ModelInputWizard.WizardAction; 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.ModelInputWizard.WizardPage;
import ch.ethz.seb.sebserver.gui.service.page.impl.PageAction; 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.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.GetUserAccount;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.useraccount.GetUserDependency; 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 @Lazy
@Component @Component
@GuiProfile @GuiProfile
public class UserAccountDeletePopup { 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_CONFIGS = "WITH_CONFIGS";
private final static String ARG_WITH_EXAMS = "WITH_EXAMS"; private final static String ARG_WITH_EXAMS = "WITH_EXAMS";
@ -52,6 +69,17 @@ public class UserAccountDeletePopup {
new LocTextKey("sebserver.useraccount.delete.form.title"); new LocTextKey("sebserver.useraccount.delete.form.title");
private final static LocTextKey FORM_INFO = private final static LocTextKey FORM_INFO =
new LocTextKey("sebserver.useraccount.delete.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 = private final static LocTextKey FORM_NAME =
new LocTextKey("sebserver.useraccount.delete.form.accountName"); new LocTextKey("sebserver.useraccount.delete.form.accountName");
private final static LocTextKey FORM_CONFIGS = private final static LocTextKey FORM_CONFIGS =
@ -63,6 +91,9 @@ public class UserAccountDeletePopup {
private final static LocTextKey ACTION_REPORT = private final static LocTextKey ACTION_REPORT =
new LocTextKey("sebserver.useraccount.delete.form.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; private final PageService pageService;
protected UserAccountDeletePopup(final PageService pageService) { protected UserAccountDeletePopup(final PageService pageService) {
@ -76,15 +107,17 @@ public class UserAccountDeletePopup {
new ModelInputWizard<PageContext>( new ModelInputWizard<PageContext>(
action.pageContext().getParent().getShell(), action.pageContext().getParent().getShell(),
this.pageService.getWidgetFactory()) this.pageService.getWidgetFactory())
.setLargeDialogWidth(); .setVeryLargeDialogWidth();
final String page1Id = "DELETE_PAGE"; final String page1Id = "DELETE_PAGE";
final String page2Id = "REPORT_PAGE"; final String page2Id = "REPORT_PAGE";
final Predicate<PageContext> callback = pc -> doDelete(this.pageService, pc); final Predicate<PageContext> callback = pc -> doDelete(this.pageService, pc);
final BiFunction<PageContext, Composite, Supplier<PageContext>> composePage1 = final BiFunction<PageContext, Composite, Supplier<PageContext>> composePage1 =
(formHandle, content) -> composeDeleteDialog(content, pageContext); (prefPageContext, content) -> composeDeleteDialog(content,
(prefPageContext != null) ? prefPageContext : pageContext);
final BiFunction<PageContext, Composite, Supplier<PageContext>> composePage2 = final BiFunction<PageContext, Composite, Supplier<PageContext>> composePage2 =
(formHandle, content) -> composeReportDialog(content, pageContext); (prefPageContext, content) -> composeReportDialog(content,
(prefPageContext != null) ? prefPageContext : pageContext);
final WizardPage<PageContext> page1 = new WizardPage<>( final WizardPage<PageContext> page1 = new WizardPage<>(
page1Id, page1Id,
@ -109,15 +142,81 @@ public class UserAccountDeletePopup {
final PageService pageService, final PageService pageService,
final PageContext pageContext) { final PageContext pageContext) {
final boolean withConfigs = BooleanUtils.toBoolean(pageContext.getAttribute(ARG_WITH_CONFIGS)); try {
final boolean withExams = BooleanUtils.toBoolean(pageContext.getAttribute(ARG_WITH_EXAMS)); final boolean withConfigs = BooleanUtils.toBoolean(pageContext.getAttribute(ARG_WITH_CONFIGS));
return true; 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<EntityProcessingReport>.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<EntityKey> 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<PageContext> composeDeleteDialog( private Supplier<PageContext> composeDeleteDialog(
final Composite parent, final Composite parent,
final PageContext pageContext) { 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 EntityKey entityKey = pageContext.getEntityKey();
final UserInfo userInfo = this.pageService.getRestService() final UserInfo userInfo = this.pageService.getRestService()
.getBuilder(GetUserAccount.class) .getBuilder(GetUserAccount.class)
@ -126,8 +225,10 @@ public class UserAccountDeletePopup {
.get(); .get();
final FormHandle<?> formHandle = this.pageService.formBuilder( final FormHandle<?> formHandle = this.pageService.formBuilder(
pageContext.copyOf(parent)) pageContext.copyOf(grid))
.readonly(false) .readonly(false)
.withDefaultSpanLabel(3)
.withDefaultSpanInput(4)
.addField(FormBuilder.text( .addField(FormBuilder.text(
"USE_NAME", "USE_NAME",
FORM_NAME, FORM_NAME,
@ -153,36 +254,67 @@ public class UserAccountDeletePopup {
final Composite parent, final Composite parent,
final PageContext pageContext) { final PageContext pageContext) {
// get selection final Composite grid = this.pageService.getWidgetFactory()
final boolean withConfigs = BooleanUtils.toBoolean(pageContext.getAttribute(ARG_WITH_CONFIGS)); .createPopupScrollCompositeFilled(parent);
final boolean withExams = BooleanUtils.toBoolean(pageContext.getAttribute(ARG_WITH_EXAMS)); final I18nSupport i18nSupport = this.pageService.getI18nSupport();
// get dependencies final Label title = this.pageService.getWidgetFactory()
final EntityKey entityKey = pageContext.getEntityKey(); .labelLocalized(grid, CustomVariant.TEXT_H3, FORM_REPORT_INFO);
final RestCall<Set<EntityDependency>>.RestCallBuilder restCallBuilder = this.pageService.getRestService() final GridData gridData = new GridData();
.getBuilder(GetUserDependency.class) gridData.horizontalIndent = 10;
.withURIVariable(API.PARAM_MODEL_ID, entityKey.modelId); gridData.verticalIndent = 10;
title.setLayoutData(gridData);
if (withConfigs) { try {
restCallBuilder.withQueryParam( // get selection
API.PARAM_BULK_ACTION_INCLUDES, final boolean withConfigs = BooleanUtils.toBoolean(pageContext.getAttribute(ARG_WITH_CONFIGS));
EntityType.CONFIGURATION_NODE.name()); final boolean withExams = BooleanUtils.toBoolean(pageContext.getAttribute(ARG_WITH_EXAMS));
// get dependencies
final EntityKey entityKey = pageContext.getEntityKey();
final RestCall<Set<EntityDependency>>.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<EntityDependency> dependencies = restCallBuilder.call().getOrThrow();
final List<EntityDependency> list = dependencies.stream().sorted().collect(Collectors.toList());
this.pageService.<EntityDependency> staticListTableBuilder(list, null)
.withEmptyMessage(FORM_REPORT_NONE)
.withColumn(new ColumnDefinition<>(
"FORM_REPORT_LIST_TYPE",
FORM_REPORT_LIST_TYPE,
dep -> i18nSupport
.getText("sebserver.overall.types.entityType." + dep.self.entityType.name()) +
" (" + 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<ConfigurationNode> configTable =
// this.pageService.entityTableBuilder(GetUserDependency.class)
// .
final Set<EntityDependency> dependencies = restCallBuilder.call().getOrThrow();
// TODO get dependencies in case of selection and show all in a list (type / name)
return () -> pageContext;
} }
} }

View file

@ -133,8 +133,8 @@ public enum ActionDefinition {
USER_ACCOUNT_DELETE( USER_ACCOUNT_DELETE(
new LocTextKey("sebserver.useraccount.action.delete"), new LocTextKey("sebserver.useraccount.action.delete"),
ImageIcon.DELETE, ImageIcon.DELETE,
PageStateDefinitionImpl.USER_ACCOUNT_LIST, PageStateDefinitionImpl.USER_ACCOUNT_VIEW,
ActionCategory.USER_ACCOUNT_LIST), ActionCategory.FORM),
USER_ACCOUNT_CHANGE_PASSWORD( USER_ACCOUNT_CHANGE_PASSWORD(
new LocTextKey("sebserver.useraccount.action.change.password"), new LocTextKey("sebserver.useraccount.action.change.password"),

View file

@ -10,6 +10,7 @@ package ch.ethz.seb.sebserver.gui.service.page;
import java.util.Arrays; import java.util.Arrays;
import java.util.HashSet; import java.util.HashSet;
import java.util.List;
import java.util.Set; import java.util.Set;
import java.util.function.BooleanSupplier; import java.util.function.BooleanSupplier;
import java.util.function.Consumer; 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 apiCall the SEB Server API RestCall that feeds the table with data
* @param <T> the type of the Entity of the table * @param <T> the type of the Entity of the table
* @return TableBuilder of specified type */ * @return TableBuilder of specified type */
default <T extends Entity> TableBuilder<T> entityTableBuilder(final RestCall<Page<T>> apiCall) { default <T> TableBuilder<T> entityTableBuilder(final RestCall<Page<T>> apiCall) {
return entityTableBuilder(apiCall.getClass().getSimpleName(), 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 apiCall the SEB Server API RestCall that feeds the table with data
* @param <T> the type of the Entity of the table * @param <T> the type of the Entity of the table
* @return TableBuilder of specified type */ * @return TableBuilder of specified type */
<T extends Entity> TableBuilder<T> entityTableBuilder( <T> TableBuilder<T> entityTableBuilder(
String name, String name,
RestCall<Page<T>> apiCall); RestCall<Page<T>> apiCall);
<T> TableBuilder<T> staticListTableBuilder(final List<T> staticList, EntityType entityType);
/** Get a new PageActionBuilder for a given PageContext. /** Get a new PageActionBuilder for a given PageContext.
* *
* @param pageContext the PageContext that is used by the new PageActionBuilder * @param pageContext the PageContext that is used by the new PageActionBuilder
@ -347,9 +350,29 @@ public interface PageService {
final Composite parent, final Composite parent,
final Function<ScrolledComposite, Composite> contentFunction, final Function<ScrolledComposite, Composite> contentFunction,
final boolean showScrollbars) { 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<ScrolledComposite, Composite> contentFunction,
final boolean showScrollbars,
final boolean fill) {
final ScrolledComposite scrolledComposite = new ScrolledComposite(parent, SWT.BORDER | SWT.V_SCROLL); 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); final Composite content = contentFunction.apply(scrolledComposite);
scrolledComposite.setContent(content); scrolledComposite.setContent(content);

View file

@ -115,15 +115,15 @@ public class ModalInputDialog<T> extends Dialog {
final ModalInputDialogComposer<T> contentComposer) { final ModalInputDialogComposer<T> contentComposer) {
// Create the selection dialog window // Create the selection dialog window
final Shell shell = new Shell(getParent(), getStyle()); this.shell = new Shell(getParent(), getStyle());
shell.setText(getText()); this.shell.setText(getText());
shell.setData(RWT.CUSTOM_VARIANT, CustomVariant.MESSAGE.key); this.shell.setData(RWT.CUSTOM_VARIANT, CustomVariant.MESSAGE.key);
shell.setText(this.widgetFactory.getI18nSupport().getText(title)); this.shell.setText(this.widgetFactory.getI18nSupport().getText(title));
shell.setLayout(new GridLayout(2, true)); this.shell.setLayout(new GridLayout(2, true));
final GridData gridData2 = new GridData(SWT.FILL, SWT.TOP, false, false); 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()); main.setLayout(new GridLayout());
final GridData gridData = new GridData(SWT.FILL, SWT.FILL, true, true); final GridData gridData = new GridData(SWT.FILL, SWT.FILL, true, true);
gridData.horizontalSpan = 2; gridData.horizontalSpan = 2;
@ -133,7 +133,7 @@ public class ModalInputDialog<T> extends Dialog {
final Supplier<T> valueSupplier = contentComposer.compose(main); final Supplier<T> valueSupplier = contentComposer.compose(main);
gridData.heightHint = calcDialogHeight(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); GridData data = new GridData(GridData.HORIZONTAL_ALIGN_END);
data.widthHint = this.buttonWidth; data.widthHint = this.buttonWidth;
ok.setLayoutData(data); ok.setLayoutData(data);
@ -141,16 +141,16 @@ public class ModalInputDialog<T> extends Dialog {
if (valueSupplier != null) { if (valueSupplier != null) {
final T result = valueSupplier.get(); final T result = valueSupplier.get();
if (callback.test(result)) { if (callback.test(result)) {
shell.close(); this.shell.close();
} }
} else { } 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 = new GridData(GridData.HORIZONTAL_ALIGN_BEGINNING);
data.widthHint = this.buttonWidth; data.widthHint = this.buttonWidth;
cancel.setLayoutData(data); cancel.setLayoutData(data);
@ -158,10 +158,10 @@ public class ModalInputDialog<T> extends Dialog {
if (cancelCallback != null) { if (cancelCallback != null) {
cancelCallback.run(); cancelCallback.run();
} }
shell.close(); this.shell.close();
}); });
finishUp(shell); finishUp(this.shell);
} }
public void open( public void open(

View file

@ -25,6 +25,8 @@ import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Dialog; import org.eclipse.swt.widgets.Dialog;
import org.eclipse.swt.widgets.Shell; 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.gbl.util.Utils;
import ch.ethz.seb.sebserver.gui.service.i18n.LocTextKey; 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<T> extends Dialog { public class ModelInputWizard<T> extends Dialog {
private static final Logger log = LoggerFactory.getLogger(ModelInputWizard.class);
private static final long serialVersionUID = -3314062148477979319L; private static final long serialVersionUID = -3314062148477979319L;
private final WidgetFactory widgetFactory; private final WidgetFactory widgetFactory;
@ -81,33 +85,92 @@ public class ModelInputWizard<T> extends Dialog {
final WizardPage<T>... pages) { final WizardPage<T>... pages) {
// Create the selection dialog window // Create the selection dialog window
final Shell shell = new Shell(getParent(), getStyle()); this.shell = new Shell(getParent(), getStyle());
shell.setText(getText()); this.shell.setText(getText());
shell.setData(RWT.CUSTOM_VARIANT, CustomVariant.MESSAGE.key); this.shell.setData(RWT.CUSTOM_VARIANT, CustomVariant.MESSAGE.key);
shell.setText(this.widgetFactory.getI18nSupport().getText(title)); this.shell.setText(this.widgetFactory.getI18nSupport().getText(title));
shell.setLayout(new GridLayout(1, true)); this.shell.setLayout(new GridLayout());
final GridData gridData2 = new GridData(SWT.FILL, SWT.TOP, false, false); final GridData gridData2 = new GridData(SWT.FILL, SWT.FILL, true, true);
shell.setLayoutData(gridData2); this.shell.setLayoutData(gridData2);
// the content composite // the content composite
final Composite main = new Composite(shell, SWT.NONE); final Composite main = new Composite(this.shell, SWT.NONE);
main.setLayout(new GridLayout()); main.setLayout(new GridLayout());
final GridData gridData = new GridData(SWT.FILL, SWT.FILL, true, true); final GridData gridData = new GridData(SWT.FILL, SWT.FILL, true, true);
gridData.widthHint = this.dialogWidth; gridData.widthHint = this.dialogWidth;
main.setLayoutData(gridData); 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.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); final Composite actionsComp = new Composite(actionComp, SWT.NONE);
actionsComp.setLayout(new RowLayout(SWT.HORIZONTAL)); 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<WizardPage<T>> pageList = Utils.immutableListOf(pages); final List<WizardPage<T>> 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<WizardPage<T>> pages,
final T valueFromPrevPage,
final Composite contentComp,
final Composite actionsComp,
final Runnable cancelCallback) {
try {
final Optional<WizardPage<T>> 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<T> page = newPage.get();
final Supplier<T> valueSupplier = page.contentCompose.apply(valueFromPrevPage, contentComp);
if (page.actions != null) {
for (final WizardAction<T> 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 Button cancel = this.widgetFactory.buttonLocalized(actionsComp, ModalInputDialog.CANCEL_TEXT_KEY);
final RowData data = new RowData(); final RowData data = new RowData();
data.width = this.buttonWidth; data.width = this.buttonWidth;
@ -116,60 +179,24 @@ public class ModelInputWizard<T> extends Dialog {
if (cancelCallback != null) { if (cancelCallback != null) {
cancelCallback.run(); cancelCallback.run();
} }
shell.close(); this.shell.close();
}); });
}
gridData.heightHint = calcDialogHeight(main); try {
finishUp(shell); 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( contentComp.getShell().layout(true, true);
final String pageId, } catch (final Exception e) {
final List<WizardPage<T>> pages, log.warn("Failed to calculate dialog height: {}", e.getMessage());
final T valueFromPrevPage,
final Composite contentComp,
final Composite actionsComp) {
final Optional<WizardPage<T>> 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<T> page = newPage.get();
PageService.clearComposite(contentComp);
PageService.clearComposite(actionsComp);
final Supplier<T> valueSupplier = page.contentCompose.apply(valueFromPrevPage, contentComp);
if (page.actions != null) {
for (final WizardAction<T> 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();
}
});
} }
} catch (final Exception e) {
log.error("Unexpected error: ", e);
this.shell.close();
} }
} }

View file

@ -358,13 +358,25 @@ public class PageServiceImpl implements PageService {
} }
@Override @Override
public <T extends Entity> TableBuilder<T> entityTableBuilder( public <T> TableBuilder<T> entityTableBuilder(
final String name, final String name,
final RestCall<Page<T>> apiCall) { final RestCall<Page<T>> apiCall) {
return new TableBuilder<>(name, this, apiCall); return new TableBuilder<>(name, this, apiCall);
} }
@Override
public <T> TableBuilder<T> staticListTableBuilder(
final List<T> staticList,
final EntityType entityType) {
return new TableBuilder<>(
(entityType != null)
? entityType.name()
: "",
this, staticList, entityType);
}
@Override @Override
public void logout(final PageContext pageContext) { public void logout(final PageContext pageContext) {
this.clearState(); this.clearState();

View file

@ -12,12 +12,11 @@ import java.util.List;
import java.util.function.Function; import java.util.function.Function;
import java.util.function.Supplier; 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.gbl.util.Tuple;
import ch.ethz.seb.sebserver.gui.service.i18n.LocTextKey; import ch.ethz.seb.sebserver.gui.service.i18n.LocTextKey;
import ch.ethz.seb.sebserver.gui.table.TableFilter.CriteriaType; import ch.ethz.seb.sebserver.gui.table.TableFilter.CriteriaType;
public final class ColumnDefinition<ROW extends Entity> { public final class ColumnDefinition<ROW> {
private static final String TOOLTIP_TEXT_KEY_SUFFIX = ".tooltip"; private static final String TOOLTIP_TEXT_KEY_SUFFIX = ".tooltip";

View file

@ -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.PageMessageException;
import ch.ethz.seb.sebserver.gui.service.page.PageService; 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.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.service.remote.webservice.auth.CurrentUser;
import ch.ethz.seb.sebserver.gui.widget.WidgetFactory; import ch.ethz.seb.sebserver.gui.widget.WidgetFactory;
import ch.ethz.seb.sebserver.gui.widget.WidgetFactory.ImageIcon; import ch.ethz.seb.sebserver.gui.widget.WidgetFactory.ImageIcon;
import io.micrometer.core.instrument.util.StringUtils; import io.micrometer.core.instrument.util.StringUtils;
public class EntityTable<ROW extends Entity> { public class EntityTable<ROW> {
private static final Logger log = LoggerFactory.getLogger(EntityTable.class); private static final Logger log = LoggerFactory.getLogger(EntityTable.class);
@ -108,7 +107,7 @@ public class EntityTable<ROW extends Entity> {
final boolean markupEnabled, final boolean markupEnabled,
final int type, final int type,
final PageContext pageContext, final PageContext pageContext,
final RestCall<Page<ROW>> restCall, final PageSupplier<ROW> pageSupplier,
final Function<PageSupplier.Builder<ROW>, PageSupplier.Builder<ROW>> pageSupplierAdapter, final Function<PageSupplier.Builder<ROW>, PageSupplier.Builder<ROW>> pageSupplierAdapter,
final PageService pageService, final PageService pageService,
final List<ColumnDefinition<ROW>> columns, final List<ColumnDefinition<ROW>> columns,
@ -132,7 +131,7 @@ public class EntityTable<ROW extends Entity> {
this.i18nSupport = pageService.getI18nSupport(); this.i18nSupport = pageService.getI18nSupport();
this.pageContext = pageContext; this.pageContext = pageContext;
this.widgetFactory = pageService.getWidgetFactory(); this.widgetFactory = pageService.getWidgetFactory();
this.pageSupplier = new RestCallPageSupplier<>(restCall); this.pageSupplier = pageSupplier;
this.pageSupplierAdapter = (pageSupplierAdapter != null) ? pageSupplierAdapter : Function.identity(); this.pageSupplierAdapter = (pageSupplierAdapter != null) ? pageSupplierAdapter : Function.identity();
this.columns = Utils.immutableListOf(columns); this.columns = Utils.immutableListOf(columns);
this.emptyMessage = emptyMessage; this.emptyMessage = emptyMessage;
@ -581,7 +580,11 @@ public class EntityTable<ROW extends Entity> {
} }
private EntityKey getRowDataId(final TableItem item) { 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<ROW> table) { private void updateValues(final EntityTable<ROW> table) {

View file

@ -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<T> implements PageSupplier<T> {
private final EntityType entityType;
private final List<T> list;
public StaticListPageSupplier(final List<T> list, final EntityType entityType) {
this.list = list;
this.entityType = entityType;
}
@Override
public EntityType getEntityType() {
return this.entityType;
}
@Override
public Builder<T> newBuilder() {
return new StaticListTableBuilderAdapter<>(this.list);
}
public static final class StaticListTableBuilderAdapter<T> implements Builder<T> {
private final List<T> list;
private int pageNumber;
private int pageSize;
private String column;
private PageSortOrder order;
private StaticListTableBuilderAdapter(final List<T> list) {
this.list = new ArrayList<>(list);
}
@Override
public Builder<T> withPaging(final int pageNumber, final int pageSize) {
this.pageNumber = pageNumber;
this.pageSize = pageSize;
return this;
}
@Override
public Builder<T> withSorting(final String column, final PageSortOrder order) {
this.column = column;
this.order = order;
return this;
}
@Override
public Builder<T> withQueryParams(final MultiValueMap<String, String> params) {
return this;
}
@Override
public Builder<T> withQueryParam(final String name, final String value) {
return this;
}
@Override
public Builder<T> withURIVariable(final String name, final String id) {
return this;
}
@Override
public Builder<T> apply(final Function<Builder<T>, Builder<T>> f) {
return f.apply(this);
}
@Override
public Result<Page<T>> 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<T> subList = this.list.subList(this.pageNumber * this.pageSize,
this.pageNumber * this.pageSize + this.pageSize);
return new Page<>(numOfPages, this.pageNumber, this.column, subList);
});
}
}
}

View file

@ -22,7 +22,7 @@ import org.eclipse.swt.widgets.TableItem;
import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap; 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.gbl.model.Page;
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.PageContext; 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.page.impl.PageAction;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestCall; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestCall;
public class TableBuilder<ROW extends Entity> { public class TableBuilder<ROW> {
private final String name; private final String name;
private final PageService pageService; private final PageService pageService;
final RestCall<Page<ROW>> restCall; final RestCall<Page<ROW>> restCall;
final List<ROW> staticList;
final EntityType entityType;
private final MultiValueMap<String, String> staticQueryParams; private final MultiValueMap<String, String> staticQueryParams;
final List<ColumnDefinition<ROW>> columns = new ArrayList<>(); final List<ColumnDefinition<ROW>> columns = new ArrayList<>();
LocTextKey emptyMessage; LocTextKey emptyMessage;
@ -55,6 +57,22 @@ public class TableBuilder<ROW extends Entity> {
this.name = name; this.name = name;
this.pageService = pageService; this.pageService = pageService;
this.restCall = restCall; this.restCall = restCall;
this.staticList = null;
this.entityType = null;
this.staticQueryParams = new LinkedMultiValueMap<>();
}
public TableBuilder(
final String name,
final PageService pageService,
final List<ROW> staticList,
final EntityType entityType) {
this.name = name;
this.pageService = pageService;
this.restCall = null;
this.staticList = staticList;
this.entityType = entityType;
this.staticQueryParams = new LinkedMultiValueMap<>(); this.staticQueryParams = new LinkedMultiValueMap<>();
} }
@ -153,12 +171,14 @@ public class TableBuilder<ROW extends Entity> {
} }
public EntityTable<ROW> compose(final PageContext pageContext) { public EntityTable<ROW> compose(final PageContext pageContext) {
return new EntityTable<>( return new EntityTable<ROW>(
this.name, this.name,
this.markupEnabled, this.markupEnabled,
this.type, this.type,
pageContext, pageContext,
this.restCall, (this.restCall != null)
? new RestCallPageSupplier<>(this.restCall)
: new StaticListPageSupplier<>(this.staticList, this.entityType),
this.restCallAdapter, this.restCallAdapter,
this.pageService, this.pageService,
this.columns, this.columns,

View file

@ -31,7 +31,6 @@ import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap; import org.springframework.util.MultiValueMap;
import ch.ethz.seb.sebserver.gbl.Constants; 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.model.user.UserInfo;
import ch.ethz.seb.sebserver.gbl.util.Tuple; import ch.ethz.seb.sebserver.gbl.util.Tuple;
import ch.ethz.seb.sebserver.gbl.util.Utils; 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;
import ch.ethz.seb.sebserver.gui.widget.WidgetFactory.ImageIcon; import ch.ethz.seb.sebserver.gui.widget.WidgetFactory.ImageIcon;
public class TableFilter<ROW extends Entity> { public class TableFilter<ROW> {
private static final Logger log = LoggerFactory.getLogger(TableFilter.class); private static final Logger log = LoggerFactory.getLogger(TableFilter.class);
@ -365,8 +364,8 @@ public class TableFilter<ROW extends Entity> {
final Supplier<List<Tuple<String>>> _resourceSupplier = resourceSupplier; final Supplier<List<Tuple<String>>> _resourceSupplier = resourceSupplier;
resourceSupplier = () -> { resourceSupplier = () -> {
List<Tuple<String>> list = _resourceSupplier.get(); final List<Tuple<String>> list = _resourceSupplier.get();
list.add(new Tuple<>(StringUtils.EMPTY, entityTable.i18nSupport.getText(ALL_TEXT))); list.add(new Tuple<>(StringUtils.EMPTY, TableFilter.this.entityTable.i18nSupport.getText(ALL_TEXT)));
return list; return list;
}; };

View file

@ -302,9 +302,27 @@ public class WidgetFactory {
g.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false)); g.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false));
return g; return g;
}, },
false,
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) { public Composite createWarningPanel(final Composite parent) {
final Composite composite = new Composite(parent, SWT.NONE); final Composite composite = new Composite(parent, SWT.NONE);
final GridData gridData = new GridData(SWT.FILL, SWT.FILL, true, false); final GridData gridData = new GridData(SWT.FILL, SWT.FILL, true, false);

View file

@ -449,6 +449,10 @@ public class ExamConfigurationMapDAOImpl implements ExamConfigurationMapDAO {
.build() .build()
.execute(); .execute();
if (examsIds.isEmpty()) {
return Collections.emptyList();
}
return toDependencies( return toDependencies(
this.examConfigurationMapRecordMapper.selectByExample() this.examConfigurationMapRecordMapper.selectByExample()
.where( .where(

View file

@ -335,6 +335,12 @@ public class UserDAOImpl implements UserDAO {
final List<Long> ids = extractListOfPKs(all); final List<Long> ids = extractListOfPKs(all);
// get all user records for later processing
final List<UserRecord> users = this.userRecordMapper.selectByExample()
.where(UserRecordDynamicSqlSupport.id, isIn(ids))
.build()
.execute();
// first delete assigned user roles // first delete assigned user roles
this.roleRecordMapper.deleteByExample() this.roleRecordMapper.deleteByExample()
.where(RoleRecordDynamicSqlSupport.userId, isIn(ids)) .where(RoleRecordDynamicSqlSupport.userId, isIn(ids))
@ -347,8 +353,8 @@ public class UserDAOImpl implements UserDAO {
.build() .build()
.execute(); .execute();
return ids.stream() return users.stream()
.map(id -> new EntityKey(id, EntityType.USER)) .map(rec -> new EntityKey(rec.getUuid(), EntityType.USER))
.collect(Collectors.toList()); .collect(Collectors.toList());
}); });
} }

View file

@ -56,6 +56,7 @@ public class ClientConnectionController extends ReadonlyEntityController<ClientC
public Collection<EntityDependency> getDependencies( public Collection<EntityDependency> getDependencies(
final String modelId, final String modelId,
final BulkActionType bulkActionType, final BulkActionType bulkActionType,
final boolean addIncludes,
final List<String> includes) { final List<String> includes) {
throw new UnsupportedOperationException(); throw new UnsupportedOperationException();
} }

View file

@ -118,6 +118,7 @@ public class ClientEventController extends ReadonlyEntityController<ClientEvent,
public Collection<EntityDependency> getDependencies( public Collection<EntityDependency> getDependencies(
final String modelId, final String modelId,
final BulkActionType bulkActionType, final BulkActionType bulkActionType,
final boolean addIncludes,
final List<String> includes) { final List<String> includes) {
throw new UnsupportedOperationException(); throw new UnsupportedOperationException();
} }

View file

@ -114,6 +114,7 @@ public class ConfigurationController extends ReadonlyEntityController<Configurat
public Collection<EntityDependency> getDependencies( public Collection<EntityDependency> getDependencies(
final String modelId, final String modelId,
final BulkActionType bulkActionType, final BulkActionType bulkActionType,
final boolean addIncludes,
final List<String> includes) { final List<String> includes) {
throw new UnsupportedOperationException(); throw new UnsupportedOperationException();
} }

View file

@ -90,6 +90,7 @@ public class ConfigurationValueController extends EntityController<Configuration
@Override @Override
public EntityProcessingReport hardDelete( public EntityProcessingReport hardDelete(
final String modelId, final String modelId,
final boolean addIncludes,
final List<String> includes) { final List<String> includes) {
throw new UnsupportedOperationException(); throw new UnsupportedOperationException();
} }

View file

@ -213,6 +213,7 @@ public abstract class EntityController<T extends Entity, M extends Entity> {
public Collection<EntityDependency> getDependencies( public Collection<EntityDependency> getDependencies(
@PathVariable final String modelId, @PathVariable final String modelId,
@RequestParam(name = API.PARAM_BULK_ACTION_TYPE, required = true) final BulkActionType bulkActionType, @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<String> includes) { @RequestParam(name = API.PARAM_BULK_ACTION_INCLUDES, required = false) final List<String> includes) {
this.entityDAO this.entityDAO
@ -223,7 +224,7 @@ public abstract class EntityController<T extends Entity, M extends Entity> {
bulkActionType, bulkActionType,
this.entityDAO.entityType(), this.entityDAO.entityType(),
Arrays.asList(new EntityKey(modelId, this.entityDAO.entityType())), Arrays.asList(new EntityKey(modelId, this.entityDAO.entityType())),
convertToEntityType(includes)); convertToEntityType(addIncludes, includes));
this.bulkActionService.collectDependencies(bulkAction); this.bulkActionService.collectDependencies(bulkAction);
return bulkAction.getDependencies(); return bulkAction.getDependencies();
@ -326,29 +327,32 @@ public abstract class EntityController<T extends Entity, M extends Entity> {
produces = MediaType.APPLICATION_JSON_UTF8_VALUE) produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
public EntityProcessingReport hardDelete( public EntityProcessingReport hardDelete(
@PathVariable final String modelId, @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) { @RequestParam(name = API.PARAM_BULK_ACTION_INCLUDES, required = false) final List<String> includes) {
return this.entityDAO.byModelId(modelId) return this.entityDAO.byModelId(modelId)
.flatMap(this::checkWriteAccess) .flatMap(this::checkWriteAccess)
.flatMap(this::validForDelete) .flatMap(this::validForDelete)
.flatMap(entity -> bulkDelete(entity, convertToEntityType(includes))) .flatMap(entity -> bulkDelete(entity, convertToEntityType(addIncludes, includes)))
.flatMap(this::notifyDeleted) .flatMap(this::notifyDeleted)
.flatMap(pair -> this.logBulkAction(pair.b)) .flatMap(pair -> this.logBulkAction(pair.b))
.getOrThrow(); .getOrThrow();
} }
protected EnumSet<EntityType> convertToEntityType(final List<String> includes) { protected EnumSet<EntityType> convertToEntityType(final boolean addIncludes, final List<String> includes) {
final EnumSet<EntityType> includeDependencies = (includes != null) final EnumSet<EntityType> includeDependencies = (includes != null)
? EnumSet.copyOf(includes.stream().map(name -> { ? (includes.isEmpty())
try { ? EnumSet.noneOf(EntityType.class)
return EntityType.valueOf(name); : EnumSet.copyOf(includes.stream().map(name -> {
} catch (final Exception e) { try {
return null; return EntityType.valueOf(name);
} } catch (final Exception e) {
}) return null;
.filter(Objects::nonNull) }
.collect(Collectors.toSet())) })
: null; .filter(Objects::nonNull)
.collect(Collectors.toSet()))
: (addIncludes) ? EnumSet.noneOf(EntityType.class) : null;
return includeDependencies; return includeDependencies;
} }

View file

@ -166,13 +166,14 @@ public class ExamConfigurationMappingController extends EntityController<ExamCon
produces = MediaType.APPLICATION_JSON_UTF8_VALUE) produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
public EntityProcessingReport hardDelete( public EntityProcessingReport hardDelete(
@PathVariable final String modelId, @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) { @RequestParam(name = API.PARAM_BULK_ACTION_INCLUDES, required = false) final List<String> includes) {
return this.entityDAO.byModelId(modelId) return this.entityDAO.byModelId(modelId)
.flatMap(this::checkWriteAccess) .flatMap(this::checkWriteAccess)
.flatMap(entity -> this.examConfigUpdateService.processExamConfigurationMappingChange( .flatMap(entity -> this.examConfigUpdateService.processExamConfigurationMappingChange(
entity, entity,
e -> bulkDelete(e, convertToEntityType(includes)))) e -> bulkDelete(e, convertToEntityType(addIncludes, includes))))
.flatMap(this::notifyDeleted) .flatMap(this::notifyDeleted)
.flatMap(pair -> this.logBulkAction(pair.b)) .flatMap(pair -> this.logBulkAction(pair.b))
.getOrThrow(); .getOrThrow();

View file

@ -63,6 +63,7 @@ public abstract class ReadonlyEntityController<T extends Entity, M extends Entit
@Override @Override
public EntityProcessingReport hardDelete( public EntityProcessingReport hardDelete(
final String modelId, final String modelId,
final boolean addIncludes,
final List<String> includes) { final List<String> includes) {
throw new UnsupportedOperationException(ONLY_READ_ACCESS); throw new UnsupportedOperationException(ONLY_READ_ACCESS);
} }

View file

@ -57,6 +57,7 @@ sebserver.overall.types.entityType.CONFIGURATION=Exam Configuration History
sebserver.overall.types.entityType.CONFIGURATION_NODE=Exam Configuration sebserver.overall.types.entityType.CONFIGURATION_NODE=Exam Configuration
sebserver.overall.types.entityType.EXAM_CONFIGURATION_MAP=Exam Configuration Mapping sebserver.overall.types.entityType.EXAM_CONFIGURATION_MAP=Exam Configuration Mapping
sebserver.overall.types.entityType.EXAM=Exam 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.INDICATOR=Indicator
sebserver.overall.types.entityType.THRESHOLD=Threshold sebserver.overall.types.entityType.THRESHOLD=Threshold
sebserver.overall.types.entityType.INSTITUTION=Institution 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.form.password.new.confirm.tooltip=Please confirm the password
sebserver.useraccount.delete.form.title=Delete User Account 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:<br/>&nbsp;&nbsp;&nbsp;&nbsp;This deletes the particular User Account with all selected dependencies.<br/>&nbsp;&nbsp;&nbsp;&nbsp;The User Account and selected dependent exams and exam configurations, will be lost after deletion.<br/>&nbsp;&nbsp;&nbsp;&nbsp;Please use the "Show Report" action below to see a report of all objects that will be<br/>&nbsp;&nbsp;&nbsp;&nbsp;deleted and check them carefully.
sebserver.useraccount.delete.form.report.info=The following objects will be deleted within this User Account deletion.<br/>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.accountName=Name
sebserver.useraccount.delete.form.deleteAlsoConfigs=Include all Exam Configurations 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 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.<br/>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.deleteAlsoExams.tooltip=This includes all Exams which the user has imported and is owner of.<br/>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.delete=Delete
sebserver.useraccount.delete.form.action.report=Show Report 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.<br/>Also the following number dependencies where successfully deleted: {1}.<br/><br/>And there where {2} errors.
################################ ################################
# LMS Setup # LMS Setup