You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@openmeetings.apache.org by so...@apache.org on 2014/05/06 20:39:13 UTC

svn commit: r1592834 - in /openmeetings/trunk/singlewebapp/openmeetings-web/src/main: java/org/apache/openmeetings/web/app/ java/org/apache/openmeetings/web/pages/ java/org/apache/openmeetings/web/pages/install/ java/org/apache/openmeetings/web/room/ j...

Author: solomax
Date: Tue May  6 18:39:13 2014
New Revision: 1592834

URL: http://svn.apache.org/r1592834
Log:
[OPENMEETINGS-896] basic user list is added

Added:
    openmeetings/trunk/singlewebapp/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Client.java
    openmeetings/trunk/singlewebapp/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/room.js
    openmeetings/trunk/singlewebapp/openmeetings-web/src/main/java/org/apache/openmeetings/web/user/chat.js
Modified:
    openmeetings/trunk/singlewebapp/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application.java
    openmeetings/trunk/singlewebapp/openmeetings-web/src/main/java/org/apache/openmeetings/web/pages/MainPage.java
    openmeetings/trunk/singlewebapp/openmeetings-web/src/main/java/org/apache/openmeetings/web/pages/install/InstallWizard.java
    openmeetings/trunk/singlewebapp/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/RoomPanel.html
    openmeetings/trunk/singlewebapp/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/RoomPanel.java
    openmeetings/trunk/singlewebapp/openmeetings-web/src/main/java/org/apache/openmeetings/web/user/ChatPanel.html
    openmeetings/trunk/singlewebapp/openmeetings-web/src/main/java/org/apache/openmeetings/web/user/ChatPanel.java
    openmeetings/trunk/singlewebapp/openmeetings-web/src/main/webapp/css/room.css
    openmeetings/trunk/singlewebapp/openmeetings-web/src/main/webapp/css/theme.css

Modified: openmeetings/trunk/singlewebapp/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application.java
URL: http://svn.apache.org/viewvc/openmeetings/trunk/singlewebapp/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application.java?rev=1592834&r1=1592833&r2=1592834&view=diff
==============================================================================
--- openmeetings/trunk/singlewebapp/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application.java (original)
+++ openmeetings/trunk/singlewebapp/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application.java Tue May  6 18:39:13 2014
@@ -26,8 +26,10 @@ import static org.red5.logging.Red5Logge
 import static org.springframework.web.context.WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE;
 import static org.springframework.web.context.support.WebApplicationContextUtils.getWebApplicationContext;
 
+import java.util.HashSet;
 import java.util.Map;
 import java.util.Set;
+import java.util.UUID;
 import java.util.concurrent.ConcurrentHashMap;
 
 import javax.servlet.ServletContext;
@@ -90,7 +92,9 @@ import ro.fortsoft.wicket.dashboard.web.
 public class Application extends AuthenticatedWebApplication implements IApplication {
 	private static final Logger log = getLogger(Application.class, webAppRootKey);
 	private static boolean isInstalled;
-	private static Map<Long, Set<String>> ONLINE_USERS = new ConcurrentHashMap<Long, Set<String>>();
+	private static Map<Long, Set<Client>> ONLINE_USERS = new ConcurrentHashMap<Long, Set<Client>>();
+	private static Map<Long, Set<Client>> ROOMS = new ConcurrentHashMap<Long, Set<Client>>();
+	//additional maps for faster searching should be created
 	private DashboardContext dashboardContext;
 	
 	@Override
@@ -196,26 +200,67 @@ public class Application extends Authent
 		return get().dashboardContext;
 	}
 	
