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/04/30 17:29:26 UTC

[14/50] [abbrv] openmeetings git commit: [OPENMEETINGS-551] basic save is added, code clean-up

[OPENMEETINGS-551] basic save is added, code clean-up


Project: http://git-wip-us.apache.org/repos/asf/openmeetings/repo
Commit: http://git-wip-us.apache.org/repos/asf/openmeetings/commit/5394af08
Tree: http://git-wip-us.apache.org/repos/asf/openmeetings/tree/5394af08
Diff: http://git-wip-us.apache.org/repos/asf/openmeetings/diff/5394af08

Branch: refs/heads/master
Commit: 5394af089c42f56d8bd383609564ce08fbd4589b
Parents: 8a900e5
Author: Maxim Solodovnik <so...@apache.org>
Authored: Wed Apr 5 03:56:54 2017 +0000
Committer: Maxim Solodovnik <so...@apache.org>
Committed: Wed Apr 5 03:56:54 2017 +0000

----------------------------------------------------------------------
 .../db/dto/file/FileExplorerItemDTO.java        |  19 ++-
 .../openmeetings/db/dto/room/Whiteboard.java    |  11 ++
 .../web/common/AddFolderDialog.html             |  29 -----
 .../web/common/AddFolderDialog.java             | 107 ----------------
 .../openmeetings/web/common/NameDialog.html     |  29 +++++
 .../openmeetings/web/common/NameDialog.java     | 125 +++++++++++++++++++
 .../web/common/tree/FileTreePanel.java          |   6 +-
 .../web/room/sidebar/RoomFilePanel.java         |   4 +-
 .../web/room/sidebar/RoomSidebar.java           |   4 +-
 .../openmeetings/web/room/wb/WbPanel.html       |   2 +-
 .../openmeetings/web/room/wb/WbPanel.java       |  71 ++++++++---
 .../apache/openmeetings/web/room/wb/fabric.js   |  58 +++++----
 .../openmeetings/web/room/wb/fabric.min.js      |  18 +--
 .../org/apache/openmeetings/web/room/wb/wb.js   |  15 ++-
 .../web/user/profile/MessagesContactsPanel.java |   4 +-
 .../web/user/record/RecordingsPanel.java        |   4 +-
 .../src/main/webapp/WEB-INF/web.xml             |  12 +-
 17 files changed, 305 insertions(+), 213 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/openmeetings/blob/5394af08/openmeetings-db/src/main/java/org/apache/openmeetings/db/dto/file/FileExplorerItemDTO.java
----------------------------------------------------------------------
diff --git a/openmeetings-db/src/main/java/org/apache/openmeetings/db/dto/file/FileExplorerItemDTO.java b/openmeetings-db/src/main/java/org/apache/openmeetings/db/dto/file/FileExplorerItemDTO.java
index e384ad5..49f7b23 100644
--- a/openmeetings-db/src/main/java/org/apache/openmeetings/db/dto/file/FileExplorerItemDTO.java
+++ b/openmeetings-db/src/main/java/org/apache/openmeetings/db/dto/file/FileExplorerItemDTO.java
@@ -31,7 +31,7 @@ import org.apache.openmeetings.db.entity.file.FileItem.Type;
 
 /**
  * This Object will represent a File on the File-System
- * 
+ *
  * @author sebastianwagner
  *
  */
