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 2017/03/26 08:20:56 UTC

svn commit: r1788714 - in /openmeetings/application/trunk: openmeetings-core/src/main/java/org/apache/openmeetings/core/util/ openmeetings-web/src/main/java/org/apache/openmeetings/web/room/ openmeetings-web/src/main/java/org/apache/openmeetings/web/ro...

Author: solomax
Date: Sun Mar 26 08:20:55 2017
New Revision: 1788714

URL: http://svn.apache.org/viewvc?rev=1788714&view=rev
Log:
[OPENMEETINGS-551] tabs-scrolling, image file drop are added; code clean-up

Added:
    openmeetings/application/trunk/openmeetings-web/src/main/webapp/css/images/next.png   (with props)
    openmeetings/application/trunk/openmeetings-web/src/main/webapp/css/images/prev.png   (with props)
Modified:
    openmeetings/application/trunk/openmeetings-core/src/main/java/org/apache/openmeetings/core/util/WebSocketHelper.java
    openmeetings/application/trunk/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/RoomPanel.java
    openmeetings/application/trunk/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/RoomResourceReference.java
    openmeetings/application/trunk/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/sidebar/UploadDialog.java
    openmeetings/application/trunk/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/wb/WbPanel.html
    openmeetings/application/trunk/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/wb/WbPanel.java
    openmeetings/application/trunk/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/wb/wb.js
    openmeetings/application/trunk/openmeetings-web/src/main/java/org/apache/openmeetings/web/user/chat/Chat.java
    openmeetings/application/trunk/openmeetings-web/src/main/webapp/css/room.css

Modified: openmeetings/application/trunk/openmeetings-core/src/main/java/org/apache/openmeetings/core/util/WebSocketHelper.java
URL: http://svn.apache.org/viewvc/openmeetings/application/trunk/openmeetings-core/src/main/java/org/apache/openmeetings/core/util/WebSocketHelper.java?rev=1788714&r1=1788713&r2=1788714&view=diff
==============================================================================
--- openmeetings/application/trunk/openmeetings-core/src/main/java/org/apache/openmeetings/core/util/WebSocketHelper.java (original)
+++ openmeetings/application/trunk/openmeetings-core/src/main/java/org/apache/openmeetings/core/util/WebSocketHelper.java Sun Mar 26 08:20:55 2017
@@ -24,7 +24,7 @@ import java.io.IOException;
 import java.util.Arrays;
 import java.util.List;
 import java.util.function.BiConsumer;
-import java.util.function.Consumer;
+import java.util.function.BiFunction;
 import java.util.function.Function;
 import java.util.function.Predicate;
 
