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

View file

@ -371,15 +371,18 @@ public class MonitoringClientConnection implements TemplateComposer {
.call() .call()
.onError(error -> log.error("Failed to get ProctoringServiceSettings", error)) .onError(error -> log.error("Failed to get ProctoringServiceSettings", error))
.getOr(null); .getOr(null);
final ProctoringGUIService proctoringGUIService = currentUser.getProctoringGUIService();
if (procotringSettings != null && procotringSettings.enableProctoring) { if (procotringSettings != null && procotringSettings.enableProctoring) {
final ProctoringGUIService proctoringGUIService = this.resourceService
.getCurrentUser()
.getProctoringGUIService();
actionBuilder actionBuilder
.newAction(ActionDefinition.MONITOR_EXAM_CLIENT_CONNECTION_PROCTORING) .newAction(ActionDefinition.MONITOR_EXAM_CLIENT_CONNECTION_PROCTORING)
.withEntityKey(parentEntityKey) .withEntityKey(parentEntityKey)
.withExec(action -> this.monitoringProctoringService.openOneToOneRoom( .withExec(action -> this.monitoringProctoringService.openOneToOneRoom(
action, action,
connectionData, proctoringGUIService)) connectionData,
proctoringGUIService))
.noEventPropagation() .noEventPropagation()
.publish() .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.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.TemplateComposer; 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.ActionActivationEventListener;
import ch.ethz.seb.sebserver.gui.service.page.event.ActionPublishEventListener; import ch.ethz.seb.sebserver.gui.service.page.event.ActionPublishEventListener;
import ch.ethz.seb.sebserver.gui.service.page.event.PageEventListener; import ch.ethz.seb.sebserver.gui.service.page.event.PageEventListener;
@ -118,19 +119,7 @@ public class ActionPane implements TemplateComposer {
continue; continue;
} }
final PageAction action = (PageAction) actionItem.getData(ACTION_EVENT_CALL_KEY); de_activate_action_icon(event, parent, actionItem);
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);
}
} }
if (event.decoration != null) { if (event.decoration != null) {
@ -141,10 +130,33 @@ public class ActionPane implements TemplateComposer {
ActionPane.this.pageService.getPolyglotPageService().injectI18n( ActionPane.this.pageService.getPolyglotPageService().injectI18n(
actionItemToDecorate, event.decoration._2.title); 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( private TreeItem findAction(
final Map<String, Tree> actionTrees, final Map<String, Tree> actionTrees,
final Composite parent, 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 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 static final String ENTITY_LIST_TYPE = null;
private final I18nSupport i18nSupport; private final I18nSupport i18nSupport;
@ -312,6 +313,18 @@ public class PageContextImpl implements PageContext {
@Override @Override
public void notifyError(final LocTextKey message, final Exception error) { 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()); log.error("Unexpected GUI error notified: {}", error.getMessage());
final String errorMessage = message != null final String errorMessage = message != null

View file

@ -59,6 +59,19 @@ public class MonitoringProctoringService {
private static final Logger log = LoggerFactory.getLogger(MonitoringProctoringService.class); 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 = private static final LocTextKey EXAM_ROOM_NAME =
new LocTextKey("sebserver.monitoring.exam.proctoring.room.all.name"); new LocTextKey("sebserver.monitoring.exam.proctoring.room.all.name");
@ -109,7 +122,7 @@ public class MonitoringProctoringService {
final PageAction action) { final PageAction action) {
if (isTownhallRoomActive(action.getEntityKey().modelId)) { if (isTownhallRoomActive(action.getEntityKey().modelId)) {
closeTownhallRoom(proctoringGUIService, action); if (closeTownhallRoom(proctoringGUIService, action)) {
this.pageService.firePageEvent( this.pageService.firePageEvent(
new ActionActivationEvent( new ActionActivationEvent(
true, true,
@ -117,9 +130,12 @@ public class MonitoringProctoringService {
ActionDefinition.MONITOR_EXAM_OPEN_TOWNHALL_PROCTOR_ROOM, ActionDefinition.MONITOR_EXAM_OPEN_TOWNHALL_PROCTOR_ROOM,
ActionDefinition.MONITOR_EXAM_OPEN_TOWNHALL_PROCTOR_ROOM)), ActionDefinition.MONITOR_EXAM_OPEN_TOWNHALL_PROCTOR_ROOM)),
action.pageContext()); action.pageContext());
return action;
} else { } else {
openTownhallRoom(proctoringGUIService, action); action.pageContext().notifyError(CLOSE_TOWNHALL_ERROR, null);
}
} else {
if (openTownhallRoom(proctoringGUIService, action)) {
this.pageService.firePageEvent( this.pageService.firePageEvent(
new ActionActivationEvent( new ActionActivationEvent(
true, true,
@ -127,9 +143,12 @@ public class MonitoringProctoringService {
ActionDefinition.MONITOR_EXAM_OPEN_TOWNHALL_PROCTOR_ROOM, ActionDefinition.MONITOR_EXAM_OPEN_TOWNHALL_PROCTOR_ROOM,
ActionDefinition.MONITOR_EXAM_CLOSE_TOWNHALL_PROCTOR_ROOM)), ActionDefinition.MONITOR_EXAM_CLOSE_TOWNHALL_PROCTOR_ROOM)),
action.pageContext()); action.pageContext());
return action; } else {
action.pageContext().notifyError(OPEN_TOWNHALL_ERROR, null);
} }
} }
return action;
}
public void initCollectingRoomActions( public void initCollectingRoomActions(
final PageContext pageContext, final PageContext pageContext,
@ -219,6 +238,7 @@ public class MonitoringProctoringService {
final PageAction action, final PageAction action,
final ClientConnectionData connectionData) { final ClientConnectionData connectionData) {
try {
final String examId = action.getEntityKey().modelId; final String examId = action.getEntityKey().modelId;
final ProctoringServiceSettings proctoringSettings = this.pageService.getRestService() final ProctoringServiceSettings proctoringSettings = this.pageService.getRestService()
@ -265,6 +285,11 @@ public class MonitoringProctoringService {
.registerProctoringWindow(examId, room.name, room.name); .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; return action;
} }
@ -273,6 +298,8 @@ public class MonitoringProctoringService {
final ClientConnectionData connectionData, final ClientConnectionData connectionData,
final ProctoringGUIService proctoringGUIService) { final ProctoringGUIService proctoringGUIService) {
try {
final String connectionToken = connectionData.clientConnection.connectionToken; final String connectionToken = connectionData.clientConnection.connectionToken;
final String examId = action.getEntityKey().modelId; final String examId = action.getEntityKey().modelId;
@ -304,10 +331,16 @@ public class MonitoringProctoringService {
this.guiServiceInfo.getExternalServerURIBuilder().toUriString(), this.guiServiceInfo.getExternalServerURIBuilder().toUriString(),
this.remoteProctoringEndpoint); this.remoteProctoringEndpoint);
javaScriptExecutor.execute(script); 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);
}
return action; return action;
} }
private PageAction openTownhallRoom( private boolean openTownhallRoom(
final ProctoringGUIService proctoringGUIService, final ProctoringGUIService proctoringGUIService,
final PageAction action) { final PageAction action) {
@ -342,11 +375,13 @@ public class MonitoringProctoringService {
} catch (final Exception e) { } catch (final Exception e) {
log.error("Failed to open popup for town-hall room: ", e); log.error("Failed to open popup for town-hall room: ", e);
} return false;
return action;
} }
private PageAction closeTownhallRoom( return true;
}
private boolean closeTownhallRoom(
final ProctoringGUIService proctoringGUIService, final ProctoringGUIService proctoringGUIService,
final PageAction action) { final PageAction action) {
@ -360,8 +395,10 @@ public class MonitoringProctoringService {
} catch (final Exception e) { } catch (final Exception e) {
log.error("Failed to close proctoring town-hall room for exam: {}", examId); log.error("Failed to close proctoring town-hall room for exam: {}", examId);
return false;
} }
return action;
return true;
} }
private void updateTownhallButton( private void updateTownhallButton(
@ -384,16 +421,18 @@ public class MonitoringProctoringService {
this.pageService.firePageEvent( this.pageService.firePageEvent(
new ActionActivationEvent( new ActionActivationEvent(
false, false,
new Tuple<>(
ActionDefinition.MONITOR_EXAM_OPEN_TOWNHALL_PROCTOR_ROOM, ActionDefinition.MONITOR_EXAM_OPEN_TOWNHALL_PROCTOR_ROOM,
ActionDefinition.MONITOR_EXAM_CLOSE_TOWNHALL_PROCTOR_ROOM), ActionDefinition.MONITOR_EXAM_CLOSE_TOWNHALL_PROCTOR_ROOM)),
pageContext); pageContext);
} }
} else { } else {
this.pageService.firePageEvent( this.pageService.firePageEvent(
new ActionActivationEvent( new ActionActivationEvent(
proctoringGUIService.getNumberOfProctoringParticipants() > 0, proctoringGUIService.getNumberOfProctoringParticipants() > 0,
new Tuple<>(
ActionDefinition.MONITOR_EXAM_OPEN_TOWNHALL_PROCTOR_ROOM, ActionDefinition.MONITOR_EXAM_OPEN_TOWNHALL_PROCTOR_ROOM,
ActionDefinition.MONITOR_EXAM_CLOSE_TOWNHALL_PROCTOR_ROOM), ActionDefinition.MONITOR_EXAM_OPEN_TOWNHALL_PROCTOR_ROOM)),
pageContext); pageContext);
} }
} }

