SEBSERV-180 fixes

This commit is contained in:
anhefti 2021-05-18 15:16:36 +02:00
parent 7a686c0e6e
commit a172326fa5
10 changed files with 200 additions and 107 deletions

View file

@ -382,7 +382,7 @@ public final class Result<T> {
@Override
public String toString() {
throw new RuntimeException("!!!!!!!!!!!!!");
throw new RuntimeException("Result.toString is probably called by mistake !!!");
//return "Result [value=" + this.value + ", error=" + this.error + "]";
}

View file

@ -371,15 +371,18 @@ public class MonitoringClientConnection implements TemplateComposer {
.call()
.onError(error -> log.error("Failed to get ProctoringServiceSettings", error))
.getOr(null);
final ProctoringGUIService proctoringGUIService = currentUser.getProctoringGUIService();
if (procotringSettings != null && procotringSettings.enableProctoring) {
final ProctoringGUIService proctoringGUIService = this.resourceService
.getCurrentUser()
.getProctoringGUIService();
actionBuilder
.newAction(ActionDefinition.MONITOR_EXAM_CLIENT_CONNECTION_PROCTORING)
.withEntityKey(parentEntityKey)
.withExec(action -> this.monitoringProctoringService.openOneToOneRoom(
action,
connectionData, proctoringGUIService))
connectionData,
proctoringGUIService))
.noEventPropagation()
.publish()

View file

@ -36,6 +36,7 @@ import ch.ethz.seb.sebserver.gui.service.i18n.PolyglotPageService;
import ch.ethz.seb.sebserver.gui.service.page.PageContext;
import ch.ethz.seb.sebserver.gui.service.page.PageService;
import ch.ethz.seb.sebserver.gui.service.page.TemplateComposer;
import ch.ethz.seb.sebserver.gui.service.page.event.ActionActivationEvent;
import ch.ethz.seb.sebserver.gui.service.page.event.ActionActivationEventListener;
import ch.ethz.seb.sebserver.gui.service.page.event.ActionPublishEventListener;
import ch.ethz.seb.sebserver.gui.service.page.event.PageEventListener;
@ -118,19 +119,7 @@ public class ActionPane implements TemplateComposer {
continue;
}
final PageAction action = (PageAction) actionItem.getData(ACTION_EVENT_CALL_KEY);
final Image image = event.activation
? ad.icon.getImage(parent.getDisplay())
: ad.icon.getGreyedImage(parent.getDisplay());
actionItem.setImage(image);
if (event.activation) {
actionItem.setForeground(null);
} else {
actionItem.setForeground(new Color(parent.getDisplay(), new RGBA(150, 150, 150, 50)));
ActionPane.this.pageService.getPolyglotPageService().injectI18n(
actionItem,
(action != null) ? action.getTitle() : ad.title);
}
de_activate_action_icon(event, parent, actionItem);
}
if (event.decoration != null) {
@ -141,10 +130,33 @@ public class ActionPane implements TemplateComposer {
ActionPane.this.pageService.getPolyglotPageService().injectI18n(
actionItemToDecorate, event.decoration._2.title);
}
de_activate_action_icon(event, parent, actionItemToDecorate);
}
});
}
private void de_activate_action_icon(
final ActionActivationEvent event,
final Composite parent,
final TreeItem actionItemToDecorate) {
final PageAction action = (PageAction) actionItemToDecorate.getData(ACTION_EVENT_CALL_KEY);
final Image image = event.activation
? event.decoration._1.icon.getImage(parent.getDisplay())
: event.decoration._1.icon.getGreyedImage(parent.getDisplay());
actionItemToDecorate.setImage(image);
if (event.activation) {
actionItemToDecorate.setForeground(null);
} else {
actionItemToDecorate
.setForeground(new Color(parent.getDisplay(), new RGBA(150, 150, 150, 50)));
ActionPane.this.pageService.getPolyglotPageService().injectI18n(
actionItemToDecorate,
(action != null) ? action.getTitle() : event.decoration._1.title);
}
}
private TreeItem findAction(
final Map<String, Tree> actionTrees,
final Composite parent,

View file

@ -44,6 +44,7 @@ public class PageContextImpl implements PageContext {
private static final Logger log = LoggerFactory.getLogger(PageContextImpl.class);
private static final LocTextKey UNEXPECTED_ERROR = new LocTextKey("sebserver.error.unexpected");
private static final String ENTITY_LIST_TYPE = null;
private final I18nSupport i18nSupport;
@ -312,6 +313,18 @@ public class PageContextImpl implements PageContext {
@Override
public void notifyError(final LocTextKey message, final Exception error) {
if (error == null) {
final MessageBox messageBox = new Message(
getShell(),
this.i18nSupport.getText(UNEXPECTED_ERROR),
this.i18nSupport.getText(message),
SWT.ERROR,
this.i18nSupport);
messageBox.setMarkupEnabled(true);
messageBox.open(null);
return;
}
log.error("Unexpected GUI error notified: {}", error.getMessage());
final String errorMessage = message != null

View file

@ -59,6 +59,19 @@ public class MonitoringProctoringService {
private static final Logger log = LoggerFactory.getLogger(MonitoringProctoringService.class);
public static final LocTextKey OPEN_TOWNHALL_ERROR =
new LocTextKey("sebserver.exam.proctoring.townhall.open.error");
public static final LocTextKey CLOSE_TOWNHALL_ERROR =
new LocTextKey("sebserver.exam.proctoring.townhall.close.error");
public static final LocTextKey OPEN_ONE_ERROR =
new LocTextKey("sebserver.exam.proctoring.one.open.error");
public static final LocTextKey CLOSE_ONE_ERROR =
new LocTextKey("sebserver.exam.proctoring.one.close.error");
public static final LocTextKey OPEN_COLLECTING_ERROR =
new LocTextKey("sebserver.exam.proctoring.collecting.open.error");
public static final LocTextKey CLOSE_COLLECTING_ERROR =
new LocTextKey("sebserver.exam.proctoring.collecting.close.error");
private static final LocTextKey EXAM_ROOM_NAME =
new LocTextKey("sebserver.monitoring.exam.proctoring.room.all.name");
@ -109,26 +122,32 @@ public class MonitoringProctoringService {
final PageAction action) {
if (isTownhallRoomActive(action.getEntityKey().modelId)) {
closeTownhallRoom(proctoringGUIService, action);
this.pageService.firePageEvent(
new ActionActivationEvent(
true,
new Tuple<>(
ActionDefinition.MONITOR_EXAM_OPEN_TOWNHALL_PROCTOR_ROOM,
ActionDefinition.MONITOR_EXAM_OPEN_TOWNHALL_PROCTOR_ROOM)),
action.pageContext());
return action;
if (closeTownhallRoom(proctoringGUIService, action)) {
this.pageService.firePageEvent(
new ActionActivationEvent(
true,
new Tuple<>(
ActionDefinition.MONITOR_EXAM_OPEN_TOWNHALL_PROCTOR_ROOM,
ActionDefinition.MONITOR_EXAM_OPEN_TOWNHALL_PROCTOR_ROOM)),
action.pageContext());
} else {
action.pageContext().notifyError(CLOSE_TOWNHALL_ERROR, null);
}
} else {
openTownhallRoom(proctoringGUIService, action);
this.pageService.firePageEvent(
new ActionActivationEvent(
true,
new Tuple<>(
ActionDefinition.MONITOR_EXAM_OPEN_TOWNHALL_PROCTOR_ROOM,
ActionDefinition.MONITOR_EXAM_CLOSE_TOWNHALL_PROCTOR_ROOM)),
action.pageContext());
return action;
if (openTownhallRoom(proctoringGUIService, action)) {
this.pageService.firePageEvent(
new ActionActivationEvent(
true,
new Tuple<>(
ActionDefinition.MONITOR_EXAM_OPEN_TOWNHALL_PROCTOR_ROOM,
ActionDefinition.MONITOR_EXAM_CLOSE_TOWNHALL_PROCTOR_ROOM)),
action.pageContext());
} else {
action.pageContext().notifyError(OPEN_TOWNHALL_ERROR, null);
}
}
return action;
}
public void initCollectingRoomActions(
@ -219,50 +238,56 @@ public class MonitoringProctoringService {
final PageAction action,
final ClientConnectionData connectionData) {
final String examId = action.getEntityKey().modelId;
try {
final String examId = action.getEntityKey().modelId;
final ProctoringServiceSettings proctoringSettings = this.pageService.getRestService()
.getBuilder(GetProctoringSettings.class)
.withURIVariable(API.PARAM_MODEL_ID, examId)
.call()
.getOrThrow();
final Optional<RemoteProctoringRoom> roomOptional =
this.pageService.getRestService().getBuilder(GetCollectingRooms.class)
.withURIVariable(API.PARAM_MODEL_ID, examId)
.call()
.getOrThrow()
.stream()
.filter(room -> room.id.equals(connectionData.clientConnection.remoteProctoringRoomId))
.findFirst();
if (roomOptional.isPresent()) {
final RemoteProctoringRoom room = roomOptional.get();
final ProctoringRoomConnection proctoringConnectionData = this.pageService
.getRestService()
.getBuilder(GetProctorRoomConnection.class)
.withURIVariable(API.PARAM_MODEL_ID, String.valueOf(proctoringSettings.examId))
.withQueryParam(ProctoringRoomConnection.ATTR_ROOM_NAME, room.name)
.withQueryParam(ProctoringRoomConnection.ATTR_SUBJECT, Utils.encodeFormURL_UTF_8(room.subject))
final ProctoringServiceSettings proctoringSettings = this.pageService.getRestService()
.getBuilder(GetProctoringSettings.class)
.withURIVariable(API.PARAM_MODEL_ID, examId)
.call()
.getOrThrow();
ProctoringGUIService.setCurrentProctoringWindowData(examId, proctoringConnectionData);
final String script = String.format(
MonitoringProctoringService.OPEN_ROOM_SCRIPT,
room.name,
800,
1200,
this.guiServiceInfo.getExternalServerURIBuilder().toUriString(),
this.remoteProctoringEndpoint);
final Optional<RemoteProctoringRoom> roomOptional =
this.pageService.getRestService().getBuilder(GetCollectingRooms.class)
.withURIVariable(API.PARAM_MODEL_ID, examId)
.call()
.getOrThrow()
.stream()
.filter(room -> room.id.equals(connectionData.clientConnection.remoteProctoringRoomId))
.findFirst();
RWT.getClient()
.getService(JavaScriptExecutor.class)
.execute(script);
if (roomOptional.isPresent()) {
final RemoteProctoringRoom room = roomOptional.get();
final ProctoringRoomConnection proctoringConnectionData = this.pageService
.getRestService()
.getBuilder(GetProctorRoomConnection.class)
.withURIVariable(API.PARAM_MODEL_ID, String.valueOf(proctoringSettings.examId))
.withQueryParam(ProctoringRoomConnection.ATTR_ROOM_NAME, room.name)
.withQueryParam(ProctoringRoomConnection.ATTR_SUBJECT, Utils.encodeFormURL_UTF_8(room.subject))
.call()
.getOrThrow();
this.pageService.getCurrentUser()
.getProctoringGUIService()
.registerProctoringWindow(examId, room.name, room.name);
ProctoringGUIService.setCurrentProctoringWindowData(examId, proctoringConnectionData);
final String script = String.format(
MonitoringProctoringService.OPEN_ROOM_SCRIPT,
room.name,
800,
1200,
this.guiServiceInfo.getExternalServerURIBuilder().toUriString(),
this.remoteProctoringEndpoint);
RWT.getClient()
.getService(JavaScriptExecutor.class)
.execute(script);
this.pageService.getCurrentUser()
.getProctoringGUIService()
.registerProctoringWindow(examId, room.name, room.name);
}
} catch (final Exception e) {
log.error("Failed to open popup for collecting room: ", e);
action.pageContext().notifyError(CLOSE_COLLECTING_ERROR, e);
}
return action;
@ -273,41 +298,49 @@ public class MonitoringProctoringService {
final ClientConnectionData connectionData,
final ProctoringGUIService proctoringGUIService) {
final String connectionToken = connectionData.clientConnection.connectionToken;
final String examId = action.getEntityKey().modelId;
try {
if (!proctoringGUIService.hasWindow(connectionToken)) {
final ProctoringRoomConnection proctoringConnectionData = proctoringGUIService
.openBreakOutRoom(
examId,
connectionToken,
connectionData.clientConnection.userSessionId,
Arrays.asList(connectionToken))
.onError(error -> log.error(
"Failed to open single proctoring room for connection {} {}",
connectionToken,
error.getMessage()))
.getOr(null);
final String connectionToken = connectionData.clientConnection.connectionToken;
final String examId = action.getEntityKey().modelId;
ProctoringGUIService.setCurrentProctoringWindowData(
examId,
if (!proctoringGUIService.hasWindow(connectionToken)) {
final ProctoringRoomConnection proctoringConnectionData = proctoringGUIService
.openBreakOutRoom(
examId,
connectionToken,
connectionData.clientConnection.userSessionId,
Arrays.asList(connectionToken))
.onError(error -> log.error(
"Failed to open single proctoring room for connection {} {}",
connectionToken,
error.getMessage()))
.getOr(null);
ProctoringGUIService.setCurrentProctoringWindowData(
examId,
connectionToken,
proctoringConnectionData);
}
final JavaScriptExecutor javaScriptExecutor = RWT.getClient().getService(JavaScriptExecutor.class);
final String script = String.format(
MonitoringProctoringService.OPEN_ROOM_SCRIPT,
connectionToken,
proctoringConnectionData);
420,
640,
this.guiServiceInfo.getExternalServerURIBuilder().toUriString(),
this.remoteProctoringEndpoint);
javaScriptExecutor.execute(script);
} catch (final Exception e) {
log.error("Failed to open popup for one to one room: ", e);
action.pageContext().notifyError(OPEN_ONE_ERROR, e);
}
final JavaScriptExecutor javaScriptExecutor = RWT.getClient().getService(JavaScriptExecutor.class);
final String script = String.format(
MonitoringProctoringService.OPEN_ROOM_SCRIPT,
connectionToken,
420,
640,
this.guiServiceInfo.getExternalServerURIBuilder().toUriString(),
this.remoteProctoringEndpoint);
javaScriptExecutor.execute(script);
return action;
}
private PageAction openTownhallRoom(
private boolean openTownhallRoom(
final ProctoringGUIService proctoringGUIService,
final PageAction action) {
@ -342,11 +375,13 @@ public class MonitoringProctoringService {
} catch (final Exception e) {
log.error("Failed to open popup for town-hall room: ", e);
return false;
}
return action;
return true;
}
private PageAction closeTownhallRoom(
private boolean closeTownhallRoom(
final ProctoringGUIService proctoringGUIService,
final PageAction action) {
@ -360,8 +395,10 @@ public class MonitoringProctoringService {
} catch (final Exception e) {
log.error("Failed to close proctoring town-hall room for exam: {}", examId);
return false;
}
return action;
return true;
}
private void updateTownhallButton(
@ -384,16 +421,18 @@ public class MonitoringProctoringService {
this.pageService.firePageEvent(
new ActionActivationEvent(
false,
ActionDefinition.MONITOR_EXAM_OPEN_TOWNHALL_PROCTOR_ROOM,
ActionDefinition.MONITOR_EXAM_CLOSE_TOWNHALL_PROCTOR_ROOM),
new Tuple<>(
ActionDefinition.MONITOR_EXAM_OPEN_TOWNHALL_PROCTOR_ROOM,
ActionDefinition.MONITOR_EXAM_CLOSE_TOWNHALL_PROCTOR_ROOM)),
pageContext);
}
} else {
this.pageService.firePageEvent(
new ActionActivationEvent(
proctoringGUIService.getNumberOfProctoringParticipants() > 0,
ActionDefinition.MONITOR_EXAM_OPEN_TOWNHALL_PROCTOR_ROOM,
ActionDefinition.MONITOR_EXAM_CLOSE_TOWNHALL_PROCTOR_ROOM),
new Tuple<>(
ActionDefinition.MONITOR_EXAM_OPEN_TOWNHALL_PROCTOR_ROOM,
ActionDefinition.MONITOR_EXAM_OPEN_TOWNHALL_PROCTOR_ROOM)),
pageContext);
}
}

View file

@ -246,7 +246,9 @@ public class ExamAdminServiceImpl implements ExamAdminService {
EntityType.EXAM,
examId,
ProctoringServiceSettings.ATTR_APP_SECRET,
this.cryptor.encrypt(proctoringServiceSettings.appSecret).toString());
this.cryptor.encrypt(proctoringServiceSettings.appSecret)
.getOrThrow()
.toString());
return proctoringServiceSettings;
});

View file

@ -11,6 +11,7 @@ package ch.ethz.seb.sebserver.webservice.servicelayer.session;
import java.util.Collection;
import java.util.Map;
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.ProctoringRoomConnection;
import ch.ethz.seb.sebserver.gbl.model.session.ClientConnection;
@ -75,6 +76,8 @@ public interface ExamProctoringRoomService {
* @return Result refer to the RemoteProctoringRoom data or to an error when happened */
Result<RemoteProctoringRoom> getTownhallRoomData(final Long examId);
Result<EntityKey> closeTownhallRoom(Long examId);
/** Used to create a break out room for all active SEB clients given by the connectionTokens.
* This first notifies the underling proctoring specific service layer on room creation that will create a room
* on the meeting service if necessary. Then creating the room internally for holding data and tracking the new

View file

@ -22,6 +22,7 @@ import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import ch.ethz.seb.sebserver.gbl.Constants;
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.ExamStatus;
import ch.ethz.seb.sebserver.gbl.model.exam.ProctoringRoomConnection;
@ -323,6 +324,16 @@ public class ExamProctoringRoomServiceImpl implements ExamProctoringRoomService
return this.remoteProctoringRoomDAO.isTownhallRoomActive(examId);
}
@Override
public Result<EntityKey> closeTownhallRoom(final Long examId) {
if (isTownhallRoomActive(examId)) {
return this.remoteProctoringRoomDAO.getTownhallRoom(examId)
.flatMap(room -> this.remoteProctoringRoomDAO.deleteRoom(room.id));
}
return Result.ofRuntimeError("No active town-hall for exam: " + examId);
}
private void closeTownhall(
final Long examId,
final ProctoringServiceSettings proctoringSettings,

View file

@ -286,6 +286,9 @@ public class ExamProctoringController {
checkAccess(institutionId, examId);
return this.examProcotringRoomService
.openTownhallRoom(examId, subject)
.onError(error -> this.examProcotringRoomService.closeTownhallRoom(examId)
.onError(err -> log.error("Failed to close town-hall after failed opening: {}",
err.getMessage())))
.getOrThrow();
}

View file

@ -665,6 +665,13 @@ sebserver.exam.proctoring.type.servertype.JITSI_MEET.tooltip=Use a Jitsi Meet se
sebserver.exam.proctoring.type.servertype.ZOOM=Zoom Server
sebserver.exam.proctoring.type.servertype.ZOOM.tooltip=Use a Zoom meeting server for proctoring
sebserver.exam.proctoring.townhall.open.error=Failed to open the town-hall room.
sebserver.exam.proctoring.townhall.close.error=Failed to close the town-hall room properly.
sebserver.exam.proctoring.one.open.error=Failed to open the one-to-one room.
sebserver.exam.proctoring.one.close.error=Failed to close the one-to-one room properly.
sebserver.exam.proctoring.collecting.open.error=Failed to open the collecting room.
sebserver.exam.proctoring.collecting.close.error=Failed to close the collecting room properly.
################################
# Connection Configuration