diff --git a/src/main/java/ch/ethz/seb/sebserver/gbl/model/user/UserFeatures.java b/src/main/java/ch/ethz/seb/sebserver/gbl/model/user/UserFeatures.java index 357126ce..4f5cc0aa 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gbl/model/user/UserFeatures.java +++ b/src/main/java/ch/ethz/seb/sebserver/gbl/model/user/UserFeatures.java @@ -27,15 +27,20 @@ public class UserFeatures { CONFIG_CERTIFICATE("config.certificate"), LMS_SETUP( "lms.setup"), LMS_SETUP_TEST("lms.setup.type.MOCKUP"), - LMS_SETUP_MOODLE("lms.setup.type.moodle"), - LMS_SETUP_MOODLE_PLUGIN("lms.setup.type.moodle.plugin"), - LMS_SETUP_OPEN_EDX("lms.setup.type.openedx"), - LMS_SETUP_ANS("lms.setup.type.ans"), - LMS_SETUP_OPEN_OLAT("lms.setup.type.openolat"), + LMS_SETUP_MOODLE("lms.setup.type.MOODLE"), + LMS_SETUP_MOODLE_PLUGIN("lms.setup.type.MOODLE_PLUGIN"), + LMS_SETUP_OPEN_EDX("lms.setup.type.OPEN_EDX"), + LMS_SETUP_ANS("lms.setup.type.ANS_DELFT"), + LMS_SETUP_OPEN_OLAT("lms.setup.type.OPEN_OLAT"), + QUIZ_LOOKUP("lms.quiz.lookup"), + + EXAM_ADMIN("exam.administration"), EXAM_ASK("exam.ask"), + EXAM_CONNECTION_CONFIG("exam.connection.config"), EXAM_SEB_RESTRICTION( "exam.seb.restriction"), EXAM_LIVE_PROCTORING("exam.seb.liveProctoring"), + EXAM_NO_LMS("exam.noLMS"), EXAM_SCREEN_PROCTORING("exam.seb.screenProctoring"), EXAM_INDICATORS("exam.monitoring.indicators"), @@ -46,16 +51,22 @@ public class UserFeatures { MONITORING_RUNNING_EXAM_DETAIL_VIEW("monitoring.running.exam.detailview"), MONITORING_RUNNING_EXAM_DETAIL_VIEW_LOG_EXPORT("monitoring.running.exam.detailview.logexport"), //more? ... - MONITORING_RUNNING_EXAM_QUIT_ALL("monitoring.running.exam.quitall"), + MONITORING_RUNNING_EXAM_QUIT("monitoring.running.exam.quit"), + MONITORING_RUNNING_EXAM_LOCKSCREEN("monitoring.running.exam.lockscreen"), - // more? ... + + MONITORING_RUNNING_EXAM_CANCEL_CON("monitoring.running.exam.cancel.connection"), + + MONITORING_RUNNING_EXAM_STATE_FILTER("monitoring.running.exam.state.filter"), + MONITORING_RUNNING_EXAM_ISSUE_FILTER("monitoring.running.exam.issue.filter"), + MONITORING_RUNNING_EXAM_CLIENT_FILTER("monitoring.running.exam.client.filter"), + MONITORING_RUNNING_EXAM_LIVE_PROCTORING("monitoring.running.exam.live.proctoring"), + MONITORING_RUNNING_EXAM_SCREEN_PROCTORING("monitoring.running.exam.screen.proctoring"), MONITORING_FINISHED_EXAMS("monitoring.finished.exams"), MONITORING_OVERALL_LOG_EXPORT("monitoring.overall.export"), - EXAM_NO_LMS("exam.noLMS"), - ; public final String featureName; diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/content/LoginPage.java b/src/main/java/ch/ethz/seb/sebserver/gui/content/LoginPage.java index 88a79c9c..ff99a662 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/content/LoginPage.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/content/LoginPage.java @@ -67,7 +67,6 @@ public class LoginPage implements TemplateComposer { this.i18nSupport = pageService.getI18nSupport(); this.defaultRegisterPage = defaultRegisterPage; this.registeringEnabled = BooleanUtils.toBoolean(registeringEnabled); - } @Override @@ -133,7 +132,7 @@ public class LoginPage implements TemplateComposer { } }); - if (this.registeringEnabled) { + if (this.registeringEnabled && !pageService.isLightSetup()) { final Button registerButton = this.widgetFactory.buttonLocalized(buttons, TEXT_REGISTER); gridData = new GridData(SWT.LEFT, SWT.TOP, false, false); gridData.verticalIndent = 10; diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/content/activity/ActivitiesPane.java b/src/main/java/ch/ethz/seb/sebserver/gui/content/activity/ActivitiesPane.java index 8e9dc585..e4d18088 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/content/activity/ActivitiesPane.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/content/activity/ActivitiesPane.java @@ -135,7 +135,9 @@ public class ActivitiesPane implements TemplateComposer { // User Account // if current user has role seb-server admin or institutional-admin, show list - if (isServerOrInstAdmin && !pageService.isSEBServerLightSetup()) { + if (isServerOrInstAdmin + && !pageService.isLightSetup() + && currentUser.isFeatureEnabled(UserFeatures.Feature.ADMIN_USER_ADMINISTRATION)) { final TreeItem userAccounts = this.widgetFactory.treeItemLocalized( sebAdmin, @@ -145,15 +147,15 @@ public class ActivitiesPane implements TemplateComposer { actionBuilder .newAction(ActionDefinition.USER_ACCOUNT_VIEW_LIST) .create()); - } else { + } else if (currentUser.isFeatureEnabled(UserFeatures.Feature.ADMIN_USER_ACCOUNT)) { // otherwise show the user account form for current user - final TreeItem userAccounts = pageService.isSEBServerLightSetup() - ? this.widgetFactory.treeItemLocalized( - sebAdmin, - ActivityDefinition.USER_ACCOUNT.displayName) - : this.widgetFactory.treeItemLocalized( - navigation, - ActivityDefinition.USER_ACCOUNT.displayName); + final TreeItem userAccounts = pageService.isLightSetup() || !currentUser.isFeatureEnabled(UserFeatures.Feature.ADMIN_USER_ADMINISTRATION) + ? this.widgetFactory.treeItemLocalized( + sebAdmin, + ActivityDefinition.USER_ACCOUNT.displayName) + : this.widgetFactory.treeItemLocalized( + navigation, + ActivityDefinition.USER_ACCOUNT.displayName); injectActivitySelection( userAccounts, actionBuilder.newAction(ActionDefinition.USER_ACCOUNT_VIEW_FORM) @@ -165,7 +167,8 @@ public class ActivitiesPane implements TemplateComposer { // User Activity Logs final boolean viewUserActivityLogs = this.currentUser.hasInstitutionalPrivilege( PrivilegeType.READ, - EntityType.USER_ACTIVITY_LOG); + EntityType.USER_ACTIVITY_LOG) + && currentUser.isFeatureEnabled(UserFeatures.Feature.ADMIN_AUDIT_LOGS); if (viewUserActivityLogs) { final TreeItem activityLogs = this.widgetFactory.treeItemLocalized( sebAdmin, @@ -201,13 +204,19 @@ public class ActivitiesPane implements TemplateComposer { PrivilegeType.READ, EntityType.CONFIGURATION_NODE); - if ((clientConfigRead || examConfigRead) && !isSupporterOnly) { + final boolean connConfigEnabled = currentUser.isFeatureEnabled(UserFeatures.Feature.CONFIG_CONNECTION_CONFIGURATION); + final boolean examConfigEnabled = currentUser.isFeatureEnabled(UserFeatures.Feature.CONFIG_EXAM_CONFIGURATION); + final boolean templateEnabled = currentUser.isFeatureEnabled(UserFeatures.Feature.CONFIG_TEMPLATE); + final boolean certificatesEnabled = currentUser.isFeatureEnabled(UserFeatures.Feature.CONFIG_CERTIFICATE); + final boolean anyEnabled = connConfigEnabled || examConfigEnabled || templateEnabled || certificatesEnabled; + + if (anyEnabled && (clientConfigRead || examConfigRead) && !isSupporterOnly) { final TreeItem sebConfigs = this.widgetFactory.treeItemLocalized( navigation, ActivityDefinition.SEB_CONFIGURATION.displayName); // SEB Client Config - if (clientConfigRead) { + if (clientConfigRead && connConfigEnabled) { final TreeItem clientConfig = this.widgetFactory.treeItemLocalized( sebConfigs, ActivityDefinition.SEB_CLIENT_CONFIG.displayName); @@ -219,7 +228,7 @@ public class ActivitiesPane implements TemplateComposer { } // SEB Exam Config - if (examConfigRead) { + if (examConfigRead && examConfigEnabled) { final TreeItem examConfig = this.widgetFactory.treeItemLocalized( sebConfigs, ActivityDefinition.SEB_EXAM_CONFIG.displayName); @@ -231,7 +240,7 @@ public class ActivitiesPane implements TemplateComposer { } // SEB Exam Config Template - if (examConfigRead) { + if (examConfigRead && templateEnabled) { final TreeItem examConfigTemplate = this.widgetFactory.treeItemLocalized( sebConfigs, ActivityDefinition.SEB_EXAM_CONFIG_TEMPLATE.displayName); @@ -243,7 +252,7 @@ public class ActivitiesPane implements TemplateComposer { } // Certificate management - if (!isSupporterOnly) { + if (!isSupporterOnly && certificatesEnabled) { final TreeItem examConfigTemplate = this.widgetFactory.treeItemLocalized( sebConfigs, ActivityDefinition.SEB_CERTIFICATE_MANAGEMENT.displayName); @@ -268,63 +277,73 @@ public class ActivitiesPane implements TemplateComposer { this.currentUser.hasInstitutionalPrivilege(PrivilegeType.READ, EntityType.EXAM); final boolean examWrite = this.currentUser.hasInstitutionalPrivilege(PrivilegeType.WRITE, EntityType.EXAM); - // Exam Administration - final TreeItem examAdmin = this.widgetFactory.treeItemLocalized( - navigation, - ActivityDefinition.EXAM_ADMINISTRATION.displayName); + final boolean lmsSetupEnabled = currentUser.isFeatureEnabled(UserFeatures.Feature.LMS_SETUP); + final boolean quizLookupEnabled = currentUser.isFeatureEnabled(UserFeatures.Feature.QUIZ_LOOKUP); + final boolean examEnabled = currentUser.isFeatureEnabled(UserFeatures.Feature.EXAM_ADMIN); + final boolean examTemplateEnabled = currentUser.isFeatureEnabled(UserFeatures.Feature.EXAM_TEMPLATE); + final boolean anyExamAdminEnabled = lmsSetupEnabled || quizLookupEnabled || examEnabled || examTemplateEnabled; - if (examRead || lmsRead) { - // LMS Setup - if (lmsRead && !isSupporterOnly) { - final TreeItem lmsSetup = this.widgetFactory.treeItemLocalized( - examAdmin, - ActivityDefinition.LMS_SETUP.displayName); - injectActivitySelection( - lmsSetup, - actionBuilder - .newAction(ActionDefinition.LMS_SETUP_VIEW_LIST) - .create()); - } + if (anyExamAdminEnabled) { + // Exam Administration + final TreeItem examAdmin = this.widgetFactory.treeItemLocalized( + navigation, + ActivityDefinition.EXAM_ADMINISTRATION.displayName); - if (examRead) { - - if (examWrite) { - // Quiz Discovery - final TreeItem quizDiscovery = this.widgetFactory.treeItemLocalized( + if ((examRead || lmsRead) && lmsSetupEnabled) { + // LMS Setup + if (lmsRead && !isSupporterOnly) { + final TreeItem lmsSetup = this.widgetFactory.treeItemLocalized( examAdmin, - ActivityDefinition.QUIZ_DISCOVERY.displayName); + ActivityDefinition.LMS_SETUP.displayName); injectActivitySelection( - quizDiscovery, + lmsSetup, actionBuilder - .newAction(ActionDefinition.QUIZ_DISCOVERY_VIEW_LIST) + .newAction(ActionDefinition.LMS_SETUP_VIEW_LIST) .create()); } - // Exam - final TreeItem exam = this.widgetFactory.treeItemLocalized( - examAdmin, - ActivityDefinition.EXAM.displayName); - injectActivitySelection( - exam, - actionBuilder - .newAction(ActionDefinition.EXAM_VIEW_LIST) - .create()); + if (examRead) { + if (examWrite && quizLookupEnabled) { + // Quiz Discovery + final TreeItem quizDiscovery = this.widgetFactory.treeItemLocalized( + examAdmin, + ActivityDefinition.QUIZ_DISCOVERY.displayName); + injectActivitySelection( + quizDiscovery, + actionBuilder + .newAction(ActionDefinition.QUIZ_DISCOVERY_VIEW_LIST) + .create()); + } + + if (examEnabled) { + // Exam + final TreeItem exam = this.widgetFactory.treeItemLocalized( + examAdmin, + ActivityDefinition.EXAM.displayName); + injectActivitySelection( + exam, + actionBuilder + .newAction(ActionDefinition.EXAM_VIEW_LIST) + .create()); + } + } + + if (this.currentUser.hasInstitutionalPrivilege(PrivilegeType.READ, EntityType.EXAM_TEMPLATE) + && examTemplateEnabled) { + // Exam Template + final TreeItem examTemplate = this.widgetFactory.treeItemLocalized( + examAdmin, + ActivityDefinition.EXAM_TEMPLATE.displayName); + injectActivitySelection( + examTemplate, + actionBuilder + .newAction(ActionDefinition.EXAM_TEMPLATE_VIEW_LIST) + .create()); + } + + examAdmin.setExpanded(this.currentUser.get().hasAnyRole(UserRole.EXAM_ADMIN)); } - - if (this.currentUser.hasInstitutionalPrivilege(PrivilegeType.READ, EntityType.EXAM_TEMPLATE)) { - // Exam Template - final TreeItem examTemplate = this.widgetFactory.treeItemLocalized( - examAdmin, - ActivityDefinition.EXAM_TEMPLATE.displayName); - injectActivitySelection( - examTemplate, - actionBuilder - .newAction(ActionDefinition.EXAM_TEMPLATE_VIEW_LIST) - .create()); - } - - examAdmin.setExpanded(this.currentUser.get().hasAnyRole(UserRole.EXAM_ADMIN)); } // ---- EXAM ADMINISTRATION ------------------------------------------------------------ @@ -335,12 +354,15 @@ public class ActivitiesPane implements TemplateComposer { final boolean isSupporter = this.currentUser.get().hasAnyRole(UserRole.EXAM_SUPPORTER) || this.currentUser.get().hasAnyRole(UserRole.EXAM_ADMIN); - final boolean viewSEBClientLogs = this.currentUser.hasInstitutionalPrivilege( - PrivilegeType.READ, - EntityType.EXAM) || - this.currentUser.get().hasRole(UserRole.EXAM_SUPPORTER); + final boolean viewSEBClientLogs = + currentUser.isFeatureEnabled(UserFeatures.Feature.MONITORING_OVERALL_LOG_EXPORT) + && ( this.currentUser.hasInstitutionalPrivilege(PrivilegeType.READ, EntityType.EXAM) + || this.currentUser.get().hasRole(UserRole.EXAM_SUPPORTER)); + final boolean monitoringEnabled = currentUser.isFeatureEnabled(UserFeatures.Feature.MONITORING_RUNNING_EXAMS); + final boolean finishedEnabled = currentUser.isFeatureEnabled(UserFeatures.Feature.MONITORING_FINISHED_EXAMS); - if (isSupporter || viewSEBClientLogs) { + + if (viewSEBClientLogs || monitoringEnabled || finishedEnabled) { // Monitoring final TreeItem monitoring = this.widgetFactory.treeItemLocalized( navigation, @@ -349,24 +371,27 @@ public class ActivitiesPane implements TemplateComposer { // Monitoring exams if (isSupporter) { - final TreeItem monitoringExams = this.widgetFactory.treeItemLocalized( - monitoring, - ActivityDefinition.MONITORING_EXAMS.displayName); - injectActivitySelection( - monitoringExams, - actionBuilder - .newAction(ActionDefinition.RUNNING_EXAM_VIEW_LIST) - .create()); - - final TreeItem clientConfig = this.widgetFactory.treeItemLocalized( - monitoring, - ActivityDefinition.FINISHED_EXAMS.displayName); - injectActivitySelection( - clientConfig, - actionBuilder - .newAction(ActionDefinition.FINISHED_EXAM_VIEW_LIST) - .create()); + if (monitoringEnabled) { + final TreeItem monitoringExams = this.widgetFactory.treeItemLocalized( + monitoring, + ActivityDefinition.MONITORING_EXAMS.displayName); + injectActivitySelection( + monitoringExams, + actionBuilder + .newAction(ActionDefinition.RUNNING_EXAM_VIEW_LIST) + .create()); + } + if (finishedEnabled) { + final TreeItem finishedExams = this.widgetFactory.treeItemLocalized( + monitoring, + ActivityDefinition.FINISHED_EXAMS.displayName); + injectActivitySelection( + finishedExams, + actionBuilder + .newAction(ActionDefinition.FINISHED_EXAM_VIEW_LIST) + .create()); + } } // SEB Client Logs @@ -434,9 +459,11 @@ public class ActivitiesPane implements TemplateComposer { final PageState state = this.pageService.getCurrentState(); if (state == null) { final TreeItem item = getDefaultSelectionFor(navigation, this.currentUser); - final TreeItem actionItem = getActionItem(item); - final PageAction activityAction = getActivitySelection(actionItem); - this.pageService.executePageAction(activityAction); + if (item != null) { + final TreeItem actionItem = getActionItem(item); + final PageAction activityAction = getActivitySelection(actionItem); + this.pageService.executePageAction(activityAction); + } } else { final TreeItem item = findItemByActionDefinition( navigation.getItems(), @@ -451,7 +478,7 @@ public class ActivitiesPane implements TemplateComposer { private TreeItem getDefaultSelectionFor(final Tree navigation, final CurrentUser currentUser2) { try { if (this.currentUser.get().hasAnyRole(UserRole.SEB_SERVER_ADMIN, UserRole.INSTITUTIONAL_ADMIN)) { - if (pageService.isSEBServerLightSetup()) { + if (pageService.isLightSetup()) { return navigation.getItem(0).getItem(1); } return navigation.getItem(0); @@ -467,7 +494,11 @@ public class ActivitiesPane implements TemplateComposer { return navigation.getItem(0); } } catch (final Exception e) { - return navigation.getItem(0); + try { + return navigation.getItem(0); + } catch (final Exception ignored) { + return null; + } } } diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/content/admin/InstitutionList.java b/src/main/java/ch/ethz/seb/sebserver/gui/content/admin/InstitutionList.java index dfdb9f5f..d2dcdda3 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/content/admin/InstitutionList.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/content/admin/InstitutionList.java @@ -89,7 +89,7 @@ public class InstitutionList implements TemplateComposer { @Override public void compose(final PageContext pageContext) { - if (pageService.isSEBServerLightSetup()) { + if (pageService.isLightSetup()) { pageService.applyFullVersionNote(pageContext.getParent(), pageContext); return; } diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/content/admin/UserAccountForm.java b/src/main/java/ch/ethz/seb/sebserver/gui/content/admin/UserAccountForm.java index e83b317b..c47c4d30 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/content/admin/UserAccountForm.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/content/admin/UserAccountForm.java @@ -99,7 +99,6 @@ public class UserAccountForm implements TemplateComposer { @Override public void compose(final PageContext pageContext) { - final CurrentUser currentUser = this.resourceService.getCurrentUser(); final RestService restService = this.resourceService.getRestService(); final WidgetFactory widgetFactory = this.pageService.getWidgetFactory(); @@ -108,11 +107,12 @@ public class UserAccountForm implements TemplateComposer { final EntityKey entityKey = pageContext.getEntityKey(); final EntityKey parentEntityKey = pageContext.getParentEntityKey(); final boolean readonly = pageContext.isReadonly(); - final boolean isLight = pageService.isSEBServerLightSetup(); + final boolean isLight = pageService.isLightSetup(); final BooleanSupplier isNew = () -> entityKey == null; final BooleanSupplier isNotNew = () -> !isNew.getAsBoolean(); final BooleanSupplier isSEBAdmin = () -> user.hasRole(UserRole.SEB_SERVER_ADMIN); + final boolean newAccountPrivilege = !isLight && currentUser.isFeatureEnabled(UserFeatures.Feature.ADMIN_USER_ADMINISTRATION); if (isLight && !readonly && isNew.getAsBoolean()) { pageService.applyFullVersionNote(pageContext.getParent(), pageContext); @@ -142,8 +142,12 @@ public class UserAccountForm implements TemplateComposer { final boolean ownAccount = user.uuid.equals(userAccount.getModelId()); final EntityGrantCheck userGrantCheck = currentUser.entityGrantCheck(userAccount); - final boolean writeGrant = roleBasedEditGrant && userGrantCheck.w(); - final boolean modifyGrant = roleBasedEditGrant && userGrantCheck.m(); + final boolean writeGrant = roleBasedEditGrant + && userGrantCheck.w() + && currentUser.isFeatureEnabled(UserFeatures.Feature.ADMIN_USER_ADMINISTRATION); + final boolean modifyGrant = roleBasedEditGrant + && userGrantCheck.m() + && currentUser.isFeatureEnabled(UserFeatures.Feature.ADMIN_USER_ACCOUNT); final boolean institutionalWriteGrant = currentUser.hasInstitutionalPrivilege( PrivilegeType.WRITE, EntityType.USER); @@ -232,7 +236,6 @@ public class UserAccountForm implements TemplateComposer { FORM_ROLES_TEXT_KEY, StringUtils.join(userAccount.getRoles(), Constants.LIST_SEPARATOR_CHAR), this.resourceService::userRoleResources) - .visibleIf(writeGrant) .mandatory(!readonly)) .addFieldIf( isNew, @@ -252,11 +255,13 @@ public class UserAccountForm implements TemplateComposer { ? restService.getRestCall(NewUserAccount.class) : restService.getRestCall(SaveUserAccount.class)); + + // propagate content actions to action-pane this.pageService.pageActionBuilder(formContext.clearEntityKeys()) .newAction(ActionDefinition.USER_ACCOUNT_NEW) - .publishIf(() -> institutionalWriteGrant && readonly && institutionActive) + .publishIf(() -> newAccountPrivilege && institutionalWriteGrant && readonly && institutionActive) .newAction(ActionDefinition.USER_ACCOUNT_MODIFY) .withEntityKey(entityKey) diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/content/admin/UserActivityLogs.java b/src/main/java/ch/ethz/seb/sebserver/gui/content/admin/UserActivityLogs.java index 18e7daaf..9ba90b60 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/content/admin/UserActivityLogs.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/content/admin/UserActivityLogs.java @@ -196,7 +196,7 @@ public class UserActivityLogs implements TemplateComposer { .withColumnIf( () -> isSEBAdmin.getAsBoolean() && currentUser.isFeatureEnabled(UserFeatures.Feature.ADMIN_INSTITUTION) - && !pageService.isSEBServerLightSetup(), + && !pageService.isLightSetup(), () -> new ColumnDefinition<>( UserActivityLog.FILTER_ATTR_INSTITUTION, INSTITUTION_TEXT_KEY, diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/content/configs/CertificateList.java b/src/main/java/ch/ethz/seb/sebserver/gui/content/configs/CertificateList.java index ab90960f..f31959d1 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/content/configs/CertificateList.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/content/configs/CertificateList.java @@ -105,7 +105,7 @@ public class CertificateList implements TemplateComposer { @Override public void compose(final PageContext pageContext) { - if (pageService.isSEBServerLightSetup()) { + if (pageService.isLightSetup()) { pageService.applyFullVersionNote(pageContext.getParent(), pageContext); return; } diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/content/configs/ConfigTemplateList.java b/src/main/java/ch/ethz/seb/sebserver/gui/content/configs/ConfigTemplateList.java index a2f68b82..ec1bfcf7 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/content/configs/ConfigTemplateList.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/content/configs/ConfigTemplateList.java @@ -96,7 +96,7 @@ public class ConfigTemplateList implements TemplateComposer { @Override public void compose(final PageContext pageContext) { - if (pageService.isSEBServerLightSetup()) { + if (pageService.isLightSetup()) { pageService.applyFullVersionNote(pageContext.getParent(), pageContext); return; } diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/content/configs/SEBClientConfigList.java b/src/main/java/ch/ethz/seb/sebserver/gui/content/configs/SEBClientConfigList.java index afae6f33..3c9b460d 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/content/configs/SEBClientConfigList.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/content/configs/SEBClientConfigList.java @@ -129,7 +129,7 @@ public class SEBClientConfigList implements TemplateComposer { .withColumnIf( () -> isSEBAdmin && currentUser.isFeatureEnabled(UserFeatures.Feature.ADMIN_INSTITUTION) - && !pageService.isSEBServerLightSetup(), + && !pageService.isLightSetup(), () -> new ColumnDefinition<>( Domain.LMS_SETUP.ATTR_INSTITUTION_ID, INSTITUTION_TEXT_KEY, diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/content/configs/SEBExamConfigForm.java b/src/main/java/ch/ethz/seb/sebserver/gui/content/configs/SEBExamConfigForm.java index 6642e414..09dff2a1 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/content/configs/SEBExamConfigForm.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/content/configs/SEBExamConfigForm.java @@ -13,6 +13,7 @@ import java.util.HashSet; import java.util.List; import java.util.function.Function; +import ch.ethz.seb.sebserver.gbl.model.user.UserFeatures; import org.eclipse.swt.SWT; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Text; @@ -159,7 +160,7 @@ public class SEBExamConfigForm implements TemplateComposer { final EntityKey entityKey = pageContext.getEntityKey(); final EntityKey parentEntityKey = pageContext.getParentEntityKey(); final boolean isNew = entityKey == null; - final boolean isLight = pageService.isSEBServerLightSetup(); + final boolean isLight = pageService.isLightSetup(); // get data or create new. Handle error if happen final ConfigurationNode examConfig = (isNew) @@ -350,7 +351,7 @@ public class SEBExamConfigForm implements TemplateComposer { .withExec(this.pageService.backToCurrentFunction()) .publishIf(() -> !isReadonly); - if (isAttachedToExam && isReadonly) { + if (isAttachedToExam && isReadonly && currentUser.isFeatureEnabled(UserFeatures.Feature.EXAM_ADMIN)) { widgetFactory.addFormSubContextHeader( content, diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/content/configs/SEBExamConfigList.java b/src/main/java/ch/ethz/seb/sebserver/gui/content/configs/SEBExamConfigList.java index 9225b93a..28d58ff6 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/content/configs/SEBExamConfigList.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/content/configs/SEBExamConfigList.java @@ -133,7 +133,7 @@ public class SEBExamConfigList implements TemplateComposer { TITLE_CONFIGURATION_TEXT_KEY); final boolean isSEBAdmin = this.currentUser.get().hasRole(UserRole.SEB_SERVER_ADMIN); - final boolean isLight = pageService.isSEBServerLightSetup(); + final boolean isLight = pageService.isLightSetup(); final PageActionBuilder pageActionBuilder = this.pageService.pageActionBuilder(pageContext.clearEntityKeys()); @@ -150,7 +150,7 @@ public class SEBExamConfigList implements TemplateComposer { .withColumnIf( () -> isSEBAdmin && currentUser.isFeatureEnabled(UserFeatures.Feature.ADMIN_INSTITUTION) - && !pageService.isSEBServerLightSetup(), + && !pageService.isLightSetup(), () -> new ColumnDefinition<>( Domain.LMS_SETUP.ATTR_INSTITUTION_ID, INSTITUTION_TEXT_KEY, diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/content/exam/ClientGroupForm.java b/src/main/java/ch/ethz/seb/sebserver/gui/content/exam/ClientGroupForm.java index e92c50e6..31f60775 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/content/exam/ClientGroupForm.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/content/exam/ClientGroupForm.java @@ -87,7 +87,7 @@ public class ClientGroupForm implements TemplateComposer { @Override public void compose(final PageContext pageContext) { - if (pageService.isSEBServerLightSetup()) { + if (pageService.isLightSetup()) { pageService.applyFullVersionNote(pageContext.getParent(), pageContext); this.pageService.pageActionBuilder(pageContext.clearEntityKeys()) .newAction(ActionDefinition.EXAM_CLIENT_GROUP_CANCEL_MODIFY) diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/content/exam/ExamClientGroupList.java b/src/main/java/ch/ethz/seb/sebserver/gui/content/exam/ExamClientGroupList.java index a5ae8a87..aa206c08 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/content/exam/ExamClientGroupList.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/content/exam/ExamClientGroupList.java @@ -8,6 +8,8 @@ package ch.ethz.seb.sebserver.gui.content.exam; +import ch.ethz.seb.sebserver.gbl.model.user.UserFeatures; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.auth.CurrentUser; import org.apache.commons.lang3.BooleanUtils; import org.eclipse.swt.widgets.Composite; import org.springframework.context.annotation.Lazy; @@ -71,10 +73,12 @@ public class ExamClientGroupList implements TemplateComposer { @Override public void compose(final PageContext pageContext) { + final CurrentUser currentUser = pageService.getCurrentUser(); final Composite content = pageContext.getParent(); final EntityKey entityKey = pageContext.getEntityKey(); final boolean editable = BooleanUtils.toBoolean(pageContext.getAttribute(ExamForm.ATTR_EDITABLE)); - final boolean isLight = pageService.isSEBServerLightSetup(); + final boolean isLight = pageService.isLightSetup(); + final boolean clientGroupsEnabled = currentUser.isFeatureEnabled(UserFeatures.Feature.EXAM_SEB_CLIENT_GROUPS); // List of ClientGroups this.widgetFactory.addFormSubContextHeader( @@ -124,7 +128,7 @@ public class ExamClientGroupList implements TemplateComposer { .widthProportion(3)) .withDefaultActionIf( - () -> editable, + () -> editable && clientGroupsEnabled, () -> actionBuilder .newAction(ActionDefinition.EXAM_CLIENT_GROUP_MODIFY_FROM_LIST) .withParentEntityKey(entityKey) @@ -145,7 +149,7 @@ public class ExamClientGroupList implements TemplateComposer { clientGroupTable::getMultiSelection, PageAction::applySingleSelectionAsEntityKey, CLIENT_GROUP_EMPTY_SELECTION_TEXT_KEY) - .publishIf(() -> editable && clientGroupTable.hasAnyContent(), false) + .publishIf(() -> clientGroupsEnabled && editable && clientGroupTable.hasAnyContent(), false) .newAction(ActionDefinition.EXAM_CLIENT_GROUP_DELETE_FROM_LIST) .withEntityKey(entityKey) @@ -153,11 +157,11 @@ public class ExamClientGroupList implements TemplateComposer { clientGroupTable::getMultiSelection, this::deleteSelectedClientGroup, CLIENT_GROUP_EMPTY_SELECTION_TEXT_KEY) - .publishIf(() -> !isLight && editable && clientGroupTable.hasAnyContent(), false) + .publishIf(() -> clientGroupsEnabled && !isLight && editable && clientGroupTable.hasAnyContent(), false) .newAction(ActionDefinition.EXAM_CLIENT_GROUP_NEW) .withParentEntityKey(entityKey) - .publishIf(() -> editable); + .publishIf(() -> clientGroupsEnabled && editable); } private PageAction deleteSelectedClientGroup(final PageAction action) { diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/content/exam/ExamForm.java b/src/main/java/ch/ethz/seb/sebserver/gui/content/exam/ExamForm.java index 77b1b48c..c9b06641 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/content/exam/ExamForm.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/content/exam/ExamForm.java @@ -8,7 +8,7 @@ package ch.ethz.seb.sebserver.gui.content.exam; -import static ch.ethz.seb.sebserver.gbl.model.user.UserFeatures.Feature.EXAM_SCREEN_PROCTORING; +import static ch.ethz.seb.sebserver.gbl.model.user.UserFeatures.Feature.*; import java.util.*; import java.util.function.Function; @@ -16,6 +16,7 @@ import java.util.stream.Collectors; import java.util.stream.Stream; import ch.ethz.seb.sebserver.gbl.api.POSTMapper; +import ch.ethz.seb.sebserver.gbl.model.user.UserFeatures; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.*; import org.apache.commons.lang3.BooleanUtils; import org.apache.commons.lang3.StringUtils; @@ -197,7 +198,7 @@ public class ExamForm implements TemplateComposer { final EntityKey entityKey = (readonly || !newExamNoLMS) ? pageContext.getEntityKey() : null; final PageContext formContext = pageContext.withEntityKey(exam.getEntityKey()); final EntityGrantCheck entityGrantCheck = currentUser.entityGrantCheck(exam); - final boolean isLight = pageService.isSEBServerLightSetup(); + final boolean isLight = pageService.isLightSetup(); final boolean modifyGrant = entityGrantCheck.m(); final boolean writeGrant = entityGrantCheck.w(); final boolean editable = modifyGrant && (exam.getStatus() == ExamStatus.UP_COMING || exam.getStatus() == ExamStatus.RUNNING); @@ -214,6 +215,11 @@ public class ExamForm implements TemplateComposer { sebRestrictionAvailable && isRestricted != exam.sebRestriction && exam.status == ExamStatus.RUNNING; + final boolean connectionConfigEnabled = currentUser.isFeatureEnabled(EXAM_CONNECTION_CONFIG); + final boolean askEnabled = currentUser.isFeatureEnabled(UserFeatures.Feature.EXAM_ASK); + final boolean spsFeatureEnabled = currentUser.isFeatureEnabled(EXAM_SCREEN_PROCTORING); + final boolean lpEnabled = currentUser.isFeatureEnabled(UserFeatures.Feature.EXAM_LIVE_PROCTORING); + final boolean restrictionEnabled = currentUser.isFeatureEnabled(UserFeatures.Feature.EXAM_SEB_RESTRICTION); // check exam consistency and inform the user if needed Collection warnings = null; @@ -255,7 +261,6 @@ public class ExamForm implements TemplateComposer { .map(ProctoringServiceSettings::getEnableProctoring) .getOr(false); - final boolean spsFeatureEnabled = currentUser.isFeatureEnabled(EXAM_SCREEN_PROCTORING); final boolean screenProctoringEnabled = readonly && spsFeatureEnabled && this.restService .getBuilder(GetScreenProctoringSettings.class) .withURIVariable(API.PARAM_MODEL_ID, entityKey.modelId) @@ -306,15 +311,15 @@ public class ExamForm implements TemplateComposer { .withExec(this.examCreateClientConfigPopup.exportFunction( exam.institutionId, exam.getName())) - .publishIf(() -> editable && readonly) + .publishIf(() -> connectionConfigEnabled && editable && readonly) .newAction(ActionDefinition.EXAM_SECURITY_KEY_ENABLED) .withEntityKey(entityKey) - .publishIf(() -> signatureKeyCheckEnabled && readonly) + .publishIf(() -> askEnabled && signatureKeyCheckEnabled && readonly) .newAction(ActionDefinition.EXAM_SECURITY_KEY_DISABLED) .withEntityKey(entityKey) - .publishIf(() -> !signatureKeyCheckEnabled && readonly) + .publishIf(() -> askEnabled && !signatureKeyCheckEnabled && readonly) .newAction(ActionDefinition.EXAM_MODIFY_SEB_RESTRICTION_DETAILS) .withEntityKey(entityKey) @@ -322,7 +327,7 @@ public class ExamForm implements TemplateComposer { .withAttribute(ExamSEBRestrictionSettings.PAGE_CONTEXT_ATTR_LMS_ID, String.valueOf(exam.lmsSetupId)) .withAttribute(PageContext.AttributeKeys.FORCE_READ_ONLY, String.valueOf(!modifyGrant || !editable)) .noEventPropagation() - .publishIf(() -> sebRestrictionAvailable && readonly) + .publishIf(() -> restrictionEnabled && sebRestrictionAvailable && readonly) .newAction(ActionDefinition.EXAM_ENABLE_SEB_RESTRICTION) .withEntityKey(entityKey) @@ -341,20 +346,20 @@ public class ExamForm implements TemplateComposer { .withEntityKey(entityKey) .withExec(this.proctoringSettingsPopup.settingsFunction(this.pageService, modifyGrant && editable)) .noEventPropagation() - .publishIf(() -> !isLight && proctoringEnabled && readonly) + .publishIf(() -> lpEnabled && !isLight && proctoringEnabled && readonly) .newAction(ActionDefinition.EXAM_PROCTORING_OFF) .withEntityKey(entityKey) .withExec(this.proctoringSettingsPopup.settingsFunction(this.pageService, modifyGrant && editable)) .noEventPropagation() - .publishIf(() -> !isLight && !proctoringEnabled && readonly) + .publishIf(() -> lpEnabled && !isLight && !proctoringEnabled && readonly) .newAction(ActionDefinition.SCREEN_PROCTORING_ON) .withEntityKey(entityKey) .withExec( this.screenProctoringSettingsPopup.settingsFunction(this.pageService, modifyGrant && editable)) .noEventPropagation() - .publishIf(() -> spsFeatureEnabled && screenProctoringEnabled && readonly) + .publishIf(() -> spsFeatureEnabled && screenProctoringEnabled && readonly) .newAction(ActionDefinition.SCREEN_PROCTORING_OFF) .withEntityKey(entityKey) @@ -501,8 +506,9 @@ public class ExamForm implements TemplateComposer { final Composite content, final Exam exam) { + final boolean templateEnabled = pageService.getCurrentUser().isFeatureEnabled(EXAM_TEMPLATE); final I18nSupport i18nSupport = formContext.getI18nSupport(); - final boolean isLight = pageService.isSEBServerLightSetup(); + final boolean isLight = pageService.isLightSetup(); final boolean newExam = exam.id == null; final boolean hasLMS = exam.lmsSetupId != null; final boolean importFromLMS = newExam && hasLMS; @@ -545,7 +551,7 @@ public class ExamForm implements TemplateComposer { this.resourceService::lmsSetupResource) .readonly(true)) - .addFieldIf(() -> !isLight && exam.id == null, + .addFieldIf(() -> templateEnabled && !isLight && exam.id == null, () -> FormBuilder.singleSelection( Domain.EXAM.ATTR_EXAM_TEMPLATE_ID, FORM_EXAM_TEMPLATE_TEXT_KEY, @@ -630,7 +636,7 @@ public class ExamForm implements TemplateComposer { DateTime.now(timeZone).plusHours(1), Exam.ExamType.UNDEFINED, null, - pageService.isSEBServerLightSetup() + pageService.isLightSetup() ? Stream.of(this.pageService.getCurrentUser().get().uuid).collect(Collectors.toList()) : null, ExamStatus.UP_COMING, @@ -811,7 +817,7 @@ public class ExamForm implements TemplateComposer { final EntityKey entityKey = pageContext.getEntityKey(); final EntityKey parentEntityKey = pageContext.getParentEntityKey(); final POSTMapper mapper = new POSTMapper(null, null); - if (pageService.isSEBServerLightSetup()) { + if (pageService.isLightSetup()) { mapper.putIfAbsent(Domain.EXAM.ATTR_SUPPORTER, this.pageService.getCurrentUser().get().uuid); } return this.restService.getBuilder(GetQuizData.class) diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/content/exam/ExamFormConfigs.java b/src/main/java/ch/ethz/seb/sebserver/gui/content/exam/ExamFormConfigs.java index 7132c693..c13d387f 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/content/exam/ExamFormConfigs.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/content/exam/ExamFormConfigs.java @@ -14,6 +14,8 @@ import java.util.HashSet; import java.util.Set; import java.util.function.Supplier; +import ch.ethz.seb.sebserver.gbl.model.user.UserFeatures; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.auth.CurrentUser; import org.apache.commons.lang3.BooleanUtils; import org.eclipse.swt.widgets.Composite; import org.springframework.context.annotation.Lazy; @@ -88,6 +90,7 @@ public class ExamFormConfigs implements TemplateComposer { @Override public void compose(final PageContext pageContext) { + final CurrentUser currentUser = pageService.getCurrentUser(); final Composite content = pageContext.getParent(); final EntityKey entityKey = pageContext.getEntityKey(); final boolean editable = BooleanUtils.toBoolean( @@ -97,6 +100,7 @@ public class ExamFormConfigs implements TemplateComposer { final ExamStatus examStatus = ExamStatus.valueOf( pageContext.getAttribute(ExamForm.ATTR_EXAM_STATUS)); final boolean isExamRunning = examStatus == ExamStatus.RUNNING; + final boolean examConfigEnabled = currentUser.isFeatureEnabled(UserFeatures.Feature.CONFIG_EXAM_CONFIGURATION); // List of SEB Configuration this.widgetFactory.addFormSubContextHeader( @@ -128,7 +132,7 @@ public class ExamFormConfigs implements TemplateComposer { this.resourceService::localizedExamConfigStatusName) .widthProportion(1)) .withDefaultActionIf( - () -> readGrant, + () -> readGrant && examConfigEnabled, this::viewExamConfigPageAction) .withSelectionListener(this.pageService.getSelectionPublisher( @@ -156,12 +160,12 @@ public class ExamFormConfigs implements TemplateComposer { .withParentEntityKey(entityKey) .withExec(this.examToConfigBindingForm.bindFunction()) .noEventPropagation() - .publishIf(() -> editable && !configurationTable.hasAnyContent()) + .publishIf(() -> examConfigEnabled && editable && !configurationTable.hasAnyContent()) .newAction(ActionDefinition.EXAM_CONFIGURATION_EXAM_CONFIG_VIEW_PROP) .withParentEntityKey(entityKey) .withEntityKey(configMapKey) - .publishIf(() -> readGrant && configurationTable.hasAnyContent(), false) + .publishIf(() -> examConfigEnabled && readGrant && configurationTable.hasAnyContent(), false) .newAction(ActionDefinition.EXAM_CONFIGURATION_DELETE_FROM_LIST) .withEntityKey(entityKey) @@ -175,7 +179,7 @@ public class ExamFormConfigs implements TemplateComposer { } return null; }) - .publishIf(() -> editable && configurationTable.hasAnyContent() && editable, false) + .publishIf(() -> examConfigEnabled && editable && configurationTable.hasAnyContent() && editable, false) .newAction(ActionDefinition.EXAM_CONFIGURATION_GET_CONFIG_KEY) .withSelect( diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/content/exam/ExamIndicatorsList.java b/src/main/java/ch/ethz/seb/sebserver/gui/content/exam/ExamIndicatorsList.java index 9436db71..bee112ea 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/content/exam/ExamIndicatorsList.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/content/exam/ExamIndicatorsList.java @@ -8,6 +8,8 @@ package ch.ethz.seb.sebserver.gui.content.exam; +import ch.ethz.seb.sebserver.gbl.model.user.UserFeatures; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.auth.CurrentUser; import org.apache.commons.lang3.BooleanUtils; import org.eclipse.swt.widgets.Composite; import org.springframework.context.annotation.Lazy; @@ -70,10 +72,12 @@ public class ExamIndicatorsList implements TemplateComposer { @Override public void compose(final PageContext pageContext) { + final CurrentUser currentUser = pageService.getCurrentUser(); final Composite content = pageContext.getParent(); final EntityKey entityKey = pageContext.getEntityKey(); final boolean editable = BooleanUtils.toBoolean(pageContext.getAttribute(ExamForm.ATTR_EDITABLE)); - final boolean isLight = pageService.isSEBServerLightSetup(); + final boolean isLight = pageService.isLightSetup(); + final boolean indicatorEnabled = currentUser.isFeatureEnabled(UserFeatures.Feature.EXAM_INDICATORS); // List of Indicators this.widgetFactory.addFormSubContextHeader( @@ -111,7 +115,7 @@ public class ExamIndicatorsList implements TemplateComposer { .asMarkup() .widthProportion(4)) .withDefaultActionIf( - () -> editable, + () -> editable && indicatorEnabled, () -> actionBuilder .newAction(ActionDefinition.EXAM_INDICATOR_MODIFY_FROM_LIST) .withParentEntityKey(entityKey) @@ -132,7 +136,7 @@ public class ExamIndicatorsList implements TemplateComposer { indicatorTable::getMultiSelection, PageAction::applySingleSelectionAsEntityKey, INDICATOR_EMPTY_SELECTION_TEXT_KEY) - .publishIf(() -> editable && indicatorTable.hasAnyContent(), false) + .publishIf(() -> indicatorEnabled && editable && indicatorTable.hasAnyContent(), false) .newAction(ActionDefinition.EXAM_INDICATOR_DELETE_FROM_LIST) .withEntityKey(entityKey) @@ -140,11 +144,11 @@ public class ExamIndicatorsList implements TemplateComposer { indicatorTable::getMultiSelection, this::deleteSelectedIndicator, INDICATOR_EMPTY_SELECTION_TEXT_KEY) - .publishIf(() -> !isLight && editable && indicatorTable.hasAnyContent(), false) + .publishIf(() -> indicatorEnabled && !isLight && editable && indicatorTable.hasAnyContent(), false) .newAction(ActionDefinition.EXAM_INDICATOR_NEW) .withParentEntityKey(entityKey) - .publishIf(() -> editable); + .publishIf(() -> indicatorEnabled && editable); } diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/content/exam/ExamList.java b/src/main/java/ch/ethz/seb/sebserver/gui/content/exam/ExamList.java index 3f31b036..50cbcf56 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/content/exam/ExamList.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/content/exam/ExamList.java @@ -177,7 +177,7 @@ public class ExamList implements TemplateComposer { .withColumnIf( () -> isSEBAdmin.getAsBoolean() && currentUser.isFeatureEnabled(UserFeatures.Feature.ADMIN_INSTITUTION) - && !pageService.isSEBServerLightSetup(), + && !pageService.isLightSetup(), () -> new ColumnDefinition( Domain.EXAM.ATTR_INSTITUTION_ID, COLUMN_TITLE_INSTITUTION_KEY, diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/content/exam/ExamTemplateList.java b/src/main/java/ch/ethz/seb/sebserver/gui/content/exam/ExamTemplateList.java index de0eab47..1552c457 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/content/exam/ExamTemplateList.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/content/exam/ExamTemplateList.java @@ -106,7 +106,7 @@ public class ExamTemplateList implements TemplateComposer { @Override public void compose(final PageContext pageContext) { - if (pageService.isSEBServerLightSetup()) { + if (pageService.isLightSetup()) { pageService.applyFullVersionNote(pageContext.getParent(), pageContext); return; } diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/content/exam/IndicatorForm.java b/src/main/java/ch/ethz/seb/sebserver/gui/content/exam/IndicatorForm.java index df02fbf5..88b15e51 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/content/exam/IndicatorForm.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/content/exam/IndicatorForm.java @@ -84,7 +84,7 @@ public class IndicatorForm implements TemplateComposer { @Override public void compose(final PageContext pageContext) { - if (pageService.isSEBServerLightSetup()) { + if (pageService.isLightSetup()) { pageService.applyFullVersionNote(pageContext.getParent(), pageContext); this.pageService.pageActionBuilder(pageContext.clearEntityKeys()) .newAction(ActionDefinition.EXAM_INDICATOR_CANCEL_MODIFY) diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/content/exam/LmsSetupList.java b/src/main/java/ch/ethz/seb/sebserver/gui/content/exam/LmsSetupList.java index 0abf4126..8c02b352 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/content/exam/LmsSetupList.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/content/exam/LmsSetupList.java @@ -109,7 +109,7 @@ public class LmsSetupList implements TemplateComposer { final WidgetFactory widgetFactory = this.pageService.getWidgetFactory(); final CurrentUser currentUser = this.resourceService.getCurrentUser(); final RestService restService = this.resourceService.getRestService(); - final boolean isLight = pageService.isSEBServerLightSetup(); + final boolean isLight = pageService.isLightSetup(); // content page layout with title final Composite content = widgetFactory.defaultPageLayout( diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/content/exam/QuizLookupList.java b/src/main/java/ch/ethz/seb/sebserver/gui/content/exam/QuizLookupList.java index 89a63ff6..d70f27c8 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/content/exam/QuizLookupList.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/content/exam/QuizLookupList.java @@ -195,7 +195,7 @@ public class QuizLookupList implements TemplateComposer { .withColumnIf( () -> isSEBAdmin.getAsBoolean() && currentUser.isFeatureEnabled(UserFeatures.Feature.ADMIN_INSTITUTION) - && !pageService.isSEBServerLightSetup(), + && !pageService.isLightSetup(), () -> new ColumnDefinition( QuizData.QUIZ_ATTR_INSTITUTION_ID, INSTITUTION_TEXT_KEY, diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/content/monitoring/MonitoringClientConnection.java b/src/main/java/ch/ethz/seb/sebserver/gui/content/monitoring/MonitoringClientConnection.java index 84b4b3dc..8e989536 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/content/monitoring/MonitoringClientConnection.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/content/monitoring/MonitoringClientConnection.java @@ -8,6 +8,8 @@ package ch.ethz.seb.sebserver.gui.content.monitoring; +import static ch.ethz.seb.sebserver.gbl.model.user.UserFeatures.Feature.*; + import java.util.Arrays; import java.util.Collection; import java.util.HashSet; @@ -181,6 +183,12 @@ public class MonitoringClientConnection implements TemplateComposer { final EntityKey entityKey = pageContext.getEntityKey(); final String connectionToken = pageContext.getAttribute(Domain.CLIENT_CONNECTION.ATTR_CONNECTION_TOKEN); + final boolean quitEnabled = currentUser.isFeatureEnabled(MONITORING_RUNNING_EXAM_QUIT); + final boolean lockscreenEnabled = currentUser.isFeatureEnabled(MONITORING_RUNNING_EXAM_QUIT); + final boolean cancelEnabled = currentUser.isFeatureEnabled(MONITORING_RUNNING_EXAM_CANCEL_CON); + final boolean askEnabled = currentUser.isFeatureEnabled(EXAM_ASK); + final boolean liveProctoringEnabled = currentUser.isFeatureEnabled(MONITORING_RUNNING_EXAM_LIVE_PROCTORING); + if (connectionToken == null) { pageContext.notifyUnexpectedError(new IllegalAccessException("connectionToken has null reference")); return; @@ -291,7 +299,7 @@ public class MonitoringClientConnection implements TemplateComposer { .withParentEntityKey(parentEntityKey) .withConfirm(() -> NOTIFICATION_LIST_CONFIRM_TEXT_KEY) .withSelect( - () -> notificationTable.getMultiSelection(), + notificationTable::getMultiSelection, action -> this.confirmNotification(action, connectionData, notificationTable), NOTIFICATION_LIST_NO_SELECTION_KEY) @@ -398,7 +406,7 @@ public class MonitoringClientConnection implements TemplateComposer { return action; }) .noEventPropagation() - .publishIf(() -> isExamSupporter.getAsBoolean() && + .publishIf(() -> quitEnabled && isExamSupporter.getAsBoolean() && connectionData.clientConnection.status.clientActiveStatus) .newAction(ActionDefinition.MONITOR_EXAM_CLIENT_CONNECTION_LOCK) @@ -406,7 +414,7 @@ public class MonitoringClientConnection implements TemplateComposer { .withExec(action -> this.sebSendLockPopup.show(action, some -> new HashSet<>(Arrays.asList(connectionToken)))) .noEventPropagation() - .publishIf(() -> isExamSupporter.getAsBoolean() && + .publishIf(() -> lockscreenEnabled && isExamSupporter.getAsBoolean() && connectionData.clientConnection.status.clientActiveStatus) .newAction(ActionDefinition.MONITOR_EXAM_CLIENT_DISABLE_CONNECTION) @@ -419,11 +427,11 @@ public class MonitoringClientConnection implements TemplateComposer { : null) .withExec(action -> this.disableClientConnection(action, clientConnectionDetails)) .noEventPropagation() - .publishIf(() -> isExamSupporter.getAsBoolean() && + .publishIf(() -> cancelEnabled && isExamSupporter.getAsBoolean() && (connectionData.clientConnection.status.clientActiveStatus || connectionData.clientConnection.status == ConnectionStatus.CLOSED)); - if (clientConnectionDetails.checkSecurityGrant) { + if (askEnabled && clientConnectionDetails.checkSecurityGrant) { final SecurityKey securityKey = this.pageService .getRestService() .getBuilder(GetClientConnectionSecurityKey.class) @@ -444,7 +452,7 @@ public class MonitoringClientConnection implements TemplateComposer { } } - if (connectionData.clientConnection.status == ConnectionStatus.ACTIVE) { + if (liveProctoringEnabled && connectionData.clientConnection.status == ConnectionStatus.ACTIVE) { final ProctoringServiceSettings proctoringSettings = restService .getBuilder(GetExamProctoringSettings.class) .withURIVariable(API.PARAM_MODEL_ID, parentEntityKey.modelId) diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/content/monitoring/MonitoringRunningExam.java b/src/main/java/ch/ethz/seb/sebserver/gui/content/monitoring/MonitoringRunningExam.java index 0b37f670..91dc2708 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/content/monitoring/MonitoringRunningExam.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/content/monitoring/MonitoringRunningExam.java @@ -8,6 +8,8 @@ package ch.ethz.seb.sebserver.gui.content.monitoring; +import static ch.ethz.seb.sebserver.gbl.model.user.UserFeatures.Feature.*; + import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -148,6 +150,11 @@ public class MonitoringRunningExam implements TemplateComposer { .call() .getOrThrow(); final UserInfo user = currentUser.get(); + + final boolean detailEnabled = currentUser.isFeatureEnabled(MONITORING_RUNNING_EXAM_DETAIL_VIEW); + final boolean quitEnabled = currentUser.isFeatureEnabled(MONITORING_RUNNING_EXAM_QUIT); + final boolean lockscreenEnabled = currentUser.isFeatureEnabled(MONITORING_RUNNING_EXAM_QUIT); + final boolean cancelEnabled = currentUser.isFeatureEnabled(MONITORING_RUNNING_EXAM_CANCEL_CON); final boolean supporting = user.hasRole(UserRole.EXAM_SUPPORTER) && exam.supporter.contains(user.uuid); final BooleanSupplier isExamSupporter = () -> supporting || user.hasRole(UserRole.EXAM_ADMIN); @@ -195,7 +202,7 @@ public class MonitoringRunningExam implements TemplateComposer { guiUpdates.add(clientTable); clientTable - .withDefaultAction( + .withDefaultActionIf(() -> detailEnabled, actionBuilder .newAction(ActionDefinition.MONITOR_EXAM_CLIENT_CONNECTION) .withParentEntityKey(entityKey) @@ -221,7 +228,7 @@ public class MonitoringRunningExam implements TemplateComposer { .withConfirm(() -> CONFIRM_QUIT_ALL) .withExec(action -> this.quitSEBClients(action, clientTable, true)) .noEventPropagation() - .publishIf(isExamSupporter) + .publishIf(() -> isExamSupporter.getAsBoolean() && quitEnabled) .newAction(ActionDefinition.MONITOR_EXAM_QUIT_SELECTED) .withEntityKey(entityKey) @@ -231,7 +238,7 @@ public class MonitoringRunningExam implements TemplateComposer { action -> this.quitSEBClients(action, clientTable, false), EMPTY_ACTIVE_SELECTION_TEXT_KEY) .noEventPropagation() - .publishIf(isExamSupporter, false) + .publishIf(() -> isExamSupporter.getAsBoolean() && quitEnabled, false) .newAction(ActionDefinition.MONITOR_EXAM_LOCK_SELECTED) .withEntityKey(entityKey) @@ -240,7 +247,7 @@ public class MonitoringRunningExam implements TemplateComposer { action -> this.showSEBLockActionPopup(action, clientTable), EMPTY_ACTIVE_SELECTION_TEXT_KEY) .noEventPropagation() - .publishIf(isExamSupporter, false) + .publishIf(() -> isExamSupporter.getAsBoolean() && lockscreenEnabled, false) .newAction(ActionDefinition.MONITOR_EXAM_CLIENT_CONNECTION) .withParentEntityKey(entityKey) @@ -260,7 +267,7 @@ public class MonitoringRunningExam implements TemplateComposer { return copyOfPageAction; }) - .publishIf(isExamSupporter, false) + .publishIf(() -> isExamSupporter.getAsBoolean() && detailEnabled, false) .newAction(ActionDefinition.MONITOR_EXAM_DISABLE_SELECTED_CONNECTION) .withEntityKey(entityKey) @@ -270,7 +277,7 @@ public class MonitoringRunningExam implements TemplateComposer { action -> this.disableSEBClients(action, clientTable, false), EMPTY_SELECTION_TEXT_KEY) .noEventPropagation() - .publishIf(isExamSupporter, false); + .publishIf(() -> isExamSupporter.getAsBoolean() && cancelEnabled, false); if (isExamSupporter.getAsBoolean()) { guiUpdates.add(createFilterActions( @@ -317,13 +324,16 @@ public class MonitoringRunningExam implements TemplateComposer { final PageContext pageContext, final Composite parent) { + final CurrentUser currentUser = pageService.getCurrentUser(); final PageActionBuilder actionBuilder = this.pageService .pageActionBuilder(pageContext.clearEntityKeys()); - final boolean proctoringEnabled = proctoringSettings != null && - BooleanUtils.toBoolean(proctoringSettings.enableProctoring); - final boolean screenProctoringEnabled = screenProctoringSettings != null && - BooleanUtils.toBoolean(screenProctoringSettings.enableScreenProctoring); + final boolean proctoringEnabled = proctoringSettings != null + && currentUser.isFeatureEnabled(MONITORING_RUNNING_EXAM_LIVE_PROCTORING) + && BooleanUtils.toBoolean(proctoringSettings.enableProctoring); + final boolean screenProctoringEnabled = screenProctoringSettings != null + && currentUser.isFeatureEnabled(MONITORING_RUNNING_EXAM_SCREEN_PROCTORING) + && BooleanUtils.toBoolean(screenProctoringSettings.enableScreenProctoring); if (!proctoringEnabled && !screenProctoringEnabled) { return monitoringStatus -> { @@ -408,43 +418,50 @@ public class MonitoringRunningExam implements TemplateComposer { final boolean isAskCheckEnabled, final List allowedSEBVersions) { + final CurrentUser currentUser = pageService.getCurrentUser(); + final boolean stateFilterEnabled = currentUser.isFeatureEnabled(MONITORING_RUNNING_EXAM_STATE_FILTER); + final boolean issueFilterEnabled = currentUser.isFeatureEnabled(MONITORING_RUNNING_EXAM_ISSUE_FILTER); + final boolean clientFilterEnabled = currentUser.isFeatureEnabled(MONITORING_RUNNING_EXAM_CLIENT_FILTER); + final FilterGUIUpdate statusFilterGUIUpdate = new FilterGUIUpdate(this.pageService.getPolyglotPageService()); - addFilterAction( - monitoringStatus, - statusFilterGUIUpdate, - actionBuilder, - clientTable, - ConnectionStatus.CONNECTION_REQUESTED, - ActionDefinition.MONITOR_EXAM_SHOW_REQUESTED_CONNECTION, - ActionDefinition.MONITOR_EXAM_HIDE_REQUESTED_CONNECTION); - addFilterAction( - monitoringStatus, - statusFilterGUIUpdate, - actionBuilder, - clientTable, - ConnectionStatus.ACTIVE, - ActionDefinition.MONITOR_EXAM_SHOW_ACTIVE_CONNECTION, - ActionDefinition.MONITOR_EXAM_HIDE_ACTIVE_CONNECTION); - addFilterAction( - monitoringStatus, - statusFilterGUIUpdate, - actionBuilder, - clientTable, - ConnectionStatus.CLOSED, - ActionDefinition.MONITOR_EXAM_SHOW_CLOSED_CONNECTION, - ActionDefinition.MONITOR_EXAM_HIDE_CLOSED_CONNECTION); - addFilterAction( - monitoringStatus, - statusFilterGUIUpdate, - actionBuilder, - clientTable, - ConnectionStatus.DISABLED, - ActionDefinition.MONITOR_EXAM_SHOW_DISABLED_CONNECTION, - ActionDefinition.MONITOR_EXAM_HIDE_DISABLED_CONNECTION); + if (stateFilterEnabled) { + addFilterAction( + monitoringStatus, + statusFilterGUIUpdate, + actionBuilder, + clientTable, + ConnectionStatus.CONNECTION_REQUESTED, + ActionDefinition.MONITOR_EXAM_SHOW_REQUESTED_CONNECTION, + ActionDefinition.MONITOR_EXAM_HIDE_REQUESTED_CONNECTION); + addFilterAction( + monitoringStatus, + statusFilterGUIUpdate, + actionBuilder, + clientTable, + ConnectionStatus.ACTIVE, + ActionDefinition.MONITOR_EXAM_SHOW_ACTIVE_CONNECTION, + ActionDefinition.MONITOR_EXAM_HIDE_ACTIVE_CONNECTION); + addFilterAction( + monitoringStatus, + statusFilterGUIUpdate, + actionBuilder, + clientTable, + ConnectionStatus.CLOSED, + ActionDefinition.MONITOR_EXAM_SHOW_CLOSED_CONNECTION, + ActionDefinition.MONITOR_EXAM_HIDE_CLOSED_CONNECTION); + addFilterAction( + monitoringStatus, + statusFilterGUIUpdate, + actionBuilder, + clientTable, + ConnectionStatus.DISABLED, + ActionDefinition.MONITOR_EXAM_SHOW_DISABLED_CONNECTION, + ActionDefinition.MONITOR_EXAM_HIDE_DISABLED_CONNECTION); + } - if(isAskCheckEnabled){ + if(issueFilterEnabled && isAskCheckEnabled) { addIssueFilterAction( monitoringStatus, statusFilterGUIUpdate, @@ -455,7 +472,7 @@ public class MonitoringRunningExam implements TemplateComposer { ActionDefinition.MONITOR_EXAM_HIDE_ASK_GRANTED); } - if(allowedSEBVersions != null) { + if(issueFilterEnabled && allowedSEBVersions != null) { addIssueFilterAction( monitoringStatus, statusFilterGUIUpdate, @@ -466,7 +483,7 @@ public class MonitoringRunningExam implements TemplateComposer { ActionDefinition.MONITOR_EXAM_HIDE_SEB_VERSION_GRANTED); } - if (clientGroups != null && !clientGroups.isEmpty()) { + if (clientFilterEnabled && clientGroups != null && !clientGroups.isEmpty()) { clientGroups.forEach(clientGroup -> { addClientGroupFilterAction( diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/service/ResourceService.java b/src/main/java/ch/ethz/seb/sebserver/gui/service/ResourceService.java index 546ace1a..676d5524 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/service/ResourceService.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/service/ResourceService.java @@ -22,6 +22,7 @@ import java.util.function.Supplier; import java.util.stream.Collectors; import java.util.stream.Stream; +import ch.ethz.seb.sebserver.gbl.model.user.*; import org.apache.commons.lang3.StringUtils; import org.joda.time.DateTimeZone; import org.slf4j.Logger; @@ -64,10 +65,6 @@ import ch.ethz.seb.sebserver.gbl.model.session.ClientEvent; import ch.ethz.seb.sebserver.gbl.model.session.ClientEvent.EventType; import ch.ethz.seb.sebserver.gbl.model.session.ClientNotification; import ch.ethz.seb.sebserver.gbl.model.session.ClientNotification.NotificationType; -import ch.ethz.seb.sebserver.gbl.model.user.UserActivityLog; -import ch.ethz.seb.sebserver.gbl.model.user.UserInfo; -import ch.ethz.seb.sebserver.gbl.model.user.UserLogActivityType; -import ch.ethz.seb.sebserver.gbl.model.user.UserRole; import ch.ethz.seb.sebserver.gbl.profile.GuiProfile; import ch.ethz.seb.sebserver.gbl.util.Result; import ch.ethz.seb.sebserver.gbl.util.Tuple; @@ -197,7 +194,7 @@ public class ResourceService { public List> lmsTypeResources() { return Arrays.stream(LmsType.values()) - .filter(lmsType -> this.currentUser.isFeatureEnabled("lms.type." + lmsType.name())) + .filter(lmsType -> this.currentUser.isFeatureEnabled("lms.setup.type." + lmsType.name())) .map(lmsType -> new Tuple<>( lmsType.name(), this.i18nSupport.getText(LMSSETUP_TYPE_PREFIX + lmsType.name(), lmsType.name()))) @@ -294,8 +291,10 @@ public class ResourceService { } public List> userRoleResources() { + final boolean showServerAdminRole = this.currentUser.isFeatureEnabled(UserFeatures.Feature.ADMIN_INSTITUTION); return UserRole.publicRolesForUser(this.currentUser.get()) .stream() + .filter(ur -> ur != UserRole.SEB_SERVER_ADMIN || showServerAdminRole) .map(ur -> new Tuple3<>( ur.name(), this.i18nSupport.getText(USERACCOUNT_ROLE_PREFIX + ur.name()), diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/service/examconfig/impl/AttributeMapping.java b/src/main/java/ch/ethz/seb/sebserver/gui/service/examconfig/impl/AttributeMapping.java index 8fa85bad..9b6582e8 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/service/examconfig/impl/AttributeMapping.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/service/examconfig/impl/AttributeMapping.java @@ -8,14 +8,11 @@ package ch.ethz.seb.sebserver.gui.service.examconfig.impl; -import java.util.Collection; -import java.util.Collections; -import java.util.List; -import java.util.Map; -import java.util.Objects; +import java.util.*; import java.util.function.Function; import java.util.stream.Collectors; +import ch.ethz.seb.sebserver.webservice.datalayer.batis.model.OrientationRecord; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -54,34 +51,41 @@ public class AttributeMapping { .stream() .collect(Collectors.toMap( o -> o.attributeId, - Function.identity()))); - + Function.identity(), + (first, second) -> { + log.warn("*** Found duplicate orientation, use {} instead of {}", second, first); + return second; + }))); this.attributeIdMapping = Utils.immutableMapOf(attributes .stream() .filter(attr -> this.orientationAttributeMapping.containsKey(attr.id)) .collect(Collectors.toMap( attr -> attr.id, - Function.identity()))); + Function.identity(), + (first, second) -> second))); this.attributeNameIdMapping = Utils.immutableMapOf(attributes .stream() .filter(attr -> this.orientationAttributeMapping.containsKey(attr.id)) .collect(Collectors.toMap( attr -> attr.name, - attr -> attr.id))); + attr -> attr.id, + (first, second) -> second))); this.orientationAttributeNameMapping = Utils.immutableMapOf(orientations .stream() .collect(Collectors.toMap( o -> this.attributeIdMapping.get(o.attributeId).name, - Function.identity()))); + Function.identity(), + (first, second) -> second))); this.childAttributeMapping = Utils.immutableMapOf(attributes .stream() .filter(attr -> this.orientationAttributeMapping.containsKey(attr.id)) .collect(Collectors.toMap( attr -> attr.id, - this::getChildAttributes))); + this::getChildAttributes, + (first, second) -> second))); this.attributeGroupMapping = Utils.immutableMapOf(orientations .stream() @@ -91,7 +95,8 @@ public class AttributeMapping { .stream() .collect(Collectors.toMap( Function.identity(), - this::getAttributesOfGroup))); + this::getAttributesOfGroup, + (first, second) -> second))); } public Collection getAttributes() { 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 05ef5125..d172b046 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 @@ -130,7 +130,7 @@ public interface PageService { /** Indicates if SEB Server runs with SEB Server light setup * * @return true if this is a SEB Server light setup*/ - boolean isSEBServerLightSetup(); + boolean isLightSetup(); void applyFullVersionNote(Composite content, PageContext 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 c3294045..78678e97 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 @@ -188,7 +188,7 @@ public class PageServiceImpl implements PageService { } @Override - public boolean isSEBServerLightSetup() { + public boolean isLightSetup() { return this.guiServiceInfo.isLightSetup(); } diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/service/session/ClientConnectionTable.java b/src/main/java/ch/ethz/seb/sebserver/gui/service/session/ClientConnectionTable.java index adabece4..3054d6b2 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/service/session/ClientConnectionTable.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/service/session/ClientConnectionTable.java @@ -19,6 +19,7 @@ import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; +import java.util.function.BooleanSupplier; import java.util.function.Consumer; import java.util.function.Function; import java.util.function.Predicate; @@ -221,6 +222,16 @@ public final class ClientConnectionTable implements FullPageMonitoringGUIUpdate return this.exam; } + public ClientConnectionTable withDefaultActionIf( + final BooleanSupplier check, + final PageAction pageAction, + final PageService pageService) { + if (check.getAsBoolean()) { + return withDefaultAction(pageAction, pageService); + } + return this; + } + public ClientConnectionTable withDefaultAction(final PageAction pageAction, final PageService pageService) { this.table.addListener(SWT.MouseDoubleClick, event -> { final Tuple selection = getSingleSelection(); diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/checks/OrientationTableDuplicatesCheck.java b/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/checks/OrientationTableDuplicatesCheck.java index b5935d6d..d3d5224f 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/checks/OrientationTableDuplicatesCheck.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/checks/OrientationTableDuplicatesCheck.java @@ -8,9 +8,7 @@ package ch.ethz.seb.sebserver.webservice.datalayer.checks; -import java.util.HashSet; -import java.util.List; -import java.util.Set; +import java.util.*; import java.util.stream.Collectors; import org.mybatis.dynamic.sql.SqlBuilder; @@ -68,7 +66,8 @@ public class OrientationTableDuplicatesCheck implements DBIntegrityCheck { final List checkedToDelete = toDelete .stream() - .filter(this::doubleCheck) + .map(this::getOldestForDeletion) + .filter(Objects::nonNull) .collect(Collectors.toList()); if (checkedToDelete == null || checkedToDelete.isEmpty()) { @@ -88,21 +87,38 @@ public class OrientationTableDuplicatesCheck implements DBIntegrityCheck { }); } - private boolean doubleCheck(final Long id) { + private Long getOldestForDeletion(final Long id) { try { final OrientationRecord selectByPrimaryKey = this.orientationRecordMapper.selectByPrimaryKey(id); - final Long count = this.orientationRecordMapper.countByExample() + final List records = this.orientationRecordMapper.selectByExample() .where( OrientationRecordDynamicSqlSupport.configAttributeId, SqlBuilder.isEqualTo(selectByPrimaryKey.getConfigAttributeId())) .and( OrientationRecordDynamicSqlSupport.templateId, SqlBuilder.isEqualTo(selectByPrimaryKey.getTemplateId())) + .orderBy(OrientationRecordDynamicSqlSupport.id) .build() .execute(); - return count != null && count.longValue() > 1; + + // get latest entry of duplicates + if (records != null && records.size() > 1) { + Long result = null; + for (int i = 0; i < records.size(); i++) { + final OrientationRecord rec = records.get(i); + if (result == null) { + result = rec.getId(); + continue; + } + if (result > rec.getId()) { + result = rec.getId(); + } + } + return result; + } + return null; } catch (final Exception e) { - return false; + return null; } } diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/bulkaction/impl/BatchActionServiceImpl.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/bulkaction/impl/BatchActionServiceImpl.java index 42be44b6..1b5b1aa9 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/bulkaction/impl/BatchActionServiceImpl.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/bulkaction/impl/BatchActionServiceImpl.java @@ -253,7 +253,7 @@ public class BatchActionServiceImpl implements BatchActionService { this.batchActionExec .doSingleAction(modelId, this.batchAction) .onError(error -> this.batchActionHandler.handleError(modelId, error)) - .onSuccess(entityKey -> this.batchActionHandler.handleSuccess(entityKey)); + .onSuccess(this.batchActionHandler::handleSuccess); }); this.batchActionHandler.finishUp(); diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/exam/ExamAdminService.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/exam/ExamAdminService.java index fe51b753..d848f93f 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/exam/ExamAdminService.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/exam/ExamAdminService.java @@ -161,9 +161,9 @@ public interface ExamAdminService { /** Gets invoked after an exam has been changed and saved. * * @param exam the exam that has been changed and saved */ - void notifyExamSaved(Exam exam); + Result notifyExamSaved(Exam exam); - void applyQuitPassword(Exam entity); + Result applyQuitPassword(Exam exam); static void newExamFieldValidation(final POSTMapper postParams) { noLMSFieldValidation(new Exam(postParams)); diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/exam/impl/ExamAdminServiceImpl.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/exam/impl/ExamAdminServiceImpl.java index 0f4cb7b8..a41e67f1 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/exam/impl/ExamAdminServiceImpl.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/exam/impl/ExamAdminServiceImpl.java @@ -69,6 +69,7 @@ public class ExamAdminServiceImpl implements ExamAdminService { private final boolean appSignatureKeyEnabled; private final int defaultNumericalTrustThreshold; private final ExamConfigurationValueService examConfigurationValueService; + private final SEBRestrictionService sebRestrictionService; protected ExamAdminServiceImpl( final ExamDAO examDAO, @@ -78,6 +79,7 @@ public class ExamAdminServiceImpl implements ExamAdminService { final ExamConfigurationMapDAO examConfigurationMapDAO, final LmsAPIService lmsAPIService, final ExamConfigurationValueService examConfigurationValueService, + final SEBRestrictionService sebRestrictionService, final @Value("${sebserver.webservice.api.admin.exam.app.signature.key.enabled:false}") boolean appSignatureKeyEnabled, final @Value("${sebserver.webservice.api.admin.exam.app.signature.key.numerical.threshold:2}") int defaultNumericalTrustThreshold) { @@ -90,6 +92,7 @@ public class ExamAdminServiceImpl implements ExamAdminService { this.examConfigurationValueService = examConfigurationValueService; this.appSignatureKeyEnabled = appSignatureKeyEnabled; this.defaultNumericalTrustThreshold = defaultNumericalTrustThreshold; + this.sebRestrictionService = sebRestrictionService; } @Override @@ -323,16 +326,21 @@ public class ExamAdminServiceImpl implements ExamAdminService { } @Override - public void notifyExamSaved(final Exam exam) { - updateAdditionalExamConfigAttributes(exam.id); - this.proctoringAdminService.notifyExamSaved(exam); + public Result notifyExamSaved(final Exam exam) { + return Result.tryCatch(() -> { + updateAdditionalExamConfigAttributes(exam.id); + this.proctoringAdminService.notifyExamSaved(exam); + return exam; + }); } @Override - public void applyQuitPassword(final Exam exam) { - this.examConfigurationValueService + public Result applyQuitPassword(final Exam exam) { + return this.examConfigurationValueService .applyQuitPasswordToConfigs(exam.id, exam.quitPassword) - .getOrThrow(); + .flatMap(id -> this.sebRestrictionService.applySEBClientRestriction(exam)) + .flatMap(e -> this.examDAO.setSEBRestriction(e.id, true)) + .onError(t -> log.error("Failed to update SEB Client restriction for Exam: {}", exam, t)); } private Result initAdditionalAttributesForMoodleExams(final Exam exam) { diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/ExamAPI_V1_Controller.java b/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/ExamAPI_V1_Controller.java index 193f4492..9b37dc09 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/ExamAPI_V1_Controller.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/ExamAPI_V1_Controller.java @@ -19,7 +19,6 @@ import java.util.concurrent.CompletableFuture; import java.util.concurrent.Executor; import java.util.stream.Collectors; -import javax.servlet.ServletOutputStream; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @@ -40,15 +39,12 @@ import org.springframework.web.bind.annotation.RestController; 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.JSONMapper; import ch.ethz.seb.sebserver.gbl.api.POSTMapper; import ch.ethz.seb.sebserver.gbl.async.AsyncServiceSpringConfig; import ch.ethz.seb.sebserver.gbl.model.exam.Exam; import ch.ethz.seb.sebserver.gbl.model.session.ClientConnection; import ch.ethz.seb.sebserver.gbl.model.session.RunningExamInfo; import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile; -import ch.ethz.seb.sebserver.gbl.util.Utils; import ch.ethz.seb.sebserver.webservice.servicelayer.dao.LmsSetupDAO; import ch.ethz.seb.sebserver.webservice.servicelayer.dao.SEBClientConfigDAO; import ch.ethz.seb.sebserver.webservice.servicelayer.lms.SEBRestrictionService; @@ -68,7 +64,6 @@ public class ExamAPI_V1_Controller { private final SEBClientConnectionService sebClientConnectionService; private final SEBClientSessionService sebClientSessionService; private final SEBClientConfigDAO sebClientConfigDAO; - private final JSONMapper jsonMapper; private final Executor executor; protected ExamAPI_V1_Controller( @@ -77,7 +72,6 @@ public class ExamAPI_V1_Controller { final SEBClientConnectionService sebClientConnectionService, final SEBClientSessionService sebClientSessionService, final SEBClientConfigDAO sebClientConfigDAO, - final JSONMapper jsonMapper, @Qualifier(AsyncServiceSpringConfig.EXAM_API_EXECUTOR_BEAN_NAME) final Executor executor) { this.lmsSetupDAO = lmsSetupDAO; @@ -85,7 +79,6 @@ public class ExamAPI_V1_Controller { this.sebClientConnectionService = sebClientConnectionService; this.sebClientSessionService = sebClientSessionService; this.sebClientConfigDAO = sebClientConfigDAO; - this.jsonMapper = jsonMapper; this.executor = executor; } diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/ExamAdministrationController.java b/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/ExamAdministrationController.java index 6ff20ca6..75df0c6f 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/ExamAdministrationController.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/ExamAdministrationController.java @@ -15,7 +15,6 @@ import java.util.stream.Collectors; import javax.validation.Valid; import ch.ethz.seb.sebserver.gbl.util.Cryptor; -import ch.ethz.seb.sebserver.webservice.servicelayer.exam.ExamConfigurationValueService; import ch.ethz.seb.sebserver.webservice.servicelayer.lms.impl.NoSEBRestrictionException; import org.apache.commons.lang3.StringUtils; import org.joda.time.DateTime; @@ -669,12 +668,9 @@ public class ExamAdministrationController extends EntityController { @Override protected Result notifySaved(final Exam entity) { - return Result.tryCatch(() -> { - this.examAdminService.notifyExamSaved(entity); - this.examAdminService.applyQuitPassword(entity); - this.examSessionService.flushCache(entity); - return entity; - }); + return this.examAdminService.notifyExamSaved(entity) + .flatMap(this.examAdminService::applyQuitPassword) + .flatMap(this.examSessionService::flushCache); } @Override diff --git a/src/main/resources/config/application-dev-ws.properties b/src/main/resources/config/application-dev-ws.properties index 927a17aa..9f9e5f2b 100644 --- a/src/main/resources/config/application-dev-ws.properties +++ b/src/main/resources/config/application-dev-ws.properties @@ -23,9 +23,11 @@ sebserver.webservice.distributed.updateInterval=1000 sebserver.webservice.distributed.connectionUpdate=2000 sebserver.webservice.clean-db-on-startup=false +sebserver.init.database.integrity.try-fix=true + # webservice setup configuration sebserver.init.adminaccount.gen-on-init=false -sebserver.webservice.light.setup=true +sebserver.webservice.light.setup=false sebserver.webservice.distributed=false #sebserver.webservice.master.delay.threshold=10000 sebserver.webservice.http.external.scheme=http @@ -76,5 +78,39 @@ sebserver.feature.seb.screenProctoring.bundled.url=http://localhost:8090 sebserver.feature.seb.screenProctoring.bundled.clientId=sebserverClient sebserver.feature.seb.screenProctoring.bundled.sebserveraccount.username=SEBServerAPIAccount -sebserver.feature.admin.institution.enabled=true - +#sebserver.feature.admin.user.administration.enabled=false +#sebserver.feature.admin.user.account.enabled=false +#sebserver.feature.admin.institution.enabled=false +#sebserver.feature.admin.auditlogs.enabled=false +# +#sebserver.feature.config.connection.configuration.enabled=false +#sebserver.feature.config.exam.configuration.enabled=false +#sebserver.feature.config.template.enabled=false +#sebserver.feature.config.certificate.enabled=false +# +#sebserver.feature.lms.setup.type.ANS_DELFT.enabled=false +#sebserver.feature.lms.setup.type.OPEN_OLAT.enabled=false +# +#sebserver.feature.exam.ask.enabled=false +#sebserver.feature.exam.seb.restriction.enabled=false +#sebserver.feature.exam.seb.liveProctoring.enabled=false +#sebserver.feature.exam.seb.screenProctoring.enabled=false +#sebserver.feature.exam.monitoring.indicators.enabled=false +#sebserver.feature.exam.connection.config.enabled=false +#sebserver.feature.exam.seb.clientgroups.enabled=false +# +#sebserver.feature.exam.template.enabled=false +#sebserver.feature.exam.noLMS.enabled=false +# +#sebserver.feature.monitoring.running.exam.quit.enabled=false +#sebserver.feature.monitoring.running.exam.lockscreen.enabled=false +#sebserver.feature.monitoring.running.exam.cancel.connection.enabled=false +#sebserver.feature.monitoring.running.exam.state.filter.enabled=false +# +#sebserver.feature.monitoring.running.exams.enabled=false +#sebserver.feature.monitoring.finished.exams.enabled=false +#sebserver.feature.monitoring.overall.export.enabled=false +# +#sebserver.feature.lms.quiz.lookup.enabled=false +#sebserver.feature.exam.administration.enabled=false +#sebserver.feature.lms.setup.enabled=false