From 9438206c9dbaa25d237c34cfff7be2027322f449 Mon Sep 17 00:00:00 2001 From: anhefti Date: Tue, 28 Jul 2020 15:34:54 +0200 Subject: [PATCH] SEBSERV-133 added deletion to SEB client events --- .../gbl/model/EntityProcessingReport.java | 9 ++ .../content/SEBClientEventDeletePopup.java | 152 ++++++++++++++++++ ...EBClientLogs.java => SEBClientEvents.java} | 55 ++++++- .../gui/content/action/ActionDefinition.java | 5 + .../activity/PageStateDefinitionImpl.java | 4 +- .../gui/service/page/PageContext.java | 14 ++ .../gui/service/page/PageService.java | 5 + .../service/page/impl/PageContextImpl.java | 35 ++++ .../remote/webservice/api/RestCall.java | 13 +- .../webservice/api/exam/DeleteExam.java | 2 +- .../api/session/DeleteAllClientEvents.java | 40 +++++ .../api/session/GetClientEventNames.java | 41 +++++ .../seb/sebserver/gui/table/EntityTable.java | 9 ++ .../impl/AuthorizationServiceImpl.java | 10 +- .../dao/impl/ClientEventDAOImpl.java | 33 +++- .../impl/moodle/MoodleCourseRestriction.java | 4 - .../moodle/MoodleLmsAPITemplateFactory.java | 1 - .../impl/SEBClientConnectionServiceImpl.java | 2 +- .../weblayer/api/ClientEventController.java | 84 +++++++++- .../weblayer/api/EntityController.java | 51 ++++++ .../api/ReadonlyEntityController.java | 17 ++ .../config/application-dev.properties | 2 + src/main/resources/messages.properties | 11 +- .../gbl/model/ModelObjectJSONGenerator.java | 28 ++++ 24 files changed, 601 insertions(+), 26 deletions(-) create mode 100644 src/main/java/ch/ethz/seb/sebserver/gui/content/SEBClientEventDeletePopup.java rename src/main/java/ch/ethz/seb/sebserver/gui/content/{SEBClientLogs.java => SEBClientEvents.java} (78%) create mode 100644 src/main/java/ch/ethz/seb/sebserver/gui/service/remote/webservice/api/session/DeleteAllClientEvents.java create mode 100644 src/main/java/ch/ethz/seb/sebserver/gui/service/remote/webservice/api/session/GetClientEventNames.java diff --git a/src/main/java/ch/ethz/seb/sebserver/gbl/model/EntityProcessingReport.java b/src/main/java/ch/ethz/seb/sebserver/gbl/model/EntityProcessingReport.java index e89d3685..4edfa9d3 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gbl/model/EntityProcessingReport.java +++ b/src/main/java/ch/ethz/seb/sebserver/gbl/model/EntityProcessingReport.java @@ -8,7 +8,9 @@ package ch.ethz.seb.sebserver.gbl.model; +import java.util.Arrays; import java.util.Collection; +import java.util.Collections; import java.util.Set; import com.fasterxml.jackson.annotation.JsonCreator; @@ -113,4 +115,11 @@ public class EntityProcessingReport { return builder.toString(); } + public static EntityProcessingReport ofEmptyError() { + return new EntityProcessingReport( + Collections.emptyList(), + Collections.emptyList(), + Arrays.asList(new ErrorEntry(null, APIMessage.ErrorMessage.RESOURCE_NOT_FOUND.of()))); + } + } diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/content/SEBClientEventDeletePopup.java b/src/main/java/ch/ethz/seb/sebserver/gui/content/SEBClientEventDeletePopup.java new file mode 100644 index 00000000..efaa4166 --- /dev/null +++ b/src/main/java/ch/ethz/seb/sebserver/gui/content/SEBClientEventDeletePopup.java @@ -0,0 +1,152 @@ +/* + * Copyright (c) 2020 ETH Zürich, Educational Development and Technology (LET) + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +package ch.ethz.seb.sebserver.gui.content; + +import java.util.function.BiFunction; +import java.util.function.Function; +import java.util.function.Predicate; +import java.util.function.Supplier; + +import org.apache.commons.lang3.StringUtils; +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.model.EntityProcessingReport; +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.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.session.DeleteAllClientEvents; +import ch.ethz.seb.sebserver.gui.widget.WidgetFactory.CustomVariant; + +@Lazy +@Component +@GuiProfile +public class SEBClientEventDeletePopup { + + private static final Logger log = LoggerFactory.getLogger(SEBClientEventDeletePopup.class); + + private final static LocTextKey FORM_TITLE = + new LocTextKey("sebserver.seblogs.delete.form.title"); + private final static LocTextKey ACTION_DELETE = + new LocTextKey("sebserver.seblogs.delete.action.delete"); + private final static LocTextKey DELETE_CONFIRM_TITLE = + new LocTextKey("sebserver.seblogs.delete.confirm.title"); + + private final PageService pageService; + + protected SEBClientEventDeletePopup(final PageService pageService) { + this.pageService = pageService; + } + + public Function deleteWizardFunction(final PageContext pageContext) { + return action -> { + + final ModelInputWizard wizard = + new ModelInputWizard( + action.pageContext().getParent().getShell(), + this.pageService.getWidgetFactory()) + .setVeryLargeDialogWidth(); + + final String page1Id = "DELETE_PAGE"; + final Predicate callback = pc -> doDelete(this.pageService, pc); + final BiFunction> composePage1 = + (prefPageContext, content) -> composeDeleteDialog(content, + (prefPageContext != null) ? prefPageContext : pageContext); + + final WizardPage page1 = new WizardPage<>( + page1Id, + true, + composePage1, + new WizardAction<>(ACTION_DELETE, callback)); + + wizard.open(FORM_TITLE, Utils.EMPTY_EXECUTION, page1); + + return action; + }; + } + + private boolean doDelete( + final PageService pageService, + final PageContext pageContext) { + + try { + final String idsToDelete = pageContext.getAttribute(PageContext.AttributeKeys.ENTITY_ID_LIST); + + final RestCall.RestCallBuilder restCallBuilder = this.pageService.getRestService() + .getBuilder(DeleteAllClientEvents.class) + .withFormParam(API.PARAM_MODEL_ID_LIST, idsToDelete) + .withFormParam(API.PARAM_BULK_ACTION_TYPE, BulkActionType.HARD_DELETE.name()); + + final EntityProcessingReport report = restCallBuilder.call().getOrThrow(); + + final PageAction action = this.pageService.pageActionBuilder(pageContext) + .newAction(ActionDefinition.LOGS_SEB_CLIENT) + .create(); + + this.pageService.firePageEvent( + new ActionEvent(action), + action.pageContext()); + + pageContext.publishPageMessage( + DELETE_CONFIRM_TITLE, + new LocTextKey( + "sebserver.seblogs.delete.confirm.message", + report.results.size(), + (report.errors.isEmpty()) ? "no" : String.valueOf((report.errors.size())))); + + return true; + } catch (final Exception e) { + log.error("Unexpected error while trying to delete SEB client logs:", e); + pageContext.notifyUnexpectedError(e); + return false; + } + } + + private Supplier composeDeleteDialog( + final Composite parent, + final PageContext pageContext) { + + final String idsToDelete = pageContext.getAttribute(PageContext.AttributeKeys.ENTITY_ID_LIST); + final int number = (StringUtils.isNotBlank(idsToDelete)) + ? idsToDelete.split(Constants.LIST_SEPARATOR).length + : 0; + + final Composite grid = this.pageService.getWidgetFactory() + .createPopupScrollComposite(parent); + + final Label title = this.pageService.getWidgetFactory().labelLocalized( + grid, + CustomVariant.TEXT_H3, + new LocTextKey("sebserver.seblogs.delete.form.info", number)); + final GridData gridData = new GridData(); + gridData.horizontalIndent = 10; + gridData.verticalIndent = 10; + title.setLayoutData(gridData); + + return () -> pageContext; + } + +} diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/content/SEBClientLogs.java b/src/main/java/ch/ethz/seb/sebserver/gui/content/SEBClientEvents.java similarity index 78% rename from src/main/java/ch/ethz/seb/sebserver/gui/content/SEBClientLogs.java rename to src/main/java/ch/ethz/seb/sebserver/gui/content/SEBClientEvents.java index c273ed80..f5dc72e0 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/content/SEBClientLogs.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/content/SEBClientEvents.java @@ -8,16 +8,25 @@ package ch.ethz.seb.sebserver.gui.content; +import java.util.List; import java.util.Map; import java.util.function.Function; +import java.util.stream.Collectors; +import org.apache.tomcat.util.buf.StringUtils; import org.eclipse.swt.widgets.Composite; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Component; +import org.springframework.util.MultiValueMap; import ch.ethz.seb.sebserver.gbl.Constants; +import ch.ethz.seb.sebserver.gbl.api.EntityType; +import ch.ethz.seb.sebserver.gbl.api.authorization.PrivilegeType; import ch.ethz.seb.sebserver.gbl.model.Domain; +import ch.ethz.seb.sebserver.gbl.model.EntityName; import ch.ethz.seb.sebserver.gbl.model.session.ClientConnection; import ch.ethz.seb.sebserver.gbl.model.session.ClientEvent; import ch.ethz.seb.sebserver.gbl.model.session.ExtendedClientEvent; @@ -31,8 +40,10 @@ 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.PageActionBuilder; import ch.ethz.seb.sebserver.gui.service.page.TemplateComposer; +import ch.ethz.seb.sebserver.gui.service.page.impl.PageAction; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestService; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.logs.GetExtendedClientEventPage; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.session.GetClientEventNames; import ch.ethz.seb.sebserver.gui.table.ColumnDefinition; import ch.ethz.seb.sebserver.gui.table.ColumnDefinition.TableFilterAttribute; import ch.ethz.seb.sebserver.gui.table.EntityTable; @@ -42,7 +53,9 @@ import ch.ethz.seb.sebserver.gui.widget.WidgetFactory; @Lazy @Component @GuiProfile -public class SEBClientLogs implements TemplateComposer { +public class SEBClientEvents implements TemplateComposer { + + private static final Logger log = LoggerFactory.getLogger(SEBClientEvents.class); private static final LocTextKey TITLE_TEXT_KEY = new LocTextKey("sebserver.seblogs.list.title"); @@ -71,11 +84,13 @@ public class SEBClientLogs implements TemplateComposer { private final RestService restService; private final I18nSupport i18nSupport; private final SEBClientLogDetailsPopup sebClientLogDetailsPopup; + private final SEBClientEventDeletePopup sebClientEventDeletePopup; private final int pageSize; - public SEBClientLogs( + public SEBClientEvents( final PageService pageService, final SEBClientLogDetailsPopup sebClientLogDetailsPopup, + final SEBClientEventDeletePopup sebClientEventDeletePopup, @Value("${sebserver.gui.list.page.size:20}") final Integer pageSize) { this.pageService = pageService; @@ -83,6 +98,7 @@ public class SEBClientLogs implements TemplateComposer { this.restService = this.resourceService.getRestService(); this.i18nSupport = this.resourceService.getI18nSupport(); this.sebClientLogDetailsPopup = sebClientLogDetailsPopup; + this.sebClientEventDeletePopup = sebClientEventDeletePopup; this.pageSize = pageSize; this.examFilter = new TableFilterAttribute( @@ -113,6 +129,9 @@ public class SEBClientLogs implements TemplateComposer { .clearEntityKeys() .clearAttributes()); + final boolean writeGrant = this.pageService.getCurrentUser() + .hasInstitutionalPrivilege(PrivilegeType.WRITE, EntityType.CLIENT_EVENT); + // table final EntityTable table = this.pageService.entityTableBuilder( this.restService.getRestCall(GetExtendedClientEventPage.class)) @@ -185,7 +204,37 @@ public class SEBClientLogs implements TemplateComposer { action -> this.sebClientLogDetailsPopup.showDetails(action, table.getSingleSelectedROWData()), EMPTY_SELECTION_TEXT) .noEventPropagation() - .publishIf(table::hasAnyContent, false); + .publishIf(table::hasAnyContent, false) + + .newAction(ActionDefinition.LOGS_SEB_CLIENT_DELETE_ALL) + .withExec(action -> this.getOpenDelete(action, table.getFilterCriteria())) + .noEventPropagation() + .publishIf(() -> writeGrant); + } + + private PageAction getOpenDelete(final PageAction pageAction, final MultiValueMap filterCriteria) { + try { + final List ids = this.restService.getBuilder(GetClientEventNames.class) + .withQueryParams(filterCriteria) + .call() + .getOrThrow() + .stream() + .map(EntityName::getModelId) + .collect(Collectors.toList()); + + final PageAction deleteAction = pageAction.withAttribute( + PageContext.AttributeKeys.ENTITY_ID_LIST, + StringUtils.join(ids, Constants.COMMA)) + .withAttribute( + PageContext.AttributeKeys.ENTITY_LIST_TYPE, + EntityType.CLIENT_EVENT.name()); + + return this.sebClientEventDeletePopup.deleteWizardFunction(deleteAction.pageContext()) + .apply(deleteAction); + } catch (final Exception e) { + log.error("Unexpected error while try to open SEB client log delete popup", e); + return pageAction; + } } private Function examNameFunction() { 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 34f76bd1..955c8787 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 @@ -683,6 +683,11 @@ public enum ActionDefinition { new LocTextKey("sebserver.logs.activity.seblogs.details"), ImageIcon.SHOW, ActionCategory.LOGS_SEB_CLIENT_LIST), + LOGS_SEB_CLIENT_DELETE_ALL( + new LocTextKey("sebserver.seblogs.action.delete"), + ImageIcon.DELETE, + PageStateDefinitionImpl.SEB_CLIENT_LOGS, + ActionCategory.FORM), ; diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/content/activity/PageStateDefinitionImpl.java b/src/main/java/ch/ethz/seb/sebserver/gui/content/activity/PageStateDefinitionImpl.java index 2e42d752..191fa0cc 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/content/activity/PageStateDefinitionImpl.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/content/activity/PageStateDefinitionImpl.java @@ -24,7 +24,7 @@ import ch.ethz.seb.sebserver.gui.content.MonitoringRunningExamList; import ch.ethz.seb.sebserver.gui.content.QuizLookupList; import ch.ethz.seb.sebserver.gui.content.SEBClientConfigForm; import ch.ethz.seb.sebserver.gui.content.SEBClientConfigList; -import ch.ethz.seb.sebserver.gui.content.SEBClientLogs; +import ch.ethz.seb.sebserver.gui.content.SEBClientEvents; import ch.ethz.seb.sebserver.gui.content.SEBExamConfigList; import ch.ethz.seb.sebserver.gui.content.SEBExamConfigForm; import ch.ethz.seb.sebserver.gui.content.SEBSettingsForm; @@ -85,7 +85,7 @@ public enum PageStateDefinitionImpl implements PageStateDefinition { MONITORING_CLIENT_CONNECTION(Type.FORM_VIEW, MonitoringClientConnection.class, ActivityDefinition.MONITORING_EXAMS), USER_ACTIVITY_LOGS(Type.LIST_VIEW, UserActivityLogs.class, ActivityDefinition.USER_ACTIVITY_LOGS), - SEB_CLIENT_LOGS(Type.LIST_VIEW, SEBClientLogs.class, ActivityDefinition.SEB_CLIENT_LOGS) + SEB_CLIENT_LOGS(Type.LIST_VIEW, SEBClientEvents.class, ActivityDefinition.SEB_CLIENT_LOGS) ; diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/service/page/PageContext.java b/src/main/java/ch/ethz/seb/sebserver/gui/service/page/PageContext.java index 95041c95..406a61c6 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/service/page/PageContext.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/service/page/PageContext.java @@ -8,6 +8,7 @@ package ch.ethz.seb.sebserver.gui.service.page; +import java.util.List; import java.util.function.Consumer; import org.eclipse.swt.widgets.Composite; @@ -39,6 +40,8 @@ public interface PageContext { String PARENT_ENTITY_ID = "PARENT_ENTITY_ID"; String ENTITY_TYPE = "ENTITY_TYPE"; String PARENT_ENTITY_TYPE = "PARENT_ENTITY_TYPE"; + String ENTITY_ID_LIST = "ENTITY_ID_LIST"; + String ENTITY_LIST_TYPE = "ENTITY_TYPE"; String IMPORT_FROM_QUIZ_DATA = "IMPORT_FROM_QUIZ_DATA"; @@ -149,6 +152,11 @@ public interface PageContext { * @return the EntityKey of the parent Entity that is associated within this PageContext */ EntityKey getParentEntityKey(); + /** Get a list of entity keys within the attribute ENTITY_ID_LIST and ENTITY_LIST_TYPE. + * + * @return A list of entity keys if available from the attributes map */ + List getEntityKeyList(); + /** Adds a given EntityKey as base Entity key to a new PageContext that is returned as a copy of this PageContext. * * @param entityKey the EntityKey to add as base Entity key @@ -161,6 +169,12 @@ public interface PageContext { * @return the new PageContext with the EntityKey added */ PageContext withParentEntityKey(EntityKey entityKey); + /** Adds a given collection of EntityKey to a new PageContext that is returned as a copy of this PageContext. + * + * @param entityKeys the list of EntityKey to add + * @return the new PageContext with the list of EntityKey added */ + PageContext withEntityKeys(List entityKeys); + /** Create a copy of this PageContext and resets both entity keys attributes, the base and the parent EntityKey * * @return copy of this PageContext with reset EntityKey attributes (base and parent) */ 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 384bc6fa..83c80912 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 @@ -565,6 +565,11 @@ public interface PageService { return this; } + public PageActionBuilder withEntityKeys(final List entityKeys) { + this.pageContext = this.pageContext.withEntityKeys(entityKeys); + return this; + } + public PageActionBuilder ignoreMoveAwayFromEdit() { this.ignoreMoveAwayFromEdit = true; return this; diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/service/page/impl/PageContextImpl.java b/src/main/java/ch/ethz/seb/sebserver/gui/service/page/impl/PageContextImpl.java index e390b1bd..bf82fbc2 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/service/page/impl/PageContextImpl.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/service/page/impl/PageContextImpl.java @@ -8,12 +8,16 @@ package ch.ethz.seb.sebserver.gui.service.page.impl; +import java.util.Arrays; +import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.function.Consumer; +import java.util.stream.Collectors; import org.apache.commons.lang3.BooleanUtils; +import org.apache.commons.lang3.StringUtils; import org.eclipse.rap.rwt.widgets.DialogCallback; import org.eclipse.swt.SWT; import org.eclipse.swt.widgets.Composite; @@ -22,6 +26,7 @@ import org.eclipse.swt.widgets.Shell; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import ch.ethz.seb.sebserver.gbl.Constants; import ch.ethz.seb.sebserver.gbl.api.APIMessage; import ch.ethz.seb.sebserver.gbl.api.APIMessageError; import ch.ethz.seb.sebserver.gbl.api.EntityType; @@ -39,6 +44,8 @@ public class PageContextImpl implements PageContext { private static final Logger log = LoggerFactory.getLogger(PageContextImpl.class); + private static final String ENTITY_LIST_TYPE = null; + private final I18nSupport i18nSupport; private final ComposerService composerService; private final Composite root; @@ -185,6 +192,34 @@ public class PageContextImpl implements PageContext { .withAttribute(AttributeKeys.PARENT_ENTITY_TYPE, entityKey.entityType.name()); } + @Override + public List getEntityKeyList() { + if (hasAttribute(AttributeKeys.ENTITY_ID_LIST) && hasAttribute(AttributeKeys.ENTITY_LIST_TYPE)) { + final EntityType type = EntityType.valueOf(getAttribute(ENTITY_LIST_TYPE)); + Arrays.asList(StringUtils.split(getAttribute(AttributeKeys.ENTITY_ID_LIST), Constants.COMMA)) + .stream() + .map(id -> new EntityKey(id, type)) + .collect(Collectors.toList()); + } + + return Collections.emptyList(); + } + + @Override + public PageContext withEntityKeys(final List entityKeys) { + if (entityKeys == null || entityKeys.isEmpty()) { + return removeAttribute(AttributeKeys.ENTITY_ID_LIST) + .removeAttribute(AttributeKeys.ENTITY_LIST_TYPE); + } + final List ids = entityKeys + .stream() + .map(EntityKey::getModelId) + .collect(Collectors.toList()); + final String joinedIds = StringUtils.join(ids, Constants.COMMA); + return withAttribute(AttributeKeys.ENTITY_ID_LIST, joinedIds) + .withAttribute(AttributeKeys.ENTITY_LIST_TYPE, entityKeys.get(0).entityType.name()); + } + @Override public PageContext clearEntityKeys() { return withEntityKey(null) diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/service/remote/webservice/api/RestCall.java b/src/main/java/ch/ethz/seb/sebserver/gui/service/remote/webservice/api/RestCall.java index 4940fb07..575b139b 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/service/remote/webservice/api/RestCall.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/service/remote/webservice/api/RestCall.java @@ -316,6 +316,18 @@ public abstract class RestCall { return this; } + public RestCallBuilder withFormParams(final MultiValueMap params) { + if (params != null) { + params.entrySet() + .stream() + .forEach(param -> { + final String name = param.getKey(); + param.getValue().stream().forEach(p -> withFormParam(name, p)); + }); + } + return this; + } + public RestCallBuilder withPaging(final int pageNumber, final int pageSize) { this.queryParams.put(Page.ATTR_PAGE_NUMBER, Arrays.asList(String.valueOf(pageNumber))); this.queryParams.put(Page.ATTR_PAGE_SIZE, Arrays.asList(String.valueOf(pageSize))); @@ -375,7 +387,6 @@ public abstract class RestCall { + this.queryParams + ", uriVariables=" + this.uriVariables + "]"; } - } public static final class TypeKey { diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/service/remote/webservice/api/exam/DeleteExam.java b/src/main/java/ch/ethz/seb/sebserver/gui/service/remote/webservice/api/exam/DeleteExam.java index 270ca99a..34227762 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/service/remote/webservice/api/exam/DeleteExam.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/service/remote/webservice/api/exam/DeleteExam.java @@ -28,7 +28,7 @@ public class DeleteExam extends RestCall { public DeleteExam() { super(new TypeKey<>( - CallType.ACTIVATION_DEACTIVATE, + CallType.DELETE, EntityType.EXAM, new TypeReference() { }), diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/service/remote/webservice/api/session/DeleteAllClientEvents.java b/src/main/java/ch/ethz/seb/sebserver/gui/service/remote/webservice/api/session/DeleteAllClientEvents.java new file mode 100644 index 00000000..239d7e30 --- /dev/null +++ b/src/main/java/ch/ethz/seb/sebserver/gui/service/remote/webservice/api/session/DeleteAllClientEvents.java @@ -0,0 +1,40 @@ +/* + * 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.service.remote.webservice.api.session; + +import org.springframework.context.annotation.Lazy; +import org.springframework.http.HttpMethod; +import org.springframework.http.MediaType; +import org.springframework.stereotype.Component; + +import com.fasterxml.jackson.core.type.TypeReference; + +import ch.ethz.seb.sebserver.gbl.api.API; +import ch.ethz.seb.sebserver.gbl.api.EntityType; +import ch.ethz.seb.sebserver.gbl.model.EntityProcessingReport; +import ch.ethz.seb.sebserver.gbl.profile.GuiProfile; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestCall; + +@Lazy +@Component +@GuiProfile +public class DeleteAllClientEvents extends RestCall { + + public DeleteAllClientEvents() { + super(new TypeKey<>( + CallType.DELETE, + EntityType.CLIENT_EVENT, + new TypeReference() { + }), + HttpMethod.DELETE, + MediaType.APPLICATION_FORM_URLENCODED, + API.SEB_CLIENT_EVENT_ENDPOINT); + } + +} diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/service/remote/webservice/api/session/GetClientEventNames.java b/src/main/java/ch/ethz/seb/sebserver/gui/service/remote/webservice/api/session/GetClientEventNames.java new file mode 100644 index 00000000..8b20e440 --- /dev/null +++ b/src/main/java/ch/ethz/seb/sebserver/gui/service/remote/webservice/api/session/GetClientEventNames.java @@ -0,0 +1,41 @@ +/* + * 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.service.remote.webservice.api.session; + +import java.util.List; + +import org.springframework.context.annotation.Lazy; +import org.springframework.http.HttpMethod; +import org.springframework.http.MediaType; +import org.springframework.stereotype.Component; + +import com.fasterxml.jackson.core.type.TypeReference; + +import ch.ethz.seb.sebserver.gbl.api.API; +import ch.ethz.seb.sebserver.gbl.api.EntityType; +import ch.ethz.seb.sebserver.gbl.model.EntityName; +import ch.ethz.seb.sebserver.gbl.profile.GuiProfile; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestCall; + +@Lazy +@Component +@GuiProfile +public class GetClientEventNames extends RestCall> { + + public GetClientEventNames() { + super(new TypeKey<>( + CallType.GET_NAMES, + EntityType.CLIENT_EVENT, + new TypeReference>() { + }), + HttpMethod.GET, + MediaType.APPLICATION_FORM_URLENCODED, + API.SEB_CLIENT_EVENT_ENDPOINT + API.NAMES_PATH_SEGMENT); + } +} 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 03ee9169..720a71e2 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 @@ -37,6 +37,7 @@ import org.eclipse.swt.widgets.Widget; import org.joda.time.DateTime; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.MultiValueMap; import org.springframework.web.util.HtmlUtils; @@ -294,6 +295,14 @@ public class EntityTable { } } + public MultiValueMap getFilterCriteria() { + if (this.filter == null) { + return new LinkedMultiValueMap<>(); + } + + return this.filter.getFilterParameter(); + } + public void applySort(final String columnName) { try { this.sortColumn = columnName; diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/authorization/impl/AuthorizationServiceImpl.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/authorization/impl/AuthorizationServiceImpl.java index 72740033..2d82592c 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/authorization/impl/AuthorizationServiceImpl.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/authorization/impl/AuthorizationServiceImpl.java @@ -179,7 +179,15 @@ public class AuthorizationServiceImpl implements AuthorizationService { .andForRole(UserRole.INSTITUTIONAL_ADMIN) .withInstitutionalPrivilege(PrivilegeType.READ) .andForRole(UserRole.EXAM_ADMIN) - .withInstitutionalPrivilege(PrivilegeType.READ) + .withInstitutionalPrivilege(PrivilegeType.WRITE) + .andForRole(UserRole.EXAM_SUPPORTER) + .withOwnerPrivilege(PrivilegeType.READ) + .create(); + + // grants for SEB client events + addPrivilege(EntityType.CLIENT_EVENT) + .forRole(UserRole.EXAM_ADMIN) + .withInstitutionalPrivilege(PrivilegeType.WRITE) .andForRole(UserRole.EXAM_SUPPORTER) .withOwnerPrivilege(PrivilegeType.READ) .create(); diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/ClientEventDAOImpl.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/ClientEventDAOImpl.java index 1470ba12..75b02f07 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/ClientEventDAOImpl.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/ClientEventDAOImpl.java @@ -8,12 +8,12 @@ package ch.ethz.seb.sebserver.webservice.servicelayer.dao.impl; -import static org.mybatis.dynamic.sql.SqlBuilder.isEqualToWhenPresent; -import static org.mybatis.dynamic.sql.SqlBuilder.isIn; +import static org.mybatis.dynamic.sql.SqlBuilder.*; import java.math.BigDecimal; import java.util.ArrayList; import java.util.Collection; +import java.util.Objects; import java.util.Set; import java.util.function.Predicate; import java.util.stream.Collectors; @@ -79,7 +79,17 @@ public class ClientEventDAOImpl implements ClientEventDAO { return Result.tryCatch(() -> this.clientEventRecordMapper .selectByExample() + .leftJoin(ClientConnectionRecordDynamicSqlSupport.clientConnectionRecord) + .on( + ClientConnectionRecordDynamicSqlSupport.id, + equalTo(ClientEventRecordDynamicSqlSupport.clientConnectionId)) .where( + ClientConnectionRecordDynamicSqlSupport.institutionId, + isEqualToWhenPresent(filterMap.getInstitutionId())) + .and( + ClientConnectionRecordDynamicSqlSupport.examId, + isEqualToWhenPresent(filterMap.getClientEventExamId())) + .and( ClientEventRecordDynamicSqlSupport.clientConnectionId, isEqualToWhenPresent(filterMap.getClientEventConnectionId())) .and( @@ -205,9 +215,22 @@ public class ClientEventDAOImpl implements ClientEventDAO { @Override @Transactional public Result> delete(final Set all) { - throw new UnsupportedOperationException( - "Delete is not supported for particular client events. " - + "Use delete of a client connection to delete also all client events of this connection."); + return Result.tryCatch(() -> { + return all + .stream() + .map(EntityKey::getModelId) + .map(Long::parseLong) + .map(pk -> { + final int deleted = this.clientEventRecordMapper.deleteByPrimaryKey(pk); + if (deleted == 1) { + return new EntityKey(String.valueOf(pk), EntityType.CLIENT_EVENT); + } else { + return null; + } + }) + .filter(Objects::nonNull) + .collect(Collectors.toList()); + }); } private Result recordById(final Long id) { diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/moodle/MoodleCourseRestriction.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/moodle/MoodleCourseRestriction.java index 8b21501f..a8abe75f 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/moodle/MoodleCourseRestriction.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/moodle/MoodleCourseRestriction.java @@ -19,7 +19,6 @@ import com.fasterxml.jackson.core.type.TypeReference; import ch.ethz.seb.sebserver.gbl.api.JSONMapper; import ch.ethz.seb.sebserver.gbl.model.exam.MoodleSEBRestriction; -import ch.ethz.seb.sebserver.gbl.model.institution.LmsSetup; import ch.ethz.seb.sebserver.gbl.model.institution.LmsSetupTestResult; import ch.ethz.seb.sebserver.gbl.util.Result; import ch.ethz.seb.sebserver.webservice.servicelayer.lms.impl.moodle.MoodleRestTemplateFactory.MoodleAPIRestTemplate; @@ -48,18 +47,15 @@ public class MoodleCourseRestriction { private static final String MOODLE_DEFAULT_COURSE_RESTRICTION_BROWSER_KEY = "browserKey"; private final JSONMapper jsonMapper; - private final LmsSetup lmsSetup; private final MoodleRestTemplateFactory moodleRestTemplateFactory; private MoodleAPIRestTemplate restTemplate; protected MoodleCourseRestriction( final JSONMapper jsonMapper, - final LmsSetup lmsSetup, final MoodleRestTemplateFactory moodleRestTemplateFactory) { this.jsonMapper = jsonMapper; - this.lmsSetup = lmsSetup; this.moodleRestTemplateFactory = moodleRestTemplateFactory; } diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/moodle/MoodleLmsAPITemplateFactory.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/moodle/MoodleLmsAPITemplateFactory.java index f31cd03e..7c64426c 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/moodle/MoodleLmsAPITemplateFactory.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/moodle/MoodleLmsAPITemplateFactory.java @@ -75,7 +75,6 @@ public class MoodleLmsAPITemplateFactory { final MoodleCourseRestriction moodleCourseRestriction = new MoodleCourseRestriction( this.jsonMapper, - lmsSetup, moodleRestTemplateFactory); return new MoodleLmsAPITemplate( diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/SEBClientConnectionServiceImpl.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/SEBClientConnectionServiceImpl.java index 71583eeb..a87ea238 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/SEBClientConnectionServiceImpl.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/SEBClientConnectionServiceImpl.java @@ -630,7 +630,7 @@ public class SEBClientConnectionServiceImpl implements SEBClientConnectionServic // check Exam has an default SEB Exam configuration attached if (!this.examSessionService.hasDefaultConfigurationAttached(examId)) { throw new APIConstraintViolationException( - "Exam is currently has no default SEB Exam configuration attached"); + "Exam is currently running but has no default SEB Exam configuration attached"); } } 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 d89ccb67..dcbb0b45 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 @@ -8,8 +8,13 @@ package ch.ethz.seb.sebserver.webservice.weblayer.api; +import java.util.Arrays; import java.util.Collection; +import java.util.Collections; +import java.util.EnumSet; import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; import javax.servlet.http.HttpServletRequest; @@ -23,14 +28,20 @@ import org.springframework.web.bind.annotation.RestController; import ch.ethz.seb.sebserver.gbl.api.API; import ch.ethz.seb.sebserver.gbl.api.API.BulkActionType; +import ch.ethz.seb.sebserver.gbl.api.APIMessage; import ch.ethz.seb.sebserver.gbl.api.EntityType; import ch.ethz.seb.sebserver.gbl.api.authorization.PrivilegeType; 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.EntityProcessingReport.ErrorEntry; import ch.ethz.seb.sebserver.gbl.model.GrantEntity; import ch.ethz.seb.sebserver.gbl.model.Page; import ch.ethz.seb.sebserver.gbl.model.session.ClientEvent; import ch.ethz.seb.sebserver.gbl.model.session.ExtendedClientEvent; +import ch.ethz.seb.sebserver.gbl.model.user.UserRole; import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile; +import ch.ethz.seb.sebserver.gbl.util.Result; import ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.ClientEventRecordDynamicSqlSupport; import ch.ethz.seb.sebserver.webservice.servicelayer.PaginationService; import ch.ethz.seb.sebserver.webservice.servicelayer.authorization.AuthorizationService; @@ -49,7 +60,7 @@ import ch.ethz.seb.sebserver.webservice.servicelayer.validation.BeanValidationSe @RequestMapping("${sebserver.webservice.api.admin.endpoint}" + API.SEB_CLIENT_EVENT_ENDPOINT) public class ClientEventController extends ReadonlyEntityController { - private final ExamDAO examDAO; + private final ExamDAO examDao; private final ClientEventDAO clientEventDAO; protected ClientEventController( @@ -59,7 +70,7 @@ public class ClientEventController extends ReadonlyEntityController ids, + @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, + @RequestParam( + name = API.PARAM_INSTITUTION_ID, + required = true, + defaultValue = UserService.USERS_INSTITUTION_AS_DEFAULT) final Long institutionId) { + + this.checkWritePrivilege(institutionId); + + if (ids == null || ids.isEmpty()) { + return EntityProcessingReport.ofEmptyError(); + } + + final Set sources = ids.stream() + .map(id -> new EntityKey(id, EntityType.CLIENT_EVENT)) + .collect(Collectors.toSet()); + + final Result> delete = this.clientEventDAO.delete(sources); + + if (delete.hasError()) { + return new EntityProcessingReport( + Collections.emptyList(), + Collections.emptyList(), + Arrays.asList(new ErrorEntry(null, APIMessage.ErrorMessage.UNEXPECTED.of(delete.getError())))); + } else { + return new EntityProcessingReport( + sources, + delete.get(), + Collections.emptyList()); + } + } + @Override public Collection getDependencies( final String modelId, @@ -128,10 +178,34 @@ public class ClientEventController extends ReadonlyEntityController checkReadAccess(final ClientEvent entity) { + return Result.tryCatch(() -> { + final EnumSet userRoles = this.authorization + .getUserService() + .getCurrentUser() + .getUserRoles(); + final boolean isSupporterOnly = userRoles.size() == 1 && userRoles.contains(UserRole.EXAM_SUPPORTER); + if (isSupporterOnly) { + // check owner grant be getting exam + return super.checkReadAccess(entity) + .getOrThrow(); + } else { + // institutional read access + return entity; + } + }); + } + + @Override + protected boolean hasReadAccess(final ClientEvent entity) { + return !checkReadAccess(entity).hasError(); + } + @Override protected GrantEntity toGrantEntity(final ClientEvent entity) { - return this.examDAO - .byClientConnection(entity.connectionId) + return this.examDao + .byPK(entity.connectionId) .get(); } 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 43a1c70f..9b56631e 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 @@ -339,6 +339,46 @@ public abstract class EntityController { .getOrThrow(); } + // ************************** + // * DELETE ALL (hard-delete) + // ************************** + + @RequestMapping( + method = RequestMethod.DELETE, + consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE, + produces = MediaType.APPLICATION_JSON_UTF8_VALUE) + public EntityProcessingReport hardDeleteAll( + @RequestParam(name = API.PARAM_MODEL_ID_LIST) final List ids, + @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, + @RequestParam( + name = API.PARAM_INSTITUTION_ID, + required = true, + defaultValue = UserService.USERS_INSTITUTION_AS_DEFAULT) final Long institutionId) { + + this.checkWritePrivilege(institutionId); + + if (ids == null || ids.isEmpty()) { + return EntityProcessingReport.ofEmptyError(); + } + + final EntityType entityType = this.entityDAO.entityType(); + final Collection sources = ids.stream() + .map(id -> new EntityKey(id, entityType)) + .collect(Collectors.toList()); + + final BulkAction bulkAction = new BulkAction( + BulkActionType.HARD_DELETE, + entityType, + sources, + convertToEntityType(addIncludes, includes)); + + return this.bulkActionService + .createReport(bulkAction) + .flatMap(this::notifyAllDeleted) + .getOrThrow(); + } + protected EnumSet convertToEntityType(final boolean addIncludes, final List includes) { final EnumSet includeDependencies = (includes != null) ? (includes.isEmpty()) @@ -387,6 +427,13 @@ public abstract class EntityController { institutionId); } + protected void checkWritePrivilege(final Long institutionId) { + this.authorization.check( + PrivilegeType.WRITE, + getGrantEntityType(), + institutionId); + } + protected Result> getAll(final FilterMap filterMap) { return this.entityDAO.allMatching( filterMap, @@ -430,6 +477,10 @@ public abstract class EntityController { return Result.of(pair); } + protected Result notifyAllDeleted(final EntityProcessingReport pair) { + return Result.of(pair); + } + protected Result checkReadAccess(final T entity) { final GrantEntity grantEntity = toGrantEntity(entity); if (grantEntity != null) { 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 a9fdee3a..338c7063 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 @@ -13,6 +13,7 @@ import java.util.List; import javax.servlet.http.HttpServletRequest; import javax.validation.Valid; +import org.mybatis.dynamic.sql.SqlTable; import org.springframework.util.MultiValueMap; import ch.ethz.seb.sebserver.gbl.api.POSTMapper; @@ -68,6 +69,22 @@ public abstract class ReadonlyEntityController ids, + final boolean addIncludes, + final List includes, + final Long institutionId) { + + throw new UnsupportedOperationException(ONLY_READ_ACCESS); + } + + @Override + protected SqlTable getSQLTableOfEntity() { + // TODO Auto-generated method stub + return null; + } + @Override protected M createNew(final POSTMapper postParams) { throw new UnsupportedOperationException(ONLY_READ_ACCESS); diff --git a/src/main/resources/config/application-dev.properties b/src/main/resources/config/application-dev.properties index e8c0d7db..1a7f85fe 100644 --- a/src/main/resources/config/application-dev.properties +++ b/src/main/resources/config/application-dev.properties @@ -5,3 +5,5 @@ server.port=8080 server.servlet.context-path=/ server.tomcat.uri-encoding=UTF-8 +logging.level.ch=DEBUG + diff --git a/src/main/resources/messages.properties b/src/main/resources/messages.properties index 94d76690..6df2185d 100644 --- a/src/main/resources/messages.properties +++ b/src/main/resources/messages.properties @@ -616,7 +616,7 @@ sebserver.exam.delete.report.list.type=Type sebserver.exam.delete.report.list.name=Name sebserver.exam.delete.report.list.description=Description sebserver.exam.delete.action.delete=Delete Exam -sebserver.exam.delete.confirm.title==Deletion Successful +sebserver.exam.delete.confirm.title=Deletion Successful sebserver.exam.delete.confirm.message=The Exam ({0}) was successfully deleted.
Also the following number dependencies where successfully deleted: {1}.

And there where {2} errors. sebserver.exam.delete.report.list.empty=No dependencies will be deleted. @@ -1504,4 +1504,11 @@ sebserver.seblogs.form.column.exam.type.tooltip=The type of the exam sebserver.seblogs.form.column.exam.startTime=Start Time sebserver.seblogs.form.column.exam.startTime.tooltip=The start date and time of the exam sebserver.seblogs.form.column.exam.endTime=End Time -sebserver.seblogs.form.column.exam.endTime.tooltip=The end date and time of the exam \ No newline at end of file +sebserver.seblogs.form.column.exam.endTime.tooltip=The end date and time of the exam + +sebserver.seblogs.action.delete=Delete Logs +sebserver.seblogs.delete.form.title=Delete SEB Logs +sebserver.seblogs.delete.form.info=This will delete all SEB client logs from the current filtered list.
Please check carefully if all SEB client logs from the list shall be deleted.

There are currently {0} logs within the list. +sebserver.seblogs.delete.action.delete=Delete All Logs +sebserver.seblogs.delete.confirm.title=Deletion Successful +sebserver.seblogs.delete.confirm.message={0} SEB client logs where successfully deleted.

And there where {1} errors. diff --git a/src/test/java/ch/ethz/seb/sebserver/gbl/model/ModelObjectJSONGenerator.java b/src/test/java/ch/ethz/seb/sebserver/gbl/model/ModelObjectJSONGenerator.java index 9ee92687..2e94c249 100644 --- a/src/test/java/ch/ethz/seb/sebserver/gbl/model/ModelObjectJSONGenerator.java +++ b/src/test/java/ch/ethz/seb/sebserver/gbl/model/ModelObjectJSONGenerator.java @@ -40,6 +40,7 @@ import ch.ethz.seb.sebserver.gbl.model.institution.LmsSetupTestResult.ErrorType; import ch.ethz.seb.sebserver.gbl.model.sebconfig.AttributeType; import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigCreationInfo; import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigKey; +import ch.ethz.seb.sebserver.gbl.model.sebconfig.Configuration; import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationAttribute; import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationNode; import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationNode.ConfigurationStatus; @@ -125,6 +126,10 @@ public class ModelObjectJSONGenerator { System.out.println(domainObject.getClass().getSimpleName() + ":"); System.out.println(writerWithDefaultPrettyPrinter.writeValueAsString(domainObject)); + domainObject = new Configuration(1L, 1L, 1L, "v1", DateTime.now(), false); + System.out.println(domainObject.getClass().getSimpleName() + ":"); + System.out.println(writerWithDefaultPrettyPrinter.writeValueAsString(domainObject)); + domainObject = new View(1L, "name", 20, 1, 1L); System.out.println(domainObject.getClass().getSimpleName() + ":"); System.out.println(writerWithDefaultPrettyPrinter.writeValueAsString(domainObject)); @@ -265,6 +270,29 @@ public class ModelObjectJSONGenerator { System.out.println(domainObject.getClass().getSimpleName() + ":"); System.out.println(writerWithDefaultPrettyPrinter.writeValueAsString(domainObject)); + domainObject = new EntityDependency( + new EntityKey(1L, EntityType.EXAM), + new EntityKey(1L, EntityType.INDICATOR), + "IndicatorName", "some description"); + System.out.println(domainObject.getClass().getSimpleName() + ":"); + System.out.println(writerWithDefaultPrettyPrinter.writeValueAsString(domainObject)); + + domainObject = new EntityProcessingReport( + Arrays.asList(new EntityKey(1L, EntityType.EXAM)), + Arrays.asList(new EntityKey(1L, EntityType.INDICATOR), new EntityKey(2L, EntityType.INDICATOR)), + Arrays.asList(new EntityProcessingReport.ErrorEntry(new EntityKey(2L, EntityType.INDICATOR), + APIMessage.ErrorMessage.UNEXPECTED.of()))); + System.out.println(domainObject.getClass().getSimpleName() + ":"); + System.out.println(writerWithDefaultPrettyPrinter.writeValueAsString(domainObject)); + + domainObject = APIMessage.ErrorMessage.UNEXPECTED.of( + new RuntimeException("some unexpected exception"), + "attribute1", + "attribute2", + "attribute3"); + System.out.println(domainObject.getClass().getSimpleName() + ":"); + System.out.println(writerWithDefaultPrettyPrinter.writeValueAsString(domainObject)); + } }