blob: 959ad3ae78a858c5035380c23d4b26a12562fc89 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License") + you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.openmeetings.web.room;
import static org.apache.openmeetings.util.OpenmeetingsVariables.webAppRootKey;
import static org.apache.openmeetings.web.app.Application.addUserToRoom;
import static org.apache.openmeetings.web.app.Application.getBean;
import static org.apache.openmeetings.web.app.Application.getRoomClients;
import static org.apache.openmeetings.web.app.WebSession.getDateFormat;
import static org.apache.openmeetings.web.app.WebSession.getUserId;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Calendar;
import org.apache.openmeetings.db.dao.basic.ConfigurationDao;
import org.apache.openmeetings.db.dao.calendar.AppointmentDao;
import org.apache.openmeetings.db.dao.user.UserDao;
import org.apache.openmeetings.db.entity.calendar.Appointment;
import org.apache.openmeetings.db.entity.calendar.MeetingMember;
import org.apache.openmeetings.db.entity.file.FileItem;
import org.apache.openmeetings.db.entity.room.Room;
import org.apache.openmeetings.db.entity.room.Room.Right;
import org.apache.openmeetings.db.entity.room.Room.RoomElement;
import org.apache.openmeetings.db.entity.room.RoomGroup;
import org.apache.openmeetings.db.entity.user.GroupUser;
import org.apache.openmeetings.db.entity.user.User;
import org.apache.openmeetings.db.util.AuthLevelUtil;
import org.apache.openmeetings.util.message.RoomMessage;
import org.apache.openmeetings.util.message.RoomMessage.Type;
import org.apache.openmeetings.util.message.TextRoomMessage;
import org.apache.openmeetings.web.app.Application;
import org.apache.openmeetings.web.app.Client;
import org.apache.openmeetings.web.app.WebSession;
import org.apache.openmeetings.web.common.BasePanel;
import org.apache.openmeetings.web.room.activities.ActivitiesPanel;
import org.apache.openmeetings.web.room.activities.Activity;
import org.apache.openmeetings.web.room.menu.RoomMenuPanel;
import org.apache.openmeetings.web.room.sidebar.RoomSidebar;
import org.apache.wicket.Component;
import org.apache.wicket.ajax.AbstractDefaultAjaxBehavior;
import org.apache.wicket.ajax.AjaxRequestTarget;
import org.apache.wicket.ajax.json.JSONArray;
import org.apache.wicket.ajax.json.JSONException;
import org.apache.wicket.ajax.json.JSONObject;
import org.apache.wicket.authroles.authorization.strategies.role.annotations.AuthorizeInstantiation;
import org.apache.wicket.core.request.handler.IPartialPageRequestHandler;
import org.apache.wicket.event.IEvent;
import org.apache.wicket.markup.head.IHeaderResponse;
import org.apache.wicket.markup.head.JavaScriptHeaderItem;
import org.apache.wicket.markup.head.OnDomReadyHeaderItem;
import org.apache.wicket.markup.head.PriorityHeaderItem;
import org.apache.wicket.markup.html.WebMarkupContainer;
import org.apache.wicket.protocol.ws.WebSocketSettings;
import org.apache.wicket.protocol.ws.api.IWebSocketConnection;
import org.apache.wicket.protocol.ws.api.event.WebSocketPushPayload;
import org.apache.wicket.protocol.ws.api.registry.IWebSocketConnectionRegistry;
import org.apache.wicket.protocol.ws.api.registry.PageIdKey;
import org.apache.wicket.protocol.ws.concurrent.Executor;
import org.apache.wicket.request.resource.JavaScriptResourceReference;
import org.apache.wicket.request.resource.ResourceReference;
import org.red5.logging.Red5LoggerFactory;
import org.slf4j.Logger;
import org.wicketstuff.whiteboard.WhiteboardBehavior;
import com.googlecode.wicket.jquery.core.JQueryBehavior;
import com.googlecode.wicket.jquery.core.Options;
import com.googlecode.wicket.jquery.ui.interaction.droppable.Droppable;
import com.googlecode.wicket.jquery.ui.widget.dialog.DialogButton;
import com.googlecode.wicket.jquery.ui.widget.dialog.DialogButtons;
import com.googlecode.wicket.jquery.ui.widget.dialog.DialogIcon;
import com.googlecode.wicket.jquery.ui.widget.dialog.MessageDialog;
@AuthorizeInstantiation("Room")
public class RoomPanel extends BasePanel {
private static final long serialVersionUID = 1L;
private static final Logger log = Red5LoggerFactory.getLogger(RoomPanel.class, webAppRootKey);
private static final String ACCESS_DENIED_ID = "access-denied";
private static final String EVENT_DETAILS_ID = "event-details";
public enum Action {
kick
, refresh
}
private final Room r;
private final WebMarkupContainer room = new WebMarkupContainer("roomContainer");
private final AbstractDefaultAjaxBehavior aab = new AbstractDefaultAjaxBehavior() {
private static final long serialVersionUID = 1L;
@Override
protected void respond(AjaxRequestTarget target) {
target.appendJavaScript("setHeight();");
//TODO SID etc
ConfigurationDao cfgDao = getBean(ConfigurationDao.class);
try {
URL url = new URL(cfgDao.getBaseUrl());
String path = url.getPath();
path = path.substring(1, path.indexOf('/', 2) + 1);
target.appendJavaScript(String.format("initVideo(%s);", new JSONObject()
.put("uid", getClient().getUid())
.put("audioOnly", r.isAudioOnly())
.put("SID", WebSession.getSid())
.put("interview", Room.Type.interview == r.getType())
//.put("protocol", cfgDao.getConfValue(CONFIG_FLASH_PROTOCOL, String.class, ""))
.put("host", url.getHost())
//.put("port", cfgDao.getConfValue(CONFIG_FLASH_PORT, String.class, ""))
.put("app", path + r.getId())
.put("labels", getStringLabels(448, 449, 450, 451, 758, 447, 52, 53, 1429, 1430, 775, 452, 767, 764, 765, 918, 54, 761, 762))
.toString()
));
broadcast(new RoomMessage(r.getId(), getUserId(), RoomMessage.Type.roomEnter));
getMainPage().getChat().roomEnter(r, target);
} catch (MalformedURLException e) {
log.error("Error while constructing room parameters", e);
}
}
};
private RedirectMessageDialog roomClosed;
private RoomMenuPanel menu;
private RoomSidebar sidebar;
private ActivitiesPanel activities;
private String sharingUser = null;
private String recordingUser = null;
private String publishingUser = null; //TODO add
public RoomPanel(String id, Room r) {
super(id);
this.r = r;
//TODO check here and set
//private String recordingUser = null;
//private String sharingUser = null;
//private String publishingUser = null;
}
@Override
protected void onInitialize() {
super.onInitialize();
Component accessDenied = new WebMarkupContainer(ACCESS_DENIED_ID).setVisible(false);
Component eventDetail = new WebMarkupContainer(EVENT_DETAILS_ID).setVisible(false);
room.add(menu = new RoomMenuPanel("menu", this));
Droppable<FileItem> wbArea = new Droppable<FileItem>("wb-area") {
private static final long serialVersionUID = 1L;
@Override
public void onConfigure(JQueryBehavior behavior) {
super.onConfigure(behavior);
behavior.setOption("hoverClass", Options.asString("ui-state-hover"));
behavior.setOption("accept", Options.asString(".recorditem, .fileitem"));
}
@Override
public void onDrop(AjaxRequestTarget target, Component component) {
Object o = component.getDefaultModelObject();
if (o instanceof FileItem) {
}
}
};
WebMarkupContainer wb = new WebMarkupContainer("whiteboard");
room.add(wbArea.add(wb.setOutputMarkupId(true)));
room.add(new WhiteboardBehavior("1", wb.getMarkupId(), null, null, null));
room.add(aab);
room.add(sidebar = new RoomSidebar("sidebar", this));
room.add(activities = new ActivitiesPanel("activities", this));
add(roomClosed = new RedirectMessageDialog("room-closed", "1098", r.isClosed(), r.getRedirectURL()));
if (r.isClosed()) {
room.setVisible(false);
} else if (getRoomClients(r.getId()).size() >= r.getNumberOfPartizipants()) {
accessDenied = new ExpiredMessageDialog(ACCESS_DENIED_ID, getString("99"), menu);
room.setVisible(false);
} else {
boolean allowed = false;
String deniedMessage = null;
if (r.isAppointment()) {
Appointment a = getBean(AppointmentDao.class).getByRoom(r.getId());
if (a != null && !a.isDeleted()) {
allowed = a.getOwner().getId().equals(getUserId());
log.debug("appointed room, isOwner ? " + allowed);
if (!allowed) {
for (MeetingMember mm : a.getMeetingMembers()) {
if (getUserId().equals(mm.getUser().getId())) {
allowed = true;
break;
}
}
}
if (allowed) {
Calendar c = WebSession.getCalendar();
if (c.getTime().after(a.getStart()) && c.getTime().before(a.getEnd())) {
eventDetail = new EventDetailDialog(EVENT_DETAILS_ID, a);
} else {
allowed = false;
deniedMessage = getString("1271") + String.format(" %s - %s", getDateFormat().format(a.getStart()), getDateFormat().format(a.getEnd()));
}
}
}
} else {
allowed = r.getIspublic() || (r.getOwnerId() != null && r.getOwnerId().equals(getUserId()));
log.debug("public ? " + r.getIspublic() + ", ownedId ? " + r.getOwnerId() + " " + allowed);
if (!allowed) {
User u = getBean(UserDao.class).get(getUserId());
for (RoomGroup ro : r.getRoomGroups()) {
for (GroupUser ou : u.getGroupUsers()) {
if (ro.getGroup().getId().equals(ou.getGroup().getId())) {
allowed = true;
break;
}
}
if (allowed) {
break;
}
}
}
}
if (!allowed) {
if (deniedMessage == null) {
deniedMessage = getString("1599");
}
accessDenied = new ExpiredMessageDialog(ACCESS_DENIED_ID, deniedMessage, menu);
room.setVisible(false);
}
}
add(room, accessDenied, eventDetail);
if (r.isWaitForRecording()) {
add(new MessageDialog("wait-for-recording", getString("1316"), getString("1315"), DialogButtons.OK, DialogIcon.LIGHT) {
private static final long serialVersionUID = 1L;
@Override
public void onConfigure(JQueryBehavior behavior) {
super.onConfigure(behavior);
behavior.setOption("autoOpen", true);
behavior.setOption("resizable", false);
}
@Override
public void onClose(IPartialPageRequestHandler handler, DialogButton button) {
}
});
} else {
add(new WebMarkupContainer("wait-for-recording").setVisible(false));
}
}
@Override
public void onEvent(IEvent<?> event) {
if (event.getPayload() instanceof WebSocketPushPayload) {
WebSocketPushPayload wsEvent = (WebSocketPushPayload) event.getPayload();
if (wsEvent.getMessage() instanceof RoomMessage) {
RoomMessage m = (RoomMessage)wsEvent.getMessage();
IPartialPageRequestHandler handler = wsEvent.getHandler();
switch (m.getType()) {
case pollCreated:
if (getUserId() != m.getUserId()) {
menu.pollCreated(handler);
}
break;
case recordingStoped:
//TODO check recordingUser == ((TextRoomMessage)m).getText();
recordingUser = null;
menu.update(handler);
break;
case recordingStarted:
{
recordingUser = ((TextRoomMessage)m).getText();
menu.update(handler);
}
break;
case sharingStoped:
//TODO check sharingUser == ((TextRoomMessage)m).getText();
sharingUser = null;
menu.update(handler);
break;
case sharingStarted:
{
sharingUser = ((TextRoomMessage)m).getText();
menu.update(handler);
}
break;
case rightUpdated:
sidebar.updateUsers(handler);
menu.update(handler);
break;
case roomEnter:
sidebar.updateUsers(handler);
menu.update(handler);
// TODO should this be fixed?
//activities.addActivity(new Activity(m, Activity.Type.roomEnter), handler);
break;
case roomExit:
//TODO check user/remove tab
sidebar.updateUsers(handler);
activities.add(new Activity(m, Activity.Type.roomExit), handler);
break;
case roomClosed:
handler.add(room.setVisible(false));
roomClosed.open(handler);
break;
case requestRightModerator:
activities.add(new Activity((TextRoomMessage)m, Activity.Type.reqRightModerator), handler);
break;
case requestRightWb:
activities.add(new Activity((TextRoomMessage)m, Activity.Type.reqRightWb), handler);
break;
case requestRightShare:
activities.add(new Activity((TextRoomMessage)m, Activity.Type.reqRightShare), handler);
break;
case requestRightRemote:
activities.add(new Activity((TextRoomMessage)m, Activity.Type.reqRightRemote), handler);
break;
case requestRightA:
activities.add(new Activity((TextRoomMessage)m, Activity.Type.reqRightA), handler);
break;
case requestRightAv:
activities.add(new Activity((TextRoomMessage)m, Activity.Type.reqRightAv), handler);
break;
case requestRightMute:
activities.add(new Activity((TextRoomMessage)m, Activity.Type.reqRightMute), handler);
break;
case requestRightExclusive:
activities.add(new Activity((TextRoomMessage)m, Activity.Type.reqRightExclusive), handler);
break;
case activityRemove:
{
TextRoomMessage tm = (TextRoomMessage)m;
activities.remove(tm.getText(), handler);
}
break;
}
}
}
super.onEvent(event);
}
private String getStringLabels(long... ids) {
JSONArray arr = new JSONArray();
try {
for (long id : ids) {
arr.put(new JSONObject().put("id", id).put("value", Application.getString(id)));
}
} catch (JSONException e) {
log.error("", e);
}
return arr.toString();
}
@Override
protected void onBeforeRender() {
super.onBeforeRender();
if (room.isVisible()) {
//We are setting initial rights here
Client c = getClient();
addUserToRoom(c.setRoomId(getRoom().getId()));
User u = getBean(UserDao.class).get(getUserId());
Right rr = AuthLevelUtil.getRoomRight(u, r, r.isAppointment() ? getBean(AppointmentDao.class).getByRoom(r.getId()) : null, getRoomClients(r.getId()).size());
if (rr != null) {
c.getRights().add(rr);
}
}
}
public static void broadcast(final RoomMessage m) {
WebSocketSettings settings = WebSocketSettings.Holder.get(Application.get());
IWebSocketConnectionRegistry reg = settings.getConnectionRegistry();
Executor executor = settings.getWebSocketPushMessageExecutor();
for (Client c : getRoomClients(m.getRoomId())) {
try {
final IWebSocketConnection wsConnection = reg.getConnection(Application.get(), c.getSessionId(), new PageIdKey(c.getPageId()));
if (wsConnection != null) {
executor.run(new Runnable() {
@Override
public void run() {
wsConnection.sendMessage(m);
}
});
}
} catch (Exception e) {
log.error("Error while broadcasting message to room", e);
}
}
}
public static boolean isModerator(long userId, long roomId) {
return hasRight(userId, roomId, Right.moderator);
}
public static boolean hasRight(long userId, long roomId, Right r) {
for (Client c : getRoomClients(roomId)) {
if (c.getUserId().equals(userId) && c.hasRight(r)) {
return true;
}
}
return false;
}
public static void sendRoom(long roomId, String msg) {
IWebSocketConnectionRegistry reg = WebSocketSettings.Holder.get(Application.get()).getConnectionRegistry();
for (Client c : getRoomClients(roomId)) {
try {
reg.getConnection(Application.get(), c.getSessionId(), new PageIdKey(c.getPageId())).sendMessage(msg);
} catch (Exception e) {
log.error("Error while sending message to room", e);
}
}
}
@Override
public void onMenuPanelLoad(IPartialPageRequestHandler handler) {
handler.add(getMainPage().getHeader().setVisible(false), getMainPage().getTopControls().setVisible(false));
if (r.isHidden(RoomElement.Chat)) {
getMainPage().getChat().toggle(handler, false);
}
handler.appendJavaScript("roomLoad();");
}
@Override
public void cleanup(IPartialPageRequestHandler handler) {
handler.add(getMainPage().getHeader().setVisible(true), getMainPage().getTopControls().setVisible(true));
if (r.isHidden(RoomElement.Chat)) {
getMainPage().getChat().toggle(handler, true);
}
handler.appendJavaScript("$(window).off('resize.openmeetings');");
RoomMenuPanel.roomExit(this);
getMainPage().getChat().roomExit(r, handler);
}
private static ResourceReference newResourceReference() {
return new JavaScriptResourceReference(RoomPanel.class, "room.js");
}
@Override
public void renderHead(IHeaderResponse response) {
super.renderHead(response);
response.render(new PriorityHeaderItem(JavaScriptHeaderItem.forReference(newResourceReference())));
if (room.isVisible()) {
response.render(OnDomReadyHeaderItem.forScript(aab.getCallbackScript()));
}
}
public void requestRight(AjaxRequestTarget target, Right right) {
RoomMessage.Type reqType = null;
switch (right) {
case moderator:
reqType = Type.requestRightModerator;
break;
case whiteBoard:
reqType = Type.requestRightWb;
break;
case share:
reqType = Type.requestRightWb;
break;
case audio:
reqType = Type.requestRightA;
break;
case exclusive:
reqType = Type.requestRightExclusive;
break;
case mute:
reqType = Type.requestRightMute;
break;
case remoteControl:
reqType = Type.requestRightRemote;
break;
case video:
reqType = Type.requestRightAv;
break;
default:
break;
}
if (reqType != null) {
RoomPanel.broadcast(new TextRoomMessage(getRoom().getId(), getUserId(), reqType, getClient().getUid()));
}
}
public void allowRight(AjaxRequestTarget target, Client client, Right... rights) {
for (Right right : rights) {
client.getRights().add(right);
}
broadcast(target, client);
}
public void denyRight(AjaxRequestTarget target, Client client, Right... rights) {
for (Right right : rights) {
client.getRights().remove(right);
}
broadcast(target, client);
}
public void broadcast(AjaxRequestTarget target, Client client) {
broadcast(new RoomMessage(getRoom().getId(), getUserId(), RoomMessage.Type.rightUpdated));
RoomBroadcaster.sendUpdatedClient(client);
}
public Room getRoom() {
return r;
}
public Client getClient() {
return getMainPage().getClient();
}
public boolean screenShareAllowed() {
Room r = getRoom();
return Room.Type.interview != r.getType() && !r.isHidden(RoomElement.ScreenSharing)
&& r.isAllowRecording() && getClient().hasRight(Right.share) && getSharingUser() == null;
}
public RoomSidebar getSidebar() {
return sidebar;
}
public ActivitiesPanel getActivities() {
return activities;
}
public String getSharingUser() {
return sharingUser;
}
public String getRecordingUser() {
return recordingUser;
}
public String getPublishingUser() {
return publishingUser;
}
}