Merge branch 'development' of github.com:SafeExamBrowser/seb-server into SEBSERV-398

This commit is contained in:
Nadim Ritter 2024-02-07 16:26:55 +01:00
commit f187582a2b
35 changed files with 407 additions and 253 deletions

View file

@ -27,15 +27,20 @@ public class UserFeatures {
CONFIG_CERTIFICATE("config.certificate"), CONFIG_CERTIFICATE("config.certificate"),
LMS_SETUP( "lms.setup"), LMS_SETUP( "lms.setup"),
LMS_SETUP_TEST("lms.setup.type.MOCKUP"), LMS_SETUP_TEST("lms.setup.type.MOCKUP"),
LMS_SETUP_MOODLE("lms.setup.type.moodle"), LMS_SETUP_MOODLE("lms.setup.type.MOODLE"),
LMS_SETUP_MOODLE_PLUGIN("lms.setup.type.moodle.plugin"), LMS_SETUP_MOODLE_PLUGIN("lms.setup.type.MOODLE_PLUGIN"),
LMS_SETUP_OPEN_EDX("lms.setup.type.openedx"), LMS_SETUP_OPEN_EDX("lms.setup.type.OPEN_EDX"),
LMS_SETUP_ANS("lms.setup.type.ans"), LMS_SETUP_ANS("lms.setup.type.ANS_DELFT"),
LMS_SETUP_OPEN_OLAT("lms.setup.type.openolat"), LMS_SETUP_OPEN_OLAT("lms.setup.type.OPEN_OLAT"),
QUIZ_LOOKUP("lms.quiz.lookup"),
EXAM_ADMIN("exam.administration"),
EXAM_ASK("exam.ask"), EXAM_ASK("exam.ask"),
EXAM_CONNECTION_CONFIG("exam.connection.config"),
EXAM_SEB_RESTRICTION( "exam.seb.restriction"), EXAM_SEB_RESTRICTION( "exam.seb.restriction"),
EXAM_LIVE_PROCTORING("exam.seb.liveProctoring"), EXAM_LIVE_PROCTORING("exam.seb.liveProctoring"),
EXAM_NO_LMS("exam.noLMS"),
EXAM_SCREEN_PROCTORING("exam.seb.screenProctoring"), EXAM_SCREEN_PROCTORING("exam.seb.screenProctoring"),
EXAM_INDICATORS("exam.monitoring.indicators"), 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("monitoring.running.exam.detailview"),
MONITORING_RUNNING_EXAM_DETAIL_VIEW_LOG_EXPORT("monitoring.running.exam.detailview.logexport"), MONITORING_RUNNING_EXAM_DETAIL_VIEW_LOG_EXPORT("monitoring.running.exam.detailview.logexport"),
//more? ... //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"), 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_FINISHED_EXAMS("monitoring.finished.exams"),
MONITORING_OVERALL_LOG_EXPORT("monitoring.overall.export"), MONITORING_OVERALL_LOG_EXPORT("monitoring.overall.export"),
EXAM_NO_LMS("exam.noLMS"),
; ;
public final String featureName; public final String featureName;

View file

@ -67,7 +67,6 @@ public class LoginPage implements TemplateComposer {
this.i18nSupport = pageService.getI18nSupport(); this.i18nSupport = pageService.getI18nSupport();
this.defaultRegisterPage = defaultRegisterPage; this.defaultRegisterPage = defaultRegisterPage;
this.registeringEnabled = BooleanUtils.toBoolean(registeringEnabled); this.registeringEnabled = BooleanUtils.toBoolean(registeringEnabled);
} }
@Override @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); final Button registerButton = this.widgetFactory.buttonLocalized(buttons, TEXT_REGISTER);
gridData = new GridData(SWT.LEFT, SWT.TOP, false, false); gridData = new GridData(SWT.LEFT, SWT.TOP, false, false);
gridData.verticalIndent = 10; gridData.verticalIndent = 10;

View file