-	public static void addOnlineUser(long userId, String sessionId) {
+	public static void addOnlineUser(Client client) {
+		long userId = client.getUserId();
 		if (!ONLINE_USERS.containsKey(userId)) {
-			ONLINE_USERS.put(userId, new ConcurrentHashSet<String>());
+			ONLINE_USERS.put(userId, new ConcurrentHashSet<Client>());
 		}
-		ONLINE_USERS.get(userId).add(sessionId);
+		ONLINE_USERS.get(userId).add(client);
 	}
 	
-	public static void removeOnlineUser(long userId, String sessionId) {
+	public static void removeOnlineUser(Client c) {
+		long userId = c.getUserId();
 		if (ONLINE_USERS.containsKey(userId)) {
-			Set<String> sessions = ONLINE_USERS.get(userId);
-			if (sessions.isEmpty()) {
+			Set<Client> clients = ONLINE_USERS.get(userId);
+			clients.remove(c);
+			if (clients.isEmpty()) {
 				ONLINE_USERS.remove(userId);
-			} else if (sessions.contains(sessionId)) {
-				if (sessions.size() > 1) {
-					sessions.remove(sessionId);
-				} else {
-					ONLINE_USERS.remove(userId);
+			}
+		}
+	}
+	
+	public static Client addUserToRoom(long roomId, int pageId) {
+		if (!ROOMS.containsKey(roomId)) {
+			ROOMS.put(roomId, new ConcurrentHashSet<Client>());
+		}
+		Client c = new Client(WebSession.get().getId(), pageId, WebSession.getUserId());
+		c.setUid(UUID.randomUUID().toString());
+		ROOMS.get(roomId).add(c);
+		return c;
+	}
+	
+	public static void removeUserFromRoom(long roomId, int pageId) {
+		removeUserFromRoom(roomId, new Client(WebSession.get().getId(), pageId, WebSession.getUserId()));
+	}
+	
+	public static Client removeUserFromRoom(long roomId, Client _c) {
+		if (ROOMS.containsKey(roomId)) {
+			Set<Client> clients = ROOMS.get(roomId);
+			for (Client c : clients) {
+				if (c.equals(_c)) {
+					clients.remove(c);
+					return c;
 				}
 			}
+			if (clients.isEmpty()) {
+				ROOMS.remove(roomId);
+			}
 		}
+		return _c;
+	}
+	
+	public static long getRoom(Client c) {
+		for (long roomId : ROOMS.keySet()) {
+			Set<Client> clients = ROOMS.get(roomId);
+			if (clients.contains(c)) {
+				return roomId;
+			}
+		}
+		return -1;
+	}
+	
+	public static Set<Client> getRoomUsers(long roomId) {
+		return ROOMS.containsKey(roomId) ? ROOMS.get(roomId) : new HashSet<Client>();
 	}
 	
 	public static boolean isUserOnline(long userId) {

Added: openmeetings/trunk/singlewebapp/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Client.java
URL: http://svn.apache.org/viewvc/openmeetings/trunk/singlewebapp/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Client.java?rev=1592834&view=auto
==============================================================================
--- openmeetings/trunk/singlewebapp/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Client.java (added)
+++ openmeetings/trunk/singlewebapp/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Client.java Tue May  6 18:39:13 2014
@@ -0,0 +1,113 @@
+/*
+ * 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.app;
+
+import java.io.Serializable;
+
+import org.apache.wicket.protocol.ws.api.registry.IKey;
+
+/**
+ * Temporary class, later will be merged with {@link org.apache.openmeetings.db.entity.room.Client}
+ * @author solomax
+ *
+ */
+public class Client implements Serializable {
+	private static final long serialVersionUID = 1L;
+
+	private String sessionId;
+	private int pageId;
+	private long userId;
+	private String uid;
+
+	public Client() {
+	}
+	
+	public Client(String sessionId, IKey key, long userId) {
+		this(sessionId, key.hashCode(), userId);
+	}
+	
+	public Client(String sessionId, int pageId, long userId) {
+		this.sessionId = sessionId;
+		this.pageId = pageId;
+		this.userId = userId;
+	}
+
+	public String getSessionId() {
+		return sessionId;
+	}
+
+	public void setSessionId(String sessionId) {
+		this.sessionId = sessionId;
+	}
+
+	public int getPageId() {
+		return pageId;
+	}
+
+	public void setPageId(int pageId) {
+		this.pageId = pageId;
+	}
+
+	public long getUserId() {
+		return userId;
+	}
+
+	public void setUserId(long userId) {
+		this.userId = userId;
+	}
+
+	public String getUid() {
+		return uid;
+	}
+
+	public void setUid(String uid) {
+		this.uid = uid;
+	}
+
+	@Override
+	public int hashCode() {
+		final int prime = 31;
+		int result = 1;
+		result = prime * result + pageId;
+		result = prime * result + ((sessionId == null) ? 0 : sessionId.hashCode());
+		result = prime * result + (int) (userId ^ (userId >>> 32));
+		return result;
+	}
+
+	@Override
+	public boolean equals(Object obj) {
+		if (this == obj)
+			return true;
+		if (obj == null)
+			return false;
+		if (getClass() != obj.getClass())
+			return false;
+		Client other = (Client) obj;
+		if (pageId != other.pageId)
+			return false;
+		if (sessionId == null) {
+			if (other.sessionId != null)
+				return false;
+		} else if (!sessionId.equals(other.sessionId))
+			return false;
+		if (userId != other.userId)
+			return false;
+		return true;
+	}
+}

Modified: openmeetings/trunk/singlewebapp/openmeetings-web/src/main/java/org/apache/openmeetings/web/pages/MainPage.java
URL: http://svn.apache.org/viewvc/openmeetings/trunk/singlewebapp/openmeetings-web/src/main/java/org/apache/openmeetings/web/pages/MainPage.java?rev=1592834&r1=1592833&r2=1592834&view=diff
==============================================================================
--- openmeetings/trunk/singlewebapp/openmeetings-web/src/main/java/org/apache/openmeetings/web/pages/MainPage.java (original)
+++ openmeetings/trunk/singlewebapp/openmeetings-web/src/main/java/org/apache/openmeetings/web/pages/MainPage.java Tue May  6 18:39:13 2014
@@ -37,12 +37,14 @@ import org.apache.openmeetings.db.dao.ba
 import org.apache.openmeetings.db.entity.basic.Naviglobal;
 import org.apache.openmeetings.db.entity.basic.Navimain;
 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.common.ConfirmableAjaxLink;
 import org.apache.openmeetings.web.common.menu.MainMenuItem;
 import org.apache.openmeetings.web.common.menu.MenuItem;
 import org.apache.openmeetings.web.common.menu.MenuPanel;
+import org.apache.openmeetings.web.room.RoomPanel;
 import org.apache.openmeetings.web.user.AboutDialog;
 import org.apache.openmeetings.web.user.ChatPanel;
 import org.apache.openmeetings.web.util.OmUrlFragment;
@@ -133,20 +135,26 @@ public class MainPage extends BaseInited
 			@Override
 			protected void onConnect(ConnectedMessage message) {
 				super.onConnect(message);
-				addOnlineUser(getUserId(), WebSession.get().getId());
-				log.debug("WebSocketBehavior::onConnect");
+				addOnlineUser(new Client(message.getSessionId(), message.getKey(), getUserId()));
+				log.debug(String.format("WebSocketBehavior::onConnect [session: %s, key: %s]", message.getSessionId(), message.getKey()));
 			}
 			
 			@Override
 			protected void onClose(ClosedMessage message) {
 				super.onClose(message);
-				removeOnlineUser(getUserId(), WebSession.get().getId());
-				log.debug("WebSocketBehavior::onClose");
+				Client _c = new Client(message.getSessionId(), message.getKey(), getUserId());
+				removeOnlineUser(_c);
+				log.debug(String.format("WebSocketBehavior::onClose [session: %s, key: %s]", message.getSessionId(), message.getKey()));
+				long roomId = Application.getRoom(_c);
+				if (roomId > 0) {
+					Client c = Application.removeUserFromRoom(roomId, _c);
+					RoomPanel.sendRoom(roomId, RoomPanel.removeUser(c));
+				}
 			}
 		});
 		//load preselected content
 		add(areaBehavior = new AbstractAjaxTimerBehavior(Duration.ONE_SECOND) {
-			private static final long serialVersionUID = -1551197896975384329L;
+			private static final long serialVersionUID = 1L;
 
 			@Override
 			protected void onTimer(AjaxRequestTarget target) {

Modified: openmeetings/trunk/singlewebapp/openmeetings-web/src/main/java/org/apache/openmeetings/web/pages/install/InstallWizard.java
URL: http://svn.apache.org/viewvc/openmeetings/trunk/singlewebapp/openmeetings-web/src/main/java/org/apache/openmeetings/web/pages/install/InstallWizard.java?rev=1592834&r1=1592833&r2=1592834&view=diff
==============================================================================
--- openmeetings/trunk/singlewebapp/openmeetings-web/src/main/java/org/apache/openmeetings/web/pages/install/InstallWizard.java (original)
+++ openmeetings/trunk/singlewebapp/openmeetings-web/src/main/java/org/apache/openmeetings/web/pages/install/InstallWizard.java Tue May  6 18:39:13 2014
@@ -83,7 +83,7 @@ public class InstallWizard extends Wizar
 	private Throwable th = null;
 	
 	public void initTzDropDown() {
-		paramsStep1.tzDropDown.init();
+		paramsStep1.tzDropDown.setOption();
 	}
 	
 	//onInit, applyState
@@ -424,10 +424,9 @@ public class InstallWizard extends Wizar
 	private final class TzDropDown extends WizardDropDown<String> {
 		private static final long serialVersionUID = 1L;
 
-		public void init() {
-			List<String> tzList = AVAILABLE_TIMEZONES;
+		public void setOption() {
 			String tzId = WebSession.get().getClientTZCode();
-			option = AVAILABLE_TIMEZONE_SET.contains(tzId) ? tzId : tzList.get(0);
+			option = AVAILABLE_TIMEZONE_SET.contains(tzId) ? tzId : AVAILABLE_TIMEZONES.get(0);
 		}
 		
 		public TzDropDown(String id) throws Exception {

Modified: openmeetings/trunk/singlewebapp/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/RoomPanel.html
URL: http://svn.apache.org/viewvc/openmeetings/trunk/singlewebapp/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/RoomPanel.html?rev=1592834&r1=1592833&r2=1592834&view=diff
==============================================================================
--- openmeetings/trunk/singlewebapp/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/RoomPanel.html (original)
+++ openmeetings/trunk/singlewebapp/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/RoomPanel.html Tue May  6 18:39:13 2014
@@ -20,7 +20,15 @@
 -->
 <html xmlns:wicket="http://wicket.apache.org">
 <wicket:panel>
-	<div wicket:id="menu"></div>
-	This is the room prototype
+	<div wicket:id="roomMenu"></div>
+	<div class="room sidebar left">
+		<div class="room top stub"></div>
+		<div class="user list">
+		</div>
+	</div>
+	<div class="room wb area">
+		<div class="room top stub"></div>
+		This is the room prototype
+	</div>
 </wicket:panel>
 </html>

Modified: openmeetings/trunk/singlewebapp/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/RoomPanel.java
URL: http://svn.apache.org/viewvc/openmeetings/trunk/singlewebapp/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/RoomPanel.java?rev=1592834&r1=1592833&r2=1592834&view=diff
==============================================================================
--- openmeetings/trunk/singlewebapp/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/RoomPanel.java (original)
+++ openmeetings/trunk/singlewebapp/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/RoomPanel.java Tue May  6 18:39:13 2014
@@ -18,24 +18,140 @@
  */
 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.getRoomUsers;
+
 import java.util.ArrayList;
 import java.util.List;
 
+import org.apache.openmeetings.db.dao.user.UserDao;
+import org.apache.openmeetings.db.entity.user.User;
+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.common.menu.MenuItem;
 import org.apache.openmeetings.web.common.menu.MenuPanel;
 import org.apache.openmeetings.web.common.menu.RoomMenuItem;
+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.behavior.AttributeAppender;
+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.protocol.ws.IWebSocketSettings;
+import org.apache.wicket.protocol.ws.api.registry.IWebSocketConnectionRegistry;
+import org.apache.wicket.protocol.ws.api.registry.PageIdKey;
+import org.apache.wicket.request.resource.JavaScriptResourceReference;
+import org.apache.wicket.request.resource.ResourceReference;
+import org.red5.logging.Red5LoggerFactory;
+import org.slf4j.Logger;
 
 public class RoomPanel extends BasePanel {
 	private static final long serialVersionUID = 1L;
+	private static final Logger log = Red5LoggerFactory.getLogger(RoomPanel.class, webAppRootKey);
+	private long roomId;
+	private Client c;
+	private AbstractDefaultAjaxBehavior aab = new AbstractDefaultAjaxBehavior() {
+		private static final long serialVersionUID = 1L;
 
-	public RoomPanel(String id, long roomId) {
+		@Override
+		protected void respond(AjaxRequestTarget target) {
+			target.appendJavaScript("roomMessage(" + userList(roomId, c) + ");");
+		}
+	};
+	
+	public RoomPanel(String id, long _roomId) {
 		super(id);
-		add(new MenuPanel("menu", getMenu()));
+		this.roomId = _roomId;
+		add(new MenuPanel("roomMenu", getMenu()));
+		add(aab, AttributeAppender.append("style", "height: 100%;")/*, new AbstractAjaxTimerBehavior(Duration.seconds(10)) {
+			private static final long serialVersionUID = 1L;
+
+			@Override
+			protected void onTimer(AjaxRequestTarget target) {
+				// re-request user list to avoid possible issues
+				//target.appendJavaScript("roomMessage(" + userList(roomId, c) + ");"); //TODO is it nessesary ??
+			}
+		}*/);
 	}
 
+	@Override
+	protected void onBeforeRender() {
+		super.onBeforeRender();
+		c = addUserToRoom(roomId, getPage().getPageId());
+		sendRoom(roomId, addUser(c));
+	}
+	
+	public static JSONObject getUser(Client c, boolean current) throws JSONException {
+		User u = getBean(UserDao.class).get(c.getUserId());
+		return new JSONObject()
+			.put("uid", c.getUid())
+			.put("id", u.getUser_id())
+			.put("firstname", u.getFirstname())
+			.put("lastname", u.getLastname())
+			.put("email", u.getAdresses() == null ? null : u.getAdresses().getEmail())
+			.put("current", current);
+	}
+	
+	public static String userList(long roomId, Client cur) {
+		try {
+			JSONArray ul = new JSONArray();
+			for (Client c : getRoomUsers(roomId)) {
+				ul.put(getUser(c, c.equals(cur)));
+			}
+			return new JSONObject()
+				.put("type", "room")
+				.put("msg", "users")
+				.put("timestamp", System.currentTimeMillis())
+				.put("users", ul).toString();
+		} catch (Exception e) {
+			log.error("Error while creating message", e);
+		}
+		return "{}";
+	}
+	
+	public static String addUser(Client c) {
+		try {
+			return new JSONObject()
+				.put("type", "room")
+				.put("msg", "addUser")
+				.put("user", getUser(c, false)).toString();
+		} catch (Exception e) {
+			log.error("Error while creating message", e);
+		}
+		return "{}";
+	}
+	
+	public static String removeUser(Client c) {
+		try {
+			return new JSONObject()
+				.put("type", "room")
+				.put("msg", "removeUser")
+				.put("uid", c.getUid()).toString();
+		} catch (Exception e) {
+			log.error("Error while creating message", e);
+		}
+		return "{}";
+	}
+	
+	public static void sendRoom(long roomId, String msg) {
+		IWebSocketConnectionRegistry reg = IWebSocketSettings.Holder.get(Application.get()).getConnectionRegistry();
+		for (Client c : getRoomUsers(roomId)) {
+			try {
+				reg.getConnection(Application.get(), c.getSessionId(), new PageIdKey(c.getPageId())).sendMessage(msg);
+			} catch (Exception e) {
+				log.error("Error while sending message", e);
+			}
+		}
+	}
+	
 	private List<MenuItem> getMenu() {
 		//TODO hide/show
 		List<MenuItem> menu = new ArrayList<MenuItem>();
@@ -73,5 +189,17 @@ public class RoomPanel extends BasePanel
 	public void cleanup(AjaxRequestTarget target) {
 		target.add(getMainPage().getHeader().setVisible(true), getMainPage().getMenu().setVisible(true)
 				, getMainPage().getTopLinks().setVisible(true));
+		sendRoom(roomId, removeUser(c));
+	}
+
+	private 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())));
+		response.render(OnDomReadyHeaderItem.forScript(aab.getCallbackScript()));
 	}
 }

Added: openmeetings/trunk/singlewebapp/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/room.js
URL: http://svn.apache.org/viewvc/openmeetings/trunk/singlewebapp/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/room.js?rev=1592834&view=auto
==============================================================================
--- openmeetings/trunk/singlewebapp/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/room.js (added)
+++ openmeetings/trunk/singlewebapp/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/room.js Tue May  6 18:39:13 2014
@@ -0,0 +1,76 @@
+/**
+ * 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.
+ */
+
+function getUserId(uid) { return 'user' + uid; }
+
+function addUser(u, uld) {
+	var s = u.firstname + ' ' + u.lastname;
+	var d = $('<div class="user ui-corner-all ui-widget-content"></div>').attr('id', getUserId(u.uid)).attr('data-id', u.id);
+	if (u.current) {
+		d.append($('<b></b>').text(s));
+	} else {
+		d.text(s);
+	}
+	uld.append(d);
+	//TODO add activity
+}
+
+function removeUser(id) {
+	$('#' + id).remove();
+	//TODO add activity
+}
+
+function roomMessage(m) {
+	if (m && m.type == "room") {
+		//TODO add timestamp support
+		switch (m.msg) {
+			case "users":
+				var uld = $('.user.list');
+				var ulist = [];
+				uld.children('[id^="user"]').each(function() {
+					ulist.push(this.id); 
+				});
+				for (var i = 0; i < m.users.length; ++i) {
+					var u = m.users[i];
+					var id = getUserId(u.uid);
+					if ($('#' + id).length == 0) {
+						addUser(u, uld);
+					} else {
+						var idx = ulist.indexOf(id);
+						if (idx > -1) {
+							ulist.splice(idx, 1);
+						}
+					}
+				}
+				for (var i = 0; i < ulist.length; ++i) {
+					removeUser(ulist[i]);
+				}
+				break;
+			case "addUser":
+				var id = getUserId(m.user.uid);
+				if ($('#' + id).length == 0) {
+					addUser(m.user, $('.user.list'));
+				}
+				break;
+			case "removeUser":
+				removeUser(getUserId(m.uid));
+				break;
+		}
+	}
+}

Modified: openmeetings/trunk/singlewebapp/openmeetings-web/src/main/java/org/apache/openmeetings/web/user/ChatPanel.html
URL: http://svn.apache.org/viewvc/openmeetings/trunk/singlewebapp/openmeetings-web/src/main/java/org/apache/openmeetings/web/user/ChatPanel.html?rev=1592834&r1=1592833&r2=1592834&view=diff
==============================================================================
--- openmeetings/trunk/singlewebapp/openmeetings-web/src/main/java/org/apache/openmeetings/web/user/ChatPanel.html (original)
+++ openmeetings/trunk/singlewebapp/openmeetings-web/src/main/java/org/apache/openmeetings/web/user/ChatPanel.html Tue May  6 18:39:13 2014
@@ -19,47 +19,19 @@
   
 -->
 <html xmlns:wicket="http://wicket.apache.org">
-	<wicket:head>
-		<script type="text/javascript">
-			function toggleChat() {
-				var chat = $('#chat');
-				$('#chat #controlBlock #control')
-					.removeClass('ui-icon-carat-1-' + (chat.height() < 20 ? 'n' : 's'))
-					.addClass('ui-icon-carat-1-' + (chat.height() < 20 ? 's' : 'n'));
-				chat.animate({ height: chat.height() < 20 ? "300px" : "16px" }, 1000);
-			}
-			function addChatMessageInternal(m) {
-				if (m && m.type == "chat") {
-					var msg = $('<div><span class="from">' + m.msg.from + '</span><span class="date">'
-							+ m.msg.sent + '</span>' + m.msg.message + '</div>');
-					$('#messageArea').append(msg);
-					msg[0].scrollIntoView();
-				}
-			}
-			function addChatMessage(m) {
-				if (m && m.type == "chat") {
-					addChatMessageInternal(m)
-					$('#messageArea').emoticonize();
-				}
-			}
-			Wicket.Event.subscribe("/websocket/message", function(jqEvent, msg) {
-				addChatMessage(jQuery.parseJSON(msg)); 
-			});
-		</script>
-	</wicket:head>
-	<wicket:panel>
-		<div id="chat">
-			<div id="controlBlock" onclick="toggleChat();"><div id="control" class="ui-icon ui-icon-carat-1-n sort-icon"></div></div>
-			<div wicket:id="messages"></div>
-			<form wicket:id="sendForm">
-				<div wicket:id="toolbarContainer"></div>
-				<table style="width: 100%">
-					<tr>
-						<td><div id="chatMessage" wicket:id="chatMessage"></div></td>
-						<td style="width: 50px"><div wicket:id="send"><wicket:ommessage key="220"/></div></td>
-					</tr>
-				</table>
-			</form>
-		</div>
-	</wicket:panel>
+<wicket:panel>
+	<div id="chat">
+		<div id="controlBlock" onclick="toggleChat();"><div id="control" class="ui-icon ui-icon-carat-1-n sort-icon"></div></div>
+		<div wicket:id="messages"></div>
+		<form wicket:id="sendForm">
+			<div wicket:id="toolbarContainer"></div>
+			<table style="width: 100%">
+				<tr>
+					<td><div id="chatMessage" wicket:id="chatMessage"></div></td>
+					<td style="width: 50px"><div wicket:id="send"><wicket:ommessage key="220"/></div></td>
+				</tr>
+			</table>
+		</form>
+	</div>
+</wicket:panel>
 </html>

Modified: openmeetings/trunk/singlewebapp/openmeetings-web/src/main/java/org/apache/openmeetings/web/user/ChatPanel.java
URL: http://svn.apache.org/viewvc/openmeetings/trunk/singlewebapp/openmeetings-web/src/main/java/org/apache/openmeetings/web/user/ChatPanel.java?rev=1592834&r1=1592833&r2=1592834&view=diff
==============================================================================
--- openmeetings/trunk/singlewebapp/openmeetings-web/src/main/java/org/apache/openmeetings/web/user/ChatPanel.java (original)
+++ openmeetings/trunk/singlewebapp/openmeetings-web/src/main/java/org/apache/openmeetings/web/user/ChatPanel.java Tue May  6 18:39:13 2014
@@ -36,13 +36,17 @@ import org.apache.wicket.ajax.json.JSONE
 import org.apache.wicket.ajax.json.JSONObject;
 import org.apache.wicket.behavior.Behavior;
 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.markup.html.form.Form;
 import org.apache.wicket.model.Model;
 import org.apache.wicket.protocol.ws.IWebSocketSettings;
 import org.apache.wicket.protocol.ws.api.IWebSocketConnection;
 import org.apache.wicket.protocol.ws.api.registry.IWebSocketConnectionRegistry;
+import org.apache.wicket.request.resource.JavaScriptResourceReference;
+import org.apache.wicket.request.resource.ResourceReference;
 import org.red5.logging.Red5LoggerFactory;
 import org.slf4j.Logger;
 
@@ -126,4 +130,14 @@ public class ChatPanel extends UserPanel
 					};
 				}));
 	}
+
+	private ResourceReference newResourceReference() {
+		return new JavaScriptResourceReference(ChatPanel.class, "chat.js");
+	}
+	
+	@Override
+	public void renderHead(IHeaderResponse response) {
+		super.renderHead(response);
+		response.render(new PriorityHeaderItem(JavaScriptHeaderItem.forReference(newResourceReference())));
+	}
 }

Added: openmeetings/trunk/singlewebapp/openmeetings-web/src/main/java/org/apache/openmeetings/web/user/chat.js
URL: http://svn.apache.org/viewvc/openmeetings/trunk/singlewebapp/openmeetings-web/src/main/java/org/apache/openmeetings/web/user/chat.js?rev=1592834&view=auto
==============================================================================
--- openmeetings/trunk/singlewebapp/openmeetings-web/src/main/java/org/apache/openmeetings/web/user/chat.js (added)
+++ openmeetings/trunk/singlewebapp/openmeetings-web/src/main/java/org/apache/openmeetings/web/user/chat.js Tue May  6 18:39:13 2014
@@ -0,0 +1,56 @@
+/**
+ * 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.
+ */
+$(function() {
+	Wicket.Event.subscribe("/websocket/message", function(jqEvent, msg) {
+		var m = jQuery.parseJSON(msg);
+		if (m) {
+			switch(m.type) {
+				case "chat":
+					addChatMessage(m);
+					break;
+				case "room":
+					if (typeof(roomMessage) == "function") {
+						roomMessage(m);
+					}
+					break;
+			}
+		}
+	});
+});
+function toggleChat() {
+	var chat = $('#chat');
+	$('#chat #controlBlock #control')
+		.removeClass('ui-icon-carat-1-' + (chat.height() < 20 ? 'n' : 's'))
+		.addClass('ui-icon-carat-1-' + (chat.height() < 20 ? 's' : 'n'));
+	chat.animate({ height: chat.height() < 20 ? "300px" : "16px" }, 1000);
+}
+function addChatMessageInternal(m) {
+	if (m && m.type == "chat") {
+		var msg = $('<div><span class="from">' + m.msg.from + '</span><span class="date">'
+				+ m.msg.sent + '</span>' + m.msg.message + '</div>');
+		$('#messageArea').append(msg);
+		msg[0].scrollIntoView();
+	}
+}
+function addChatMessage(m) {
+	if (m && m.type == "chat") {
+		addChatMessageInternal(m);
+		$('#messageArea').emoticonize();
+	}
+}

Modified: openmeetings/trunk/singlewebapp/openmeetings-web/src/main/webapp/css/room.css
URL: http://svn.apache.org/viewvc/openmeetings/trunk/singlewebapp/openmeetings-web/src/main/webapp/css/room.css?rev=1592834&r1=1592833&r2=1592834&view=diff
==============================================================================
--- openmeetings/trunk/singlewebapp/openmeetings-web/src/main/webapp/css/room.css (original)
+++ openmeetings/trunk/singlewebapp/openmeetings-web/src/main/webapp/css/room.css Tue May  6 18:39:13 2014
@@ -16,13 +16,36 @@
  * specific language governing permissions and limitations
  * under the License.
  */
+#roomMenu {
+	position: absolute;
+    top: 0;
+    width: 100%;
+}
+.room.top.stub {
+	height: 35px;
+}
 .room.container {
 	width: 100%;
 }
