SEBSERV-139 implementing GUI
This commit is contained in:
		
							parent
							
								
									6eedcbb4a0
								
							
						
					
					
						commit
						548d4d132f
					
				
					 13 changed files with 261 additions and 17 deletions
				
			
		|  | @ -126,7 +126,6 @@ public final class API { | ||||||
|     public static final String EXAM_ADMINISTRATION_CHECK_RESTRICTION_PATH_SEGMENT = "/check-seb-restriction"; |     public static final String EXAM_ADMINISTRATION_CHECK_RESTRICTION_PATH_SEGMENT = "/check-seb-restriction"; | ||||||
|     public static final String EXAM_ADMINISTRATION_CHECK_IMPORTED_PATH_SEGMENT = "/check-imported"; |     public static final String EXAM_ADMINISTRATION_CHECK_IMPORTED_PATH_SEGMENT = "/check-imported"; | ||||||
|     public static final String EXAM_ADMINISTRATION_SEB_RESTRICTION_CHAPTERS_PATH_SEGMENT = "/chapters"; |     public static final String EXAM_ADMINISTRATION_SEB_RESTRICTION_CHAPTERS_PATH_SEGMENT = "/chapters"; | ||||||
| 
 |  | ||||||
|     public static final String EXAM_ADMINISTRATION_PROCTOR_PATH_SEGMENT = "/proctoring"; |     public static final String EXAM_ADMINISTRATION_PROCTOR_PATH_SEGMENT = "/proctoring"; | ||||||
| 
 | 
 | ||||||
|     public static final String EXAM_INDICATOR_ENDPOINT = "/indicator"; |     public static final String EXAM_INDICATOR_ENDPOINT = "/indicator"; | ||||||
|  |  | ||||||
|  | @ -18,8 +18,10 @@ import com.fasterxml.jackson.annotation.JsonProperty; | ||||||
| import ch.ethz.seb.sebserver.gbl.api.EntityType; | import ch.ethz.seb.sebserver.gbl.api.EntityType; | ||||||
| import ch.ethz.seb.sebserver.gbl.model.Domain; | import ch.ethz.seb.sebserver.gbl.model.Domain; | ||||||
| import ch.ethz.seb.sebserver.gbl.model.Entity; | import ch.ethz.seb.sebserver.gbl.model.Entity; | ||||||
|  | import ch.ethz.seb.sebserver.webservice.servicelayer.validation.ValidProctoringSettings; | ||||||
| 
 | 
 | ||||||
| @JsonIgnoreProperties(ignoreUnknown = true) | @JsonIgnoreProperties(ignoreUnknown = true) | ||||||
|  | @ValidProctoringSettings | ||||||
| public class ProctoringSettings implements Entity { | public class ProctoringSettings implements Entity { | ||||||
| 
 | 
 | ||||||
|     public enum ServerType { |     public enum ServerType { | ||||||
|  | @ -42,7 +44,7 @@ public class ProctoringSettings implements Entity { | ||||||
|     public final ServerType serverType; |     public final ServerType serverType; | ||||||
| 
 | 
 | ||||||
|     @JsonProperty(ATTR_SERVER_URL) |     @JsonProperty(ATTR_SERVER_URL) | ||||||
|     @URL(message = "examProctoring:serverURL:invalidURL") |     @URL(message = "proctoringSettings:serverURL:invalidURL") | ||||||
|     public final String serverURL; |     public final String serverURL; | ||||||
| 
 | 
 | ||||||
|     @JsonProperty(ATTR_APP_KEY) |     @JsonProperty(ATTR_APP_KEY) | ||||||
|  |  | ||||||
|  | @ -33,6 +33,7 @@ import ch.ethz.seb.sebserver.gbl.model.Domain; | ||||||
| import ch.ethz.seb.sebserver.gbl.model.EntityKey; | import ch.ethz.seb.sebserver.gbl.model.EntityKey; | ||||||
| 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.exam.Exam.ExamStatus; | import ch.ethz.seb.sebserver.gbl.model.exam.Exam.ExamStatus; | ||||||
|  | import ch.ethz.seb.sebserver.gbl.model.exam.ProctoringSettings; | ||||||
| import ch.ethz.seb.sebserver.gbl.model.exam.QuizData; | import ch.ethz.seb.sebserver.gbl.model.exam.QuizData; | ||||||
| import ch.ethz.seb.sebserver.gbl.model.institution.LmsSetupTestResult; | import ch.ethz.seb.sebserver.gbl.model.institution.LmsSetupTestResult; | ||||||
| import ch.ethz.seb.sebserver.gbl.model.institution.LmsSetupTestResult.ErrorType; | import ch.ethz.seb.sebserver.gbl.model.institution.LmsSetupTestResult.ErrorType; | ||||||
|  | @ -57,6 +58,7 @@ import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestService; | ||||||
| import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.CheckExamConsistency; | import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.CheckExamConsistency; | ||||||
| import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.CheckSEBRestriction; | import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.CheckSEBRestriction; | ||||||
| import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.GetExam; | import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.GetExam; | ||||||
|  | import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.GetProctoringSettings; | ||||||
| import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.SaveExam; | import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.SaveExam; | ||||||
| import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.lmssetup.GetLmsSetup; | import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.lmssetup.GetLmsSetup; | ||||||
| import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.lmssetup.TestLmsSetup; | import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.lmssetup.TestLmsSetup; | ||||||
|  | @ -119,6 +121,7 @@ public class ExamForm implements TemplateComposer { | ||||||
|     private final PageService pageService; |     private final PageService pageService; | ||||||
|     private final ResourceService resourceService; |     private final ResourceService resourceService; | ||||||
|     private final ExamSEBRestrictionSettings examSEBRestrictionSettings; |     private final ExamSEBRestrictionSettings examSEBRestrictionSettings; | ||||||
|  |     private final ExamProctoringSettings examProctoringSettings; | ||||||
|     private final WidgetFactory widgetFactory; |     private final WidgetFactory widgetFactory; | ||||||
|     private final RestService restService; |     private final RestService restService; | ||||||
|     private final ExamDeletePopup examDeletePopup; |     private final ExamDeletePopup examDeletePopup; | ||||||
|  | @ -128,6 +131,7 @@ public class ExamForm implements TemplateComposer { | ||||||
|     protected ExamForm( |     protected ExamForm( | ||||||
|             final PageService pageService, |             final PageService pageService, | ||||||
|             final ExamSEBRestrictionSettings examSEBRestrictionSettings, |             final ExamSEBRestrictionSettings examSEBRestrictionSettings, | ||||||
|  |             final ExamProctoringSettings examProctoringSettings, | ||||||
|             final ExamToConfigBindingForm examToConfigBindingForm, |             final ExamToConfigBindingForm examToConfigBindingForm, | ||||||
|             final DownloadService downloadService, |             final DownloadService downloadService, | ||||||
|             final ExamDeletePopup examDeletePopup, |             final ExamDeletePopup examDeletePopup, | ||||||
|  | @ -137,6 +141,7 @@ public class ExamForm implements TemplateComposer { | ||||||
|         this.pageService = pageService; |         this.pageService = pageService; | ||||||
|         this.resourceService = pageService.getResourceService(); |         this.resourceService = pageService.getResourceService(); | ||||||
|         this.examSEBRestrictionSettings = examSEBRestrictionSettings; |         this.examSEBRestrictionSettings = examSEBRestrictionSettings; | ||||||
|  |         this.examProctoringSettings = examProctoringSettings; | ||||||
|         this.widgetFactory = pageService.getWidgetFactory(); |         this.widgetFactory = pageService.getWidgetFactory(); | ||||||
|         this.restService = this.resourceService.getRestService(); |         this.restService = this.resourceService.getRestService(); | ||||||
|         this.examDeletePopup = examDeletePopup; |         this.examDeletePopup = examDeletePopup; | ||||||
|  | @ -336,6 +341,13 @@ public class ExamForm implements TemplateComposer { | ||||||
|                         ? this.restService.getRestCall(ImportAsExam.class) |                         ? this.restService.getRestCall(ImportAsExam.class) | ||||||
|                         : this.restService.getRestCall(SaveExam.class)); |                         : this.restService.getRestCall(SaveExam.class)); | ||||||
| 
 | 
 | ||||||
|  |         final boolean proctoringEnabled = this.restService | ||||||
|  |                 .getBuilder(GetProctoringSettings.class) | ||||||
|  |                 .withURIVariable(API.PARAM_MODEL_ID, entityKey.modelId) | ||||||
|  |                 .call() | ||||||
|  |                 .map(ProctoringSettings::getEnableProctoring) | ||||||
|  |                 .getOr(false); | ||||||
|  | 
 | ||||||
|         final PageActionBuilder actionBuilder = this.pageService.pageActionBuilder(formContext |         final PageActionBuilder actionBuilder = this.pageService.pageActionBuilder(formContext | ||||||
|                 .clearEntityKeys() |                 .clearEntityKeys() | ||||||
|                 .removeAttribute(AttributeKeys.IMPORT_FROM_QUIZ_DATA)); |                 .removeAttribute(AttributeKeys.IMPORT_FROM_QUIZ_DATA)); | ||||||
|  | @ -384,6 +396,18 @@ public class ExamForm implements TemplateComposer { | ||||||
|                 .publishIf(() -> sebRestrictionAvailable && readonly && modifyGrant && !importFromQuizData |                 .publishIf(() -> sebRestrictionAvailable && readonly && modifyGrant && !importFromQuizData | ||||||
|                         && BooleanUtils.isTrue(isRestricted)) |                         && BooleanUtils.isTrue(isRestricted)) | ||||||
| 
 | 
 | ||||||
|  |                 .newAction(ActionDefinition.EXAM_PROCTORING_ON) | ||||||
|  |                 .withEntityKey(entityKey) | ||||||
|  |                 .withExec(this.examProctoringSettings.settingsFunction(this.pageService)) | ||||||
|  |                 .noEventPropagation() | ||||||
|  |                 .publishIf(() -> proctoringEnabled && readonly) | ||||||
|  | 
 | ||||||
|  |                 .newAction(ActionDefinition.EXAM_PROCTORING_OFF) | ||||||
|  |                 .withEntityKey(entityKey) | ||||||
|  |                 .withExec(this.examProctoringSettings.settingsFunction(this.pageService)) | ||||||
|  |                 .noEventPropagation() | ||||||
|  |                 .publishIf(() -> !proctoringEnabled && readonly) | ||||||
|  | 
 | ||||||
|                 .newAction(ActionDefinition.EXAM_DELETE) |                 .newAction(ActionDefinition.EXAM_DELETE) | ||||||
|                 .withEntityKey(entityKey) |                 .withEntityKey(entityKey) | ||||||
|                 .withExec(this.examDeletePopup.deleteWizardFunction(pageContext)) |                 .withExec(this.examDeletePopup.deleteWizardFunction(pageContext)) | ||||||
|  |  | ||||||
|  | @ -16,12 +16,16 @@ import org.apache.commons.lang3.BooleanUtils; | ||||||
| import org.eclipse.swt.widgets.Composite; | import org.eclipse.swt.widgets.Composite; | ||||||
| import org.slf4j.Logger; | import org.slf4j.Logger; | ||||||
| import org.slf4j.LoggerFactory; | import org.slf4j.LoggerFactory; | ||||||
|  | import org.springframework.context.annotation.Lazy; | ||||||
|  | import org.springframework.stereotype.Component; | ||||||
| 
 | 
 | ||||||
| import ch.ethz.seb.sebserver.gbl.api.API; | import ch.ethz.seb.sebserver.gbl.api.API; | ||||||
| import ch.ethz.seb.sebserver.gbl.model.EntityKey; | import ch.ethz.seb.sebserver.gbl.model.EntityKey; | ||||||
| import ch.ethz.seb.sebserver.gbl.model.exam.ProctoringSettings; | import ch.ethz.seb.sebserver.gbl.model.exam.ProctoringSettings; | ||||||
| import ch.ethz.seb.sebserver.gbl.model.exam.ProctoringSettings.ServerType; | import ch.ethz.seb.sebserver.gbl.model.exam.ProctoringSettings.ServerType; | ||||||
|  | import ch.ethz.seb.sebserver.gbl.profile.GuiProfile; | ||||||
| import ch.ethz.seb.sebserver.gbl.util.Utils; | import ch.ethz.seb.sebserver.gbl.util.Utils; | ||||||
|  | import ch.ethz.seb.sebserver.gui.content.action.ActionDefinition; | ||||||
| import ch.ethz.seb.sebserver.gui.form.Form; | import ch.ethz.seb.sebserver.gui.form.Form; | ||||||
| import ch.ethz.seb.sebserver.gui.form.FormBuilder; | import ch.ethz.seb.sebserver.gui.form.FormBuilder; | ||||||
| import ch.ethz.seb.sebserver.gui.form.FormHandle; | import ch.ethz.seb.sebserver.gui.form.FormHandle; | ||||||
|  | @ -30,12 +34,16 @@ import ch.ethz.seb.sebserver.gui.service.i18n.LocTextKey; | ||||||
| import ch.ethz.seb.sebserver.gui.service.page.ModalInputDialogComposer; | import ch.ethz.seb.sebserver.gui.service.page.ModalInputDialogComposer; | ||||||
| import ch.ethz.seb.sebserver.gui.service.page.PageContext; | import ch.ethz.seb.sebserver.gui.service.page.PageContext; | ||||||
| import ch.ethz.seb.sebserver.gui.service.page.PageService; | import ch.ethz.seb.sebserver.gui.service.page.PageService; | ||||||
|  | import ch.ethz.seb.sebserver.gui.service.page.event.ActionEvent; | ||||||
| import ch.ethz.seb.sebserver.gui.service.page.impl.ModalInputDialog; | import ch.ethz.seb.sebserver.gui.service.page.impl.ModalInputDialog; | ||||||
| import ch.ethz.seb.sebserver.gui.service.page.impl.PageAction; | import ch.ethz.seb.sebserver.gui.service.page.impl.PageAction; | ||||||
| import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestService; | import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestService; | ||||||
| import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.GetProctoringSettings; | import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.GetProctoringSettings; | ||||||
| import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.SaveProctoringSettings; | import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.SaveProctoringSettings; | ||||||
| 
 | 
 | ||||||
|  | @Lazy | ||||||
|  | @Component | ||||||
|  | @GuiProfile | ||||||
| public class ExamProctoringSettings { | public class ExamProctoringSettings { | ||||||
| 
 | 
 | ||||||
|     private static final Logger log = LoggerFactory.getLogger(ExamProctoringSettings.class); |     private static final Logger log = LoggerFactory.getLogger(ExamProctoringSettings.class); | ||||||
|  | @ -55,10 +63,6 @@ public class ExamProctoringSettings { | ||||||
|     private final static LocTextKey SEB_PROCTORING_FORM_SECRET = |     private final static LocTextKey SEB_PROCTORING_FORM_SECRET = | ||||||
|             new LocTextKey("sebserver.exam.proctoring.form.secret"); |             new LocTextKey("sebserver.exam.proctoring.form.secret"); | ||||||
| 
 | 
 | ||||||
|     public ExamProctoringSettings() { |  | ||||||
|         // TODO Auto-generated constructor stub |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     Function<PageAction, PageAction> settingsFunction(final PageService pageService) { |     Function<PageAction, PageAction> settingsFunction(final PageService pageService) { | ||||||
| 
 | 
 | ||||||
|         return action -> { |         return action -> { | ||||||
|  | @ -75,7 +79,7 @@ public class ExamProctoringSettings { | ||||||
|                     pageService, |                     pageService, | ||||||
|                     action.pageContext()); |                     action.pageContext()); | ||||||
| 
 | 
 | ||||||
|             final Predicate<FormHandle<?>> doBind = formHandle -> doCreate( |             final Predicate<FormHandle<?>> doBind = formHandle -> doSaveSettings( | ||||||
|                     pageService, |                     pageService, | ||||||
|                     pageContext, |                     pageContext, | ||||||
|                     formHandle); |                     formHandle); | ||||||
|  | @ -90,7 +94,7 @@ public class ExamProctoringSettings { | ||||||
|         }; |         }; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     private boolean doCreate( |     private boolean doSaveSettings( | ||||||
|             final PageService pageService, |             final PageService pageService, | ||||||
|             final PageContext pageContext, |             final PageContext pageContext, | ||||||
|             final FormHandle<?> formHandle) { |             final FormHandle<?> formHandle) { | ||||||
|  | @ -105,6 +109,8 @@ public class ExamProctoringSettings { | ||||||
|         ProctoringSettings examProctoring = null; |         ProctoringSettings examProctoring = null; | ||||||
|         try { |         try { | ||||||
|             final Form form = formHandle.getForm(); |             final Form form = formHandle.getForm(); | ||||||
|  |             form.clearErrors(); | ||||||
|  | 
 | ||||||
|             final boolean enabled = BooleanUtils.toBoolean( |             final boolean enabled = BooleanUtils.toBoolean( | ||||||
|                     form.getFieldValue(ProctoringSettings.ATTR_ENABLE_PROCTORING)); |                     form.getFieldValue(ProctoringSettings.ATTR_ENABLE_PROCTORING)); | ||||||
|             final ServerType serverType = ServerType.valueOf( |             final ServerType serverType = ServerType.valueOf( | ||||||
|  | @ -126,7 +132,7 @@ public class ExamProctoringSettings { | ||||||
|             return false; |             return false; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         return !pageService |         final boolean saveOk = !pageService | ||||||
|                 .getRestService() |                 .getRestService() | ||||||
|                 .getBuilder(SaveProctoringSettings.class) |                 .getBuilder(SaveProctoringSettings.class) | ||||||
|                 .withURIVariable(API.PARAM_MODEL_ID, entityKey.modelId) |                 .withURIVariable(API.PARAM_MODEL_ID, entityKey.modelId) | ||||||
|  | @ -134,6 +140,19 @@ public class ExamProctoringSettings { | ||||||
|                 .call() |                 .call() | ||||||
|                 .onError(formHandle::handleError) |                 .onError(formHandle::handleError) | ||||||
|                 .hasError(); |                 .hasError(); | ||||||
|  | 
 | ||||||
|  |         if (saveOk) { | ||||||
|  |             final PageAction action = pageService.pageActionBuilder(pageContext) | ||||||
|  |                     .newAction(ActionDefinition.EXAM_VIEW_FROM_LIST) | ||||||
|  |                     .create(); | ||||||
|  | 
 | ||||||
|  |             pageService.firePageEvent( | ||||||
|  |                     new ActionEvent(action), | ||||||
|  |                     action.pageContext()); | ||||||
|  |             return true; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         return false; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     private final class SEBProctoringPropertiesForm |     private final class SEBProctoringPropertiesForm | ||||||
|  | @ -196,9 +215,24 @@ public class ExamProctoringSettings { | ||||||
|                             ProctoringSettings.ATTR_SERVER_TYPE, |                             ProctoringSettings.ATTR_SERVER_TYPE, | ||||||
|                             SEB_PROCTORING_FORM_TYPE, |                             SEB_PROCTORING_FORM_TYPE, | ||||||
|                             proctoringSettings.serverType.name(), |                             proctoringSettings.serverType.name(), | ||||||
|                             this.pageService.getResourceService()::examProctoringTypeResources)) |                             resourceService::examProctoringTypeResources)) | ||||||
| 
 | 
 | ||||||
|                     // TODO |                     .addField(FormBuilder.text( | ||||||
|  |                             ProctoringSettings.ATTR_SERVER_URL, | ||||||
|  |                             SEB_PROCTORING_FORM_URL, | ||||||
|  |                             proctoringSettings.serverURL)) | ||||||
|  | 
 | ||||||
|  |                     .addField(FormBuilder.text( | ||||||
|  |                             ProctoringSettings.ATTR_APP_KEY, | ||||||
|  |                             SEB_PROCTORING_FORM_APPKEY, | ||||||
|  |                             proctoringSettings.appKey)) | ||||||
|  | 
 | ||||||
|  |                     .addField(FormBuilder.password( | ||||||
|  |                             ProctoringSettings.ATTR_APP_SECRET, | ||||||
|  |                             SEB_PROCTORING_FORM_SECRET, | ||||||
|  |                             (proctoringSettings.appSecret != null) | ||||||
|  |                                     ? String.valueOf(proctoringSettings.appSecret) | ||||||
|  |                                     : null)) | ||||||
| 
 | 
 | ||||||
|                     .build(); |                     .build(); | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -24,6 +24,7 @@ import ch.ethz.seb.sebserver.gbl.model.Domain; | ||||||
| import ch.ethz.seb.sebserver.gbl.model.EntityKey; | import ch.ethz.seb.sebserver.gbl.model.EntityKey; | ||||||
| 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.exam.Indicator; | import ch.ethz.seb.sebserver.gbl.model.exam.Indicator; | ||||||
|  | import ch.ethz.seb.sebserver.gbl.model.exam.ProctoringSettings; | ||||||
| import ch.ethz.seb.sebserver.gbl.model.session.ClientConnection.ConnectionStatus; | import ch.ethz.seb.sebserver.gbl.model.session.ClientConnection.ConnectionStatus; | ||||||
| import ch.ethz.seb.sebserver.gbl.model.session.ClientConnectionData; | import ch.ethz.seb.sebserver.gbl.model.session.ClientConnectionData; | ||||||
| import ch.ethz.seb.sebserver.gbl.model.session.ClientEvent; | import ch.ethz.seb.sebserver.gbl.model.session.ClientEvent; | ||||||
|  | @ -45,8 +46,10 @@ import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestCall; | ||||||
| import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestService; | import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestService; | ||||||
| import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.GetExam; | import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.GetExam; | ||||||
| import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.GetIndicators; | import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.GetIndicators; | ||||||
|  | import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.GetProctoringSettings; | ||||||
| import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.logs.GetExtendedClientEventPage; | import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.logs.GetExtendedClientEventPage; | ||||||
| import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.session.GetClientConnectionData; | import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.session.GetClientConnectionData; | ||||||
|  | import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.session.GetProctorURLForClient; | ||||||
| import ch.ethz.seb.sebserver.gui.service.remote.webservice.auth.CurrentUser; | import ch.ethz.seb.sebserver.gui.service.remote.webservice.auth.CurrentUser; | ||||||
| import ch.ethz.seb.sebserver.gui.service.session.ClientConnectionDetails; | import ch.ethz.seb.sebserver.gui.service.session.ClientConnectionDetails; | ||||||
| import ch.ethz.seb.sebserver.gui.service.session.InstructionProcessor; | import ch.ethz.seb.sebserver.gui.service.session.InstructionProcessor; | ||||||
|  | @ -237,6 +240,13 @@ public class MonitoringClientConnection implements TemplateComposer { | ||||||
| 
 | 
 | ||||||
|                 .compose(pageContext.copyOf(content)); |                 .compose(pageContext.copyOf(content)); | ||||||
| 
 | 
 | ||||||
|  |         final boolean proctoringEnabled = restService | ||||||
|  |                 .getBuilder(GetProctoringSettings.class) | ||||||
|  |                 .withURIVariable(API.PARAM_MODEL_ID, parentEntityKey.modelId) | ||||||
|  |                 .call() | ||||||
|  |                 .map(ProctoringSettings::getEnableProctoring) | ||||||
|  |                 .getOr(false); | ||||||
|  | 
 | ||||||
|         actionBuilder |         actionBuilder | ||||||
|                 .newAction(ActionDefinition.MONITOR_EXAM_BACK_TO_OVERVIEW) |                 .newAction(ActionDefinition.MONITOR_EXAM_BACK_TO_OVERVIEW) | ||||||
|                 .withEntityKey(parentEntityKey) |                 .withEntityKey(parentEntityKey) | ||||||
|  | @ -256,15 +266,16 @@ public class MonitoringClientConnection implements TemplateComposer { | ||||||
|                         connectionData.clientConnection.status == ConnectionStatus.ACTIVE) |                         connectionData.clientConnection.status == ConnectionStatus.ACTIVE) | ||||||
| 
 | 
 | ||||||
|                 .newAction(ActionDefinition.MONITOR_EXAM_CLIENT_CONNECTION_PROCTORING) |                 .newAction(ActionDefinition.MONITOR_EXAM_CLIENT_CONNECTION_PROCTORING) | ||||||
|                 .withExec(this::openProctorScreen) |                 .withEntityKey(parentEntityKey) | ||||||
|  |                 .withExec(action -> this.openProctorScreen(action, connectionToken)) | ||||||
|                 .noEventPropagation() |                 .noEventPropagation() | ||||||
|                 .publish() |                 .publishIf(() -> proctoringEnabled) | ||||||
| 
 | 
 | ||||||
|         ; |         ; | ||||||
| 
 | 
 | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     private PageAction openProctorScreen(final PageAction action) { |     private PageAction openProctorScreen(final PageAction action, final String connectionToken) { | ||||||
| // | // | ||||||
| //        final ProctorDialog dialog = new ProctorDialog(action.pageContext().getParent().getShell()); | //        final ProctorDialog dialog = new ProctorDialog(action.pageContext().getParent().getShell()); | ||||||
| //        dialog.open(EVENT_LIST_TITLE_KEY, | //        dialog.open(EVENT_LIST_TITLE_KEY, | ||||||
|  | @ -274,6 +285,12 @@ public class MonitoringClientConnection implements TemplateComposer { | ||||||
| //        urlLauncher.openURL( | //        urlLauncher.openURL( | ||||||
| //                "https://seb-jitsi.ethz.ch/TestRoomABC?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJjb250ZXh0Ijp7InVzZXIiOnsiYXZhdGFyIjoiaHR0cHM6Ly9leGFtcGxlLmNvbS9qb2huLWRvZSIsIm5hbWUiOiJEaXNwbGF5IE5hbWUiLCJlbWFpbCI6Im5hbWVAZXhhbXBsZS5jb20ifX0sImF1ZCI6InNlYi1qaXRzaSIsImlzcyI6InNlYi1qaXRzaSIsInN1YiI6Im1lZXQuaml0c2kiLCJyb29tIjoiKiJ9.SD9Zs78mMFqxS1tpalPTykYYaubIYsj_406WAOhcqxQ"); | //                "https://seb-jitsi.ethz.ch/TestRoomABC?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJjb250ZXh0Ijp7InVzZXIiOnsiYXZhdGFyIjoiaHR0cHM6Ly9leGFtcGxlLmNvbS9qb2huLWRvZSIsIm5hbWUiOiJEaXNwbGF5IE5hbWUiLCJlbWFpbCI6Im5hbWVAZXhhbXBsZS5jb20ifX0sImF1ZCI6InNlYi1qaXRzaSIsImlzcyI6InNlYi1qaXRzaSIsInN1YiI6Im1lZXQuaml0c2kiLCJyb29tIjoiKiJ9.SD9Zs78mMFqxS1tpalPTykYYaubIYsj_406WAOhcqxQ"); | ||||||
| 
 | 
 | ||||||
|  |         final String proctorURL = this.pageService.getRestService().getBuilder(GetProctorURLForClient.class) | ||||||
|  |                 .withURIVariable(API.PARAM_MODEL_ID, action.getEntityKey().modelId) | ||||||
|  |                 .withURIVariable(API.EXAM_API_SEB_CONNECTION_TOKEN, connectionToken) | ||||||
|  |                 .call() | ||||||
|  |                 .getOrThrow(); | ||||||
|  | 
 | ||||||
|         final JavaScriptExecutor javaScriptExecutor = RWT.getClient().getService(JavaScriptExecutor.class); |         final JavaScriptExecutor javaScriptExecutor = RWT.getClient().getService(JavaScriptExecutor.class); | ||||||
|         javaScriptExecutor.execute( |         javaScriptExecutor.execute( | ||||||
|                 "window.open(" |                 "window.open(" | ||||||
|  |  | ||||||
|  | @ -297,6 +297,16 @@ public enum ActionDefinition { | ||||||
|             ImageIcon.LOCK, |             ImageIcon.LOCK, | ||||||
|             PageStateDefinitionImpl.EXAM_VIEW, |             PageStateDefinitionImpl.EXAM_VIEW, | ||||||
|             ActionCategory.FORM), |             ActionCategory.FORM), | ||||||
|  |     EXAM_PROCTORING_ON( | ||||||
|  |             new LocTextKey("sebserver.exam.proctoring.actions.open"), | ||||||
|  |             ImageIcon.VISIBILITY, | ||||||
|  |             PageStateDefinitionImpl.EXAM_VIEW, | ||||||
|  |             ActionCategory.FORM), | ||||||
|  |     EXAM_PROCTORING_OFF( | ||||||
|  |             new LocTextKey("sebserver.exam.proctoring.actions.open"), | ||||||
|  |             ImageIcon.VISIBILITY_OFF, | ||||||
|  |             PageStateDefinitionImpl.EXAM_VIEW, | ||||||
|  |             ActionCategory.FORM), | ||||||
| 
 | 
 | ||||||
|     EXAM_CONFIGURATION_NEW( |     EXAM_CONFIGURATION_NEW( | ||||||
|             new LocTextKey("sebserver.exam.configuration.action.list.new"), |             new LocTextKey("sebserver.exam.configuration.action.list.new"), | ||||||
|  |  | ||||||
|  | @ -39,6 +39,7 @@ import ch.ethz.seb.sebserver.gbl.model.exam.ExamConfigurationMap; | ||||||
| import ch.ethz.seb.sebserver.gbl.model.exam.Indicator.IndicatorType; | import ch.ethz.seb.sebserver.gbl.model.exam.Indicator.IndicatorType; | ||||||
| import ch.ethz.seb.sebserver.gbl.model.exam.OpenEdxSEBRestriction.PermissionComponent; | import ch.ethz.seb.sebserver.gbl.model.exam.OpenEdxSEBRestriction.PermissionComponent; | ||||||
| import ch.ethz.seb.sebserver.gbl.model.exam.OpenEdxSEBRestriction.WhiteListPath; | import ch.ethz.seb.sebserver.gbl.model.exam.OpenEdxSEBRestriction.WhiteListPath; | ||||||
|  | import ch.ethz.seb.sebserver.gbl.model.exam.ProctoringSettings.ServerType; | ||||||
| import ch.ethz.seb.sebserver.gbl.model.institution.LmsSetup.LmsType; | import ch.ethz.seb.sebserver.gbl.model.institution.LmsSetup.LmsType; | ||||||
| import ch.ethz.seb.sebserver.gbl.model.sebconfig.AttributeType; | import ch.ethz.seb.sebserver.gbl.model.sebconfig.AttributeType; | ||||||
| import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationNode; | import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationNode; | ||||||
|  | @ -372,7 +373,7 @@ public class ResourceService { | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     public List<Tuple<String>> examProctoringTypeResources() { |     public List<Tuple<String>> examProctoringTypeResources() { | ||||||
|         return Arrays.stream(ExamType.values()) |         return Arrays.stream(ServerType.values()) | ||||||
|                 .map(type -> new Tuple3<>( |                 .map(type -> new Tuple3<>( | ||||||
|                         type.name(), |                         type.name(), | ||||||
|                         this.i18nSupport.getText(EXAM_PROCTORING_TYPE_PREFIX + type.name()), |                         this.i18nSupport.getText(EXAM_PROCTORING_TYPE_PREFIX + type.name()), | ||||||
|  |  | ||||||
|  | @ -36,7 +36,7 @@ public class SaveProctoringSettings extends RestCall<Exam> { | ||||||
|                 MediaType.APPLICATION_JSON_UTF8, |                 MediaType.APPLICATION_JSON_UTF8, | ||||||
|                 API.EXAM_ADMINISTRATION_ENDPOINT |                 API.EXAM_ADMINISTRATION_ENDPOINT | ||||||
|                         + API.MODEL_ID_VAR_PATH_SEGMENT |                         + API.MODEL_ID_VAR_PATH_SEGMENT | ||||||
|                         + API.EXAM_ADMINISTRATION_SEB_RESTRICTION_PATH_SEGMENT); |                         + API.EXAM_ADMINISTRATION_PROCTOR_PATH_SEGMENT); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -0,0 +1,42 @@ | ||||||
|  | /* | ||||||
|  |  * Copyright (c) 2020 ETH Zürich, Educational Development and Technology (LET) | ||||||
|  |  * | ||||||
|  |  * This Source Code Form is subject to the terms of the Mozilla Public | ||||||
|  |  * License, v. 2.0. If a copy of the MPL was not distributed with this | ||||||
|  |  * file, You can obtain one at http://mozilla.org/MPL/2.0/. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | package ch.ethz.seb.sebserver.gui.service.remote.webservice.api.session; | ||||||
|  | 
 | ||||||
|  | import org.springframework.context.annotation.Lazy; | ||||||
|  | import org.springframework.http.HttpMethod; | ||||||
|  | import org.springframework.http.MediaType; | ||||||
|  | import org.springframework.stereotype.Component; | ||||||
|  | 
 | ||||||
|  | import com.fasterxml.jackson.core.type.TypeReference; | ||||||
|  | 
 | ||||||
|  | import ch.ethz.seb.sebserver.gbl.api.API; | ||||||
|  | import ch.ethz.seb.sebserver.gbl.api.EntityType; | ||||||
|  | import ch.ethz.seb.sebserver.gbl.profile.GuiProfile; | ||||||
|  | import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestCall; | ||||||
|  | 
 | ||||||
|  | @Lazy | ||||||
|  | @Component | ||||||
|  | @GuiProfile | ||||||
|  | public class GetProctorURLForClient extends RestCall<String> { | ||||||
|  | 
 | ||||||
|  |     public GetProctorURLForClient() { | ||||||
|  |         super(new TypeKey<>( | ||||||
|  |                 CallType.GET_SINGLE, | ||||||
|  |                 EntityType.EXAM_PROCTOR_DATA, | ||||||
|  |                 new TypeReference<String>() { | ||||||
|  |                 }), | ||||||
|  |                 HttpMethod.GET, | ||||||
|  |                 MediaType.APPLICATION_JSON_UTF8, | ||||||
|  |                 API.EXAM_ADMINISTRATION_ENDPOINT | ||||||
|  |                         + API.MODEL_ID_VAR_PATH_SEGMENT | ||||||
|  |                         + API.EXAM_ADMINISTRATION_PROCTOR_PATH_SEGMENT | ||||||
|  |                         + API.EXAM_MONITORING_SEB_CONNECTION_TOKEN_PATH_SEGMENT); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  | } | ||||||
|  | @ -0,0 +1,81 @@ | ||||||
|  | /* | ||||||
|  |  * Copyright (c) 2020 ETH Zürich, Educational Development and Technology (LET) | ||||||
|  |  * | ||||||
|  |  * This Source Code Form is subject to the terms of the Mozilla Public | ||||||
|  |  * License, v. 2.0. If a copy of the MPL was not distributed with this | ||||||
|  |  * file, You can obtain one at http://mozilla.org/MPL/2.0/. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | package ch.ethz.seb.sebserver.webservice.servicelayer.validation; | ||||||
|  | 
 | ||||||
|  | import java.net.InetAddress; | ||||||
|  | import java.net.URI; | ||||||
|  | 
 | ||||||
|  | import javax.validation.ConstraintValidator; | ||||||
|  | import javax.validation.ConstraintValidatorContext; | ||||||
|  | 
 | ||||||
|  | import org.apache.commons.lang3.StringUtils; | ||||||
|  | 
 | ||||||
|  | import ch.ethz.seb.sebserver.gbl.model.exam.ProctoringSettings; | ||||||
|  | import ch.ethz.seb.sebserver.gbl.model.exam.ProctoringSettings.ServerType; | ||||||
|  | 
 | ||||||
|  | public class ProctoringSettingsValidator implements ConstraintValidator<ValidProctoringSettings, ProctoringSettings> { | ||||||
|  | 
 | ||||||
|  |     @Override | ||||||
|  |     public boolean isValid(final ProctoringSettings value, final ConstraintValidatorContext context) { | ||||||
|  |         if (value == null) { | ||||||
|  |             return false; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         if (value.enableProctoring) { | ||||||
|  |             if (value.serverType == ServerType.JITSI_MEET) { | ||||||
|  |                 boolean passed = true; | ||||||
|  |                 if (StringUtils.isBlank(value.serverURL)) { | ||||||
|  |                     context.disableDefaultConstraintViolation(); | ||||||
|  |                     context | ||||||
|  |                             .buildConstraintViolationWithTemplate("proctoringSettings:serverURL:notNull") | ||||||
|  |                             .addPropertyNode("serverURL").addConstraintViolation(); | ||||||
|  |                     passed = false; | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|  |                 try { | ||||||
|  | 
 | ||||||
|  |                     if (!InetAddress.getByName(new URI(value.serverURL).getHost()).isReachable(5000)) { | ||||||
|  |                         context.disableDefaultConstraintViolation(); | ||||||
|  |                         context | ||||||
|  |                                 .buildConstraintViolationWithTemplate("proctoringSettings:serverURL:serverNotAvailable") | ||||||
|  |                                 .addPropertyNode("serverURL").addConstraintViolation(); | ||||||
|  |                         passed = false; | ||||||
|  |                     } | ||||||
|  |                 } catch (final Exception e) { | ||||||
|  |                     context.disableDefaultConstraintViolation(); | ||||||
|  |                     context | ||||||
|  |                             .buildConstraintViolationWithTemplate("proctoringSettings:serverURL:serverNotAvailable") | ||||||
|  |                             .addPropertyNode("serverURL").addConstraintViolation(); | ||||||
|  |                     passed = false; | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|  |                 if (StringUtils.isBlank(value.appKey)) { | ||||||
|  |                     context.disableDefaultConstraintViolation(); | ||||||
|  |                     context | ||||||
|  |                             .buildConstraintViolationWithTemplate("proctoringSettings:appKey:notNull") | ||||||
|  |                             .addPropertyNode("appKey").addConstraintViolation(); | ||||||
|  |                     passed = false; | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|  |                 if (StringUtils.isBlank(value.appSecret)) { | ||||||
|  |                     context.disableDefaultConstraintViolation(); | ||||||
|  |                     context | ||||||
|  |                             .buildConstraintViolationWithTemplate("proctoringSettings:appSecret:notNull") | ||||||
|  |                             .addPropertyNode("appSecret").addConstraintViolation(); | ||||||
|  |                     passed = false; | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|  |                 return passed; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         return true; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  | } | ||||||
|  | @ -0,0 +1,32 @@ | ||||||
|  | /* | ||||||
|  |  * Copyright (c) 2020 ETH Zürich, Educational Development and Technology (LET) | ||||||
|  |  * | ||||||
|  |  * This Source Code Form is subject to the terms of the Mozilla Public | ||||||
|  |  * License, v. 2.0. If a copy of the MPL was not distributed with this | ||||||
|  |  * file, You can obtain one at http://mozilla.org/MPL/2.0/. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | package ch.ethz.seb.sebserver.webservice.servicelayer.validation; | ||||||
|  | 
 | ||||||
|  | import java.lang.annotation.Documented; | ||||||
|  | import java.lang.annotation.ElementType; | ||||||
|  | import java.lang.annotation.Retention; | ||||||
|  | import java.lang.annotation.RetentionPolicy; | ||||||
|  | import java.lang.annotation.Target; | ||||||
|  | 
 | ||||||
|  | import javax.validation.Constraint; | ||||||
|  | import javax.validation.Payload; | ||||||
|  | 
 | ||||||
|  | @Target({ ElementType.TYPE }) | ||||||
|  | @Retention(RetentionPolicy.RUNTIME) | ||||||
|  | @Constraint(validatedBy = ProctoringSettingsValidator.class) | ||||||
|  | @Documented | ||||||
|  | public @interface ValidProctoringSettings { | ||||||
|  | 
 | ||||||
|  |     String message() default "{mandatoryWhenEnabled}"; | ||||||
|  | 
 | ||||||
|  |     Class<?>[] groups() default {}; | ||||||
|  | 
 | ||||||
|  |     Class<? extends Payload>[] payload() default {}; | ||||||
|  | 
 | ||||||
|  | } | ||||||
|  | @ -429,7 +429,7 @@ public class ExamAdministrationController extends EntityController<Exam, Exam> { | ||||||
|                     + API.EXAM_ADMINISTRATION_PROCTOR_PATH_SEGMENT |                     + API.EXAM_ADMINISTRATION_PROCTOR_PATH_SEGMENT | ||||||
|                     + API.EXAM_MONITORING_SEB_CONNECTION_TOKEN_PATH_SEGMENT, |                     + API.EXAM_MONITORING_SEB_CONNECTION_TOKEN_PATH_SEGMENT, | ||||||
|             method = RequestMethod.GET, |             method = RequestMethod.GET, | ||||||
|             produces = MediaType.APPLICATION_JSON_UTF8_VALUE) |             produces = MediaType.TEXT_PLAIN_VALUE) | ||||||
|     public String getExamProctoringURL( |     public String getExamProctoringURL( | ||||||
|             @RequestParam( |             @RequestParam( | ||||||
|                     name = API.PARAM_INSTITUTION_ID, |                     name = API.PARAM_INSTITUTION_ID, | ||||||
|  |  | ||||||
|  | @ -90,6 +90,7 @@ sebserver.form.validation.fieldError.password.mismatch=The retyped password does | ||||||
| sebserver.form.validation.fieldError.invalidURL=The input does not match the URL pattern. | sebserver.form.validation.fieldError.invalidURL=The input does not match the URL pattern. | ||||||
| sebserver.form.validation.fieldError.exists=This name already exists. Please choose another one. | sebserver.form.validation.fieldError.exists=This name already exists. Please choose another one. | ||||||
| sebserver.form.validation.fieldError.email=Invalid mail address | sebserver.form.validation.fieldError.email=Invalid mail address | ||||||
|  | sebserver.form.validation.fieldError.serverNotAvailable=No service seems to be available within the given URL | ||||||
| sebserver.error.unexpected=Unexpected Error | sebserver.error.unexpected=Unexpected Error | ||||||
| sebserver.page.message=Information | sebserver.page.message=Information | ||||||
| sebserver.dialog.confirm.title=Confirmation | sebserver.dialog.confirm.title=Confirmation | ||||||
|  | @ -1396,6 +1397,7 @@ sebserver.monitoring.exam.connection.action.hide.disabled=Hide Canceled | ||||||
| sebserver.monitoring.exam.connection.action.show.disabled=Show Canceled | sebserver.monitoring.exam.connection.action.show.disabled=Show Canceled | ||||||
| sebserver.monitoring.exam.connection.action.hide.undefined=Hide Undefined | sebserver.monitoring.exam.connection.action.hide.undefined=Hide Undefined | ||||||
| sebserver.monitoring.exam.connection.action.show.undefined=Show Undefined | sebserver.monitoring.exam.connection.action.show.undefined=Show Undefined | ||||||
|  | sebserver.monitoring.exam.connection.action.proctoring=Proctoring | ||||||
| 
 | 
 | ||||||
| sebserver.monitoring.exam.connection.eventlist.title=Events | sebserver.monitoring.exam.connection.eventlist.title=Events | ||||||
| sebserver.monitoring.exam.connection.eventlist.title.tooltip=All events and logs sent by the SEB Client | sebserver.monitoring.exam.connection.eventlist.title.tooltip=All events and logs sent by the SEB Client | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		
		Reference in a new issue
	
	 anhefti
						anhefti