View file

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

View file

@ -11,6 +11,7 @@ package ch.ethz.seb.sebserver.webservice.servicelayer.session;
import java.util.Collection; import java.util.Collection;
import java.util.Map; 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.Exam;
import ch.ethz.seb.sebserver.gbl.model.exam.ProctoringRoomConnection; import ch.ethz.seb.sebserver.gbl.model.exam.ProctoringRoomConnection;
import ch.ethz.seb.sebserver.gbl.model.session.ClientConnection; 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 */ * @return Result refer to the RemoteProctoringRoom data or to an error when happened */
Result<RemoteProctoringRoom> getTownhallRoomData(final Long examId); 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. /** 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 * 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 * 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 org.springframework.stereotype.Service;
import ch.ethz.seb.sebserver.gbl.Constants; 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;
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.ProctoringRoomConnection; import ch.ethz.seb.sebserver.gbl.model.exam.ProctoringRoomConnection;
@ -323,6 +324,16 @@ public class ExamProctoringRoomServiceImpl implements ExamProctoringRoomService
return this.remoteProctoringRoomDAO.isTownhallRoomActive(examId); 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( private void closeTownhall(
final Long examId, final Long examId,
final ProctoringServiceSettings proctoringSettings, final ProctoringServiceSettings proctoringSettings,

View file

@ -286,6 +286,9 @@ public class ExamProctoringController {
checkAccess(institutionId, examId); checkAccess(institutionId, examId);
return this.examProcotringRoomService return this.examProcotringRoomService
.openTownhallRoom(examId, subject) .openTownhallRoom(examId, subject)
.onError(error -> this.examProcotringRoomService.closeTownhallRoom(examId)
.onError(err -> log.error("Failed to close town-hall after failed opening: {}",
err.getMessage())))
.getOrThrow(); .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=Zoom Server
sebserver.exam.proctoring.type.servertype.ZOOM.tooltip=Use a Zoom meeting server for proctoring 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 # Connection Configuration