-
 .room.menu.exit {
 	padding-left: 20px;
 	background-image: url(images/exit_button.png);
 	background-repeat: no-repeat;
-	background-position-x: 5px;
+	background-position: 5px 5px;
+}
+.room.sidebar.left {
+	min-width: 200px;
+	width: 20%;
+	height: 100%;
+	float: left;
+}
+.room.wb.area {
+	width: 80%;
+	height: 100%;
+	float: left;
+}
+.room.sidebar.left .user.list .user {
+	height: 40px;
+	padding-left: 5px;
+    padding-top: 5px;
 }

Modified: openmeetings/trunk/singlewebapp/openmeetings-web/src/main/webapp/css/theme.css
URL: http://svn.apache.org/viewvc/openmeetings/trunk/singlewebapp/openmeetings-web/src/main/webapp/css/theme.css?rev=1592834&r1=1592833&r2=1592834&view=diff
==============================================================================
--- openmeetings/trunk/singlewebapp/openmeetings-web/src/main/webapp/css/theme.css (original)
+++ openmeetings/trunk/singlewebapp/openmeetings-web/src/main/webapp/css/theme.css Tue May  6 18:39:13 2014
@@ -28,6 +28,7 @@ html, body {
 	background-image: url(../public/themes/basic-theme/general/logo.png);
 	background-repeat: no-repeat;
 	padding-left: 150px;
+	margin-bottom: 10px;
 }
 #topLinks {
 	position: absolute;
@@ -45,9 +46,8 @@ html, body {
 	border-right: none;
 }
 
-#menu {
+#menu, #roomMenu {
 	background-color: #AFCBFB;
-	margin-top: 10px;
 }
 .ui-menubar .ui-menu {
 	width: 250px;
@@ -343,6 +343,9 @@ div.tableWrapperSmall {
 .widget td {
 	min-width: 150px;
 }
+#contents {
+	height: 100%;
+}
 #contents .dragbox-content {
 	min-height: 300px;
 	max-height: 300px;