@@ -56,9 +56,9 @@ public class WebSocketHelper {
 	public static final String ID_ROOM_PREFIX = ID_TAB_PREFIX + "r";
 	public static final String ID_USER_PREFIX = ID_TAB_PREFIX + "u";
 
-	public static void sendClient(final Client c, byte[] b) {
-		if (c != null) {
-			send(a -> Arrays.asList(c), (t) -> {
+	public static void sendClient(final Client _c, byte[] b) {
+		if (_c != null) {
+			send(a -> Arrays.asList(_c), (t, c) -> {
 				try {
 					t.sendMessage(b, 0, b.length);
 				} catch (IOException e) {
@@ -70,11 +70,7 @@ public class WebSocketHelper {
 
 	public static void sendRoom(final RoomMessage m) {
 		log.debug("Sending WebSocket message: {} {}", m.getType(), m instanceof TextRoomMessage ? ((TextRoomMessage)m).getText() : "");
-		sendRoom(m.getRoomId(), (t) -> t.sendMessage(m), null);
-	}
-
-	public static void sendRoom(final Long roomId, final String m) {
-		sendRoom(roomId, m, null);
+		sendRoom(m.getRoomId(), (t, c) -> t.sendMessage(m), null);
 	}
 
 	private static JSONObject setScope(JSONObject o, ChatMessage m, long curUserId) {
@@ -115,16 +111,21 @@ public class WebSocketHelper {
 			.put("msg", arr);
 	}
 
+	public static void sendRoom(final Long roomId, final JSONObject m) {
+		sendRoom(roomId, m, null, null);
+	}
+
 	public static void sendRoom(ChatMessage m, JSONObject msg) {
-		sendRoom(m.getToRoom().getId(), msg.toString()
-				, c -> !m.isNeedModeration() || (m.isNeedModeration() && c.hasRight(Right.moderator)));
+		sendRoom(m.getToRoom().getId(), msg
+				, c -> !m.isNeedModeration() || (m.isNeedModeration() && c.hasRight(Right.moderator))
+				, null);
 	}
 
-	public static void sendRoom(final Long roomId, final String m, Predicate<Client> check) {
+	public static void sendRoom(final Long roomId, final JSONObject m, Predicate<Client> check, BiFunction<JSONObject, Client, String> func) {
 		log.debug("Sending WebSocket message: {}", m);
-		sendRoom(roomId, (t) -> {
+		sendRoom(roomId, (t, c) -> {
 			try {
-				t.sendMessage(m);
+				t.sendMessage(func == null ? m.toString() : func.apply(m, c));
 			} catch (IOException e) {
 				log.error("Error while broadcasting message to room", e);
 			}
@@ -132,7 +133,7 @@ public class WebSocketHelper {
 	}
 
 	public static void sendUser(final Long userId, final String m) {
-		send(a -> ((IApplication)a).getOmClients(userId), (t) -> {
+		send(a -> ((IApplication)a).getOmClients(userId), (t, c) -> {
 			try {
 				t.sendMessage(m);
 			} catch (IOException e) {
@@ -158,13 +159,13 @@ public class WebSocketHelper {
 		}
 	}
 
-	public static void sendRoom(final Long roomId, Consumer<IWebSocketConnection> consumer, Predicate<Client> check) {
+	public static void sendRoom(final Long roomId, BiConsumer<IWebSocketConnection, Client> consumer, Predicate<Client> check) {
 		send(a -> ((IApplication)a).getOmRoomClients(roomId), consumer, check);
 	}
 
 	public static void send(
 			final Function<Application, List<Client>> func
-			, Consumer<IWebSocketConnection> consumer
+			, BiConsumer<IWebSocketConnection, Client> consumer
 			, Predicate<Client> check)
 	{
 		Application app = Application.get(OpenmeetingsVariables.wicketApplicationName);
@@ -175,7 +176,7 @@ public class WebSocketHelper {
 			if (check == null || check.test(c)) {
 				final IWebSocketConnection wc = reg.getConnection(app, c.getSessionId(), new PageIdKey(c.getPageId()));
 				if (wc != null && wc.isOpen()) {
-					executor.run(() -> consumer.accept(wc));
+					executor.run(() -> consumer.accept(wc, c));
 				}
 			}
 		}

Modified: openmeetings/application/trunk/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/RoomPanel.java
URL: http://svn.apache.org/viewvc/openmeetings/application/trunk/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/RoomPanel.java?rev=1788714&r1=1788713&r2=1788714&view=diff
==============================================================================
--- openmeetings/application/trunk/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/RoomPanel.java (original)
+++ openmeetings/application/trunk/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/RoomPanel.java Sun Mar 26 08:20:55 2017
@@ -35,9 +35,6 @@ import java.util.Set;
 import java.util.UUID;
 
 import org.apache.directory.api.util.Strings;
-import org.apache.openmeetings.core.data.whiteboard.WhiteboardCache;
-import org.apache.openmeetings.core.remote.ConferenceLibrary;
-import org.apache.openmeetings.core.remote.red5.ScopeApplicationAdapter;
 import org.apache.openmeetings.core.util.WebSocketHelper;
 import org.apache.openmeetings.db.dao.calendar.AppointmentDao;
 import org.apache.openmeetings.db.dao.log.ConferenceLogDao;
@@ -66,7 +63,6 @@ import org.apache.openmeetings.web.room.
 import org.apache.openmeetings.web.room.menu.RoomMenuPanel;
 import org.apache.openmeetings.web.room.sidebar.RoomSidebar;
 import org.apache.openmeetings.web.room.wb.WbPanel;
-import org.apache.openmeetings.web.user.record.JpgRecordingResourceReference;
 import org.apache.wicket.AttributeModifier;
 import org.apache.wicket.Component;
 import org.apache.wicket.ajax.AbstractDefaultAjaxBehavior;
@@ -80,7 +76,6 @@ import org.apache.wicket.markup.head.OnD
 import org.apache.wicket.markup.head.PriorityHeaderItem;
 import org.apache.wicket.markup.html.WebMarkupContainer;
 import org.apache.wicket.protocol.ws.api.event.WebSocketPushPayload;
-import org.apache.wicket.request.mapper.parameter.PageParameters;
 import org.apache.wicket.request.resource.JavaScriptResourceReference;
 import org.apache.wicket.request.resource.ResourceReference;
 import org.red5.logging.Red5LoggerFactory;
@@ -165,7 +160,6 @@ public class RoomPanel extends BasePanel
 	private String sharingUser = null;
 	private String recordingUser = null;
 	private String publishingUser = null; //TODO add
-	private long activeWbId = -1;
 
 	public RoomPanel(String id, Room r) {
 		super(id);
@@ -200,14 +194,14 @@ public class RoomPanel extends BasePanel
 			@Override
 			public void onDrop(AjaxRequestTarget target, Component component) {
 				Object o = component.getDefaultModelObject();
-				if (activeWbId > -1 && o instanceof FileItem) {
+				if (wb.isVisible() && o instanceof FileItem) {
 					FileItem f = (FileItem)o;
 					if (sidebar.getFilesPanel().isSelected(f)) {
 						for (Entry<String, FileItem> e : sidebar.getFilesPanel().getSelected().entrySet()) {
-							sendFileToWb(e.getValue(), false);
+							wb.sendFileToWb(e.getValue(), false);
 						}
 					} else {
-						sendFileToWb(f, false);
+						wb.sendFileToWb(f, false);
 					}
 				}
 			}
@@ -630,6 +624,10 @@ public class RoomPanel extends BasePanel
 		return sidebar;
 	}
 
+	public WbPanel getWb() {
+		return wb;
+	}
+
 	public ActivitiesPanel getActivities() {
 		return activities;
 	}
@@ -645,30 +643,4 @@ public class RoomPanel extends BasePanel
 	public String getPublishingUser() {
 		return publishingUser;
 	}
-
-	public void sendFileToWb(FileItem fi, boolean clean) {
-		if (activeWbId > -1 && fi.getId() != null && FileItem.Type.Folder != fi.getType()) {
-			if (FileItem.Type.WmlFile == fi.getType()) {
-				getBean(ConferenceLibrary.class).sendToWhiteboard(getClient().getUid(), activeWbId, fi);
-			} else {
-				String url = null;
-				PageParameters pp = new PageParameters();
-				pp.add("id", fi.getId())
-					.add("ruid", getBean(WhiteboardCache.class).get(r.getId()).getUid());
-				switch (fi.getType()) {
-					case Video:
-						pp.add("preview", true);
-						url = urlFor(new RoomResourceReference(), pp).toString();
-						break;
-					case Recording:
-						url = urlFor(new JpgRecordingResourceReference(), pp).toString();
-						break;
-					default:
-						url = urlFor(new RoomResourceReference(), pp).toString();
-						break;
-				}
-				getBean(ScopeApplicationAdapter.class).sendToWhiteboard(getClient().getUid(), activeWbId, fi, url, clean);
-			}
-		}
-	}
 }

Modified: openmeetings/application/trunk/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/RoomResourceReference.java
URL: http://svn.apache.org/viewvc/openmeetings/application/trunk/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/RoomResourceReference.java?rev=1788714&r1=1788713&r2=1788714&view=diff
==============================================================================
--- openmeetings/application/trunk/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/RoomResourceReference.java (original)
+++ openmeetings/application/trunk/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/RoomResourceReference.java Sun Mar 26 08:20:55 2017
@@ -44,6 +44,8 @@ import org.apache.wicket.request.mapper.
 import org.apache.wicket.request.resource.IResource.Attributes;
 import org.apache.wicket.util.string.StringValue;
 
+import com.github.openjson.JSONObject;
+
 public class RoomResourceReference extends FileItemResourceReference<FileExplorerItem> {
 	private static final long serialVersionUID = 1L;
 	public static final String DEFAULT_NAME = "wb-room-file";
@@ -99,10 +101,12 @@ public class RoomResourceReference exten
 		}
 		FileExplorerItem f = getBean(FileExplorerItemDao.class).get(id);
 		String ruid = params.get("ruid").toString();
+		String wuid = params.get("wuid").toString();
 		Whiteboards wbs = getBean(WhiteboardCache.class).get(c.getRoomId());
-		if (!Strings.isEmpty(ruid) && ruid.equals(wbs.getUid())) {
+		if (!Strings.isEmpty(wuid) && !Strings.isEmpty(ruid) && ruid.equals(wbs.getUid())) {
 			for (Entry<Long, Whiteboard> e : wbs.getWhiteboards().entrySet()) {
-				if (e.getValue().getRoomItems().containsKey(f.getHash())) {
+				JSONObject file = e.getValue().getRoomItems().get(wuid);
+				if (file != null && f.getId().equals(file.optLong("fileId"))) {
 					return f; // item IS on WB
 				}
 			}
@@ -112,7 +116,7 @@ public class RoomResourceReference exten
 
 	protected File getFile(FileExplorerItem f, String ext) {
 		File file = f.getFile(ext);
-		if (!file.exists()) {
+		if (file == null || !file.exists()) {
 			file = new File(new File(getOmHome(), "default"), String.format("deleted.%s"
 					, FileItem.Type.Image == f.getType() ? EXTENSION_JPG : EXTENSION_SWF));
 		}

Modified: openmeetings/application/trunk/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/sidebar/UploadDialog.java
URL: http://svn.apache.org/viewvc/openmeetings/application/trunk/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/sidebar/UploadDialog.java?rev=1788714&r1=1788713&r2=1788714&view=diff
==============================================================================
--- openmeetings/application/trunk/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/sidebar/UploadDialog.java (original)
+++ openmeetings/application/trunk/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/sidebar/UploadDialog.java Sun Mar 26 08:20:55 2017
@@ -209,7 +209,7 @@ public class UploadDialog extends Abstra
 						form.error(getString("convert.errors.file"));
 					} else {
 						if (toWb.getModelObject()) {
-							room.sendFileToWb(f, clean);
+							room.getWb().sendFileToWb(f, clean);
 							clean = false;
 						}
 					}

Modified: openmeetings/application/trunk/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/wb/WbPanel.html
URL: http://svn.apache.org/viewvc/openmeetings/application/trunk/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/wb/WbPanel.html?rev=1788714&r1=1788713&r2=1788714&view=diff
==============================================================================
--- openmeetings/application/trunk/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/wb/WbPanel.html (original)
+++ openmeetings/application/trunk/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/wb/WbPanel.html Sun Mar 26 08:20:55 2017
@@ -23,7 +23,9 @@
 	<div class="tabs">
 		<div class="wb-tabbar ui-corner-all ui-widget-header">
 			<div class="add clickable om-icon big"></div>
+			<div class="prev clickable om-icon big"></div>
 			<div class="scroll-container"><ul class="scrollable"></ul></div>
+			<div class="next clickable om-icon big"></div>
 		</div>
 	</div>
 

Modified: openmeetings/application/trunk/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/wb/WbPanel.java
URL: http://svn.apache.org/viewvc/openmeetings/application/trunk/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/wb/WbPanel.java?rev=1788714&r1=1788713&r2=1788714&view=diff
==============================================================================
--- openmeetings/application/trunk/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/wb/WbPanel.java (original)
+++ openmeetings/application/trunk/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/wb/WbPanel.java Sun Mar 26 08:20:55 2017
@@ -25,15 +25,23 @@ import static org.apache.wicket.ajax.att
 
 import java.util.Arrays;
 import java.util.Map.Entry;
+import java.util.UUID;
 import java.util.function.Predicate;
 
 import org.apache.openmeetings.core.data.whiteboard.WhiteboardCache;
 import org.apache.openmeetings.core.util.WebSocketHelper;
+import org.apache.openmeetings.db.dao.file.FileExplorerItemDao;
+import org.apache.openmeetings.db.dao.record.RecordingDao;
 import org.apache.openmeetings.db.dto.room.Whiteboard;
+import org.apache.openmeetings.db.dto.room.Whiteboards;
 import org.apache.openmeetings.db.entity.basic.Client;
+import org.apache.openmeetings.db.entity.file.FileItem;
 import org.apache.openmeetings.db.entity.room.Room.Right;
+import org.apache.openmeetings.db.entity.room.Room.RoomElement;
 import org.apache.openmeetings.util.OmFileHelper;
 import org.apache.openmeetings.web.room.RoomPanel;
+import org.apache.openmeetings.web.room.RoomResourceReference;
+import org.apache.openmeetings.web.user.record.JpgRecordingResourceReference;
 import org.apache.wicket.ajax.AbstractDefaultAjaxBehavior;
 import org.apache.wicket.ajax.AjaxRequestTarget;
 import org.apache.wicket.behavior.AttributeAppender;
@@ -45,8 +53,10 @@ import org.apache.wicket.markup.head.Pri
 import org.apache.wicket.markup.html.list.ListItem;
 import org.apache.wicket.markup.html.list.ListView;
 import org.apache.wicket.markup.html.panel.Panel;
+import org.apache.wicket.request.mapper.parameter.PageParameters;
 import org.apache.wicket.request.resource.JavaScriptResourceReference;
 import org.apache.wicket.request.resource.ResourceReference;
+import org.apache.wicket.resource.FileSystemResourceReference;
 import org.apache.wicket.util.string.StringValue;
 
 import com.github.openjson.JSONArray;
@@ -80,7 +90,7 @@ public class WbPanel extends Panel {
 				JSONObject obj = sv.isEmpty() ? new JSONObject() : new JSONObject(sv.toString());
 				if (Action.createObj == a || Action.modifyObj == a) {
 					if ("pointer".equals(obj.getJSONObject("obj").getString("type"))) {
-						sendWbOthers(String.format("WbArea.%s(%s);", a.name(), obj.toString()));
+						sendWbOthers(String.format("WbArea.%s", a.name()), obj);
 						return;
 					}
 				}
@@ -91,7 +101,7 @@ public class WbPanel extends Panel {
 						case createWb:
 						{
 							Whiteboard wb = getBean(WhiteboardCache.class).add(roomId, rp.getClient().getUser().getLanguageId());
-							sendWbAll(getAddWbScript(wb.getId(), wb.getName()).toString());
+							sendWbAll("WbArea.add", getAddWbJson(wb.getId(), wb.getName()));
 						}
 							break;
 						case removeWb:
@@ -99,7 +109,7 @@ public class WbPanel extends Panel {
 							long _id = obj.optLong("id", -1);
 							Long id = _id < 0 ? null : _id;
 							getBean(WhiteboardCache.class).remove(roomId, id);
-							sendWbAll(String.format("WbArea.remove(%s);", id));
+							sendWbAll("WbArea.remove", new JSONObject().put("id", id));
 						}
 							break;
 						case createObj:
@@ -107,7 +117,7 @@ public class WbPanel extends Panel {
 							Whiteboard wb = getBean(WhiteboardCache.class).get(roomId).get(obj.getLong("wbId"));
 							JSONObject o = obj.getJSONObject("obj");
 							wb.put(o.getString("uid"), o);
-							sendWbOthers(String.format("WbArea.%s(%s);", a.name(), obj.toString()));
+							sendWbOthers(String.format("WbArea.%s", a.name()), obj);
 						}
 							break;
 						case modifyObj:
@@ -123,7 +133,7 @@ public class WbPanel extends Panel {
 									wb.put(_o.getString("uid"), _o);
 								}
 							}
-							sendWbOthers(String.format("WbArea.%s(%s);", a.name(), obj.toString()));
+							sendWbOthers(String.format("WbArea.%s", a.name()), obj);
 						}
 						case deleteObj:
 						{
@@ -132,7 +142,7 @@ public class WbPanel extends Panel {
 							for (int i = 0; i < arr.length(); ++i) {
 								wb.remove(arr.getString(i));
 							}
-							sendWbAll(String.format("WbArea.removeObj(%s);", obj.toString()));
+							sendWbAll("WbArea.removeObj", obj);
 						}
 							break;
 					}
@@ -148,19 +158,21 @@ public class WbPanel extends Panel {
 		this.rp = rp;
 		this.roomId = rp.getRoom().getId();
 		setOutputMarkupId(true);
-
-		getBean(WhiteboardCache.class).get(roomId).getWhiteboards();//TODO
-		add(new ListView<String>("clipart", Arrays.asList(OmFileHelper.getPublicClipartsDir().list())) {
-			private static final long serialVersionUID = 1L;
-
-			@Override
-			protected void populateItem(ListItem<String> item) {
-				String cls = String.format("clipart-%s", item.getIndex());
-				item.add(append("class", cls), append("data-mode", cls)
-						, new AttributeAppender("data-image", item.getModelObject()).setSeparator(""));
-			}
-		});
-		add(wbAction);
+		if (rp.getRoom().isHidden(RoomElement.Whiteboard)) {
+			setVisible(false);
+		} else {
+			add(new ListView<String>("clipart", Arrays.asList(OmFileHelper.getPublicClipartsDir().list())) {
+				private static final long serialVersionUID = 1L;
+
+				@Override
+				protected void populateItem(ListItem<String> item) {
+					String cls = String.format("clipart-%s", item.getIndex());
+					item.add(append("class", cls), append("data-mode", cls)
+							, new AttributeAppender("data-image", item.getModelObject()).setSeparator(""));
+				}
+			});
+			add(wbAction);
+		}
 	}
 
 	@Override
@@ -170,43 +182,53 @@ public class WbPanel extends Panel {
 		response.render(JavaScriptHeaderItem.forReference(WB_JS_REFERENCE));
 		response.render(new PriorityHeaderItem(getNamedFunction(FUNC_ACTION, wbAction, explicit(PARAM_ACTION), explicit(PARAM_OBJ))));
 		StringBuilder sb = new StringBuilder("WbArea.init();");
-		for (Entry<Long, Whiteboard> entry : getBean(WhiteboardCache.class).list(roomId, rp.getClient().getUser().getLanguageId())) {
+		WhiteboardCache cache = getBean(WhiteboardCache.class);
+		Whiteboards wbs = cache.get(roomId);
+		for (Entry<Long, Whiteboard> entry : cache.list(roomId, rp.getClient().getUser().getLanguageId())) {
 			sb.append(getAddWbScript(entry.getKey(), entry.getValue().getName()));
 			JSONArray arr = new JSONArray();
 			for (Entry<String, JSONObject> wbEntry : entry.getValue().getRoomItems().entrySet()) {
-				arr.put(wbEntry.getValue());
+				JSONObject o = wbEntry.getValue();
+				arr.put(addFileUrl(wbs.getUid(), o));
 			}
-			sb.append("WbArea.load(").append(new JSONObject().put("wbId", entry.getKey()).put("obj", arr).toString()).append(");");
+			sb.append("WbArea.load(").append(getObjWbJson(entry.getKey(), arr).toString()).append(");");
 		}
 		response.render(OnDomReadyHeaderItem.forScript(sb));
 	}
 
-	private void sendWbAll(CharSequence func) {
-		sendWb(func, null);
+	private void sendWbAll(CharSequence meth, JSONObject obj) {
+		sendWb(meth, obj, null);
 	}
 
-	private void sendWbOthers(CharSequence func) {
-		sendWb(func, c -> !rp.getClient().getUid().equals(c.getUid()));
+	private void sendWbOthers(CharSequence meth, JSONObject obj) {
+		sendWb(meth, obj, c -> !rp.getClient().getUid().equals(c.getUid()));
 	}
 
-	private void sendWb(CharSequence func, Predicate<Client> check) {
+	private void sendWb(CharSequence meth, JSONObject obj, Predicate<Client> check) {
 		WebSocketHelper.sendRoom(
 				roomId
 				, new JSONObject()
 						.put("type", "wb")
-						.put("func", func)
-						.toString()
 				, check
+				, (o, c) -> o.put("func", String.format("%s(%s);", meth, obj.toString())).toString()
 			);
 	}
 
+	private static JSONObject getObjWbJson(Long wbId, Object o) {
+		return new JSONObject()
+				.put("wbId", wbId)
+				.put(PARAM_OBJ, o);
+	}
+
+	private static JSONObject getAddWbJson(Long id, String name) {
+		return new JSONObject()
+				.put("id", id)
+				.put("name", name);
+	}
+
 	private static CharSequence getAddWbScript(Long id, String name) {
 		return new StringBuilder("WbArea.add(")
-				.append(new JSONObject()
-						.put("id", id)
-						.put("name", name)
-						.toString()
-						)
+				.append(getAddWbJson(id, name).toString())
 				.append(");");
 	}
 
@@ -226,4 +248,110 @@ public class WbPanel extends Panel {
 		}
 		return this;
 	}
+
+	private JSONObject addFileUrl(String ruid, JSONObject _file) {
+		try {
+		final long fid = _file.optLong("fileId", -1);
+			if (fid > 0) {
+				FileItem fi = FileItem.Type.Recording.name().equals(_file.optString("fileType"))
+						? getBean(RecordingDao.class).get(fid)
+						: getBean(FileExplorerItemDao.class).get(fid);
+				if (fi != null) {
+					return addFileUrl(ruid, _file, fi, rp.getClient());
+				}
+			}
+		} catch (Exception e) {
+			//no-op, non-file object
+		}
+		return _file;
+	}
+	private JSONObject addFileUrl(String ruid, JSONObject _file, FileItem fi, Client c) {
+		final PageParameters pp = new PageParameters();
+		final FileSystemResourceReference ref;
+		pp.add("id", fi.getId()).add("ruid", ruid).add("wuid", _file.optString("uid"));
+		switch (fi.getType()) {
+			case Video:
+				pp.add("preview", true);
+				ref = new RoomResourceReference();
+				break;
+			case Recording:
+				ref = new JpgRecordingResourceReference();
+				break;
+			default:
+				ref = new RoomResourceReference();
+				break;
+		}
+		return new JSONObject(_file, JSONObject.getNames(_file)).put("src", urlFor(ref, pp.add("uid", c.getUid())));  //FIXME TODO openjson 1.0.2
+	}
+
+	/*
+	 * OLD VERSION
+	 *
+	public void sendFileToWb(FileItem fi, boolean clean) {
+		if (wb.isVisible() && fi.getId() != null && FileItem.Type.Folder != fi.getType()) {
+			long activeWbId = -1;
+			if (FileItem.Type.WmlFile == fi.getType()) {
+				getBean(ConferenceLibrary.class).sendToWhiteboard(getClient().getUid(), activeWbId, fi);
+			} else {
+				String url = null;
+				PageParameters pp = new PageParameters();
+				pp.add("id", fi.getId())
+					.add("ruid", getBean(WhiteboardCache.class).get(r.getId()).getUid());
+				switch (fi.getType()) {
+					case Video:
+						pp.add("preview", true);
+						url = urlFor(new RoomResourceReference(), pp).toString();
+						break;
+					case Recording:
+						url = urlFor(new JpgRecordingResourceReference(), pp).toString();
+						break;
+					default:
+						url = urlFor(new RoomResourceReference(), pp).toString();
+						break;
+				}
+				getBean(ScopeApplicationAdapter.class).sendToWhiteboard(getClient().getUid(), activeWbId, fi, url, clean);
+			}
+		}
+	}
+	 */
+
+	public void sendFileToWb(FileItem fi, boolean clean) {
+		if (isVisible() && fi.getId() != null && FileItem.Type.Folder != fi.getType()) {
+			//FIXME TODO WmlFile special handling
+			Whiteboards wbs = getBean(WhiteboardCache.class).get(roomId);
+			String wuid = UUID.randomUUID().toString();
+			Whiteboard wb = wbs.getWhiteboards().values().iterator().next(); //TODO active
+			//FIXME TODO various types
+			JSONObject file = new JSONObject()
+					.put("fileId", fi.getId())
+					.put("fileType", fi.getType().name())
+					.put("type", "image")
+					.put("left", 0) //FIXME TODO constant
+					.put("top", 0) //FIXME TODO constant
+					.put("width", 800/*fi.getWidth()*/) //FIXME TODO check null
+					.put("height", 600/*fi.getHeight()*/) //FIXME TODO constant
+					//,"angle":32.86
+					//,"crossOrigin":""
+					.put("uid", wuid)
+					//,"filters":[]
+					//,"resizeFilters":[]
+					;
+			wb.put(wuid, file);
+			final String ruid = wbs.getUid();
+			WebSocketHelper.sendRoom(
+					roomId
+					, new JSONObject().put("type", "wb")
+					, null
+					, (o, c) -> {
+							return o.put("func", String.format("WbArea.%s(%s);"
+									, Action.createObj.name()
+									, getObjWbJson(wb.getId(), addFileUrl(ruid, file, fi, c)).toString()) //FIXME TODO openjson 1.0.2
+								).toString();
+						}
+					);
+		}
+	}
+
+	//FIXME TODO openjson 1.0.2
+	//private static class ObjectStringer
 }

Modified: openmeetings/application/trunk/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/wb/wb.js
URL: http://svn.apache.org/viewvc/openmeetings/application/trunk/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/wb/wb.js?rev=1788714&r1=1788713&r2=1788714&view=diff
==============================================================================
--- openmeetings/application/trunk/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/wb/wb.js (original)
+++ openmeetings/application/trunk/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/wb/wb.js Sun Mar 26 08:20:55 2017
@@ -597,6 +597,9 @@ var Wb = function() {
 		});
 	}
 
+	function toOmJson(o) {
+		return o.toJSON(['uid', 'fileId', 'fileType']);
+	}
 	//events
 	var wbObjCreatedHandler = function (o) {
 		var json = {};
@@ -606,7 +609,7 @@ var Wb = function() {
 				break;
 			default:
 				o.includeDefaultValues = false;
-				json = o.toJSON(['uid']);
+				json = toOmJson(o);
 				break;
 		}
 		wbAction('createObj', JSON.stringify({
@@ -628,10 +631,9 @@ var Wb = function() {
 	var objModifiedHandler = function (e) {
 		var o = e.target;
 		o.includeDefaultValues = false;
-		json = o.toJSON(['uid'])
 		wbAction('modifyObj', JSON.stringify({
 			wbId: wbId
-			, obj: o.toJSON(['uid'])
+			, obj: toOmJson(o)
 		}));
 		//console.log('Object Modified', o);
 	};
@@ -688,7 +690,7 @@ var Wb = function() {
 	};
 };
 var WbArea = (function() {
-	var container, area, tabs, self = {};
+	var container, area, tabs, scroll, self = {};
 
 	function getWbTabId(id) {
 		return "wb-tab-" + id;
@@ -773,6 +775,7 @@ var WbArea = (function() {
 	}
 	self.init = function() {
 		tabs = $('.room.wb.area .tabs').tabs();
+		scroll = tabs.find('.scroll-container');
 		tabs.find(".ui-tabs-nav").sortable({
 			axis: "x"
 			, stop: function() {
@@ -782,6 +785,12 @@ var WbArea = (function() {
 		tabs.find('.add.om-icon').click(function() {
 			wbAction('createWb');
 		});
+		tabs.find('.prev.om-icon').click(function() {
+			scroll.scrollLeft(scroll.scrollLeft() - 30);
+		});
+		tabs.find('.next.om-icon').click(function() {
+			scroll.scrollLeft(scroll.scrollLeft() + 30);
+		});
 		container = $(".room.wb.area");
 		area = container.find(".wb-area");
 		$(window).keyup(deleteHandler);
@@ -853,8 +862,8 @@ var WbArea = (function() {
 			_removeHandler(canvas, json.obj[i]);
 		}
 	};
-	self.remove = function(id) {
-		var tabId = getWbTabId(id);
+	self.remove = function(obj) {
+		var tabId = getWbTabId(obj.id);
 		tabs.find('li[aria-controls="' + tabId + '"]').remove();
 		$("#" + tabId).remove();
 		refreshTabs();

Modified: openmeetings/application/trunk/openmeetings-web/src/main/java/org/apache/openmeetings/web/user/chat/Chat.java
URL: http://svn.apache.org/viewvc/openmeetings/application/trunk/openmeetings-web/src/main/java/org/apache/openmeetings/web/user/chat/Chat.java?rev=1788714&r1=1788713&r2=1788714&view=diff
==============================================================================
--- openmeetings/application/trunk/openmeetings-web/src/main/java/org/apache/openmeetings/web/user/chat/Chat.java (original)
+++ openmeetings/application/trunk/openmeetings-web/src/main/java/org/apache/openmeetings/web/user/chat/Chat.java Sun Mar 26 08:20:55 2017
@@ -102,8 +102,7 @@ public class Chat extends Panel {
 					WebSocketHelper.sendRoom(roomId
 							, new JSONObject().put("type", "typing")
 									.put("active", type.indexOf("start") > -1)
-									.put("uid", getUid())
-									.toString());
+									.put("uid", getUid()));
 				}
 			} catch (Exception e) {
 				log.error("Unexpected exception while accepting chat message", e);

Added: openmeetings/application/trunk/openmeetings-web/src/main/webapp/css/images/next.png
URL: http://svn.apache.org/viewvc/openmeetings/application/trunk/openmeetings-web/src/main/webapp/css/images/next.png?rev=1788714&view=auto
==============================================================================
Binary file - no diff available.

Propchange: openmeetings/application/trunk/openmeetings-web/src/main/webapp/css/images/next.png
------------------------------------------------------------------------------
    svn:mime-type = application/octet-stream

Added: openmeetings/application/trunk/openmeetings-web/src/main/webapp/css/images/prev.png
URL: http://svn.apache.org/viewvc/openmeetings/application/trunk/openmeetings-web/src/main/webapp/css/images/prev.png?rev=1788714&view=auto
==============================================================================
Binary file - no diff available.

Propchange: openmeetings/application/trunk/openmeetings-web/src/main/webapp/css/images/prev.png
------------------------------------------------------------------------------
    svn:mime-type = application/octet-stream

Modified: openmeetings/application/trunk/openmeetings-web/src/main/webapp/css/room.css
URL: http://svn.apache.org/viewvc/openmeetings/application/trunk/openmeetings-web/src/main/webapp/css/room.css?rev=1788714&r1=1788713&r2=1788714&view=diff
==============================================================================
--- openmeetings/application/trunk/openmeetings-web/src/main/webapp/css/room.css (original)
+++ openmeetings/application/trunk/openmeetings-web/src/main/webapp/css/room.css Sun Mar 26 08:20:55 2017
@@ -177,6 +177,12 @@
 .room.wb.area .tools .om-icon.big.arrow {
 	background-image: url(images/arrow.png);
 }
+.room.wb.area .om-icon.big.next {
+	background-image: url(images/next.png);
+}
+.room.wb.area .om-icon.big.prev {
+	background-image: url(images/prev.png);
+}
 .room.sidebar.left .ui-tabs .ui-tabs-panel {
 	padding: 0;
 }
@@ -432,7 +438,8 @@
 	left: auto !important;
 }
 .wb-tabbar {
-	padding-left: 45px !important;
+	padding-left: 70px !important;
+	padding-right: 35px !important;
 }
 .wb-tabbar .scroll-container {
 	overflow: hidden;
@@ -440,8 +447,21 @@
 .wb-tabbar .scroll-container .scrollable, .wb-tabbar .scroll-container .scrollable li {
 	display: flex;
 }
+.wb-tabbar .add.disabled, .wb-tabbar .prev.disabled, .wb-tabbar .next.disabled {
+	opacity: .3;
+}
+.wb-tabbar .add, .wb-tabbar .prev, .wb-tabbar .next {
+	position: absolute;
+	top: 3px;
+}
 .wb-tabbar .add {
-	position: absolute; left: 2px;
+	left: 2px;
+}
+.wb-tabbar .prev {
+	left: 35px;
+}
+.wb-tabbar .next {
+	right: 2px;
 }
 .wb-tabbar li button {
 	width: 20px;