@@ -44,6 +44,7 @@ public class FileExplorerItemDTO implements Serializable {
 	private String hash;
 	private Long parentId;
 	private Long roomId;
+	private Long groupId;
 	private Long ownerId;
 	private Long size;
 	private String externalId;
@@ -60,6 +61,7 @@ public class FileExplorerItemDTO implements Serializable {
 		hash = f.getHash();
 		parentId = f.getParentId();
 		roomId = f.getRoomId();
+		groupId = f.getGroupId();
 		ownerId = f.getOwnerId();
 		size = f.getSize();
 		externalId = f.getExternalId();
@@ -68,7 +70,7 @@ public class FileExplorerItemDTO implements Serializable {
 		width = f.getWidth();
 		height = f.getHeight();
 	}
-	
+
 	public FileExplorerItem get() {
 		FileExplorerItem f = new FileExplorerItem();
 		f.setId(id);
@@ -76,6 +78,7 @@ public class FileExplorerItemDTO implements Serializable {
 		f.setHash(hash);
 		f.setParentId(parentId != null && parentId > 0 ? parentId : null);
 		f.setRoomId(roomId != null && roomId > 0 ? roomId : null);
+		f.setRoomId(groupId != null && groupId > 0 ? groupId : null);
 		f.setOwnerId(ownerId != null && ownerId > 0 ? ownerId : null);
 		f.setSize(size);
 		f.setExternalId(externalId);
@@ -85,11 +88,11 @@ public class FileExplorerItemDTO implements Serializable {
 		f.setHeight(height);
 		return f;
 	}
-	
+
 	public Long getId() {
 		return id;
 	}
-	
+
 	public void setId(Long id) {
 		this.id = id;
 	}
@@ -126,6 +129,14 @@ public class FileExplorerItemDTO implements Serializable {
 		this.roomId = roomId;
 	}
 
+	public Long getGroupId() {
+		return groupId;
+	}
+
+	public void setGroupId(Long groupId) {
+		this.groupId = groupId;
+	}
+
 	public Long getOwnerId() {
 		return ownerId;
 	}

http://git-wip-us.apache.org/repos/asf/openmeetings/blob/5394af08/openmeetings-db/src/main/java/org/apache/openmeetings/db/dto/room/Whiteboard.java
----------------------------------------------------------------------
diff --git a/openmeetings-db/src/main/java/org/apache/openmeetings/db/dto/room/Whiteboard.java b/openmeetings-db/src/main/java/org/apache/openmeetings/db/dto/room/Whiteboard.java
index c4a3df8..a11a800 100644
--- a/openmeetings-db/src/main/java/org/apache/openmeetings/db/dto/room/Whiteboard.java
+++ b/openmeetings-db/src/main/java/org/apache/openmeetings/db/dto/room/Whiteboard.java
@@ -140,4 +140,15 @@ public class Whiteboard {
 	public void setSlide(int slide) {
 		this.slide = slide;
 	}
+
+	public JSONObject toJson() {
+		//deep-copy
+		JSONObject json = new JSONObject(new JSONObject(this).toString());
+		json.remove("id"); //filtering
+		JSONObject items = json.getJSONObject("roomItems");
+		for (String uid : items.keySet()) {
+			items.getJSONObject(uid).remove("_src"); //filtering
+		}
+		return json;
+	}
 }

http://git-wip-us.apache.org/repos/asf/openmeetings/blob/5394af08/openmeetings-web/src/main/java/org/apache/openmeetings/web/common/AddFolderDialog.html
----------------------------------------------------------------------
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/common/AddFolderDialog.html b/openmeetings-web/src/main/java/org/apache/openmeetings/web/common/AddFolderDialog.html
deleted file mode 100644
index 5f9cdd0..0000000
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/common/AddFolderDialog.html
+++ /dev/null
@@ -1,29 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-  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.
-  
--->
-<html xmlns:wicket="http://wicket.apache.org">
-<wicket:panel>
-	<form wicket:id="form">
-		<span style="padding-left: 20px; padding-right: 20px;"><wicket:message key="572"/></span><input type="text" wicket:id="title"/>
-		<div wicket:id="feedback"></div>
-		<input type="submit" wicket:id="submit" class="invisible-form-component"/>
-	</form>
-</wicket:panel>
-</html>

http://git-wip-us.apache.org/repos/asf/openmeetings/blob/5394af08/openmeetings-web/src/main/java/org/apache/openmeetings/web/common/AddFolderDialog.java
----------------------------------------------------------------------
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/common/AddFolderDialog.java b/openmeetings-web/src/main/java/org/apache/openmeetings/web/common/AddFolderDialog.java
deleted file mode 100644
index 022ac8d..0000000
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/common/AddFolderDialog.java
+++ /dev/null
@@ -1,107 +0,0 @@
-/*
- * 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.common;
-
-import java.util.Arrays;
-import java.util.List;
-
-import org.apache.openmeetings.web.app.Application;
-import org.apache.wicket.ajax.AjaxRequestTarget;
-import org.apache.wicket.ajax.markup.html.form.AjaxButton;
-import org.apache.wicket.core.request.handler.IPartialPageRequestHandler;
-import org.apache.wicket.markup.html.form.Form;
-import org.apache.wicket.markup.html.form.RequiredTextField;
-import org.apache.wicket.markup.html.panel.FeedbackPanel;
-import org.apache.wicket.model.Model;
-
-import com.googlecode.wicket.jquery.ui.widget.dialog.AbstractFormDialog;
-import com.googlecode.wicket.jquery.ui.widget.dialog.DialogButton;
-
-public abstract class AddFolderDialog extends AbstractFormDialog<String> {
-	private static final long serialVersionUID = 1L;
-	private final DialogButton add = new DialogButton("add", Application.getString(1261));
-	private final DialogButton cancel = new DialogButton("cancel", Application.getString(219));
-	private final Form<String> form;
-	private final FeedbackPanel feedback = new FeedbackPanel("feedback");
-	private final String name;
-	private RequiredTextField<String> title;
-
-	public AddFolderDialog(String id) {
-		this(id, null);
-	}
-
-	public AddFolderDialog(String id, String name) {
-		super(id, Application.getString(1260), Model.of(name));
-		this.name = name;
-		form = new Form<String>("form", getModel()) {
-			private static final long serialVersionUID = 1L;
-			{
-				add(title = new RequiredTextField<>("title", getModel()));
-				title.setLabel(Model.of(Application.getString(572)));
-				add(feedback.setOutputMarkupId(true));
-				add(new AjaxButton("submit") { //FAKE button so "submit-on-enter" works as expected
-					private static final long serialVersionUID = 1L;
-
-					@Override
-					protected void onSubmit(AjaxRequestTarget target) {
-						AddFolderDialog.this.onSubmit(target);
-					}
-
-					@Override
-					protected void onError(AjaxRequestTarget target) {
-						AddFolderDialog.this.onError(target);
-					}
-				});
-			}
-		};
-		add(form.setOutputMarkupId(true));
-	}
-
-	@Override
-	protected void onOpen(IPartialPageRequestHandler handler) {
-		handler.add(form);
-		setModelObject(name);
-		getFeedbackMessages().clear();
-	}
-
-	@Override
-	protected void onSubmit(AjaxRequestTarget target) {
-		close(target, getSubmitButton());
-	}
-
-	@Override
-	protected List<DialogButton> getButtons() {
-		return Arrays.asList(add, cancel);
-	}
-
-	@Override
-	public DialogButton getSubmitButton() {
-		return add;
-	}
-
-	@Override
-	public Form<String> getForm() {
-		return form;
-	}
-
-	@Override
-	protected void onError(AjaxRequestTarget target) {
-		target.add(feedback);
-	}
-}

http://git-wip-us.apache.org/repos/asf/openmeetings/blob/5394af08/openmeetings-web/src/main/java/org/apache/openmeetings/web/common/NameDialog.html
----------------------------------------------------------------------
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/common/NameDialog.html b/openmeetings-web/src/main/java/org/apache/openmeetings/web/common/NameDialog.html
new file mode 100644
index 0000000..ef9e252
--- /dev/null
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/common/NameDialog.html
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  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.
+
+-->
+<html xmlns:wicket="http://wicket.apache.org">
+<wicket:panel>
+	<form wicket:id="form">
+		<label style="padding-left: 20px; padding-right: 20px;" wicket:id="label"></label><input type="text" wicket:id="title"/>
+		<div wicket:id="feedback"></div>
+		<input type="submit" wicket:id="submit" class="invisible-form-component"/>
+	</form>
+</wicket:panel>
+</html>

http://git-wip-us.apache.org/repos/asf/openmeetings/blob/5394af08/openmeetings-web/src/main/java/org/apache/openmeetings/web/common/NameDialog.java
----------------------------------------------------------------------
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/common/NameDialog.java b/openmeetings-web/src/main/java/org/apache/openmeetings/web/common/NameDialog.java
new file mode 100644
index 0000000..ab4c811
--- /dev/null
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/common/NameDialog.java
@@ -0,0 +1,125 @@
+/*
+ * 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.common;
+
+import java.util.Arrays;
+import java.util.List;
+
+import org.apache.openmeetings.web.app.Application;
+import org.apache.wicket.ajax.AjaxRequestTarget;
+import org.apache.wicket.ajax.markup.html.form.AjaxButton;
+import org.apache.wicket.core.request.handler.IPartialPageRequestHandler;
+import org.apache.wicket.markup.html.basic.Label;
+import org.apache.wicket.markup.html.form.Form;
+import org.apache.wicket.markup.html.form.RequiredTextField;
+import org.apache.wicket.model.Model;
+
+import com.googlecode.wicket.jquery.core.Options;
+import com.googlecode.wicket.jquery.ui.widget.dialog.AbstractFormDialog;
+import com.googlecode.wicket.jquery.ui.widget.dialog.DialogButton;
+import com.googlecode.wicket.kendo.ui.panel.KendoFeedbackPanel;
+
+public abstract class NameDialog extends AbstractFormDialog<String> {
+	private static final long serialVersionUID = 1L;
+	private final DialogButton add;
+	private final DialogButton cancel = new DialogButton("cancel", Application.getString(219));
+	private final Form<String> form;
+	private final KendoFeedbackPanel feedback = new KendoFeedbackPanel("feedback", new Options("button", true));
+	private final String name;
+	private RequiredTextField<String> title;
+
+	public NameDialog(String id) {
+		this(id, null);
+	}
+
+	public NameDialog(String id, String name) {
+		super(id, "", Model.of(name));
+		this.name = name;
+		add = new DialogButton("add", getAddStr());
+		form = new Form<>("form", getModel());
+	}
+
+	@Override
+	protected void onInitialize() {
+		super.onInitialize();
+		setTitle(Model.of(getTitleStr()));
+		form.add(new Label("label", getLabelStr())
+				, title = new RequiredTextField<>("title", getModel())
+				, feedback.setOutputMarkupId(true)
+				, new AjaxButton("submit") { //FAKE button so "submit-on-enter" works as expected
+					private static final long serialVersionUID = 1L;
+
+					@Override
+					protected void onSubmit(AjaxRequestTarget target) {
+						NameDialog.this.onSubmit(target);
+					}
+
+					@Override
+					protected void onError(AjaxRequestTarget target) {
+						NameDialog.this.onError(target);
+					}
+				});
+		title.setLabel(Model.of(getLabelStr()));
+		add(form.setOutputMarkupId(true));
+	}
+
+	@Override
+	protected void onOpen(IPartialPageRequestHandler handler) {
+		handler.add(form);
+		setModelObject(name);
+		getFeedbackMessages().clear();
+	}
+
+	@Override
+	protected void onSubmit(AjaxRequestTarget target) {
+		close(target, getSubmitButton());
+	}
+
+	@Override
+	protected List<DialogButton> getButtons() {
+		return Arrays.asList(add, cancel);
+	}
+
+	@Override
+	public DialogButton getSubmitButton() {
+		return add;
+	}
+
+	@Override
+	public Form<String> getForm() {
+		return form;
+	}
+
+	@Override
+	protected void onError(AjaxRequestTarget target) {
+		target.add(feedback);
+	}
+
+	protected String getTitleStr() {
+		return getString("1260");
+	}
+
+	protected String getLabelStr() {
+		return getString("572");
+	}
+
+	protected String getAddStr() {
+		return Application.getString("1261");
+	}
+}

http://git-wip-us.apache.org/repos/asf/openmeetings/blob/5394af08/openmeetings-web/src/main/java/org/apache/openmeetings/web/common/tree/FileTreePanel.java
----------------------------------------------------------------------
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/common/tree/FileTreePanel.java b/openmeetings-web/src/main/java/org/apache/openmeetings/web/common/tree/FileTreePanel.java
index a23cbd1..dff9122 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/common/tree/FileTreePanel.java
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/common/tree/FileTreePanel.java
@@ -38,7 +38,7 @@ import org.apache.openmeetings.db.entity.file.FileExplorerItem;
 import org.apache.openmeetings.db.entity.file.FileItem;
 import org.apache.openmeetings.db.entity.file.FileItem.Type;
 import org.apache.openmeetings.db.entity.record.Recording;
-import org.apache.openmeetings.web.common.AddFolderDialog;
+import org.apache.openmeetings.web.common.NameDialog;
 import org.apache.openmeetings.web.common.ConfirmableAjaxBorder;
 import org.apache.openmeetings.web.common.ConfirmableAjaxBorder.ConfirmableBorderDialog;
 import org.apache.openmeetings.web.util.AjaxDownload;
@@ -91,7 +91,7 @@ public abstract class FileTreePanel extends Panel {
 		}
 	};
 	private final Form<Void> form = new Form<>("form");
-	private final AddFolderDialog addFolder;
+	private final NameDialog addFolder;
 	private final ConfirmableBorderDialog trashConfirm;
 	private ConfirmableAjaxBorder trashBorder;
 	private final Long roomId;
@@ -106,7 +106,7 @@ public abstract class FileTreePanel extends Panel {
 	});
 	private final Component upload = new WebMarkupContainer("upload");
 
-	public FileTreePanel(String id, Long roomId, AddFolderDialog addFolder, ConfirmableBorderDialog trashConfirm) {
+	public FileTreePanel(String id, Long roomId, NameDialog addFolder, ConfirmableBorderDialog trashConfirm) {
 		super(id);
 		this.roomId = roomId;
 		this.addFolder = addFolder;

http://git-wip-us.apache.org/repos/asf/openmeetings/blob/5394af08/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/sidebar/RoomFilePanel.java
----------------------------------------------------------------------
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/sidebar/RoomFilePanel.java b/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/sidebar/RoomFilePanel.java
index c6aca8f..d215d71 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/sidebar/RoomFilePanel.java
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/sidebar/RoomFilePanel.java
@@ -26,7 +26,7 @@ import org.apache.openmeetings.db.dao.file.FileExplorerItemDao;
 import org.apache.openmeetings.db.dao.record.RecordingDao;
 import org.apache.openmeetings.db.dto.record.RecordingContainerData;
 import org.apache.openmeetings.db.entity.file.FileItem;
-import org.apache.openmeetings.web.common.AddFolderDialog;
+import org.apache.openmeetings.web.common.NameDialog;
 import org.apache.openmeetings.web.common.ConfirmableAjaxBorder.ConfirmableBorderDialog;
 import org.apache.openmeetings.web.common.tree.FileTreePanel;
 import org.apache.openmeetings.web.room.RoomPanel;
@@ -38,7 +38,7 @@ public class RoomFilePanel extends FileTreePanel {
 	private static final long serialVersionUID = 1L;
 	private final RoomPanel room;
 
-	public RoomFilePanel(String id, RoomPanel room, AddFolderDialog addFolder, ConfirmableBorderDialog trashConfirm) {
+	public RoomFilePanel(String id, RoomPanel room, NameDialog addFolder, ConfirmableBorderDialog trashConfirm) {
 		super(id, room.getRoom().getId(), addFolder, trashConfirm);
 		this.room = room;
 	}

http://git-wip-us.apache.org/repos/asf/openmeetings/blob/5394af08/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/sidebar/RoomSidebar.java
----------------------------------------------------------------------
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/sidebar/RoomSidebar.java b/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/sidebar/RoomSidebar.java
index cb0e6a5..b09e688 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/sidebar/RoomSidebar.java
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/sidebar/RoomSidebar.java
@@ -34,7 +34,7 @@ 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.web.app.Application;
-import org.apache.openmeetings.web.common.AddFolderDialog;
+import org.apache.openmeetings.web.common.NameDialog;
 import org.apache.openmeetings.web.common.ConfirmableAjaxBorder;
 import org.apache.openmeetings.web.common.ConfirmableAjaxBorder.ConfirmableBorderDialog;
 import org.apache.openmeetings.web.room.RoomBroadcaster;
@@ -97,7 +97,7 @@ public class RoomSidebar extends Panel {
 			item.add(new RoomClientPanel("user", item, room));
 		}
 	};
-	private final AddFolderDialog addFolder = new AddFolderDialog("addFolder", Application.getString(712)) {
+	private final NameDialog addFolder = new NameDialog("addFolder", Application.getString(712)) {
 		private static final long serialVersionUID = 1L;
 
 		@Override

http://git-wip-us.apache.org/repos/asf/openmeetings/blob/5394af08/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/wb/WbPanel.html
----------------------------------------------------------------------
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/wb/WbPanel.html b/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/wb/WbPanel.html
index f464953..e0eb3fa 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/wb/WbPanel.html
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/wb/WbPanel.html
@@ -61,7 +61,6 @@
 			<div wicket:message="title:62" class="ui-widget-header clickable om-icon big clear-all"></div>
 			<div wicket:message="title:1005" class="ui-widget-header clickable om-icon big clear-slide"></div>
 			<div wicket:message="title:197" class="ui-widget-header clickable om-icon big save"></div>
-<!-- save-as filename -->
 			<div wicket:message="title:70" class="ui-widget-header clickable om-icon big undo"></div>
 
 			<div wicket:message="title:72" class="ui-widget-header clickable om-icon big pointer"></div>
@@ -118,5 +117,6 @@
 			<wicket:message key="1359"/>
 		</div>
 	</div>
+	<div wicket:id="filename"></div>
 </wicket:panel>
 </html>

http://git-wip-us.apache.org/repos/asf/openmeetings/blob/5394af08/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/wb/WbPanel.java
----------------------------------------------------------------------
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/wb/WbPanel.java b/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/wb/WbPanel.java
index ad438ff..4f3762a 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/wb/WbPanel.java
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/wb/WbPanel.java
@@ -39,6 +39,8 @@ 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.app.Application;
+import org.apache.openmeetings.web.common.NameDialog;
 import org.apache.openmeetings.web.room.RoomPanel;
 import org.apache.openmeetings.web.room.RoomResourceReference;
 import org.apache.openmeetings.web.user.record.JpgRecordingResourceReference;
@@ -79,16 +81,18 @@ public class WbPanel extends Panel {
 	private boolean readOnly = true;
 	private final Long roomId;
 	private final RoomPanel rp;
+	private long wb2save = -1;
 	private enum Action {
 		createWb
 		, removeWb
-		, activeWb
+		, activateWb
 		, setSlide
 		, createObj
 		, modifyObj
 		, deleteObj
 		, clearAll
 		, clearSlide
+		, save
 	}
 	private final AbstractDefaultAjaxBehavior wbAction = new AbstractDefaultAjaxBehavior() {
 		private static final long serialVersionUID = 1L;
@@ -106,7 +110,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", a.name()), obj);
+						sendWbOthers(a, obj);
 						return;
 					}
 				}
@@ -117,7 +121,7 @@ public class WbPanel extends Panel {
 						case createWb:
 						{
 							Whiteboard wb = getBean(WhiteboardCache.class).add(roomId, rp.getClient().getUser().getLanguageId());
-							sendWbAll("WbArea.add", getAddWbJson(wb.getId(), wb.getName()));
+							sendWbAll(Action.createWb, getAddWbJson(wb.getId(), wb.getName()));
 						}
 							break;
 						case removeWb:
@@ -125,16 +129,16 @@ public class WbPanel extends Panel {
 							long _id = obj.optLong("id", -1);
 							Long id = _id < 0 ? null : _id;
 							getBean(WhiteboardCache.class).remove(roomId, id);
-							sendWbAll("WbArea.remove", new JSONObject().put("id", id));
+							sendWbAll(Action.removeWb, new JSONObject().put("id", id));
 						}
 							break;
-						case activeWb:
+						case activateWb:
 						{
 							long _id = obj.optLong("id", -1);
 							if (_id > -1) {
 								Whiteboards wbs = getBean(WhiteboardCache.class).get(roomId);
 								wbs.setActiveWb(_id);
-								sendWbAll("WbArea.activate", new JSONObject().put("id", _id));
+								sendWbAll(Action.activateWb, new JSONObject().put("id", _id));
 							}
 						}
 							break;
@@ -142,7 +146,7 @@ public class WbPanel extends Panel {
 						{
 							Whiteboard wb = getBean(WhiteboardCache.class).get(roomId).get(obj.getLong("wbId"));
 							wb.setSlide(obj.optInt("slide", 0));
-							sendWbOthers(String.format("WbArea.%s", a.name()), obj);
+							sendWbOthers(Action.setSlide, obj);
 						}
 							break;
 						case createObj:
@@ -150,7 +154,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", a.name()), obj);
+							sendWbOthers(Action.createObj, obj);
 						}
 							break;
 						case modifyObj:
@@ -166,7 +170,7 @@ public class WbPanel extends Panel {
 									wb.put(_o.getString("uid"), _o);
 								}
 							}
-							sendWbOthers(String.format("WbArea.%s", a.name()), obj);
+							sendWbOthers(Action.modifyObj, obj);
 						}
 						case deleteObj:
 						{
@@ -175,23 +179,27 @@ public class WbPanel extends Panel {
 							for (int i = 0; i < arr.length(); ++i) {
 								wb.remove(arr.getString(i));
 							}
-							sendWbAll("WbArea.removeObj", obj);
+							sendWbAll(Action.deleteObj, obj);
 						}
 							break;
 						case clearAll:
 						{
 							Whiteboard wb = getBean(WhiteboardCache.class).get(roomId).get(obj.getLong("wbId"));
 							wb.clear();
-							sendWbAll("WbArea.clearAll", obj);
+							sendWbAll(Action.clearAll, obj);
 						}
 							break;
 						case clearSlide:
 						{
 							Whiteboard wb = getBean(WhiteboardCache.class).get(roomId).get(obj.getLong("wbId"));
 							wb.entrySet().removeIf(e -> e.getValue().optInt("slide", -1) == obj.getInt("slide"));
-							sendWbAll("WbArea.clearSlide", obj);
+							sendWbAll(Action.clearSlide, obj);
 						}
 							break;
+						case save:
+							wb2save = obj.getLong("wbId");
+							fileName.open(target);
+							break;
 					}
 				}
 			} catch (Exception e) {
@@ -199,6 +207,30 @@ public class WbPanel extends Panel {
 			}
 		}
 	};
+	private final NameDialog fileName = new NameDialog("filename") {
+		private static final long serialVersionUID = 1L;
+
+		@Override
+		protected void onSubmit(AjaxRequestTarget target) {
+			Whiteboard wb = getBean(WhiteboardCache.class).get(roomId).get(wb2save);
+			wb.toJson();
+		}
+
+		@Override
+		protected String getTitleStr() {
+			return getString("199");
+		}
+
+		@Override
+		protected String getLabelStr() {
+			return getString("200");
+		}
+
+		@Override
+		protected String getAddStr() {
+			return Application.getString("203");
+		}
+	};
 
 	public WbPanel(String id, RoomPanel rp) {
 		super(id);
@@ -217,7 +249,7 @@ public class WbPanel extends Panel {
 					item.add(append("class", cls), append("data-mode", cls)
 							, new AttributeAppender("data-image", item.getModelObject()).setSeparator(""));
 				}
-			});
+			}, fileName);
 			add(wbAction);
 		}
 	}
@@ -242,25 +274,25 @@ public class WbPanel extends Panel {
 			}
 			sb.append("WbArea.load(").append(getObjWbJson(entry.getKey(), arr).toString()).append(");");
 		}
-		sb.append("WbArea.activate({id: ").append(wbs.getActiveWb()).append("});");
+		sb.append("WbArea.activateWb({id: ").append(wbs.getActiveWb()).append("});");
 		response.render(OnDomReadyHeaderItem.forScript(sb));
 	}
 
-	private void sendWbAll(CharSequence meth, JSONObject obj) {
+	private void sendWbAll(Action meth, JSONObject obj) {
 		sendWb(meth, obj, null);
 	}
 
-	private void sendWbOthers(CharSequence meth, JSONObject obj) {
+	private void sendWbOthers(Action meth, JSONObject obj) {
 		sendWb(meth, obj, c -> !rp.getClient().getUid().equals(c.getUid()));
 	}
 
-	private void sendWb(CharSequence meth, JSONObject obj, Predicate<Client> check) {
+	private void sendWb(Action meth, JSONObject obj, Predicate<Client> check) {
 		WebSocketHelper.sendRoom(
 				roomId
 				, new JSONObject()
 						.put("type", "wb")
 				, check
-				, (o, c) -> o.put("func", String.format("%s(%s);", meth, obj.toString())).toString()
+				, (o, c) -> o.put("func", String.format("WbArea.%s(%s);", meth.name(), obj.toString())).toString()
 			);
 	}
 
@@ -391,6 +423,9 @@ public class WbPanel extends Panel {
 					;
 			wb.put(wuid, file);
 			final String ruid = wbs.getUid();
+			if (clean) {
+				sendWbAll(Action.clearAll, new JSONObject().put("wbId", wb.getId()));
+			}
 			WebSocketHelper.sendRoom(
 					roomId
 					, new JSONObject().put("type", "wb")

http://git-wip-us.apache.org/repos/asf/openmeetings/blob/5394af08/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/wb/fabric.js
----------------------------------------------------------------------
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/wb/fabric.js b/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/wb/fabric.js
index f875a24..c9d71b0 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/wb/fabric.js
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/wb/fabric.js
@@ -1,8 +1,8 @@
 /* build: `node build.js modules=ALL exclude=json,gestures minifier=uglifyjs` */
  /*! Fabric.js Copyright 2008-2015, Printio (Juriy Zaytsev, Maxim Chernyak) */
-/* version 1.7.8 */
+
 /* Licensed MIT https://github.com/kangax/fabric.js/blob/master/LICENSE */
-var fabric = fabric || { version: "1.7.8" };
+var fabric = fabric || { version: "1.7.9" };
 if (typeof exports !== 'undefined') {
   exports.fabric = fabric;
 }
@@ -3804,8 +3804,10 @@ if (typeof console !== 'undefined') {
    * @param {Function} callback Callback to call when parsing is finished;
    * It's being passed an array of elements (parsed from a document).
    * @param {Function} [reviver] Method for further parsing of SVG elements, called after each fabric object created.
+   * @param {Object} [parsingOptions] options for parsing document
+   * @param {String} [parsingOptions.crossOrigin] crossOrigin settings
    */
-  fabric.parseSVGDocument = function(doc, callback, reviver) {
+  fabric.parseSVGDocument = function(doc, callback, reviver, parsingOptions) {
     if (!doc) {
       return;
     }
@@ -3815,7 +3817,7 @@ if (typeof console !== 'undefined') {
     var svgUid =  fabric.Object.__uid++,
         options = applyViewboxTransform(doc),
         descendants = fabric.util.toArray(doc.getElementsByTagName('*'));
-
+    options.crossOrigin = parsingOptions && parsingOptions.crossOrigin;
     options.svgUid = svgUid;
 
     if (descendants.length === 0 && fabric.isLikelyNode) {
@@ -3847,7 +3849,7 @@ if (typeof console !== 'undefined') {
       if (callback) {
         callback(instances, options);
       }
-    }, clone(options), reviver);
+    }, clone(options), reviver, parsingOptions);
   };
 
   var reFontDeclaration = new RegExp(
@@ -3999,8 +4001,8 @@ if (typeof console !== 'undefined') {
      * @param {Object} [options] Options object
      * @param {Function} [reviver] Method for further parsing of SVG elements, called after each fabric object created.
      */
-    parseElements: function(elements, callback, options, reviver) {
-      new fabric.ElementsParser(elements, callback, options, reviver).parse();
+    parseElements: function(elements, callback, options, reviver, parsingOptions) {
+      new fabric.ElementsParser(elements, callback, options, reviver, parsingOptions).parse();
     },
 
     /**
@@ -4126,8 +4128,10 @@ if (typeof console !== 'undefined') {
      * @param {String} url
      * @param {Function} callback
      * @param {Function} [reviver] Method for further parsing of SVG elements, called after each fabric object created.
+     * @param {Object} [options] Object containing options for parsing
+     * @param {String} [options.crossOrigin] crossOrigin crossOrigin setting to use for external resources
      */
-    loadSVGFromURL: function(url, callback, reviver) {
+    loadSVGFromURL: function(url, callback, reviver, options) {
 
       url = url.replace(/^\n\s*/, '').trim();
       new fabric.util.request(url, {
@@ -4148,9 +4152,9 @@ if (typeof console !== 'undefined') {
           callback && callback(null);
         }
 
-        fabric.parseSVGDocument(xml.documentElement, function (results, options) {
-          callback && callback(results, options);
-        }, reviver);
+        fabric.parseSVGDocument(xml.documentElement, function (results, _options) {
+          callback && callback(results, _options);
+        }, reviver, options);
       }
     },
 
@@ -4160,8 +4164,10 @@ if (typeof console !== 'undefined') {
      * @param {String} string
      * @param {Function} callback
      * @param {Function} [reviver] Method for further parsing of SVG elements, called after each fabric object created.
+     * @param {Object} [options] Object containing options for parsing
+     * @param {String} [options.crossOrigin] crossOrigin crossOrigin setting to use for external resources
      */
-    loadSVGFromString: function(string, callback, reviver) {
+    loadSVGFromString: function(string, callback, reviver, options) {
       string = string.trim();
       var doc;
       if (typeof DOMParser !== 'undefined') {
@@ -4177,21 +4183,22 @@ if (typeof console !== 'undefined') {
         doc.loadXML(string.replace(/<!DOCTYPE[\s\S]*?(\[[\s\S]*\])*?>/i, ''));
       }
 
-      fabric.parseSVGDocument(doc.documentElement, function (results, options) {
-        callback(results, options);
-      }, reviver);
+      fabric.parseSVGDocument(doc.documentElement, function (results, _options) {
+        callback(results, _options);
+      }, reviver, options);
     }
   });
 
 })(typeof exports !== 'undefined' ? exports : this);
 
 
-fabric.ElementsParser = function(elements, callback, options, reviver) {
+fabric.ElementsParser = function(elements, callback, options, reviver, parsingOptions) {
   this.elements = elements;
   this.callback = callback;
   this.options = options;
   this.reviver = reviver;
   this.svgUid = (options && options.svgUid) || 0;
+  this.parsingOptions = parsingOptions;
 };
 
 fabric.ElementsParser.prototype.parse = function() {
@@ -19223,7 +19230,7 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
    * @see {@link http://www.w3.org/TR/SVG/struct.html#ImageElement}
    */
   fabric.Image.ATTRIBUTE_NAMES =
-    fabric.SHARED_ATTRIBUTES.concat('x y width height preserveAspectRatio xlink:href'.split(' '));
+    fabric.SHARED_ATTRIBUTES.concat('x y width height preserveAspectRatio xlink:href crossOrigin'.split(' '));
 
   /**
    * Returns {@link fabric.Image} instance from an SVG element
@@ -24210,7 +24217,7 @@ fabric.Image.filters.BaseFilter.fromObject = function(object, callback) {
       }
 
       this.isEditing = true;
-
+      this.selected = true;
       this.initHiddenTextarea(e);
       this.hiddenTextarea.focus();
       this._updateTextarea();
@@ -24595,7 +24602,7 @@ fabric.Image.filters.BaseFilter.fromObject = function(object, callback) {
           }
         }
       }
-      var newStyle = style || currentLineStyles[charIndex - 1];
+      var newStyle = style || clone(currentLineStyles[charIndex - 1]);
       newStyle && (this.styles[lineIndex][charIndex] = newStyle);
       this._forceClearCache = true;
     },
@@ -24631,8 +24638,14 @@ fabric.Image.filters.BaseFilter.fromObject = function(object, callback) {
      * @param {Number} offset Can be -1 or +1
      */
     shiftLineStyles: function(lineIndex, offset) {
-      // shift all line styles by 1 upward
+      // shift all line styles by 1 upward or downward
       var clonedStyles = clone(this.styles);
+      for (var line in clonedStyles) {
+        var numericLine = parseInt(line, 10);
+        if (numericLine <= lineIndex) {
+          delete clonedStyles[numericLine];
+        }
+      }
       for (var line in this.styles) {
         var numericLine = parseInt(line, 10);
         if (numericLine > lineIndex) {
@@ -24847,7 +24860,6 @@ fabric.util.object.extend(fabric.IText.prototype, /** @lends fabric.IText.protot
         return;
       }
       var pointer = this.canvas.getPointer(options.e);
-
       this.__mousedownX = pointer.x;
       this.__mousedownY = pointer.y;
       this.__isMousedown = true;
@@ -25004,8 +25016,8 @@ fabric.util.object.extend(fabric.IText.prototype, /** @lends fabric.IText.protot
     this.hiddenTextarea = fabric.document.createElement('textarea');
     this.hiddenTextarea.setAttribute('autocapitalize', 'off');
     var style = this._calcTextareaPosition();
-    this.hiddenTextarea.style.cssText = 'position: absolute; top: ' + style.top + '; left: ' + style.left + ';'
-                                        + ' opacity: 0; width: 0px; height: 0px; z-index: -999;';
+    this.hiddenTextarea.style.cssText = 'white-space: nowrap; position: absolute; top: ' + style.top +
+      '; left: ' + style.left + '; opacity: 0; width: 1px; height: 1px; z-index: -999;';
     fabric.document.body.appendChild(this.hiddenTextarea);
 
     fabric.util.addListener(this.hiddenTextarea, 'keydown', this.onKeyDown.bind(this));