@ -135,7 +135,9 @@ public class ActivitiesPane implements TemplateComposer {
// User Account // User Account
// if current user has role seb-server admin or institutional-admin, show list // 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( final TreeItem userAccounts = this.widgetFactory.treeItemLocalized(
sebAdmin, sebAdmin,
@ -145,15 +147,15 @@ public class ActivitiesPane implements TemplateComposer {
actionBuilder actionBuilder
.newAction(ActionDefinition.USER_ACCOUNT_VIEW_LIST) .newAction(ActionDefinition.USER_ACCOUNT_VIEW_LIST)
.create()); .create());
} else { } else if (currentUser.isFeatureEnabled(UserFeatures.Feature.ADMIN_USER_ACCOUNT)) {
// otherwise show the user account form for current user // otherwise show the user account form for current user
final TreeItem userAccounts = pageService.isSEBServerLightSetup() final TreeItem userAccounts = pageService.isLightSetup() || !currentUser.isFeatureEnabled(UserFeatures.Feature.ADMIN_USER_ADMINISTRATION)
? this.widgetFactory.treeItemLocalized( ? this.widgetFactory.treeItemLocalized(
sebAdmin, sebAdmin,
ActivityDefinition.USER_ACCOUNT.displayName) ActivityDefinition.USER_ACCOUNT.displayName)
: this.widgetFactory.treeItemLocalized( : this.widgetFactory.treeItemLocalized(
navigation, navigation,
ActivityDefinition.USER_ACCOUNT.displayName); ActivityDefinition.USER_ACCOUNT.displayName);
injectActivitySelection( injectActivitySelection(
userAccounts, userAccounts,
actionBuilder.newAction(ActionDefinition.USER_ACCOUNT_VIEW_FORM) actionBuilder.newAction(ActionDefinition.USER_ACCOUNT_VIEW_FORM)
@ -165,7 +167,8 @@ public class ActivitiesPane implements TemplateComposer {
// User Activity Logs // User Activity Logs
final boolean viewUserActivityLogs = this.currentUser.hasInstitutionalPrivilege( final boolean viewUserActivityLogs = this.currentUser.hasInstitutionalPrivilege(
PrivilegeType.READ, PrivilegeType.READ,
EntityType.USER_ACTIVITY_LOG); EntityType.USER_ACTIVITY_LOG)
&& currentUser.isFeatureEnabled(UserFeatures.Feature.ADMIN_AUDIT_LOGS);
if (viewUserActivityLogs) { if (viewUserActivityLogs) {
final TreeItem activityLogs = this.widgetFactory.treeItemLocalized( final TreeItem activityLogs = this.widgetFactory.treeItemLocalized(
sebAdmin, sebAdmin,
@ -201,13 +204,19 @@ public class ActivitiesPane implements TemplateComposer {
PrivilegeType.READ, PrivilegeType.READ,
EntityType.CONFIGURATION_NODE); 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( final TreeItem sebConfigs = this.widgetFactory.treeItemLocalized(
navigation, navigation,
ActivityDefinition.SEB_CONFIGURATION.displayName); ActivityDefinition.SEB_CONFIGURATION.displayName);
// SEB Client Config // SEB Client Config
if (clientConfigRead) { if (clientConfigRead && connConfigEnabled) {
final TreeItem clientConfig = this.widgetFactory.treeItemLocalized( final TreeItem clientConfig = this.widgetFactory.treeItemLocalized(
sebConfigs, sebConfigs,
ActivityDefinition.SEB_CLIENT_CONFIG.displayName); ActivityDefinition.SEB_CLIENT_CONFIG.displayName);
@ -219,7 +228,7 @@ public class ActivitiesPane implements TemplateComposer {
} }
// SEB Exam Config // SEB Exam Config
if (examConfigRead) { if (examConfigRead && examConfigEnabled) {
final TreeItem examConfig = this.widgetFactory.treeItemLocalized( final TreeItem examConfig = this.widgetFactory.treeItemLocalized(
sebConfigs, sebConfigs,
ActivityDefinition.SEB_EXAM_CONFIG.displayName); ActivityDefinition.SEB_EXAM_CONFIG.displayName);
@ -231,7 +240,7 @@ public class ActivitiesPane implements TemplateComposer {
} }
// SEB Exam Config Template // SEB Exam Config Template
if (examConfigRead) { if (examConfigRead && templateEnabled) {
final TreeItem examConfigTemplate = this.widgetFactory.treeItemLocalized( final TreeItem examConfigTemplate = this.widgetFactory.treeItemLocalized(
sebConfigs, sebConfigs,
ActivityDefinition.SEB_EXAM_CONFIG_TEMPLATE.displayName); ActivityDefinition.SEB_EXAM_CONFIG_TEMPLATE.displayName);
@ -243,7 +252,7 @@ public class ActivitiesPane implements TemplateComposer {
} }
// Certificate management // Certificate management
if (!isSupporterOnly) { if (!isSupporterOnly && certificatesEnabled) {
final TreeItem examConfigTemplate = this.widgetFactory.treeItemLocalized( final TreeItem examConfigTemplate = this.widgetFactory.treeItemLocalized(
sebConfigs, sebConfigs,
ActivityDefinition.SEB_CERTIFICATE_MANAGEMENT.displayName); ActivityDefinition.SEB_CERTIFICATE_MANAGEMENT.displayName);
@ -268,63 +277,73 @@ public class ActivitiesPane implements TemplateComposer {
this.currentUser.hasInstitutionalPrivilege(PrivilegeType.READ, EntityType.EXAM); this.currentUser.hasInstitutionalPrivilege(PrivilegeType.READ, EntityType.EXAM);
final boolean examWrite = this.currentUser.hasInstitutionalPrivilege(PrivilegeType.WRITE, EntityType.EXAM); final boolean examWrite = this.currentUser.hasInstitutionalPrivilege(PrivilegeType.WRITE, EntityType.EXAM);
// Exam Administration final boolean lmsSetupEnabled = currentUser.isFeatureEnabled(UserFeatures.Feature.LMS_SETUP);
final TreeItem examAdmin = this.widgetFactory.treeItemLocalized( final boolean quizLookupEnabled = currentUser.isFeatureEnabled(UserFeatures.Feature.QUIZ_LOOKUP);
navigation, final boolean examEnabled = currentUser.isFeatureEnabled(UserFeatures.Feature.EXAM_ADMIN);
ActivityDefinition.EXAM_ADMINISTRATION.displayName); final boolean examTemplateEnabled = currentUser.isFeatureEnabled(UserFeatures.Feature.EXAM_TEMPLATE);
final boolean anyExamAdminEnabled = lmsSetupEnabled || quizLookupEnabled || examEnabled || examTemplateEnabled;
if (examRead || lmsRead) { if (anyExamAdminEnabled) {
// LMS Setup // Exam Administration
if (lmsRead && !isSupporterOnly) { final TreeItem examAdmin = this.widgetFactory.treeItemLocalized(
final TreeItem lmsSetup = this.widgetFactory.treeItemLocalized( navigation,
examAdmin, ActivityDefinition.EXAM_ADMINISTRATION.displayName);
ActivityDefinition.LMS_SETUP.displayName);
injectActivitySelection(
lmsSetup,
actionBuilder
.newAction(ActionDefinition.LMS_SETUP_VIEW_LIST)
.create());
}
if (examRead) { if ((examRead || lmsRead) && lmsSetupEnabled) {
// LMS Setup
if (examWrite) { if (lmsRead && !isSupporterOnly) {
// Quiz Discovery final TreeItem lmsSetup = this.widgetFactory.treeItemLocalized(
final TreeItem quizDiscovery = this.widgetFactory.treeItemLocalized(
examAdmin, examAdmin,
ActivityDefinition.QUIZ_DISCOVERY.displayName); ActivityDefinition.LMS_SETUP.displayName);
injectActivitySelection( injectActivitySelection(
quizDiscovery, lmsSetup,
actionBuilder actionBuilder
.newAction(ActionDefinition.QUIZ_DISCOVERY_VIEW_LIST) .newAction(ActionDefinition.LMS_SETUP_VIEW_LIST)
.create()); .create());
} }
// Exam if (examRead) {
final TreeItem exam = this.widgetFactory.treeItemLocalized(
examAdmin,
ActivityDefinition.EXAM.displayName);
injectActivitySelection(
exam,
actionBuilder
.newAction(ActionDefinition.EXAM_VIEW_LIST)
.create());
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 ------------------------------------------------------------ // ---- EXAM ADMINISTRATION ------------------------------------------------------------
@ -335,12 +354,15 @@ public class ActivitiesPane implements TemplateComposer {
final boolean isSupporter = this.currentUser.get().hasAnyRole(UserRole.EXAM_SUPPORTER) || final boolean isSupporter = this.currentUser.get().hasAnyRole(UserRole.EXAM_SUPPORTER) ||
this.currentUser.get().hasAnyRole(UserRole.EXAM_ADMIN); this.currentUser.get().hasAnyRole(UserRole.EXAM_ADMIN);
final boolean viewSEBClientLogs = this.currentUser.hasInstitutionalPrivilege( final boolean viewSEBClientLogs =
PrivilegeType.READ, currentUser.isFeatureEnabled(UserFeatures.Feature.MONITORING_OVERALL_LOG_EXPORT)
EntityType.EXAM) || && ( this.currentUser.hasInstitutionalPrivilege(PrivilegeType.READ, EntityType.EXAM)
this.currentUser.get().hasRole(UserRole.EXAM_SUPPORTER); || 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 // Monitoring
final TreeItem monitoring = this.widgetFactory.treeItemLocalized( final TreeItem monitoring = this.widgetFactory.treeItemLocalized(
navigation, navigation,
@ -349,24 +371,27 @@ public class ActivitiesPane implements TemplateComposer {
// Monitoring exams // Monitoring exams
if (isSupporter) { if (isSupporter) {
final TreeItem monitoringExams = this.widgetFactory.treeItemLocalized( if (monitoringEnabled) {
monitoring, final TreeItem monitoringExams = this.widgetFactory.treeItemLocalized(
ActivityDefinition.MONITORING_EXAMS.displayName); monitoring,
injectActivitySelection( ActivityDefinition.MONITORING_EXAMS.displayName);
monitoringExams, injectActivitySelection(
actionBuilder monitoringExams,
.newAction(ActionDefinition.RUNNING_EXAM_VIEW_LIST) actionBuilder
.create()); .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 (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 // SEB Client Logs
@ -434,9 +459,11 @@ public class ActivitiesPane implements TemplateComposer {
final PageState state = this.pageService.getCurrentState(); final PageState state = this.pageService.getCurrentState();
if (state == null) { if (state == null) {
final TreeItem item = getDefaultSelectionFor(navigation, this.currentUser); final TreeItem item = getDefaultSelectionFor(navigation, this.currentUser);
final TreeItem actionItem = getActionItem(item); if (item != null) {
final PageAction activityAction = getActivitySelection(actionItem); final TreeItem actionItem = getActionItem(item);
this.pageService.executePageAction(activityAction); final PageAction activityAction = getActivitySelection(actionItem);
this.pageService.executePageAction(activityAction);
}
} else { } else {
final TreeItem item = findItemByActionDefinition( final TreeItem item = findItemByActionDefinition(
navigation.getItems(), navigation.getItems(),
@ -451,7 +478,7 @@ public class ActivitiesPane implements TemplateComposer {
private TreeItem getDefaultSelectionFor(final Tree navigation, final CurrentUser currentUser2) { private TreeItem getDefaultSelectionFor(final Tree navigation, final CurrentUser currentUser2) {
try { try {
if (this.currentUser.get().hasAnyRole(UserRole.SEB_SERVER_ADMIN, UserRole.INSTITUTIONAL_ADMIN)) { 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).getItem(1);
} }
return navigation.getItem(0); return navigation.getItem(0);
@ -467,7 +494,11 @@ public class ActivitiesPane implements TemplateComposer {
return navigation.getItem(0); return navigation.getItem(0);
} }
} catch (final Exception e) { } catch (final Exception e) {
return navigation.getItem(0); try {
return navigation.getItem(0);
} catch (final Exception ignored) {
return null;
}
} }
} }

View file

@ -89,7 +89,7 @@ public class InstitutionList implements TemplateComposer {
@Override @Override
public void compose(final PageContext pageContext) { public void compose(final PageContext pageContext) {
if (pageService.isSEBServerLightSetup()) { if (pageService.isLightSetup()) {
pageService.applyFullVersionNote(pageContext.getParent(), pageContext); pageService.applyFullVersionNote(pageContext.getParent(), pageContext);
return; return;
} }

View file

@ -99,7 +99,6 @@ public class UserAccountForm implements TemplateComposer {
@Override @Override
public void compose(final PageContext pageContext) { public void compose(final PageContext pageContext) {
final CurrentUser currentUser = this.resourceService.getCurrentUser(); final CurrentUser currentUser = this.resourceService.getCurrentUser();
final RestService restService = this.resourceService.getRestService(); final RestService restService = this.resourceService.getRestService();
final WidgetFactory widgetFactory = this.pageService.getWidgetFactory(); final WidgetFactory widgetFactory = this.pageService.getWidgetFactory();
@ -108,11 +107,12 @@ public class UserAccountForm implements TemplateComposer {
final EntityKey entityKey = pageContext.getEntityKey(); final EntityKey entityKey = pageContext.getEntityKey();
final EntityKey parentEntityKey = pageContext.getParentEntityKey(); final EntityKey parentEntityKey = pageContext.getParentEntityKey();
final boolean readonly = pageContext.isReadonly(); final boolean readonly = pageContext.isReadonly();
final boolean isLight = pageService.isSEBServerLightSetup(); final boolean isLight = pageService.isLightSetup();
final BooleanSupplier isNew = () -> entityKey == null; final BooleanSupplier isNew = () -> entityKey == null;
final BooleanSupplier isNotNew = () -> !isNew.getAsBoolean(); final BooleanSupplier isNotNew = () -> !isNew.getAsBoolean();
final BooleanSupplier isSEBAdmin = () -> user.hasRole(UserRole.SEB_SERVER_ADMIN); 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()) { if (isLight && !readonly && isNew.getAsBoolean()) {
pageService.applyFullVersionNote(pageContext.getParent(), pageContext); pageService.applyFullVersionNote(pageContext.getParent(), pageContext);
@ -142,8 +142,12 @@ public class UserAccountForm implements TemplateComposer {
final boolean ownAccount = user.uuid.equals(userAccount.getModelId()); final boolean ownAccount = user.uuid.equals(userAccount.getModelId());
final EntityGrantCheck userGrantCheck = currentUser.entityGrantCheck(userAccount); final EntityGrantCheck userGrantCheck = currentUser.entityGrantCheck(userAccount);
final boolean writeGrant = roleBasedEditGrant && userGrantCheck.w(); final boolean writeGrant = roleBasedEditGrant
final boolean modifyGrant = roleBasedEditGrant && userGrantCheck.m(); && 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( final boolean institutionalWriteGrant = currentUser.hasInstitutionalPrivilege(
PrivilegeType.WRITE, PrivilegeType.WRITE,
EntityType.USER); EntityType.USER);
@ -232,7 +236,6 @@ public class UserAccountForm implements TemplateComposer {
FORM_ROLES_TEXT_KEY, FORM_ROLES_TEXT_KEY,
StringUtils.join(userAccount.getRoles(), Constants.LIST_SEPARATOR_CHAR), StringUtils.join(userAccount.getRoles(), Constants.LIST_SEPARATOR_CHAR),
this.resourceService::userRoleResources) this.resourceService::userRoleResources)
.visibleIf(writeGrant)
.mandatory(!readonly)) .mandatory(!readonly))
.addFieldIf( .addFieldIf(
isNew, isNew,
@ -252,11 +255,13 @@ public class UserAccountForm implements TemplateComposer {
? restService.getRestCall(NewUserAccount.class) ? restService.getRestCall(NewUserAccount.class)
: restService.getRestCall(SaveUserAccount.class)); : restService.getRestCall(SaveUserAccount.class));
// propagate content actions to action-pane // propagate content actions to action-pane
this.pageService.pageActionBuilder(formContext.clearEntityKeys()) this.pageService.pageActionBuilder(formContext.clearEntityKeys())
.newAction(ActionDefinition.USER_ACCOUNT_NEW) .newAction(ActionDefinition.USER_ACCOUNT_NEW)
.publishIf(() -> institutionalWriteGrant && readonly && institutionActive) .publishIf(() -> newAccountPrivilege && institutionalWriteGrant && readonly && institutionActive)
.newAction(ActionDefinition.USER_ACCOUNT_MODIFY) .newAction(ActionDefinition.USER_ACCOUNT_MODIFY)
.withEntityKey(entityKey) .withEntityKey(entityKey)

View file

@ -196,7 +196,7 @@ public class UserActivityLogs implements TemplateComposer {
.withColumnIf( .withColumnIf(
() -> isSEBAdmin.getAsBoolean() () -> isSEBAdmin.getAsBoolean()
&& currentUser.isFeatureEnabled(UserFeatures.Feature.ADMIN_INSTITUTION) && currentUser.isFeatureEnabled(UserFeatures.Feature.ADMIN_INSTITUTION)
&& !pageService.isSEBServerLightSetup(), && !pageService.isLightSetup(),
() -> new ColumnDefinition<>( () -> new ColumnDefinition<>(
UserActivityLog.FILTER_ATTR_INSTITUTION, UserActivityLog.FILTER_ATTR_INSTITUTION,
INSTITUTION_TEXT_KEY, INSTITUTION_TEXT_KEY,

View file

@ -105,7 +105,7 @@ public class CertificateList implements TemplateComposer {
@Override @Override
public void compose(final PageContext pageContext) { public void compose(final PageContext pageContext) {
if (pageService.isSEBServerLightSetup()) { if (pageService.isLightSetup()) {
pageService.applyFullVersionNote(pageContext.getParent(), pageContext); pageService.applyFullVersionNote(pageContext.getParent(), pageContext);
return; return;
} }

View file

@ -96,7 +96,7 @@ public class ConfigTemplateList implements TemplateComposer {
@Override @Override
public void compose(final PageContext pageContext) { public void compose(final PageContext pageContext) {
if (pageService.isSEBServerLightSetup()) { if (pageService.isLightSetup()) {
pageService.applyFullVersionNote(pageContext.getParent(), pageContext); pageService.applyFullVersionNote(pageContext.getParent(), pageContext);
return; return;
} }

View file

@ -129,7 +129,7 @@ public class SEBClientConfigList implements TemplateComposer {
.withColumnIf( .withColumnIf(
() -> isSEBAdmin () -> isSEBAdmin
&& currentUser.isFeatureEnabled(UserFeatures.Feature.ADMIN_INSTITUTION) && currentUser.isFeatureEnabled(UserFeatures.Feature.ADMIN_INSTITUTION)
&& !pageService.isSEBServerLightSetup(), && !pageService.isLightSetup(),
() -> new ColumnDefinition<>( () -> new ColumnDefinition<>(
Domain.LMS_SETUP.ATTR_INSTITUTION_ID, Domain.LMS_SETUP.ATTR_INSTITUTION_ID,
INSTITUTION_TEXT_KEY, INSTITUTION_TEXT_KEY,

View file

@ -13,6 +13,7 @@ import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.function.Function; import java.util.function.Function;
import ch.ethz.seb.sebserver.gbl.model.user.UserFeatures;
import org.eclipse.swt.SWT; import org.eclipse.swt.SWT;
import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Text; import org.eclipse.swt.widgets.Text;
@ -159,7 +160,7 @@ public class SEBExamConfigForm implements TemplateComposer {
final EntityKey entityKey = pageContext.getEntityKey(); final EntityKey entityKey = pageContext.getEntityKey();
final EntityKey parentEntityKey = pageContext.getParentEntityKey(); final EntityKey parentEntityKey = pageContext.getParentEntityKey();
final boolean isNew = entityKey == null; final boolean isNew = entityKey == null;
final boolean isLight = pageService.isSEBServerLightSetup(); final boolean isLight = pageService.isLightSetup();
// get data or create new. Handle error if happen // get data or create new. Handle error if happen
final ConfigurationNode examConfig = (isNew) final ConfigurationNode examConfig = (isNew)
@ -350,7 +351,7 @@ public class SEBExamConfigForm implements TemplateComposer {
.withExec(this.pageService.backToCurrentFunction()) .withExec(this.pageService.backToCurrentFunction())
.publishIf(() -> !isReadonly); .publishIf(() -> !isReadonly);
if (isAttachedToExam && isReadonly) { if (isAttachedToExam && isReadonly && currentUser.isFeatureEnabled(UserFeatures.Feature.EXAM_ADMIN)) {
widgetFactory.addFormSubContextHeader( widgetFactory.addFormSubContextHeader(
content, content,

View file

@ -133,7 +133,7 @@ public class SEBExamConfigList implements TemplateComposer {
TITLE_CONFIGURATION_TEXT_KEY); TITLE_CONFIGURATION_TEXT_KEY);
final boolean isSEBAdmin = this.currentUser.get().hasRole(UserRole.SEB_SERVER_ADMIN); final boolean isSEBAdmin = this.currentUser.get().hasRole(UserRole.SEB_SERVER_ADMIN);
final boolean isLight = pageService.isSEBServerLightSetup(); final boolean isLight = pageService.isLightSetup();
final PageActionBuilder pageActionBuilder = final PageActionBuilder pageActionBuilder =
this.pageService.pageActionBuilder(pageContext.clearEntityKeys()); this.pageService.pageActionBuilder(pageContext.clearEntityKeys());
@ -150,7 +150,7 @@ public class SEBExamConfigList implements TemplateComposer {
.withColumnIf( .withColumnIf(
() -> isSEBAdmin () -> isSEBAdmin
&& currentUser.isFeatureEnabled(UserFeatures.Feature.ADMIN_INSTITUTION) && currentUser.isFeatureEnabled(UserFeatures.Feature.ADMIN_INSTITUTION)
&& !pageService.isSEBServerLightSetup(), && !pageService.isLightSetup(),
() -> new ColumnDefinition<>( () -> new ColumnDefinition<>(
Domain.LMS_SETUP.ATTR_INSTITUTION_ID, Domain.LMS_SETUP.ATTR_INSTITUTION_ID,
INSTITUTION_TEXT_KEY, INSTITUTION_TEXT_KEY,

View file

@ -87,7 +87,7 @@ public class ClientGroupForm implements TemplateComposer {
@Override @Override
public void compose(final PageContext pageContext) { public void compose(final PageContext pageContext) {
if (pageService.isSEBServerLightSetup()) { if (pageService.isLightSetup()) {
pageService.applyFullVersionNote(pageContext.getParent(), pageContext); pageService.applyFullVersionNote(pageContext.getParent(), pageContext);
this.pageService.pageActionBuilder(pageContext.clearEntityKeys()) this.pageService.pageActionBuilder(pageContext.clearEntityKeys())
.newAction(ActionDefinition.EXAM_CLIENT_GROUP_CANCEL_MODIFY) .newAction(ActionDefinition.EXAM_CLIENT_GROUP_CANCEL_MODIFY)

View file

@ -8,6 +8,8 @@
package ch.ethz.seb.sebserver.gui.content.exam; 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.apache.commons.lang3.BooleanUtils;
import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Composite;
import org.springframework.context.annotation.Lazy; import org.springframework.context.annotation.Lazy;
@ -71,10 +73,12 @@ public class ExamClientGroupList implements TemplateComposer {
@Override @Override
public void compose(final PageContext pageContext) { public void compose(final PageContext pageContext) {
final CurrentUser currentUser = pageService.getCurrentUser();
final Composite content = pageContext.getParent(); final Composite content = pageContext.getParent();
final EntityKey entityKey = pageContext.getEntityKey(); final EntityKey entityKey = pageContext.getEntityKey();
final boolean editable = BooleanUtils.toBoolean(pageContext.getAttribute(ExamForm.ATTR_EDITABLE)); 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 // List of ClientGroups
this.widgetFactory.addFormSubContextHeader( this.widgetFactory.addFormSubContextHeader(
@ -124,7 +128,7 @@ public class ExamClientGroupList implements TemplateComposer {
.widthProportion(3)) .widthProportion(3))
.withDefaultActionIf( .withDefaultActionIf(
() -> editable, () -> editable && clientGroupsEnabled,
() -> actionBuilder () -> actionBuilder
.newAction(ActionDefinition.EXAM_CLIENT_GROUP_MODIFY_FROM_LIST) .newAction(ActionDefinition.EXAM_CLIENT_GROUP_MODIFY_FROM_LIST)
.withParentEntityKey(entityKey) .withParentEntityKey(entityKey)
@ -145,7 +149,7 @@ public class ExamClientGroupList implements TemplateComposer {
clientGroupTable::getMultiSelection, clientGroupTable::getMultiSelection,
PageAction::applySingleSelectionAsEntityKey, PageAction::applySingleSelectionAsEntityKey,
CLIENT_GROUP_EMPTY_SELECTION_TEXT_KEY) 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) .newAction(ActionDefinition.EXAM_CLIENT_GROUP_DELETE_FROM_LIST)
.withEntityKey(entityKey) .withEntityKey(entityKey)
@ -153,11 +157,11 @@ public class ExamClientGroupList implements TemplateComposer {
clientGroupTable::getMultiSelection, clientGroupTable::getMultiSelection,
this::deleteSelectedClientGroup, this::deleteSelectedClientGroup,
CLIENT_GROUP_EMPTY_SELECTION_TEXT_KEY) 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) .newAction(ActionDefinition.EXAM_CLIENT_GROUP_NEW)
.withParentEntityKey(entityKey) .withParentEntityKey(entityKey)
.publishIf(() -> editable); .publishIf(() -> clientGroupsEnabled && editable);
} }
private PageAction deleteSelectedClientGroup(final PageAction action) { private PageAction deleteSelectedClientGroup(final PageAction action) {

View file

@ -8,7 +8,7 @@
package ch.ethz.seb.sebserver.gui.content.exam; 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.*;
import java.util.function.Function; import java.util.function.Function;
@ -16,6 +16,7 @@ import java.util.stream.Collectors;
import java.util.stream.Stream; import java.util.stream.Stream;
import ch.ethz.seb.sebserver.gbl.api.POSTMapper; 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 ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.*;
import org.apache.commons.lang3.BooleanUtils; import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
@ -197,7 +198,7 @@ public class ExamForm implements TemplateComposer {
final EntityKey entityKey = (readonly || !newExamNoLMS) ? pageContext.getEntityKey() : null; final EntityKey entityKey = (readonly || !newExamNoLMS) ? pageContext.getEntityKey() : null;
final PageContext formContext = pageContext.withEntityKey(exam.getEntityKey()); final PageContext formContext = pageContext.withEntityKey(exam.getEntityKey());
final EntityGrantCheck entityGrantCheck = currentUser.entityGrantCheck(exam); final EntityGrantCheck entityGrantCheck = currentUser.entityGrantCheck(exam);
final boolean isLight = pageService.isSEBServerLightSetup(); final boolean isLight = pageService.isLightSetup();
final boolean modifyGrant = entityGrantCheck.m(); final boolean modifyGrant = entityGrantCheck.m();
final boolean writeGrant = entityGrantCheck.w(); final boolean writeGrant = entityGrantCheck.w();
final boolean editable = modifyGrant && (exam.getStatus() == ExamStatus.UP_COMING || exam.getStatus() == ExamStatus.RUNNING); final boolean editable = modifyGrant && (exam.getStatus() == ExamStatus.UP_COMING || exam.getStatus() == ExamStatus.RUNNING);
@ -214,6 +215,11 @@ public class ExamForm implements TemplateComposer {
sebRestrictionAvailable && sebRestrictionAvailable &&
isRestricted != exam.sebRestriction && isRestricted != exam.sebRestriction &&
exam.status == ExamStatus.RUNNING; 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 // check exam consistency and inform the user if needed
Collection<APIMessage> warnings = null; Collection<APIMessage> warnings = null;
@ -255,7 +261,6 @@ public class ExamForm implements TemplateComposer {
.map(ProctoringServiceSettings::getEnableProctoring) .map(ProctoringServiceSettings::getEnableProctoring)
.getOr(false); .getOr(false);
final boolean spsFeatureEnabled = currentUser.isFeatureEnabled(EXAM_SCREEN_PROCTORING);
final boolean screenProctoringEnabled = readonly && spsFeatureEnabled && this.restService final boolean screenProctoringEnabled = readonly && spsFeatureEnabled && this.restService
.getBuilder(GetScreenProctoringSettings.class) .getBuilder(GetScreenProctoringSettings.class)
.withURIVariable(API.PARAM_MODEL_ID, entityKey.modelId) .withURIVariable(API.PARAM_MODEL_ID, entityKey.modelId)
@ -306,15 +311,15 @@ public class ExamForm implements TemplateComposer {
.withExec(this.examCreateClientConfigPopup.exportFunction( .withExec(this.examCreateClientConfigPopup.exportFunction(
exam.institutionId, exam.institutionId,
exam.getName())) exam.getName()))
.publishIf(() -> editable && readonly) .publishIf(() -> connectionConfigEnabled && editable && readonly)
.newAction(ActionDefinition.EXAM_SECURITY_KEY_ENABLED) .newAction(ActionDefinition.EXAM_SECURITY_KEY_ENABLED)
.withEntityKey(entityKey) .withEntityKey(entityKey)
.publishIf(() -> signatureKeyCheckEnabled && readonly) .publishIf(() -> askEnabled && signatureKeyCheckEnabled && readonly)
.newAction(ActionDefinition.EXAM_SECURITY_KEY_DISABLED) .newAction(ActionDefinition.EXAM_SECURITY_KEY_DISABLED)
.withEntityKey(entityKey) .withEntityKey(entityKey)
.publishIf(() -> !signatureKeyCheckEnabled && readonly) .publishIf(() -> askEnabled && !signatureKeyCheckEnabled && readonly)
.newAction(ActionDefinition.EXAM_MODIFY_SEB_RESTRICTION_DETAILS) .newAction(ActionDefinition.EXAM_MODIFY_SEB_RESTRICTION_DETAILS)
.withEntityKey(entityKey) .withEntityKey(entityKey)
@ -322,7 +327,7 @@ public class ExamForm implements TemplateComposer {
.withAttribute(ExamSEBRestrictionSettings.PAGE_CONTEXT_ATTR_LMS_ID, String.valueOf(exam.lmsSetupId)) .withAttribute(ExamSEBRestrictionSettings.PAGE_CONTEXT_ATTR_LMS_ID, String.valueOf(exam.lmsSetupId))
.withAttribute(PageContext.AttributeKeys.FORCE_READ_ONLY, String.valueOf(!modifyGrant || !editable)) .withAttribute(PageContext.AttributeKeys.FORCE_READ_ONLY, String.valueOf(!modifyGrant || !editable))
.noEventPropagation() .noEventPropagation()
.publishIf(() -> sebRestrictionAvailable && readonly) .publishIf(() -> restrictionEnabled && sebRestrictionAvailable && readonly)
.newAction(ActionDefinition.EXAM_ENABLE_SEB_RESTRICTION) .newAction(ActionDefinition.EXAM_ENABLE_SEB_RESTRICTION)
.withEntityKey(entityKey) .withEntityKey(entityKey)
@ -341,20 +346,20 @@ public class ExamForm implements TemplateComposer {
.withEntityKey(entityKey) .withEntityKey(entityKey)
.withExec(this.proctoringSettingsPopup.settingsFunction(this.pageService, modifyGrant && editable)) .withExec(this.proctoringSettingsPopup.settingsFunction(this.pageService, modifyGrant && editable))
.noEventPropagation() .noEventPropagation()
.publishIf(() -> !isLight && proctoringEnabled && readonly) .publishIf(() -> lpEnabled && !isLight && proctoringEnabled && readonly)
.newAction(ActionDefinition.EXAM_PROCTORING_OFF) .newAction(ActionDefinition.EXAM_PROCTORING_OFF)
.withEntityKey(entityKey) .withEntityKey(entityKey)
.withExec(this.proctoringSettingsPopup.settingsFunction(this.pageService, modifyGrant && editable)) .withExec(this.proctoringSettingsPopup.settingsFunction(this.pageService, modifyGrant && editable))
.noEventPropagation() .noEventPropagation()
.publishIf(() -> !isLight && !proctoringEnabled && readonly) .publishIf(() -> lpEnabled && !isLight && !proctoringEnabled && readonly)
.newAction(ActionDefinition.SCREEN_PROCTORING_ON) .newAction(ActionDefinition.SCREEN_PROCTORING_ON)
.withEntityKey(entityKey) .withEntityKey(entityKey)
.withExec( .withExec(
this.screenProctoringSettingsPopup.settingsFunction(this.pageService, modifyGrant && editable)) this.screenProctoringSettingsPopup.settingsFunction(this.pageService, modifyGrant && editable))
.noEventPropagation() .noEventPropagation()
.publishIf(() -> spsFeatureEnabled && screenProctoringEnabled && readonly) .publishIf(() -> spsFeatureEnabled && screenProctoringEnabled && readonly)
.newAction(ActionDefinition.SCREEN_PROCTORING_OFF) .newAction(ActionDefinition.SCREEN_PROCTORING_OFF)
.withEntityKey(entityKey) .withEntityKey(entityKey)
@ -501,8 +506,9 @@ public class ExamForm implements TemplateComposer {
final Composite content, final Composite content,
final Exam exam) { final Exam exam) {
final boolean templateEnabled = pageService.getCurrentUser().isFeatureEnabled(EXAM_TEMPLATE);
final I18nSupport i18nSupport = formContext.getI18nSupport(); final I18nSupport i18nSupport = formContext.getI18nSupport();
final boolean isLight = pageService.isSEBServerLightSetup(); final boolean isLight = pageService.isLightSetup();
final boolean newExam = exam.id == null; final boolean newExam = exam.id == null;
final boolean hasLMS = exam.lmsSetupId != null; final boolean hasLMS = exam.lmsSetupId != null;
final boolean importFromLMS = newExam && hasLMS; final boolean importFromLMS = newExam && hasLMS;
@ -545,7 +551,7 @@ public class ExamForm implements TemplateComposer {
this.resourceService::lmsSetupResource) this.resourceService::lmsSetupResource)
.readonly(true)) .readonly(true))
.addFieldIf(() -> !isLight && exam.id == null, .addFieldIf(() -> templateEnabled && !isLight && exam.id == null,
() -> FormBuilder.singleSelection( () -> FormBuilder.singleSelection(
Domain.EXAM.ATTR_EXAM_TEMPLATE_ID, Domain.EXAM.ATTR_EXAM_TEMPLATE_ID,
FORM_EXAM_TEMPLATE_TEXT_KEY, FORM_EXAM_TEMPLATE_TEXT_KEY,
@ -630,7 +636,7 @@ public class ExamForm implements TemplateComposer {
DateTime.now(timeZone).plusHours(1), DateTime.now(timeZone).plusHours(1),
Exam.ExamType.UNDEFINED, Exam.ExamType.UNDEFINED,
null, null,
pageService.isSEBServerLightSetup() pageService.isLightSetup()
? Stream.of(this.pageService.getCurrentUser().get().uuid).collect(Collectors.toList()) ? Stream.of(this.pageService.getCurrentUser().get().uuid).collect(Collectors.toList())
: null, : null,
ExamStatus.UP_COMING, ExamStatus.UP_COMING,
@ -811,7 +817,7 @@ public class ExamForm implements TemplateComposer {
final EntityKey entityKey = pageContext.getEntityKey(); final EntityKey entityKey = pageContext.getEntityKey();
final EntityKey parentEntityKey = pageContext.getParentEntityKey(); final EntityKey parentEntityKey = pageContext.getParentEntityKey();
final POSTMapper mapper = new POSTMapper(null, null); final POSTMapper mapper = new POSTMapper(null, null);
if (pageService.isSEBServerLightSetup()) { if (pageService.isLightSetup()) {
mapper.putIfAbsent(Domain.EXAM.ATTR_SUPPORTER, this.pageService.getCurrentUser().get().uuid); mapper.putIfAbsent(Domain.EXAM.ATTR_SUPPORTER, this.pageService.getCurrentUser().get().uuid);
} }
return this.restService.getBuilder(GetQuizData.class) return this.restService.getBuilder(GetQuizData.class)

View file

@ -14,6 +14,8 @@ import java.util.HashSet;
import java.util.Set; import java.util.Set;
import java.util.function.Supplier; 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.apache.commons.lang3.BooleanUtils;
import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Composite;
import org.springframework.context.annotation.Lazy; import org.springframework.context.annotation.Lazy;
@ -88,6 +90,7 @@ public class ExamFormConfigs implements TemplateComposer {
@Override @Override
public void compose(final PageContext pageContext) { public void compose(final PageContext pageContext) {
final CurrentUser currentUser = pageService.getCurrentUser();
final Composite content = pageContext.getParent(); final Composite content = pageContext.getParent();
final EntityKey entityKey = pageContext.getEntityKey(); final EntityKey entityKey = pageContext.getEntityKey();
final boolean editable = BooleanUtils.toBoolean( final boolean editable = BooleanUtils.toBoolean(
@ -97,6 +100,7 @@ public class ExamFormConfigs implements TemplateComposer {
final ExamStatus examStatus = ExamStatus.valueOf( final ExamStatus examStatus = ExamStatus.valueOf(
pageContext.getAttribute(ExamForm.ATTR_EXAM_STATUS)); pageContext.getAttribute(ExamForm.ATTR_EXAM_STATUS));
final boolean isExamRunning = examStatus == ExamStatus.RUNNING; final boolean isExamRunning = examStatus == ExamStatus.RUNNING;
final boolean examConfigEnabled = currentUser.isFeatureEnabled(UserFeatures.Feature.CONFIG_EXAM_CONFIGURATION);
// List of SEB Configuration // List of SEB Configuration
this.widgetFactory.addFormSubContextHeader( this.widgetFactory.addFormSubContextHeader(
@ -128,7 +132,7 @@ public class ExamFormConfigs implements TemplateComposer {
this.resourceService::localizedExamConfigStatusName) this.resourceService::localizedExamConfigStatusName)
.widthProportion(1)) .widthProportion(1))
.withDefaultActionIf( .withDefaultActionIf(
() -> readGrant, () -> readGrant && examConfigEnabled,
this::viewExamConfigPageAction) this::viewExamConfigPageAction)
.withSelectionListener(this.pageService.getSelectionPublisher( .withSelectionListener(this.pageService.getSelectionPublisher(
@ -156,12 +160,12 @@ public class ExamFormConfigs implements TemplateComposer {
.withParentEntityKey(entityKey) .withParentEntityKey(entityKey)
.withExec(this.examToConfigBindingForm.bindFunction()) .withExec(this.examToConfigBindingForm.bindFunction())
.noEventPropagation() .noEventPropagation()
.publishIf(() -> editable && !configurationTable.hasAnyContent()) .publishIf(() -> examConfigEnabled && editable && !configurationTable.hasAnyContent())
.newAction(ActionDefinition.EXAM_CONFIGURATION_EXAM_CONFIG_VIEW_PROP) .newAction(ActionDefinition.EXAM_CONFIGURATION_EXAM_CONFIG_VIEW_PROP)
.withParentEntityKey(entityKey) .withParentEntityKey(entityKey)
.withEntityKey(configMapKey) .withEntityKey(configMapKey)
.publishIf(() -> readGrant && configurationTable.hasAnyContent(), false) .publishIf(() -> examConfigEnabled && readGrant && configurationTable.hasAnyContent(), false)
.newAction(ActionDefinition.EXAM_CONFIGURATION_DELETE_FROM_LIST) .newAction(ActionDefinition.EXAM_CONFIGURATION_DELETE_FROM_LIST)
.withEntityKey(entityKey) .withEntityKey(entityKey)
@ -175,7 +179,7 @@ public class ExamFormConfigs implements TemplateComposer {
} }
return null; return null;
}) })
.publishIf(() -> editable && configurationTable.hasAnyContent() && editable, false) .publishIf(() -> examConfigEnabled && editable && configurationTable.hasAnyContent() && editable, false)
.newAction(ActionDefinition.EXAM_CONFIGURATION_GET_CONFIG_KEY) .newAction(ActionDefinition.EXAM_CONFIGURATION_GET_CONFIG_KEY)
.withSelect( .withSelect(

View file

@ -8,6 +8,8 @@
package ch.ethz.seb.sebserver.gui.content.exam; 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.apache.commons.lang3.BooleanUtils;
import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Composite;
import org.springframework.context.annotation.Lazy; import org.springframework.context.annotation.Lazy;
@ -70,10 +72,12 @@ public class ExamIndicatorsList implements TemplateComposer {
@Override @Override
public void compose(final PageContext pageContext) { public void compose(final PageContext pageContext) {
final CurrentUser currentUser = pageService.getCurrentUser();
final Composite content = pageContext.getParent(); final Composite content = pageContext.getParent();
final EntityKey entityKey = pageContext.getEntityKey(); final EntityKey entityKey = pageContext.getEntityKey();
final boolean editable = BooleanUtils.toBoolean(pageContext.getAttribute(ExamForm.ATTR_EDITABLE)); 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 // List of Indicators
this.widgetFactory.addFormSubContextHeader( this.widgetFactory.addFormSubContextHeader(
@ -111,7 +115,7 @@ public class ExamIndicatorsList implements TemplateComposer {
.asMarkup() .asMarkup()
.widthProportion(4)) .widthProportion(4))
.withDefaultActionIf( .withDefaultActionIf(
() -> editable, () -> editable && indicatorEnabled,
() -> actionBuilder () -> actionBuilder
.newAction(ActionDefinition.EXAM_INDICATOR_MODIFY_FROM_LIST) .newAction(ActionDefinition.EXAM_INDICATOR_MODIFY_FROM_LIST)
.withParentEntityKey(entityKey) .withParentEntityKey(entityKey)
@ -132,7 +136,7 @@ public class ExamIndicatorsList implements TemplateComposer {
indicatorTable::getMultiSelection, indicatorTable::getMultiSelection,
PageAction::applySingleSelectionAsEntityKey, PageAction::applySingleSelectionAsEntityKey,
INDICATOR_EMPTY_SELECTION_TEXT_KEY) INDICATOR_EMPTY_SELECTION_TEXT_KEY)
.publishIf(() -> editable && indicatorTable.hasAnyContent(), false) .publishIf(() -> indicatorEnabled && editable && indicatorTable.hasAnyContent(), false)
.newAction(ActionDefinition.EXAM_INDICATOR_DELETE_FROM_LIST) .newAction(ActionDefinition.EXAM_INDICATOR_DELETE_FROM_LIST)
.withEntityKey(entityKey) .withEntityKey(entityKey)
@ -140,11 +144,11 @@ public class ExamIndicatorsList implements TemplateComposer {
indicatorTable::getMultiSelection, indicatorTable::getMultiSelection,
this::deleteSelectedIndicator, this::deleteSelectedIndicator,
INDICATOR_EMPTY_SELECTION_TEXT_KEY) INDICATOR_EMPTY_SELECTION_TEXT_KEY)
.publishIf(() -> !isLight && editable && indicatorTable.hasAnyContent(), false) .publishIf(() -> indicatorEnabled && !isLight && editable && indicatorTable.hasAnyContent(), false)
.newAction(ActionDefinition.EXAM_INDICATOR_NEW) .newAction(ActionDefinition.EXAM_INDICATOR_NEW)
.withParentEntityKey(entityKey) .withParentEntityKey(entityKey)
.publishIf(() -> editable); .publishIf(() -> indicatorEnabled && editable);
} }

View file

@ -177,7 +177,7 @@ public class ExamList implements TemplateComposer {
.withColumnIf( .withColumnIf(
() -> isSEBAdmin.getAsBoolean() () -> isSEBAdmin.getAsBoolean()
&& currentUser.isFeatureEnabled(UserFeatures.Feature.ADMIN_INSTITUTION) && currentUser.isFeatureEnabled(UserFeatures.Feature.ADMIN_INSTITUTION)
&& !pageService.isSEBServerLightSetup(), && !pageService.isLightSetup(),
() -> new ColumnDefinition<Exam>( () -> new ColumnDefinition<Exam>(
Domain.EXAM.ATTR_INSTITUTION_ID, Domain.EXAM.ATTR_INSTITUTION_ID,
COLUMN_TITLE_INSTITUTION_KEY, COLUMN_TITLE_INSTITUTION_KEY,

View file

@ -106,7 +106,7 @@ public class ExamTemplateList implements TemplateComposer {
@Override @Override
public void compose(final PageContext pageContext) { public void compose(final PageContext pageContext) {
if (pageService.isSEBServerLightSetup()) { if (pageService.isLightSetup()) {
pageService.applyFullVersionNote(pageContext.getParent(), pageContext); pageService.applyFullVersionNote(pageContext.getParent(), pageContext);
return; return;
} }

View file

@ -84,7 +84,7 @@ public class IndicatorForm implements TemplateComposer {
@Override @Override
public void compose(final PageContext pageContext) { public void compose(final PageContext pageContext) {
if (pageService.isSEBServerLightSetup()) { if (pageService.isLightSetup()) {
pageService.applyFullVersionNote(pageContext.getParent(), pageContext); pageService.applyFullVersionNote(pageContext.getParent(), pageContext);
this.pageService.pageActionBuilder(pageContext.clearEntityKeys()) this.pageService.pageActionBuilder(pageContext.clearEntityKeys())
.newAction(ActionDefinition.EXAM_INDICATOR_CANCEL_MODIFY) .newAction(ActionDefinition.EXAM_INDICATOR_CANCEL_MODIFY)

View file

@ -109,7 +109,7 @@ public class LmsSetupList implements TemplateComposer {
final WidgetFactory widgetFactory = this.pageService.getWidgetFactory(); final WidgetFactory widgetFactory = this.pageService.getWidgetFactory();
final CurrentUser currentUser = this.resourceService.getCurrentUser(); final CurrentUser currentUser = this.resourceService.getCurrentUser();
final RestService restService = this.resourceService.getRestService(); final RestService restService = this.resourceService.getRestService();
final boolean isLight = pageService.isSEBServerLightSetup(); final boolean isLight = pageService.isLightSetup();
// content page layout with title // content page layout with title
final Composite content = widgetFactory.defaultPageLayout( final Composite content = widgetFactory.defaultPageLayout(

View file

@ -195,7 +195,7 @@ public class QuizLookupList implements TemplateComposer {
.withColumnIf( .withColumnIf(
() -> isSEBAdmin.getAsBoolean() () -> isSEBAdmin.getAsBoolean()
&& currentUser.isFeatureEnabled(UserFeatures.Feature.ADMIN_INSTITUTION) && currentUser.isFeatureEnabled(UserFeatures.Feature.ADMIN_INSTITUTION)
&& !pageService.isSEBServerLightSetup(), && !pageService.isLightSetup(),
() -> new ColumnDefinition<QuizData>( () -> new ColumnDefinition<QuizData>(
QuizData.QUIZ_ATTR_INSTITUTION_ID, QuizData.QUIZ_ATTR_INSTITUTION_ID,
INSTITUTION_TEXT_KEY, INSTITUTION_TEXT_KEY,

View file

@ -8,6 +8,8 @@
package ch.ethz.seb.sebserver.gui.content.monitoring; 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.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.HashSet; import java.util.HashSet;
@ -181,6 +183,12 @@ public class MonitoringClientConnection implements TemplateComposer {
final EntityKey entityKey = pageContext.getEntityKey(); final EntityKey entityKey = pageContext.getEntityKey();
final String connectionToken = pageContext.getAttribute(Domain.CLIENT_CONNECTION.ATTR_CONNECTION_TOKEN); 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) { if (connectionToken == null) {
pageContext.notifyUnexpectedError(new IllegalAccessException("connectionToken has null reference")); pageContext.notifyUnexpectedError(new IllegalAccessException("connectionToken has null reference"));
return; return;
@ -291,7 +299,7 @@ public class MonitoringClientConnection implements TemplateComposer {
.withParentEntityKey(parentEntityKey) .withParentEntityKey(parentEntityKey)
.withConfirm(() -> NOTIFICATION_LIST_CONFIRM_TEXT_KEY) .withConfirm(() -> NOTIFICATION_LIST_CONFIRM_TEXT_KEY)
.withSelect( .withSelect(
() -> notificationTable.getMultiSelection(), notificationTable::getMultiSelection,
action -> this.confirmNotification(action, connectionData, notificationTable), action -> this.confirmNotification(action, connectionData, notificationTable),
NOTIFICATION_LIST_NO_SELECTION_KEY) NOTIFICATION_LIST_NO_SELECTION_KEY)
@ -398,7 +406,7 @@ public class MonitoringClientConnection implements TemplateComposer {
return action; return action;
}) })
.noEventPropagation() .noEventPropagation()
.publishIf(() -> isExamSupporter.getAsBoolean() && .publishIf(() -> quitEnabled && isExamSupporter.getAsBoolean() &&
connectionData.clientConnection.status.clientActiveStatus) connectionData.clientConnection.status.clientActiveStatus)
.newAction(ActionDefinition.MONITOR_EXAM_CLIENT_CONNECTION_LOCK) .newAction(ActionDefinition.MONITOR_EXAM_CLIENT_CONNECTION_LOCK)
@ -406,7 +414,7 @@ public class MonitoringClientConnection implements TemplateComposer {
.withExec(action -> this.sebSendLockPopup.show(action, .withExec(action -> this.sebSendLockPopup.show(action,
some -> new HashSet<>(Arrays.asList(connectionToken)))) some -> new HashSet<>(Arrays.asList(connectionToken))))
.noEventPropagation() .noEventPropagation()
.publishIf(() -> isExamSupporter.getAsBoolean() && .publishIf(() -> lockscreenEnabled && isExamSupporter.getAsBoolean() &&
connectionData.clientConnection.status.clientActiveStatus) connectionData.clientConnection.status.clientActiveStatus)
.newAction(ActionDefinition.MONITOR_EXAM_CLIENT_DISABLE_CONNECTION) .newAction(ActionDefinition.MONITOR_EXAM_CLIENT_DISABLE_CONNECTION)
@ -419,11 +427,11 @@ public class MonitoringClientConnection implements TemplateComposer {
: null) : null)
.withExec(action -> this.disableClientConnection(action, clientConnectionDetails)) .withExec(action -> this.disableClientConnection(action, clientConnectionDetails))
.noEventPropagation() .noEventPropagation()
.publishIf(() -> isExamSupporter.getAsBoolean() && .publishIf(() -> cancelEnabled && isExamSupporter.getAsBoolean() &&
(connectionData.clientConnection.status.clientActiveStatus || (connectionData.clientConnection.status.clientActiveStatus ||
connectionData.clientConnection.status == ConnectionStatus.CLOSED)); connectionData.clientConnection.status == ConnectionStatus.CLOSED));
if (clientConnectionDetails.checkSecurityGrant) { if (askEnabled && clientConnectionDetails.checkSecurityGrant) {
final SecurityKey securityKey = this.pageService final SecurityKey securityKey = this.pageService
.getRestService() .getRestService()
.getBuilder(GetClientConnectionSecurityKey.class) .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 final ProctoringServiceSettings proctoringSettings = restService
.getBuilder(GetExamProctoringSettings.class) .getBuilder(GetExamProctoringSettings.class)
.withURIVariable(API.PARAM_MODEL_ID, parentEntityKey.modelId) .withURIVariable(API.PARAM_MODEL_ID, parentEntityKey.modelId)

View file

@ -8,6 +8,8 @@
package ch.ethz.seb.sebserver.gui.content.monitoring; 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.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
@ -148,6 +150,11 @@ public class MonitoringRunningExam implements TemplateComposer {
.call() .call()
.getOrThrow(); .getOrThrow();
final UserInfo user = currentUser.get(); 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) && final boolean supporting = user.hasRole(UserRole.EXAM_SUPPORTER) &&
exam.supporter.contains(user.uuid); exam.supporter.contains(user.uuid);
final BooleanSupplier isExamSupporter = () -> supporting || user.hasRole(UserRole.EXAM_ADMIN); final BooleanSupplier isExamSupporter = () -> supporting || user.hasRole(UserRole.EXAM_ADMIN);
@ -195,7 +202,7 @@ public class MonitoringRunningExam implements TemplateComposer {
guiUpdates.add(clientTable); guiUpdates.add(clientTable);
clientTable clientTable
.withDefaultAction( .withDefaultActionIf(() -> detailEnabled,
actionBuilder actionBuilder
.newAction(ActionDefinition.MONITOR_EXAM_CLIENT_CONNECTION) .newAction(ActionDefinition.MONITOR_EXAM_CLIENT_CONNECTION)
.withParentEntityKey(entityKey) .withParentEntityKey(entityKey)
@ -221,7 +228,7 @@ public class MonitoringRunningExam implements TemplateComposer {
.withConfirm(() -> CONFIRM_QUIT_ALL) .withConfirm(() -> CONFIRM_QUIT_ALL)
.withExec(action -> this.quitSEBClients(action, clientTable, true)) .withExec(action -> this.quitSEBClients(action, clientTable, true))
.noEventPropagation() .noEventPropagation()
.publishIf(isExamSupporter) .publishIf(() -> isExamSupporter.getAsBoolean() && quitEnabled)
.newAction(ActionDefinition.MONITOR_EXAM_QUIT_SELECTED) .newAction(ActionDefinition.MONITOR_EXAM_QUIT_SELECTED)
.withEntityKey(entityKey) .withEntityKey(entityKey)
@ -231,7 +238,7 @@ public class MonitoringRunningExam implements TemplateComposer {
action -> this.quitSEBClients(action, clientTable, false), action -> this.quitSEBClients(action, clientTable, false),
EMPTY_ACTIVE_SELECTION_TEXT_KEY) EMPTY_ACTIVE_SELECTION_TEXT_KEY)
.noEventPropagation() .noEventPropagation()
.publishIf(isExamSupporter, false) .publishIf(() -> isExamSupporter.getAsBoolean() && quitEnabled, false)
.newAction(ActionDefinition.MONITOR_EXAM_LOCK_SELECTED) .newAction(ActionDefinition.MONITOR_EXAM_LOCK_SELECTED)
.withEntityKey(entityKey) .withEntityKey(entityKey)
@ -240,7 +247,7 @@ public class MonitoringRunningExam implements TemplateComposer {
action -> this.showSEBLockActionPopup(action, clientTable), action -> this.showSEBLockActionPopup(action, clientTable),
EMPTY_ACTIVE_SELECTION_TEXT_KEY) EMPTY_ACTIVE_SELECTION_TEXT_KEY)
.noEventPropagation() .noEventPropagation()
.publishIf(isExamSupporter, false) .publishIf(() -> isExamSupporter.getAsBoolean() && lockscreenEnabled, false)
.newAction(ActionDefinition.MONITOR_EXAM_CLIENT_CONNECTION) .newAction(ActionDefinition.MONITOR_EXAM_CLIENT_CONNECTION)
.withParentEntityKey(entityKey) .withParentEntityKey(entityKey)
@ -260,7 +267,7 @@ public class MonitoringRunningExam implements TemplateComposer {
return copyOfPageAction; return copyOfPageAction;
}) })
.publishIf(isExamSupporter, false) .publishIf(() -> isExamSupporter.getAsBoolean() && detailEnabled, false)
.newAction(ActionDefinition.MONITOR_EXAM_DISABLE_SELECTED_CONNECTION) .newAction(ActionDefinition.MONITOR_EXAM_DISABLE_SELECTED_CONNECTION)
.withEntityKey(entityKey) .withEntityKey(entityKey)
@ -270,7 +277,7 @@ public class MonitoringRunningExam implements TemplateComposer {
action -> this.disableSEBClients(action, clientTable, false), action -> this.disableSEBClients(action, clientTable, false),
EMPTY_SELECTION_TEXT_KEY) EMPTY_SELECTION_TEXT_KEY)
.noEventPropagation() .noEventPropagation()
.publishIf(isExamSupporter, false); .publishIf(() -> isExamSupporter.getAsBoolean() && cancelEnabled, false);
if (isExamSupporter.getAsBoolean()) { if (isExamSupporter.getAsBoolean()) {
guiUpdates.add(createFilterActions( guiUpdates.add(createFilterActions(
@ -317,13 +324,16 @@ public class MonitoringRunningExam implements TemplateComposer {
final PageContext pageContext, final PageContext pageContext,
final Composite parent) { final Composite parent) {
final CurrentUser currentUser = pageService.getCurrentUser();
final PageActionBuilder actionBuilder = this.pageService final PageActionBuilder actionBuilder = this.pageService
.pageActionBuilder(pageContext.clearEntityKeys()); .pageActionBuilder(pageContext.clearEntityKeys());
final boolean proctoringEnabled = proctoringSettings != null && final boolean proctoringEnabled = proctoringSettings != null
BooleanUtils.toBoolean(proctoringSettings.enableProctoring); && currentUser.isFeatureEnabled(MONITORING_RUNNING_EXAM_LIVE_PROCTORING)
final boolean screenProctoringEnabled = screenProctoringSettings != null && && BooleanUtils.toBoolean(proctoringSettings.enableProctoring);
BooleanUtils.toBoolean(screenProctoringSettings.enableScreenProctoring); final boolean screenProctoringEnabled = screenProctoringSettings != null
&& currentUser.isFeatureEnabled(MONITORING_RUNNING_EXAM_SCREEN_PROCTORING)
&& BooleanUtils.toBoolean(screenProctoringSettings.enableScreenProctoring);
if (!proctoringEnabled && !screenProctoringEnabled) { if (!proctoringEnabled && !screenProctoringEnabled) {
return monitoringStatus -> { return monitoringStatus -> {
@ -408,43 +418,50 @@ public class MonitoringRunningExam implements TemplateComposer {
final boolean isAskCheckEnabled, final boolean isAskCheckEnabled,
final List<AllowedSEBVersion> allowedSEBVersions) { final List<AllowedSEBVersion> 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 = final FilterGUIUpdate statusFilterGUIUpdate =
new FilterGUIUpdate(this.pageService.getPolyglotPageService()); new FilterGUIUpdate(this.pageService.getPolyglotPageService());
addFilterAction( if (stateFilterEnabled) {
monitoringStatus, addFilterAction(
statusFilterGUIUpdate, monitoringStatus,
actionBuilder, statusFilterGUIUpdate,
clientTable, actionBuilder,
ConnectionStatus.CONNECTION_REQUESTED, clientTable,
ActionDefinition.MONITOR_EXAM_SHOW_REQUESTED_CONNECTION, ConnectionStatus.CONNECTION_REQUESTED,
ActionDefinition.MONITOR_EXAM_HIDE_REQUESTED_CONNECTION); ActionDefinition.MONITOR_EXAM_SHOW_REQUESTED_CONNECTION,
addFilterAction( ActionDefinition.MONITOR_EXAM_HIDE_REQUESTED_CONNECTION);
monitoringStatus, addFilterAction(
statusFilterGUIUpdate, monitoringStatus,
actionBuilder, statusFilterGUIUpdate,
clientTable, actionBuilder,
ConnectionStatus.ACTIVE, clientTable,
ActionDefinition.MONITOR_EXAM_SHOW_ACTIVE_CONNECTION, ConnectionStatus.ACTIVE,
ActionDefinition.MONITOR_EXAM_HIDE_ACTIVE_CONNECTION); ActionDefinition.MONITOR_EXAM_SHOW_ACTIVE_CONNECTION,
addFilterAction( ActionDefinition.MONITOR_EXAM_HIDE_ACTIVE_CONNECTION);
monitoringStatus, addFilterAction(
statusFilterGUIUpdate, monitoringStatus,
actionBuilder, statusFilterGUIUpdate,
clientTable, actionBuilder,
ConnectionStatus.CLOSED, clientTable,
ActionDefinition.MONITOR_EXAM_SHOW_CLOSED_CONNECTION, ConnectionStatus.CLOSED,
ActionDefinition.MONITOR_EXAM_HIDE_CLOSED_CONNECTION); ActionDefinition.MONITOR_EXAM_SHOW_CLOSED_CONNECTION,
addFilterAction( ActionDefinition.MONITOR_EXAM_HIDE_CLOSED_CONNECTION);
monitoringStatus, addFilterAction(
statusFilterGUIUpdate, monitoringStatus,
actionBuilder, statusFilterGUIUpdate,
clientTable, actionBuilder,
ConnectionStatus.DISABLED, clientTable,
ActionDefinition.MONITOR_EXAM_SHOW_DISABLED_CONNECTION, ConnectionStatus.DISABLED,
ActionDefinition.MONITOR_EXAM_HIDE_DISABLED_CONNECTION); ActionDefinition.MONITOR_EXAM_SHOW_DISABLED_CONNECTION,
ActionDefinition.MONITOR_EXAM_HIDE_DISABLED_CONNECTION);
}
if(isAskCheckEnabled){ if(issueFilterEnabled && isAskCheckEnabled) {
addIssueFilterAction( addIssueFilterAction(
monitoringStatus, monitoringStatus,
statusFilterGUIUpdate, statusFilterGUIUpdate,
@ -455,7 +472,7 @@ public class MonitoringRunningExam implements TemplateComposer {
ActionDefinition.MONITOR_EXAM_HIDE_ASK_GRANTED); ActionDefinition.MONITOR_EXAM_HIDE_ASK_GRANTED);
} }
if(allowedSEBVersions != null) { if(issueFilterEnabled && allowedSEBVersions != null) {
addIssueFilterAction( addIssueFilterAction(
monitoringStatus, monitoringStatus,
statusFilterGUIUpdate, statusFilterGUIUpdate,
@ -466,7 +483,7 @@ public class MonitoringRunningExam implements TemplateComposer {
ActionDefinition.MONITOR_EXAM_HIDE_SEB_VERSION_GRANTED); ActionDefinition.MONITOR_EXAM_HIDE_SEB_VERSION_GRANTED);
} }
if (clientGroups != null && !clientGroups.isEmpty()) { if (clientFilterEnabled && clientGroups != null && !clientGroups.isEmpty()) {
clientGroups.forEach(clientGroup -> { clientGroups.forEach(clientGroup -> {
addClientGroupFilterAction( addClientGroupFilterAction(

View file

@ -22,6 +22,7 @@ import java.util.function.Supplier;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import java.util.stream.Stream; import java.util.stream.Stream;
import ch.ethz.seb.sebserver.gbl.model.user.*;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.joda.time.DateTimeZone; import org.joda.time.DateTimeZone;
import org.slf4j.Logger; 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.ClientEvent.EventType;
import ch.ethz.seb.sebserver.gbl.model.session.ClientNotification; 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.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.profile.GuiProfile;
import ch.ethz.seb.sebserver.gbl.util.Result; import ch.ethz.seb.sebserver.gbl.util.Result;
import ch.ethz.seb.sebserver.gbl.util.Tuple; import ch.ethz.seb.sebserver.gbl.util.Tuple;
@ -197,7 +194,7 @@ public class ResourceService {
public List<Tuple<String>> lmsTypeResources() { public List<Tuple<String>> lmsTypeResources() {
return Arrays.stream(LmsType.values()) 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<>( .map(lmsType -> new Tuple<>(
lmsType.name(), lmsType.name(),
this.i18nSupport.getText(LMSSETUP_TYPE_PREFIX + lmsType.name(), lmsType.name()))) this.i18nSupport.getText(LMSSETUP_TYPE_PREFIX + lmsType.name(), lmsType.name())))
@ -294,8 +291,10 @@ public class ResourceService {
} }
public List<Tuple<String>> userRoleResources() { public List<Tuple<String>> userRoleResources() {
final boolean showServerAdminRole = this.currentUser.isFeatureEnabled(UserFeatures.Feature.ADMIN_INSTITUTION);
return UserRole.publicRolesForUser(this.currentUser.get()) return UserRole.publicRolesForUser(this.currentUser.get())
.stream() .stream()
.filter(ur -> ur != UserRole.SEB_SERVER_ADMIN || showServerAdminRole)
.map(ur -> new Tuple3<>( .map(ur -> new Tuple3<>(
ur.name(), ur.name(),
this.i18nSupport.getText(USERACCOUNT_ROLE_PREFIX + ur.name()), this.i18nSupport.getText(USERACCOUNT_ROLE_PREFIX + ur.name()),

View file

@ -8,14 +8,11 @@
package ch.ethz.seb.sebserver.gui.service.examconfig.impl; package ch.ethz.seb.sebserver.gui.service.examconfig.impl;
import java.util.Collection; import java.util.*;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.function.Function; import java.util.function.Function;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import ch.ethz.seb.sebserver.webservice.datalayer.batis.model.OrientationRecord;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@ -54,34 +51,41 @@ public class AttributeMapping {
.stream() .stream()
.collect(Collectors.toMap( .collect(Collectors.toMap(
o -> o.attributeId, 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 this.attributeIdMapping = Utils.immutableMapOf(attributes
.stream() .stream()
.filter(attr -> this.orientationAttributeMapping.containsKey(attr.id)) .filter(attr -> this.orientationAttributeMapping.containsKey(attr.id))
.collect(Collectors.toMap( .collect(Collectors.toMap(
attr -> attr.id, attr -> attr.id,
Function.identity()))); Function.identity(),
(first, second) -> second)));
this.attributeNameIdMapping = Utils.immutableMapOf(attributes this.attributeNameIdMapping = Utils.immutableMapOf(attributes
.stream() .stream()
.filter(attr -> this.orientationAttributeMapping.containsKey(attr.id)) .filter(attr -> this.orientationAttributeMapping.containsKey(attr.id))
.collect(Collectors.toMap( .collect(Collectors.toMap(
attr -> attr.name, attr -> attr.name,
attr -> attr.id))); attr -> attr.id,
(first, second) -> second)));
this.orientationAttributeNameMapping = Utils.immutableMapOf(orientations this.orientationAttributeNameMapping = Utils.immutableMapOf(orientations
.stream() .stream()
.collect(Collectors.toMap( .collect(Collectors.toMap(
o -> this.attributeIdMapping.get(o.attributeId).name, o -> this.attributeIdMapping.get(o.attributeId).name,
Function.identity()))); Function.identity(),
(first, second) -> second)));
this.childAttributeMapping = Utils.immutableMapOf(attributes this.childAttributeMapping = Utils.immutableMapOf(attributes
.stream() .stream()
.filter(attr -> this.orientationAttributeMapping.containsKey(attr.id)) .filter(attr -> this.orientationAttributeMapping.containsKey(attr.id))
.collect(Collectors.toMap( .collect(Collectors.toMap(
attr -> attr.id, attr -> attr.id,
this::getChildAttributes))); this::getChildAttributes,
(first, second) -> second)));
this.attributeGroupMapping = Utils.immutableMapOf(orientations this.attributeGroupMapping = Utils.immutableMapOf(orientations
.stream() .stream()
@ -91,7 +95,8 @@ public class AttributeMapping {
.stream() .stream()
.collect(Collectors.toMap( .collect(Collectors.toMap(
Function.identity(), Function.identity(),
this::getAttributesOfGroup))); this::getAttributesOfGroup,
(first, second) -> second)));
} }
public Collection<ConfigurationAttribute> getAttributes() { public Collection<ConfigurationAttribute> getAttributes() {

View file

@ -130,7 +130,7 @@ public interface PageService {
/** Indicates if SEB Server runs with SEB Server light setup /** Indicates if SEB Server runs with SEB Server light setup
* *
* @return true if this is a SEB Server light setup*/ * @return true if this is a SEB Server light setup*/
boolean isSEBServerLightSetup(); boolean isLightSetup();
void applyFullVersionNote(Composite content, PageContext pageContext); void applyFullVersionNote(Composite content, PageContext pageContext);

View file

@ -188,7 +188,7 @@ public class PageServiceImpl implements PageService {
} }
@Override @Override
public boolean isSEBServerLightSetup() { public boolean isLightSetup() {
return this.guiServiceInfo.isLightSetup(); return this.guiServiceInfo.isLightSetup();
} }

View file

@ -19,6 +19,7 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Map.Entry; import java.util.Map.Entry;
import java.util.Set; import java.util.Set;
import java.util.function.BooleanSupplier;
import java.util.function.Consumer; import java.util.function.Consumer;
import java.util.function.Function; import java.util.function.Function;
import java.util.function.Predicate; import java.util.function.Predicate;
@ -221,6 +222,16 @@ public final class ClientConnectionTable implements FullPageMonitoringGUIUpdate
return this.exam; 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) { public ClientConnectionTable withDefaultAction(final PageAction pageAction, final PageService pageService) {
this.table.addListener(SWT.MouseDoubleClick, event -> { this.table.addListener(SWT.MouseDoubleClick, event -> {
final Tuple<String> selection = getSingleSelection(); final Tuple<String> selection = getSingleSelection();

View file

@ -8,9 +8,7 @@
package ch.ethz.seb.sebserver.webservice.datalayer.checks; package ch.ethz.seb.sebserver.webservice.datalayer.checks;
import java.util.HashSet; import java.util.*;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import org.mybatis.dynamic.sql.SqlBuilder; import org.mybatis.dynamic.sql.SqlBuilder;
@ -68,7 +66,8 @@ public class OrientationTableDuplicatesCheck implements DBIntegrityCheck {
final List<Long> checkedToDelete = toDelete final List<Long> checkedToDelete = toDelete
.stream() .stream()
.filter(this::doubleCheck) .map(this::getOldestForDeletion)
.filter(Objects::nonNull)
.collect(Collectors.toList()); .collect(Collectors.toList());
if (checkedToDelete == null || checkedToDelete.isEmpty()) { 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 { try {
final OrientationRecord selectByPrimaryKey = this.orientationRecordMapper.selectByPrimaryKey(id); final OrientationRecord selectByPrimaryKey = this.orientationRecordMapper.selectByPrimaryKey(id);
final Long count = this.orientationRecordMapper.countByExample() final List<OrientationRecord> records = this.orientationRecordMapper.selectByExample()
.where( .where(
OrientationRecordDynamicSqlSupport.configAttributeId, OrientationRecordDynamicSqlSupport.configAttributeId,
SqlBuilder.isEqualTo(selectByPrimaryKey.getConfigAttributeId())) SqlBuilder.isEqualTo(selectByPrimaryKey.getConfigAttributeId()))
.and( .and(
OrientationRecordDynamicSqlSupport.templateId, OrientationRecordDynamicSqlSupport.templateId,
SqlBuilder.isEqualTo(selectByPrimaryKey.getTemplateId())) SqlBuilder.isEqualTo(selectByPrimaryKey.getTemplateId()))
.orderBy(OrientationRecordDynamicSqlSupport.id)
.build() .build()
.execute(); .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) { } catch (final Exception e) {
return false; return null;
} }
} }

View file

@ -253,7 +253,7 @@ public class BatchActionServiceImpl implements BatchActionService {
this.batchActionExec this.batchActionExec
.doSingleAction(modelId, this.batchAction) .doSingleAction(modelId, this.batchAction)
.onError(error -> this.batchActionHandler.handleError(modelId, error)) .onError(error -> this.batchActionHandler.handleError(modelId, error))
.onSuccess(entityKey -> this.batchActionHandler.handleSuccess(entityKey)); .onSuccess(this.batchActionHandler::handleSuccess);
}); });
this.batchActionHandler.finishUp(); this.batchActionHandler.finishUp();

View file

@ -161,9 +161,9 @@ public interface ExamAdminService {
/** Gets invoked after an exam has been changed and saved. /** Gets invoked after an exam has been changed and saved.
* *
* @param exam the exam that has been changed and saved */ * @param exam the exam that has been changed and saved */
void notifyExamSaved(Exam exam); Result<Exam> notifyExamSaved(Exam exam);
void applyQuitPassword(Exam entity); Result<Exam> applyQuitPassword(Exam exam);
static void newExamFieldValidation(final POSTMapper postParams) { static void newExamFieldValidation(final POSTMapper postParams) {
noLMSFieldValidation(new Exam(postParams)); noLMSFieldValidation(new Exam(postParams));

View file

@ -69,6 +69,7 @@ public class ExamAdminServiceImpl implements ExamAdminService {
private final boolean appSignatureKeyEnabled; private final boolean appSignatureKeyEnabled;
private final int defaultNumericalTrustThreshold; private final int defaultNumericalTrustThreshold;
private final ExamConfigurationValueService examConfigurationValueService; private final ExamConfigurationValueService examConfigurationValueService;
private final SEBRestrictionService sebRestrictionService;
protected ExamAdminServiceImpl( protected ExamAdminServiceImpl(
final ExamDAO examDAO, final ExamDAO examDAO,
@ -78,6 +79,7 @@ public class ExamAdminServiceImpl implements ExamAdminService {
final ExamConfigurationMapDAO examConfigurationMapDAO, final ExamConfigurationMapDAO examConfigurationMapDAO,
final LmsAPIService lmsAPIService, final LmsAPIService lmsAPIService,
final ExamConfigurationValueService examConfigurationValueService, 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.enabled:false}") boolean appSignatureKeyEnabled,
final @Value("${sebserver.webservice.api.admin.exam.app.signature.key.numerical.threshold:2}") int defaultNumericalTrustThreshold) { 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.examConfigurationValueService = examConfigurationValueService;
this.appSignatureKeyEnabled = appSignatureKeyEnabled; this.appSignatureKeyEnabled = appSignatureKeyEnabled;
this.defaultNumericalTrustThreshold = defaultNumericalTrustThreshold; this.defaultNumericalTrustThreshold = defaultNumericalTrustThreshold;
this.sebRestrictionService = sebRestrictionService;
} }
@Override @Override
@ -323,16 +326,21 @@ public class ExamAdminServiceImpl implements ExamAdminService {
} }
@Override @Override
public void notifyExamSaved(final Exam exam) { public Result<Exam> notifyExamSaved(final Exam exam) {
updateAdditionalExamConfigAttributes(exam.id); return Result.tryCatch(() -> {
this.proctoringAdminService.notifyExamSaved(exam); updateAdditionalExamConfigAttributes(exam.id);
this.proctoringAdminService.notifyExamSaved(exam);
return exam;
});
} }
@Override @Override
public void applyQuitPassword(final Exam exam) { public Result<Exam> applyQuitPassword(final Exam exam) {
this.examConfigurationValueService return this.examConfigurationValueService
.applyQuitPasswordToConfigs(exam.id, exam.quitPassword) .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<Exam> initAdditionalAttributesForMoodleExams(final Exam exam) { private Result<Exam> initAdditionalAttributesForMoodleExams(final Exam exam) {

View file

@ -19,7 +19,6 @@ import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executor; import java.util.concurrent.Executor;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; 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.Constants;
import ch.ethz.seb.sebserver.gbl.api.API; 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.api.POSTMapper;
import ch.ethz.seb.sebserver.gbl.async.AsyncServiceSpringConfig; import ch.ethz.seb.sebserver.gbl.async.AsyncServiceSpringConfig;
import ch.ethz.seb.sebserver.gbl.model.exam.Exam; 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.ClientConnection;
import ch.ethz.seb.sebserver.gbl.model.session.RunningExamInfo; import ch.ethz.seb.sebserver.gbl.model.session.RunningExamInfo;
import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile; 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.LmsSetupDAO;
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.SEBClientConfigDAO; import ch.ethz.seb.sebserver.webservice.servicelayer.dao.SEBClientConfigDAO;
import ch.ethz.seb.sebserver.webservice.servicelayer.lms.SEBRestrictionService; import ch.ethz.seb.sebserver.webservice.servicelayer.lms.SEBRestrictionService;
@ -68,7 +64,6 @@ public class ExamAPI_V1_Controller {
private final SEBClientConnectionService sebClientConnectionService; private final SEBClientConnectionService sebClientConnectionService;
private final SEBClientSessionService sebClientSessionService; private final SEBClientSessionService sebClientSessionService;
private final SEBClientConfigDAO sebClientConfigDAO; private final SEBClientConfigDAO sebClientConfigDAO;
private final JSONMapper jsonMapper;
private final Executor executor; private final Executor executor;
protected ExamAPI_V1_Controller( protected ExamAPI_V1_Controller(
@ -77,7 +72,6 @@ public class ExamAPI_V1_Controller {
final SEBClientConnectionService sebClientConnectionService, final SEBClientConnectionService sebClientConnectionService,
final SEBClientSessionService sebClientSessionService, final SEBClientSessionService sebClientSessionService,
final SEBClientConfigDAO sebClientConfigDAO, final SEBClientConfigDAO sebClientConfigDAO,
final JSONMapper jsonMapper,
@Qualifier(AsyncServiceSpringConfig.EXAM_API_EXECUTOR_BEAN_NAME) final Executor executor) { @Qualifier(AsyncServiceSpringConfig.EXAM_API_EXECUTOR_BEAN_NAME) final Executor executor) {
this.lmsSetupDAO = lmsSetupDAO; this.lmsSetupDAO = lmsSetupDAO;
@ -85,7 +79,6 @@ public class ExamAPI_V1_Controller {
this.sebClientConnectionService = sebClientConnectionService; this.sebClientConnectionService = sebClientConnectionService;
this.sebClientSessionService = sebClientSessionService; this.sebClientSessionService = sebClientSessionService;
this.sebClientConfigDAO = sebClientConfigDAO; this.sebClientConfigDAO = sebClientConfigDAO;
this.jsonMapper = jsonMapper;
this.executor = executor; this.executor = executor;
} }

View file

@ -15,7 +15,6 @@ import java.util.stream.Collectors;
import javax.validation.Valid; import javax.validation.Valid;
import ch.ethz.seb.sebserver.gbl.util.Cryptor; 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 ch.ethz.seb.sebserver.webservice.servicelayer.lms.impl.NoSEBRestrictionException;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.joda.time.DateTime; import org.joda.time.DateTime;
@ -669,12 +668,9 @@ public class ExamAdministrationController extends EntityController<Exam, Exam> {
@Override @Override
protected Result<Exam> notifySaved(final Exam entity) { protected Result<Exam> notifySaved(final Exam entity) {
return Result.tryCatch(() -> { return this.examAdminService.notifyExamSaved(entity)
this.examAdminService.notifyExamSaved(entity); .flatMap(this.examAdminService::applyQuitPassword)
this.examAdminService.applyQuitPassword(entity); .flatMap(this.examSessionService::flushCache);
this.examSessionService.flushCache(entity);
return entity;
});
} }
@Override @Override

View file

@ -23,9 +23,11 @@ sebserver.webservice.distributed.updateInterval=1000
sebserver.webservice.distributed.connectionUpdate=2000 sebserver.webservice.distributed.connectionUpdate=2000
sebserver.webservice.clean-db-on-startup=false sebserver.webservice.clean-db-on-startup=false
sebserver.init.database.integrity.try-fix=true
# webservice setup configuration # webservice setup configuration
sebserver.init.adminaccount.gen-on-init=false sebserver.init.adminaccount.gen-on-init=false
sebserver.webservice.light.setup=true sebserver.webservice.light.setup=false
sebserver.webservice.distributed=false sebserver.webservice.distributed=false
#sebserver.webservice.master.delay.threshold=10000 #sebserver.webservice.master.delay.threshold=10000
sebserver.webservice.http.external.scheme=http 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.clientId=sebserverClient
sebserver.feature.seb.screenProctoring.bundled.sebserveraccount.username=SEBServerAPIAccount 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