SEBSERV-487 implementation

This commit is contained in:
anhefti 2024-01-30 15:51:06 +01:00
parent 12f67149f0
commit a43042ce2f
15 changed files with 115 additions and 44 deletions

View file

@ -135,7 +135,7 @@ public class ActivitiesPane implements TemplateComposer {
// User Account
// if current user has role seb-server admin or institutional-admin, show list
if (isServerOrInstAdmin) {
if (isServerOrInstAdmin && !pageService.isSEBServerLightSetup()) {
final TreeItem userAccounts = this.widgetFactory.treeItemLocalized(
sebAdmin,
@ -147,7 +147,11 @@ public class ActivitiesPane implements TemplateComposer {
.create());
} else {
// otherwise show the user account form for current user
final TreeItem userAccounts = this.widgetFactory.treeItemLocalized(
final TreeItem userAccounts = pageService.isSEBServerLightSetup()
? this.widgetFactory.treeItemLocalized(
sebAdmin,
ActivityDefinition.USER_ACCOUNT.displayName)
: this.widgetFactory.treeItemLocalized(
navigation,
ActivityDefinition.USER_ACCOUNT.displayName);
injectActivitySelection(

View file

@ -88,18 +88,18 @@ public class InstitutionList implements TemplateComposer {
@Override
public void compose(final PageContext pageContext) {
if (pageService.isSEBServerLightSetup()) {
pageService.applyFullVersionNote(pageContext.getParent(), pageContext);
return;
}
final Composite content = this.pageService
.getWidgetFactory()
.defaultPageLayout(
pageContext.getParent(),
TITLE_TEXT_KEY);
if (pageService.isSEBServerLightSetup()) {
pageService.applyFullVersionNote(content, pageContext);
return;
}
final PageActionBuilder pageActionBuilder =
this.pageService.pageActionBuilder(pageContext.clearEntityKeys());

View file

@ -98,6 +98,8 @@ 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();
@ -106,11 +108,22 @@ 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 BooleanSupplier isNew = () -> entityKey == null;
final BooleanSupplier isNotNew = () -> !isNew.getAsBoolean();
final BooleanSupplier isSEBAdmin = () -> user.hasRole(UserRole.SEB_SERVER_ADMIN);
if (isLight && !readonly && isNew.getAsBoolean()) {
pageService.applyFullVersionNote(pageContext.getParent(), pageContext);
this.pageService.pageActionBuilder(pageContext.clearEntityKeys())
.newAction(ActionDefinition.USER_ACCOUNT_CANCEL_MODIFY)
.withExec(this.pageService.backToCurrentFunction())
.ignoreMoveAwayFromEdit()
.publish();
return;
}
// get data or create new. handle error if happen
final UserAccount userAccount = isNew.getAsBoolean()
? UserMod.createNew((parentEntityKey != null)
@ -165,7 +178,7 @@ public class UserAccountForm implements TemplateComposer {
Domain.USER.ATTR_LANGUAGE,
Locale.ENGLISH.getLanguage())
.addFieldIf(
() -> isSEBAdmin.getAsBoolean() && currentUser.isFeatureEnabled(UserFeatures.Feature.ADMIN_INSTITUTION),
() -> !isLight && isSEBAdmin.getAsBoolean() && currentUser.isFeatureEnabled(UserFeatures.Feature.ADMIN_INSTITUTION),
() -> FormBuilder.singleSelection(
Domain.USER.ATTR_INSTITUTION_ID,
FORM_INSTITUTION_TEXT_KEY,
@ -213,7 +226,7 @@ public class UserAccountForm implements TemplateComposer {
this.resourceService::timeZoneResources)
.mandatory(!readonly))
.addFieldIf(
() -> modifyGrant,
() -> modifyGrant && !isLight,
() -> FormBuilder.multiCheckboxSelection(
USER_ROLE.REFERENCE_NAME,
FORM_ROLES_TEXT_KEY,
@ -257,17 +270,17 @@ public class UserAccountForm implements TemplateComposer {
.withEntityKey(entityKey)
.withSimpleRestCall(restService, DeactivateUserAccount.class)
.withConfirm(this.pageService.confirmDeactivation(userAccount))
.publishIf(() -> writeGrant && readonly && institutionActive && userAccount.isActive())
.publishIf(() -> !isLight && writeGrant && readonly && institutionActive && userAccount.isActive())
.newAction(ActionDefinition.USER_ACCOUNT_ACTIVATE)
.withEntityKey(entityKey)
.withSimpleRestCall(restService, ActivateUserAccount.class)
.publishIf(() -> writeGrant && readonly && institutionActive && !userAccount.isActive())
.publishIf(() -> !isLight && writeGrant && readonly && institutionActive && !userAccount.isActive())
.newAction(ActionDefinition.USER_ACCOUNT_DELETE)
.withEntityKey(entityKey)
.withExec(this.userAccountDeletePopup.deleteWizardFunction(pageContext))
.publishIf(() -> writeGrant && readonly && institutionActive)
.publishIf(() -> !isLight && writeGrant && readonly && institutionActive)
.newAction(ActionDefinition.USER_ACCOUNT_SAVE)
.withEntityKey(entityKey)

View file

@ -105,6 +105,11 @@ public class CertificateList implements TemplateComposer {
@Override
public void compose(final PageContext pageContext) {
if (pageService.isSEBServerLightSetup()) {
pageService.applyFullVersionNote(pageContext.getParent(), pageContext);
return;
}
final GrantCheck grantCheck = this.currentUser.grantCheck(EntityType.CERTIFICATE);
final Composite content = this.pageService

View file

@ -95,6 +95,12 @@ public class ConfigTemplateList implements TemplateComposer {
@Override
public void compose(final PageContext pageContext) {
if (pageService.isSEBServerLightSetup()) {
pageService.applyFullVersionNote(pageContext.getParent(), pageContext);
return;
}
final WidgetFactory widgetFactory = this.pageService.getWidgetFactory();
final Composite content = widgetFactory.defaultPageLayout(
pageContext.getParent(),

View file

@ -159,6 +159,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();
// get data or create new. Handle error if happen
final ConfigurationNode examConfig = (isNew)
@ -187,16 +188,12 @@ public class SEBExamConfigForm implements TemplateComposer {
final boolean hasRunningExam = isAttachedToExam && examsPage
.map(res -> res.content
.stream()
.filter(map -> map.examStatus == ExamStatus.RUNNING)
.findAny()
.isPresent())
.anyMatch(map -> map.examStatus == ExamStatus.RUNNING))
.getOr(false);
final boolean hasActiveExams = hasRunningExam || examsPage
.map(res -> res.content
.stream()
.filter(map -> map.examStatus == ExamStatus.UP_COMING)
.findAny()
.isPresent())
.anyMatch(map -> map.examStatus == ExamStatus.UP_COMING))
.getOr(false);
// new PageContext with actual EntityKey
@ -224,7 +221,7 @@ public class SEBExamConfigForm implements TemplateComposer {
Domain.CONFIGURATION_NODE.ATTR_TYPE,
ConfigurationType.EXAM_CONFIG.name())
.addFieldIf(
() -> !examConfigTemplateResources.isEmpty(),
() -> !isLight && !examConfigTemplateResources.isEmpty(),
() -> FormBuilder.singleSelection(
Domain.CONFIGURATION_NODE.ATTR_TEMPLATE_ID,
FORM_TEMPLATE_TEXT_KEY,
@ -306,10 +303,11 @@ public class SEBExamConfigForm implements TemplateComposer {
.withExec(this::restoreToTemplateSettings)
.noEventPropagation()
.publishIf(() -> modifyGrant
&& !isLight
&& isReadonly
&& examConfig.status != ConfigurationStatus.IN_USE
&& examConfig.templateId != null
&& examConfig.templateId.longValue() > 0)
&& examConfig.templateId > 0)
.newAction(ActionDefinition.SEB_EXAM_CONFIG_COPY_CONFIG)
.withEntityKey(entityKey)
@ -332,7 +330,7 @@ public class SEBExamConfigForm implements TemplateComposer {
PageContext.AttributeKeys.COPY_AS_TEMPLATE,
Constants.TRUE_STRING)))
.noEventPropagation()
.publishIf(() -> modifyGrant && isReadonly)
.publishIf(() -> modifyGrant && !isLight && isReadonly)
.newAction(ActionDefinition.SEB_EXAM_CONFIG_GET_CONFIG_KEY)
.withEntityKey(entityKey)

View file

@ -129,6 +129,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 PageActionBuilder pageActionBuilder =
this.pageService.pageActionBuilder(pageContext.clearEntityKeys());
@ -245,7 +246,7 @@ public class SEBExamConfigList implements TemplateComposer {
this.sebExamConfigBatchResetToTemplatePopup.popupCreationFunction(pageContext),
EMPTY_SELECTION_TEXT_KEY)
.noEventPropagation()
.publishIf(() -> examConfigGrant.im(), false)
.publishIf(() -> !isLight && examConfigGrant.im(), false)
.newAction(ActionDefinition.SEB_EXAM_CONFIG_IMPORT_TO_NEW_CONFIG)
.withSelect(

View file

@ -86,6 +86,18 @@ public class ClientGroupForm implements TemplateComposer {
@Override
public void compose(final PageContext pageContext) {
if (pageService.isSEBServerLightSetup()) {
pageService.applyFullVersionNote(pageContext.getParent(), pageContext);
this.pageService.pageActionBuilder(pageContext.clearEntityKeys())
.newAction(ActionDefinition.EXAM_CLIENT_GROUP_CANCEL_MODIFY)
.withEntityKey(pageContext.getParentEntityKey())
.withExec(this.pageService.backToCurrentFunction())
.ignoreMoveAwayFromEdit()
.publish();
return;
}
final RestService restService = this.resourceService.getRestService();
final WidgetFactory widgetFactory = this.pageService.getWidgetFactory();
final EntityKey entityKey = pageContext.getEntityKey();

View file

@ -73,8 +73,8 @@ public class ExamClientGroupList implements TemplateComposer {
public void compose(final PageContext pageContext) {
final Composite content = pageContext.getParent();
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();
// List of ClientGroups
this.widgetFactory.addFormSubContextHeader(
@ -106,20 +106,20 @@ public class ExamClientGroupList implements TemplateComposer {
.withColumn(new ColumnDefinition<ClientGroup>(
Domain.CLIENT_GROUP.ATTR_TYPE,
CLIENT_GROUP_TYPE_COLUMN_KEY,
cgt -> this.resourceService.clientGroupTypeName(cgt))
this.resourceService::clientGroupTypeName)
.widthProportion(1))
.withColumn(new ColumnDefinition<ClientGroup>(
Domain.CLIENT_GROUP.ATTR_COLOR,
CLIENT_GROUP_COLOR_COLUMN_KEY,
cgt -> WidgetFactory.getColorValueHTML(cgt))
WidgetFactory::getColorValueHTML)
.asMarkup()
.widthProportion(1))
.withColumn(new ColumnDefinition<ClientGroup>(
Domain.CLIENT_GROUP.ATTR_DATA,
CLIENT_GROUP_DATA_COLUMN_KEY,
cgt -> this.widgetFactory.clientGroupDataToHTML(cgt))
this.widgetFactory::clientGroupDataToHTML)
.asMarkup()
.widthProportion(3))
@ -153,7 +153,7 @@ public class ExamClientGroupList implements TemplateComposer {
clientGroupTable::getMultiSelection,
this::deleteSelectedClientGroup,
CLIENT_GROUP_EMPTY_SELECTION_TEXT_KEY)
.publishIf(() -> editable && clientGroupTable.hasAnyContent(), false)
.publishIf(() -> !isLight && editable && clientGroupTable.hasAnyContent(), false)
.newAction(ActionDefinition.EXAM_CLIENT_GROUP_NEW)
.withParentEntityKey(entityKey)

View file

@ -12,7 +12,10 @@ import static ch.ethz.seb.sebserver.gbl.model.user.UserFeatures.Feature.EXAM_SCR
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import ch.ethz.seb.sebserver.gbl.api.POSTMapper;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.*;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.StringUtils;
@ -194,6 +197,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 modifyGrant = entityGrantCheck.m();
final boolean writeGrant = entityGrantCheck.w();
final boolean editable = modifyGrant && (exam.getStatus() == ExamStatus.UP_COMING || exam.getStatus() == ExamStatus.RUNNING);
@ -337,13 +341,13 @@ public class ExamForm implements TemplateComposer {
.withEntityKey(entityKey)
.withExec(this.proctoringSettingsPopup.settingsFunction(this.pageService, modifyGrant && editable))
.noEventPropagation()
.publishIf(() -> proctoringEnabled && readonly)
.publishIf(() -> !isLight && proctoringEnabled && readonly)
.newAction(ActionDefinition.EXAM_PROCTORING_OFF)
.withEntityKey(entityKey)
.withExec(this.proctoringSettingsPopup.settingsFunction(this.pageService, modifyGrant && editable))
.noEventPropagation()
.publishIf(() -> !proctoringEnabled && readonly)
.publishIf(() -> !isLight && !proctoringEnabled && readonly)
.newAction(ActionDefinition.SCREEN_PROCTORING_ON)
.withEntityKey(entityKey)
@ -498,6 +502,7 @@ public class ExamForm implements TemplateComposer {
final Exam exam) {
final I18nSupport i18nSupport = formContext.getI18nSupport();
final boolean isLight = pageService.isSEBServerLightSetup();
final boolean newExam = exam.id == null;
final boolean hasLMS = exam.lmsSetupId != null;
final boolean importFromLMS = newExam && hasLMS;
@ -522,6 +527,9 @@ public class ExamForm implements TemplateComposer {
.putStaticValueIf(() -> exam.lmsSetupId != null,
QuizData.QUIZ_ATTR_ID,
exam.externalId)
.putStaticValueIf(() -> isLight && newExam,
Domain.EXAM.ATTR_SUPPORTER,
this.pageService.getCurrentUser().get().uuid)
.addField(FormBuilder.text(
Domain.EXAM.ATTR_STATUS + "_display",
@ -537,7 +545,7 @@ public class ExamForm implements TemplateComposer {
this.resourceService::lmsSetupResource)
.readonly(true))
.addFieldIf(() -> exam.id == null,
.addFieldIf(() -> !isLight && exam.id == null,
() -> FormBuilder.singleSelection(
Domain.EXAM.ATTR_EXAM_TEMPLATE_ID,
FORM_EXAM_TEMPLATE_TEXT_KEY,
@ -599,9 +607,8 @@ public class ExamForm implements TemplateComposer {
Domain.EXAM.ATTR_SUPPORTER,
FORM_SUPPORTER_TEXT_KEY,
StringUtils.join(exam.supporter, Constants.LIST_SEPARATOR_CHAR),
this.resourceService::examSupporterResources))
this.resourceService::examSupporterResources)
.readonlyIf(() -> isLight && newExam))
.buildFor(importFromLMS
? this.restService.getRestCall(ImportAsExam.class)
@ -623,7 +630,9 @@ public class ExamForm implements TemplateComposer {
DateTime.now(timeZone).plusHours(1),
Exam.ExamType.UNDEFINED,
null,
null,
pageService.isSEBServerLightSetup()
? Stream.of(this.pageService.getCurrentUser().get().uuid).collect(Collectors.toList())
: null,
ExamStatus.UP_COMING,
null,
false,
@ -801,11 +810,15 @@ public class ExamForm implements TemplateComposer {
private Result<Exam> createExamFromQuizData(final PageContext pageContext) {
final EntityKey entityKey = pageContext.getEntityKey();
final EntityKey parentEntityKey = pageContext.getParentEntityKey();
final POSTMapper mapper = new POSTMapper(null, null);
if (pageService.isSEBServerLightSetup()) {
mapper.putIfAbsent(Domain.EXAM.ATTR_SUPPORTER, this.pageService.getCurrentUser().get().uuid);
}
return this.restService.getBuilder(GetQuizData.class)
.withURIVariable(API.PARAM_MODEL_ID, entityKey.modelId)
.withQueryParam(QuizData.QUIZ_ATTR_LMS_SETUP_ID, parentEntityKey.modelId)
.call()
.map(Exam::new)
.map(qd -> new Exam(null, qd, mapper))
.onError(error -> pageContext.notifyLoadError(EntityType.EXAM, error));
}

View file

@ -72,8 +72,8 @@ public class ExamIndicatorsList implements TemplateComposer {
public void compose(final PageContext pageContext) {
final Composite content = pageContext.getParent();
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();
// List of Indicators
this.widgetFactory.addFormSubContextHeader(
@ -140,7 +140,7 @@ public class ExamIndicatorsList implements TemplateComposer {
indicatorTable::getMultiSelection,
this::deleteSelectedIndicator,
INDICATOR_EMPTY_SELECTION_TEXT_KEY)
.publishIf(() -> editable && indicatorTable.hasAnyContent(), false)
.publishIf(() -> !isLight && editable && indicatorTable.hasAnyContent(), false)
.newAction(ActionDefinition.EXAM_INDICATOR_NEW)
.withParentEntityKey(entityKey)

View file

@ -105,6 +105,12 @@ public class ExamTemplateList implements TemplateComposer {
@Override
public void compose(final PageContext pageContext) {
if (pageService.isSEBServerLightSetup()) {
pageService.applyFullVersionNote(pageContext.getParent(), pageContext);
return;
}
final WidgetFactory widgetFactory = this.pageService.getWidgetFactory();
final CurrentUser currentUser = this.resourceService.getCurrentUser();
final RestService restService = this.resourceService.getRestService();

View file

@ -83,6 +83,18 @@ public class IndicatorForm implements TemplateComposer {
@Override
public void compose(final PageContext pageContext) {
if (pageService.isSEBServerLightSetup()) {
pageService.applyFullVersionNote(pageContext.getParent(), pageContext);
this.pageService.pageActionBuilder(pageContext.clearEntityKeys())
.newAction(ActionDefinition.EXAM_INDICATOR_CANCEL_MODIFY)
.withEntityKey(pageContext.getParentEntityKey())
.withExec(this.pageService.backToCurrentFunction())
.ignoreMoveAwayFromEdit()
.publish();
return;
}
final RestService restService = this.resourceService.getRestService();
final WidgetFactory widgetFactory = this.pageService.getWidgetFactory();
final EntityKey entityKey = pageContext.getEntityKey();

View file

@ -109,6 +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();
// content page layout with title
final Composite content = widgetFactory.defaultPageLayout(
@ -127,7 +128,7 @@ public class LmsSetupList implements TemplateComposer {
? Domain.LMS_SETUP.ATTR_INSTITUTION_ID
: Domain.LMS_SETUP.ATTR_NAME)
.withColumnIf(
() -> isSEBAdmin && currentUser.isFeatureEnabled(UserFeatures.Feature.ADMIN_INSTITUTION),
() -> !isLight && isSEBAdmin && currentUser.isFeatureEnabled(UserFeatures.Feature.ADMIN_INSTITUTION),
() -> new ColumnDefinition<>(
Domain.LMS_SETUP.ATTR_INSTITUTION_ID,
INSTITUTION_TEXT_KEY,
@ -188,7 +189,7 @@ public class LmsSetupList implements TemplateComposer {
.withSelect(
table.getGrantedSelection(currentUser, NO_MODIFY_PRIVILEGE_ON_OTHER_INSTITUTION),
PageAction::applySingleSelectionAsEntityKey, EMPTY_SELECTION_TEXT_KEY)
.publishIf(() -> userGrant.im(), false)
.publishIf(userGrant::im, false)
.newAction(ActionDefinition.LMS_SETUP_TOGGLE_ACTIVITY)
.withSelect(
@ -199,7 +200,7 @@ public class LmsSetupList implements TemplateComposer {
action -> LmsSetupForm.testLmsSetup(action, null, restService)),
EMPTY_SELECTION_TEXT_KEY)
.withConfirm(this.pageService.confirmDeactivation(table))
.publishIf(() -> userGrant.iw(), false);
.publishIf(userGrant::iw, false);
}

View file

@ -1903,7 +1903,7 @@ sebserver.examconfig.props.label.allowWindowCapture=Allow window capture (screen
sebserver.examconfig.props.label.screenSharingMacEnforceBlocked=Enforce blocking screen sharing on Mac
sebserver.examconfig.props.label.enableMacOSAAC=Prefer Assessment Mode (AAC)
sebserver.examconfig.props.label.enableMacOSAAC.tooltip=Automatic Assessment Configuration (AAC) Assessment Mode is available from macOS Monterey 12.1 (and Catalina 10.15.4 and >= 10.15.6).<br/>It blocks various macOS (which cannot be allowed optionally, like screen capture/sharing, Siri, Dictation)
sebserver.examconfig.props.label.enableRightMouseMac=Enable right mouse button
sebserver.examconfig.props.label.enableRightMouseMac=Enable right mouse button (Mac)
sebserver.examconfig.props.label.enableRightMouseMac.tooltip=Enable to access context menu for Web Inspector in SEB for macOS (use ONLY for debugging).<br/>Disable to prevent right mouse button actions/context menu for sharing content (with modern WebView) and in browser plugins/video players
sebserver.examconfig.props.label.mobileAllowInlineMediaPlayback=Allow inline playback on iPad
sebserver.examconfig.props.label.mobileAllowInlineMediaPlayback.tooltip=Video content can be played inline on a web page or only in full screen.<br/>By disabling this option, HTML5 video players are forced to use the iOS user interface for controlling playback