diff --git a/findbugs-excludes.xml b/findbugs-excludes.xml index 76bfcb37..b18fa80f 100644 --- a/findbugs-excludes.xml +++ b/findbugs-excludes.xml @@ -22,6 +22,9 @@ + + + diff --git a/src/main/java/ch/ethz/seb/sebserver/gbl/model/user/UserAccount.java b/src/main/java/ch/ethz/seb/sebserver/gbl/model/user/UserAccount.java index 6675553e..ab94296e 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gbl/model/user/UserAccount.java +++ b/src/main/java/ch/ethz/seb/sebserver/gbl/model/user/UserAccount.java @@ -14,11 +14,12 @@ import java.util.Set; import org.joda.time.DateTimeZone; +import ch.ethz.seb.sebserver.gbl.model.Activatable; import ch.ethz.seb.sebserver.gbl.model.EntityKey; import ch.ethz.seb.sebserver.gbl.model.GrantEntity; /** Defines a User-Account object */ -public interface UserAccount extends GrantEntity { +public interface UserAccount extends GrantEntity, Activatable { /** The model id of the User-Account (UUID) */ @Override @@ -42,6 +43,7 @@ public interface UserAccount extends GrantEntity { Boolean getActive(); /** Indicates whether the User-Account is active or not */ + @Override boolean isActive(); /** The language of the User-Account */ diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/content/ExamForm.java b/src/main/java/ch/ethz/seb/sebserver/gui/content/ExamForm.java index 7ac7f32b..d44db572 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/content/ExamForm.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/content/ExamForm.java @@ -614,7 +614,7 @@ public class ExamForm implements TemplateComposer { .removeAttribute(AttributeKeys.IMPORT_FROM_QUIZ_DATA)) .newAction(ActionDefinition.EXAM_CONFIGURATION_EXAM_CONFIG_VIEW_PROP) .withSelectionSupplier(() -> { - final ExamConfigurationMap selectedROWData = table.getSelectedROWData(); + final ExamConfigurationMap selectedROWData = table.getSingleSelectedROWData(); final HashSet result = new HashSet<>(); if (selectedROWData != null) { result.add(new EntityKey( diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/content/ExamList.java b/src/main/java/ch/ethz/seb/sebserver/gui/content/ExamList.java index 91f77c57..3d310ffa 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/content/ExamList.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/content/ExamList.java @@ -220,7 +220,7 @@ public class ExamList implements TemplateComposer { } static final PageAction modifyExam(final PageAction action, final EntityTable table) { - final Exam exam = table.getSelectedROWData(); + final Exam exam = table.getSingleSelectedROWData(); if (exam == null) { throw new PageMessageException(EMPTY_SELECTION_TEXT_KEY); diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/content/InstitutionForm.java b/src/main/java/ch/ethz/seb/sebserver/gui/content/InstitutionForm.java index 995dbf34..6352bf3e 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/content/InstitutionForm.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/content/InstitutionForm.java @@ -26,7 +26,6 @@ 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.TemplateComposer; import ch.ethz.seb.sebserver.gui.service.page.impl.DefaultPageLayout; -import ch.ethz.seb.sebserver.gui.service.page.impl.PageUtils; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestService; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.institution.ActivateInstitution; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.institution.DeactivateInstitution; @@ -143,7 +142,7 @@ public class InstitutionForm implements TemplateComposer { .newAction(ActionDefinition.INSTITUTION_DEACTIVATE) .withEntityKey(entityKey) .withSimpleRestCall(this.restService, DeactivateInstitution.class) - .withConfirm(PageUtils.confirmDeactivation(institution, this.restService)) + .withConfirm(this.pageService.confirmDeactivation(institution)) .publishIf(() -> writeGrant && isReadonly && institution.isActive()) .newAction(ActionDefinition.INSTITUTION_ACTIVATE) @@ -157,6 +156,12 @@ public class InstitutionForm implements TemplateComposer { .ignoreMoveAwayFromEdit() .publishIf(() -> !isReadonly) + .newAction(ActionDefinition.INSTITUTION_SAVE_AND_ACTIVATE) + .withEntityKey(entityKey) + .withExec(formHandle::saveAndActivate) + .ignoreMoveAwayFromEdit() + .publishIf(() -> !isReadonly && !institution.isActive()) + .newAction(ActionDefinition.INSTITUTION_CANCEL_MODIFY) .withEntityKey(entityKey) .withExec(this.pageService.backToCurrentFunction()) diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/content/InstitutionList.java b/src/main/java/ch/ethz/seb/sebserver/gui/content/InstitutionList.java index f9b86e75..7ef51caa 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/content/InstitutionList.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/content/InstitutionList.java @@ -120,6 +120,11 @@ public class InstitutionList implements TemplateComposer { .newAction(ActionDefinition.INSTITUTION_MODIFY_FROM_LIST) .withSelect(table::getSelection, PageAction::applySingleSelection, EMPTY_SELECTION_TEXT_KEY) + .publishIf(() -> instGrant.m() && table.hasAnyContent()) + + .newAction(ActionDefinition.INSTITUTION_TOGGLE_ACTIVITY) + .withExec(this.pageService.activationToggleActionFunction(table, EMPTY_SELECTION_TEXT_KEY)) + .withConfirm(this.pageService.confirmDeactivation(table)) .publishIf(() -> instGrant.m() && table.hasAnyContent()); } diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/content/LmsSetupForm.java b/src/main/java/ch/ethz/seb/sebserver/gui/content/LmsSetupForm.java index 28e73d6c..5fb7d1c4 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/content/LmsSetupForm.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/content/LmsSetupForm.java @@ -40,7 +40,6 @@ 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.TemplateComposer; import ch.ethz.seb.sebserver.gui.service.page.impl.PageAction; -import ch.ethz.seb.sebserver.gui.service.page.impl.PageUtils; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestCallError; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestService; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.institution.GetInstitution; @@ -266,7 +265,7 @@ public class LmsSetupForm implements TemplateComposer { .newAction(ActionDefinition.LMS_SETUP_DEACTIVATE) .withEntityKey(entityKey) .withSimpleRestCall(restService, DeactivateLmsSetup.class) - .withConfirm(PageUtils.confirmDeactivation(lmsSetup, restService)) + .withConfirm(this.pageService.confirmDeactivation(lmsSetup)) .publishIf(() -> writeGrant && readonly && institutionActive && lmsSetup.isActive()) .newAction(ActionDefinition.LMS_SETUP_ACTIVATE) diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/content/QuizDiscoveryList.java b/src/main/java/ch/ethz/seb/sebserver/gui/content/QuizDiscoveryList.java index ab527b88..96b36c4a 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/content/QuizDiscoveryList.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/content/QuizDiscoveryList.java @@ -194,7 +194,7 @@ public class QuizDiscoveryList implements TemplateComposer { .newAction(ActionDefinition.QUIZ_DISCOVERY_SHOW_DETAILS) .withExec(action -> this.showDetails( action, - t.getSelectedROWData(), + t.getSingleSelectedROWData(), institutionNameFunction)) .noEventPropagation() .create()) @@ -214,7 +214,7 @@ public class QuizDiscoveryList implements TemplateComposer { table::getSelection, action -> this.showDetails( action, - table.getSelectedROWData(), + table.getSingleSelectedROWData(), institutionNameFunction), EMPTY_SELECTION_TEXT) .noEventPropagation() @@ -235,7 +235,7 @@ public class QuizDiscoveryList implements TemplateComposer { private PageAction importQuizData(final PageAction action, final EntityTable table) { action.getSingleSelection(); - final QuizData selectedROWData = table.getSelectedROWData(); + final QuizData selectedROWData = table.getSingleSelectedROWData(); if (selectedROWData.endTime != null) { final DateTime now = DateTime.now(DateTimeZone.UTC); diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/content/SebClientConfigForm.java b/src/main/java/ch/ethz/seb/sebserver/gui/content/SebClientConfigForm.java index 560008b3..8c96b1eb 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/content/SebClientConfigForm.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/content/SebClientConfigForm.java @@ -30,7 +30,6 @@ 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.TemplateComposer; -import ch.ethz.seb.sebserver.gui.service.page.impl.PageUtils; import ch.ethz.seb.sebserver.gui.service.remote.download.DownloadService; import ch.ethz.seb.sebserver.gui.service.remote.download.SebClientConfigDownload; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestService; @@ -183,7 +182,7 @@ public class SebClientConfigForm implements TemplateComposer { .newAction(ActionDefinition.SEB_CLIENT_CONFIG_DEACTIVATE) .withEntityKey(entityKey) .withSimpleRestCall(this.restService, DeactivateClientConfig.class) - .withConfirm(PageUtils.confirmDeactivation(clientConfig, this.restService)) + .withConfirm(this.pageService.confirmDeactivation(clientConfig)) .publishIf(() -> writeGrant && isReadonly && clientConfig.isActive()) .newAction(ActionDefinition.SEB_CLIENT_CONFIG_ACTIVATE) diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/content/SebClientLogs.java b/src/main/java/ch/ethz/seb/sebserver/gui/content/SebClientLogs.java index 2df6448a..6b13ac21 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/content/SebClientLogs.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/content/SebClientLogs.java @@ -219,7 +219,7 @@ public class SebClientLogs implements TemplateComposer { .withDefaultAction(t -> actionBuilder .newAction(ActionDefinition.LOGS_SEB_CLIENT_SHOW_DETAILS) - .withExec(action -> this.showDetails(action, t.getSelectedROWData())) + .withExec(action -> this.showDetails(action, t.getSingleSelectedROWData())) .noEventPropagation() .create()) @@ -229,7 +229,7 @@ public class SebClientLogs implements TemplateComposer { .newAction(ActionDefinition.LOGS_SEB_CLIENT_SHOW_DETAILS) .withSelect( table::getSelection, - action -> this.showDetails(action, table.getSelectedROWData()), + action -> this.showDetails(action, table.getSingleSelectedROWData()), EMPTY_SELECTION_TEXT) .noEventPropagation() .publishIf(table::hasAnyContent); diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/content/SebExamConfigPropForm.java b/src/main/java/ch/ethz/seb/sebserver/gui/content/SebExamConfigPropForm.java index a728646d..7407c218 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/content/SebExamConfigPropForm.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/content/SebExamConfigPropForm.java @@ -347,7 +347,7 @@ public class SebExamConfigPropForm implements TemplateComposer { } private ExamConfigurationMap getSelectedExamMapping(final EntityTable table) { - final ExamConfigurationMap selectedROWData = table.getSelectedROWData(); + final ExamConfigurationMap selectedROWData = table.getSingleSelectedROWData(); if (selectedROWData == null) { throw new PageMessageException(ExamList.EMPTY_SELECTION_TEXT_KEY); diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/content/UserAccountForm.java b/src/main/java/ch/ethz/seb/sebserver/gui/content/UserAccountForm.java index 2b1d5b9b..deab98a2 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/content/UserAccountForm.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/content/UserAccountForm.java @@ -37,7 +37,6 @@ 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.TemplateComposer; -import ch.ethz.seb.sebserver.gui.service.page.impl.PageUtils; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestService; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.institution.GetInstitution; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.useraccount.ActivateUserAccount; @@ -225,7 +224,7 @@ public class UserAccountForm implements TemplateComposer { .newAction(ActionDefinition.USER_ACCOUNT_DEACTIVATE) .withEntityKey(entityKey) .withSimpleRestCall(restService, DeactivateUserAccount.class) - .withConfirm(PageUtils.confirmDeactivation(userAccount, restService)) + .withConfirm(this.pageService.confirmDeactivation(userAccount)) .publishIf(() -> writeGrant && readonly && institutionActive && userAccount.isActive()) .newAction(ActionDefinition.USER_ACCOUNT_ACTIVATE) diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/content/UserActivityLogs.java b/src/main/java/ch/ethz/seb/sebserver/gui/content/UserActivityLogs.java index a24466b3..2310c621 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/content/UserActivityLogs.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/content/UserActivityLogs.java @@ -191,7 +191,7 @@ public class UserActivityLogs implements TemplateComposer { .withDefaultAction(t -> actionBuilder .newAction(ActionDefinition.LOGS_USER_ACTIVITY_SHOW_DETAILS) - .withExec(action -> this.showDetails(action, t.getSelectedROWData())) + .withExec(action -> this.showDetails(action, t.getSingleSelectedROWData())) .noEventPropagation() .create()) @@ -201,7 +201,7 @@ public class UserActivityLogs implements TemplateComposer { .newAction(ActionDefinition.LOGS_USER_ACTIVITY_SHOW_DETAILS) .withSelect( table::getSelection, - action -> this.showDetails(action, table.getSelectedROWData()), + action -> this.showDetails(action, table.getSingleSelectedROWData()), EMPTY_SELECTION_TEXT) .noEventPropagation() .publishIf(table::hasAnyContent); 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 dc451b81..6a71b08f 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 @@ -36,6 +36,11 @@ public enum ActionDefinition { ImageIcon.EDIT, PageStateDefinitionImpl.INSTITUTION_EDIT, ActionCategory.INSTITUTION_LIST), + INSTITUTION_TOGGLE_ACTIVITY( + new LocTextKey("sebserver.overall.action.toggle-activity"), + ImageIcon.SWITCH, + PageStateDefinitionImpl.INSTITUTION_LIST, + ActionCategory.INSTITUTION_LIST), INSTITUTION_MODIFY( new LocTextKey("sebserver.institution.action.modify"), ImageIcon.EDIT, @@ -51,6 +56,11 @@ public enum ActionDefinition { ImageIcon.SAVE, PageStateDefinitionImpl.INSTITUTION_VIEW, ActionCategory.FORM), + INSTITUTION_SAVE_AND_ACTIVATE( + new LocTextKey("sebserver.institution.action.activate"), + ImageIcon.ACTIVE, + PageStateDefinitionImpl.INSTITUTION_VIEW, + ActionCategory.FORM), INSTITUTION_ACTIVATE( new LocTextKey("sebserver.institution.action.activate"), ImageIcon.TOGGLE_OFF, diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/form/FormHandle.java b/src/main/java/ch/ethz/seb/sebserver/gui/form/FormHandle.java index d26360a9..39b65bdc 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/form/FormHandle.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/form/FormHandle.java @@ -15,6 +15,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import ch.ethz.seb.sebserver.gbl.Constants; +import ch.ethz.seb.sebserver.gbl.api.API; import ch.ethz.seb.sebserver.gbl.api.APIMessage; import ch.ethz.seb.sebserver.gbl.api.EntityType; import ch.ethz.seb.sebserver.gbl.model.Entity; @@ -29,6 +30,7 @@ import ch.ethz.seb.sebserver.gui.service.page.PageService; import ch.ethz.seb.sebserver.gui.service.page.impl.PageAction; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.FormBinding; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestCall; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestCall.CallType; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestCallError; public class FormHandle { @@ -79,6 +81,16 @@ public class FormHandle { return handleFormPost(doAPIPost(), action); } + public final PageAction saveAndActivate(final PageAction action) { + final PageAction handleFormPost = handleFormPost(doAPIPost(), action); + final EntityType entityType = this.post.getEntityType(); + this.pageService.getRestService().getBuilder(entityType, CallType.ACTIVATION_ACTIVATE) + .withURIVariable(API.PARAM_MODEL_ID, handleFormPost.getEntityKey().getModelId()) + .call() + .getOrThrow(); + return handleFormPost; + } + /** process a form post by first resetting all field validation errors (if there are some) * then collecting all input data from the form by form-binding to a either a JSON string in * HTTP PUT case or to an form-URL-encoded string on HTTP POST case. And PUT or POST the data @@ -136,7 +148,7 @@ public class FormHandle { return true; } else { log.error("Unexpected error while trying to post form: {}", error.getMessage()); - final EntityType resultType = this.post.getResultType(); + final EntityType resultType = this.post.getEntityType(); if (resultType != null) { this.pageContext.notifySaveError(resultType, error); } else { 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 7c1fa296..40dd68d7 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 @@ -8,23 +8,28 @@ package ch.ethz.seb.sebserver.gui.service.page; +import java.util.Arrays; +import java.util.HashSet; import java.util.Set; import java.util.function.BooleanSupplier; import java.util.function.Consumer; import java.util.function.Function; import java.util.function.Supplier; +import java.util.stream.Collectors; import org.eclipse.swt.SWT; import org.eclipse.swt.custom.ScrolledComposite; import org.eclipse.swt.graphics.Point; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import ch.ethz.seb.sebserver.gbl.api.API; import ch.ethz.seb.sebserver.gbl.api.EntityType; import ch.ethz.seb.sebserver.gbl.api.JSONMapper; +import ch.ethz.seb.sebserver.gbl.model.Activatable; import ch.ethz.seb.sebserver.gbl.model.Entity; import ch.ethz.seb.sebserver.gbl.model.EntityKey; import ch.ethz.seb.sebserver.gbl.model.Page; @@ -42,6 +47,7 @@ import ch.ethz.seb.sebserver.gui.service.page.impl.PageState; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestCall; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestService; import ch.ethz.seb.sebserver.gui.service.remote.webservice.auth.AuthorizationContextHolder; +import ch.ethz.seb.sebserver.gui.table.EntityTable; import ch.ethz.seb.sebserver.gui.table.TableBuilder; import ch.ethz.seb.sebserver.gui.widget.WidgetFactory; @@ -99,6 +105,45 @@ public interface PageService { return action -> (currentState != null) ? currentState.gotoAction : action; } + /** Get a page action execution function for switching the activity of currently selected + * entities from a given entity-table. + * + * @param table the entity table + * @param noSelectionText LocTextKey for missing selection message + * @return page action execution function for switching the activity */ + Function activationToggleActionFunction( + EntityTable table, + LocTextKey noSelectionText); + + /** Get a message supplier to notify deactivation dependencies to the user for all given entities + * + * @param entities Set of entities to collect the dependencies for + * @return a message supplier to notify deactivation dependencies to the user */ + Supplier confirmDeactivation(final Set entities); + + /** Get a message supplier to notify deactivation dependencies to the user for given entity + * + * @param entity the entity instance + * @return a message supplier to notify deactivation dependencies to the user */ + default Supplier confirmDeactivation(final T entity) { + return confirmDeactivation(new HashSet<>(Arrays.asList(entity))); + } + + /** Get a message supplier to notify deactivation dependencies to the user for given entity table selection + * + * @param table the entity table + * @return a message supplier to notify deactivation dependencies to the user */ + default Supplier confirmDeactivation(final EntityTable table) { + return () -> { + return confirmDeactivation(table + .getSelectedROWData() + .stream() + .filter(e -> e.isActive()) // NOTE: Activatable::isActive leads to an error here!? + .collect(Collectors.toSet())) + .get(); + }; + } + /** Publishes a given PageEvent to the current page tree * This goes through the page-tree and collects all listeners the are listen to * the specified page event type. @@ -229,6 +274,16 @@ public interface PageService { } } + static void clearComposite(final Composite parent) { + if (parent == null) { + return; + } + + for (final Control control : parent.getChildren()) { + control.dispose(); + } + } + public class PageActionBuilder { private final PageService pageService; private final PageContext originalPageContext; diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/service/page/impl/ComposerServiceImpl.java b/src/main/java/ch/ethz/seb/sebserver/gui/service/page/impl/ComposerServiceImpl.java index 4a62563b..36306cd7 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/service/page/impl/ComposerServiceImpl.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/service/page/impl/ComposerServiceImpl.java @@ -115,7 +115,7 @@ public class ComposerServiceImpl implements ComposerService { if (composer.validate(pageContext)) { - PageUtils.clearComposite(pageContext.getParent()); + PageService.clearComposite(pageContext.getParent()); try { composer.compose(pageContext); diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/service/page/impl/PageServiceImpl.java b/src/main/java/ch/ethz/seb/sebserver/gui/service/page/impl/PageServiceImpl.java index dc026c18..bd586ea0 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/service/page/impl/PageServiceImpl.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/service/page/impl/PageServiceImpl.java @@ -10,9 +10,14 @@ package ch.ethz.seb.sebserver.gui.service.page.impl; import java.io.Serializable; import java.util.ArrayList; +import java.util.Collection; import java.util.Comparator; import java.util.List; +import java.util.Set; import java.util.function.Consumer; +import java.util.function.Function; +import java.util.function.Supplier; +import java.util.stream.Collectors; import javax.servlet.http.HttpSession; @@ -22,8 +27,13 @@ import org.slf4j.LoggerFactory; import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Service; +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.JSONMapper; +import ch.ethz.seb.sebserver.gbl.model.Activatable; import ch.ethz.seb.sebserver.gbl.model.Entity; +import ch.ethz.seb.sebserver.gbl.model.EntityKey; import ch.ethz.seb.sebserver.gbl.model.Page; import ch.ethz.seb.sebserver.gbl.profile.GuiProfile; import ch.ethz.seb.sebserver.gbl.util.Result; @@ -34,6 +44,7 @@ import ch.ethz.seb.sebserver.gui.service.i18n.LocTextKey; import ch.ethz.seb.sebserver.gui.service.i18n.PolyglotPageService; import ch.ethz.seb.sebserver.gui.service.page.ComposerService; import ch.ethz.seb.sebserver.gui.service.page.PageContext; +import ch.ethz.seb.sebserver.gui.service.page.PageMessageException; import ch.ethz.seb.sebserver.gui.service.page.PageService; import ch.ethz.seb.sebserver.gui.service.page.PageStateDefinition.Type; import ch.ethz.seb.sebserver.gui.service.page.event.ActionEvent; @@ -41,8 +52,10 @@ import ch.ethz.seb.sebserver.gui.service.page.event.ActionPublishEvent; import ch.ethz.seb.sebserver.gui.service.page.event.PageEvent; import ch.ethz.seb.sebserver.gui.service.page.event.PageEventListener; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestCall; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestCall.CallType; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestService; import ch.ethz.seb.sebserver.gui.service.remote.webservice.auth.AuthorizationContextHolder; +import ch.ethz.seb.sebserver.gui.table.EntityTable; import ch.ethz.seb.sebserver.gui.table.TableBuilder; import ch.ethz.seb.sebserver.gui.widget.WidgetFactory; @@ -51,6 +64,11 @@ import ch.ethz.seb.sebserver.gui.widget.WidgetFactory; @GuiProfile public class PageServiceImpl implements PageService { + private static final LocTextKey CONFIRM_DEACTIVATION_NO_DEP_KEY = + new LocTextKey("sebserver.dialog.confirm.deactivation.noDependencies"); + + private static final String CONFIRM_DEACTIVATION_KEY = "sebserver.dialog.confirm.deactivation"; + private static final Logger log = LoggerFactory.getLogger(PageServiceImpl.class); private static final LocTextKey MSG_GO_AWAY_FROM_EDIT = @@ -183,6 +201,80 @@ public class PageServiceImpl implements PageService { } } + @Override + public Supplier confirmDeactivation(final Set entities) { + final RestService restService = this.resourceService.getRestService(); + return () -> { + if (entities == null || entities.isEmpty()) { + return null; + } + + try { + final int dependencies = entities.stream() + .flatMap(entity -> { + final RestCall>.RestCallBuilder builder = + restService.> getBuilder( + entity.entityType(), + CallType.GET_DEPENDENCIES); + + return builder + .withURIVariable(API.PARAM_MODEL_ID, String.valueOf(entity.getModelId())) + .withQueryParam(API.PARAM_BULK_ACTION_TYPE, BulkActionType.DEACTIVATE.name()) + .call() + .getOrThrow().stream(); + }) + .collect(Collectors.toList()) + .size(); + if (dependencies > 0) { + return new LocTextKey(CONFIRM_DEACTIVATION_KEY, String.valueOf(dependencies)); + } else { + return CONFIRM_DEACTIVATION_NO_DEP_KEY; + } + + } catch (final Exception e) { + log.warn("Failed to get dependencyies. Error: {}", e.getMessage()); + return new LocTextKey(CONFIRM_DEACTIVATION_KEY, ""); + } + }; + } + + @Override + public Function activationToggleActionFunction( + final EntityTable table, + final LocTextKey noSelectionText) { + + return action -> { + final Set selectedROWData = table.getSelectedROWData(); + if (selectedROWData == null || selectedROWData.isEmpty()) { + throw new PageMessageException(noSelectionText); + } + + final RestService restService = this.resourceService.getRestService(); + final EntityType entityType = table.getEntityType(); + + final Collection errors = new ArrayList<>(); + for (final T entity : selectedROWData) { + if (entity.isActive()) { + restService.getBuilder(entityType, CallType.ACTIVATION_DEACTIVATE) + .withURIVariable(API.PARAM_MODEL_ID, entity.getModelId()) + .call() + .onError(errors::add); + } else { + restService.getBuilder(entityType, CallType.ACTIVATION_ACTIVATE) + .withURIVariable(API.PARAM_MODEL_ID, entity.getModelId()) + .call() + .onError(errors::add); + } + } + + if (!errors.isEmpty()) { + // TODO notify message for user + } + + return action; + }; + } + private void exec(final PageAction pageAction, final Consumer> callback) { pageAction.applyAction(result -> { if (!result.hasError()) { diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/service/page/impl/PageUtils.java b/src/main/java/ch/ethz/seb/sebserver/gui/service/page/impl/PageUtils.java deleted file mode 100644 index 34629253..00000000 --- a/src/main/java/ch/ethz/seb/sebserver/gui/service/page/impl/PageUtils.java +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright (c) 2019 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.page.impl; - -import java.util.Set; -import java.util.function.Supplier; - -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Control; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import ch.ethz.seb.sebserver.gbl.api.API; -import ch.ethz.seb.sebserver.gbl.api.API.BulkActionType; -import ch.ethz.seb.sebserver.gbl.model.Entity; -import ch.ethz.seb.sebserver.gbl.model.EntityKey; -import ch.ethz.seb.sebserver.gui.service.i18n.LocTextKey; -import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestCall; -import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestCall.CallType; -import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestService; - -public final class PageUtils { - - private static final Logger log = LoggerFactory.getLogger(PageUtils.class); - - public static void clearComposite(final Composite parent) { - if (parent == null) { - return; - } - - for (final Control control : parent.getChildren()) { - control.dispose(); - } - } - - public static final Supplier confirmDeactivation( - final Entity entity, - final RestService restService) { - - return () -> { - try { - final RestCall>.RestCallBuilder builder = - restService.> getBuilder( - entity.entityType(), - CallType.GET_DEPENDENCIES); - - if (builder == null) { - throw new RuntimeException("No RestCall builder found for entity type: " + entity.entityType()); - } - - final Set dependencies = builder - .withURIVariable(API.PARAM_MODEL_ID, String.valueOf(entity.getModelId())) - .withQueryParam(API.PARAM_BULK_ACTION_TYPE, BulkActionType.DEACTIVATE.name()) - .call() - .getOrThrow(); - final int size = dependencies.size(); - if (size > 0) { - return new LocTextKey("sebserver.dialog.confirm.deactivation", String.valueOf(size)); - } else { - return new LocTextKey("sebserver.dialog.confirm.deactivation.noDependencies"); - } - } catch (final Exception e) { - log.info("Failed to get dependencyies for Entity: {} error: ", entity, e.getMessage()); - return new LocTextKey("sebserver.dialog.confirm.deactivation", ""); - } - }; - } - -} 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 42221ac7..d8214c55 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 @@ -94,7 +94,7 @@ public abstract class RestCall { return this; } - public EntityType getResultType() { + public EntityType getEntityType() { if (this.typeKey != null) { return this.typeKey.entityType; } 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 1a665bea..2eb0ddef 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 @@ -40,6 +40,7 @@ import org.slf4j.LoggerFactory; 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.model.Entity; import ch.ethz.seb.sebserver.gbl.model.EntityKey; import ch.ethz.seb.sebserver.gbl.model.GrantEntity; @@ -203,6 +204,14 @@ public class EntityTable { this.sortOrder); } + public EntityType getEntityType() { + if (this.restCall != null) { + return this.restCall.getEntityType(); + } + + return null; + } + public PageContext getPageContext() { if (this.pageContext == null) { return null; @@ -303,7 +312,7 @@ public class EntityTable { return getRowData(item); } - public ROW getSelectedROWData() { + public ROW getSingleSelectedROWData() { final TableItem[] selection = this.table.getSelection(); if (selection == null || selection.length == 0) { return null; @@ -312,6 +321,18 @@ public class EntityTable { return getRowData(selection[0]); } + public Set getSelectedROWData() { + final TableItem[] selection = this.table.getSelection(); + if (selection == null || selection.length == 0) { + return Collections.emptySet(); + } + + return Arrays.asList(selection) + .stream() + .map(this::getRowData) + .collect(Collectors.toSet()); + } + public Set getSelection() { return getSelection(null); } diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/table/TableNavigator.java b/src/main/java/ch/ethz/seb/sebserver/gui/table/TableNavigator.java index 1bc89167..ae6fd238 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/table/TableNavigator.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/table/TableNavigator.java @@ -17,7 +17,7 @@ import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Label; import ch.ethz.seb.sebserver.gbl.model.Page; -import ch.ethz.seb.sebserver.gui.service.page.impl.PageUtils; +import ch.ethz.seb.sebserver.gui.service.page.PageService; import ch.ethz.seb.sebserver.gui.widget.WidgetFactory.CustomVariant; public class TableNavigator { @@ -40,7 +40,7 @@ public class TableNavigator { public Page update(final Page pageData) { // clear all - PageUtils.clearComposite(this.composite); + PageService.clearComposite(this.composite); if (pageData.isEmpty()) { // show empty message diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/widget/MultiSelection.java b/src/main/java/ch/ethz/seb/sebserver/gui/widget/MultiSelection.java index 9ae8111c..c464b904 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/widget/MultiSelection.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/widget/MultiSelection.java @@ -24,7 +24,7 @@ import org.eclipse.swt.widgets.Listener; import ch.ethz.seb.sebserver.gbl.Constants; import ch.ethz.seb.sebserver.gbl.util.Tuple; -import ch.ethz.seb.sebserver.gui.service.page.impl.PageUtils; +import ch.ethz.seb.sebserver.gui.service.page.PageService; import ch.ethz.seb.sebserver.gui.widget.WidgetFactory.CustomVariant; public final class MultiSelection extends Composite implements Selection { @@ -61,7 +61,7 @@ public final class MultiSelection extends Composite implements Selection { final String selectionValue = getSelectionValue(); this.selected.clear(); this.labels.clear(); - PageUtils.clearComposite(this); + PageService.clearComposite(this); for (final Tuple tuple : mapping) { final Label label = new Label(this, SWT.NONE); diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/widget/MultiSelectionCheckbox.java b/src/main/java/ch/ethz/seb/sebserver/gui/widget/MultiSelectionCheckbox.java index cda8a360..cadc32d2 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/widget/MultiSelectionCheckbox.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/widget/MultiSelectionCheckbox.java @@ -24,7 +24,7 @@ import org.eclipse.swt.widgets.Listener; import ch.ethz.seb.sebserver.gbl.Constants; import ch.ethz.seb.sebserver.gbl.util.Tuple; -import ch.ethz.seb.sebserver.gui.service.page.impl.PageUtils; +import ch.ethz.seb.sebserver.gui.service.page.PageService; public final class MultiSelectionCheckbox extends Composite implements Selection { @@ -54,7 +54,7 @@ public final class MultiSelectionCheckbox extends Composite implements Selection public void applyNewMapping(final List> mapping) { final String selectionValue = getSelectionValue(); this.checkboxes.clear(); - PageUtils.clearComposite(this); + PageService.clearComposite(this); for (final Tuple tuple : mapping) { final Button button = new Button(this, SWT.CHECK); diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/widget/RadioSelection.java b/src/main/java/ch/ethz/seb/sebserver/gui/widget/RadioSelection.java index b7491851..f93b2304 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/widget/RadioSelection.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/widget/RadioSelection.java @@ -21,7 +21,7 @@ import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Listener; import ch.ethz.seb.sebserver.gbl.util.Tuple; -import ch.ethz.seb.sebserver.gui.service.page.impl.PageUtils; +import ch.ethz.seb.sebserver.gui.service.page.PageService; public final class RadioSelection extends Composite implements Selection { @@ -51,7 +51,7 @@ public final class RadioSelection extends Composite implements Selection { public void applyNewMapping(final List> mapping) { final String selectionValue = getSelectionValue(); this.radioButtons.clear(); - PageUtils.clearComposite(this); + PageService.clearComposite(this); for (final Tuple tuple : mapping) { final Button button = new Button(this, SWT.RADIO); diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/widget/WidgetFactory.java b/src/main/java/ch/ethz/seb/sebserver/gui/widget/WidgetFactory.java index 065830b9..9541a308 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/widget/WidgetFactory.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/widget/WidgetFactory.java @@ -82,6 +82,7 @@ public class WidgetFactory { INACTIVE("inactive.png"), TOGGLE_ON("toggle_on.png"), TOGGLE_OFF("toggle_off.png"), + SWITCH("switch.png"), YES("yes.png"), NO("no.png"), SAVE("save.png"), @@ -440,7 +441,7 @@ public class WidgetFactory { } public Table tableLocalized(final Composite parent) { - final Table table = new Table(parent, SWT.SINGLE | SWT.NO_SCROLL); + final Table table = new Table(parent, SWT.MULTI | SWT.NO_SCROLL); this.polyglotPageService.injectI18n(table); return table; } diff --git a/src/main/resources/config/application-dev-gui.properties b/src/main/resources/config/application-dev-gui.properties index d94a661e..6687ad78 100644 --- a/src/main/resources/config/application-dev-gui.properties +++ b/src/main/resources/config/application-dev-gui.properties @@ -16,7 +16,7 @@ sebserver.gui.webservice.mock-lms-enabled=true sebserver.gui.theme=css/sebserver.css -sebserver.gui.list.page.size=20 +sebserver.gui.list.page.size=15 sebserver.gui.date.displayformat=MM/dd/yyyy HH:mm sebserver.gui.date.displayformat.timezone=|ZZ sebserver.gui.multilingual=true diff --git a/src/main/resources/messages.properties b/src/main/resources/messages.properties index c038efd6..a77df066 100644 --- a/src/main/resources/messages.properties +++ b/src/main/resources/messages.properties @@ -28,6 +28,7 @@ sebserver.overall.date.to=To sebserver.overall.action.add=Add; sebserver.overall.action.remove=Remove sebserver.overall.action.select=Please Select +sebserver.overall.action.toggle-activity=Switch Activity sebserver.overall.types.activityType.CREATE=Create New sebserver.overall.types.activityType.IMPORT=Import diff --git a/src/main/resources/static/images/switch.png b/src/main/resources/static/images/switch.png new file mode 100644 index 00000000..7a701e07 Binary files /dev/null and b/src/main/resources/static/images/switch.png differ