Repository: incubator-zeppelin
Updated Branches:
  refs/heads/master 4818f07ad -> 7f7a7c41a


Clone a  note

Adding ability to clone a note.
New button on top of note to clone it
![add_clone_button](https://cloud.githubusercontent.com/assets/5082742/9305313/065e988c-450e-11e5-96b0-8e2919ae10d7.png)

Dialog asking for a name for the new note(clone)
![clone_dialog](https://cloud.githubusercontent.com/assets/5082742/9305314/0d4705ee-450e-11e5-98c2-33cc0c619e4c.png)

The cloned note
![new_cloned_note](https://cloud.githubusercontent.com/assets/5082742/9305346/5326191a-450e-11e5-8ceb-fa5a50464b98.png)

Author: Karuppayya <[email protected]>
Author: Karuppayya <[email protected]>

Closes #219 from Karuppayya/clonenote and squashes the following commits:

5c560a9 [Karuppayya] merge master
de4c132 [Karuppayya] Incorporate review comments
39a0919 [Karuppayya] Minor bug fixes
12a9e45 [Karuppayya] Minor bug fixes
4f1f434 [Karuppayya] implement clone method in paragraph
39c0f46 [Karuppayya] Ability to clone a note


Project: http://git-wip-us.apache.org/repos/asf/incubator-zeppelin/repo
Commit: 
http://git-wip-us.apache.org/repos/asf/incubator-zeppelin/commit/7f7a7c41
Tree: http://git-wip-us.apache.org/repos/asf/incubator-zeppelin/tree/7f7a7c41
Diff: http://git-wip-us.apache.org/repos/asf/incubator-zeppelin/diff/7f7a7c41

Branch: refs/heads/master
Commit: 7f7a7c41af7df8fa784c7c83d581d0c1c1724d67
Parents: 4818f07
Author: Karuppayya <[email protected]>
Authored: Wed Aug 19 10:28:32 2015 +0530
Committer: Lee moon soo <[email protected]>
Committed: Wed Aug 19 21:22:00 2015 -0700

----------------------------------------------------------------------
 .../org/apache/zeppelin/socket/Message.java     |  3 ++
 .../apache/zeppelin/socket/NotebookServer.java  | 31 +++++++++++++++++++-
 .../src/app/notebook/notebook.controller.js     |  9 ++++++
 zeppelin-web/src/app/notebook/notebook.html     |  7 +++++
 .../noteName-create/note-name-dialog.html       | 15 ++++++----
 .../noteName-create/notename.controller.js      | 16 ++++++----
 .../noteName-create/visible.directive.js        |  5 +++-
 .../websocketEvents/websocketMsg.service.js     |  3 ++
 zeppelin-web/src/index.html                     |  4 ++-
 .../java/org/apache/zeppelin/notebook/Note.java | 11 +++++++
 .../org/apache/zeppelin/notebook/Paragraph.java | 21 +++++++++++--
 11 files changed, 110 insertions(+), 15 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-zeppelin/blob/7f7a7c41/zeppelin-server/src/main/java/org/apache/zeppelin/socket/Message.java
----------------------------------------------------------------------
diff --git 
a/zeppelin-server/src/main/java/org/apache/zeppelin/socket/Message.java 
b/zeppelin-server/src/main/java/org/apache/zeppelin/socket/Message.java
index 1784c53..f2b34aa 100644
--- a/zeppelin-server/src/main/java/org/apache/zeppelin/socket/Message.java
+++ b/zeppelin-server/src/main/java/org/apache/zeppelin/socket/Message.java
@@ -52,6 +52,9 @@ public class Message {
     NEW_NOTE, // [c-s] create new notebook
     DEL_NOTE, // [c-s] delete notebook
               // @param id note id
+    CLONE_NOTE, // [c-s] clone new notebook
+                // @param id id of note to clone
+                // @param name name fpor the cloned note
     NOTE_UPDATE,
 
     RUN_PARAGRAPH, // [c-s] run paragraph

http://git-wip-us.apache.org/repos/asf/incubator-zeppelin/blob/7f7a7c41/zeppelin-server/src/main/java/org/apache/zeppelin/socket/NotebookServer.java
----------------------------------------------------------------------
diff --git 
a/zeppelin-server/src/main/java/org/apache/zeppelin/socket/NotebookServer.java 
b/zeppelin-server/src/main/java/org/apache/zeppelin/socket/NotebookServer.java
index 8a02b53..5467fe6 100644
--- 
a/zeppelin-server/src/main/java/org/apache/zeppelin/socket/NotebookServer.java
+++ 
b/zeppelin-server/src/main/java/org/apache/zeppelin/socket/NotebookServer.java
@@ -24,6 +24,7 @@ import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
+
 import javax.servlet.http.HttpServletRequest;
 
 import org.apache.zeppelin.conf.ZeppelinConfiguration;
@@ -38,8 +39,8 @@ import org.apache.zeppelin.notebook.Note;
 import org.apache.zeppelin.notebook.Notebook;
 import org.apache.zeppelin.notebook.Paragraph;
 import org.apache.zeppelin.scheduler.Job;
-import org.apache.zeppelin.scheduler.JobListener;
 import org.apache.zeppelin.scheduler.Job.Status;
+import org.apache.zeppelin.scheduler.JobListener;
 import org.apache.zeppelin.server.ZeppelinServer;
 import org.apache.zeppelin.socket.Message.OP;
 import org.eclipse.jetty.websocket.WebSocket;
@@ -47,6 +48,7 @@ import org.eclipse.jetty.websocket.WebSocketServlet;
 import org.quartz.SchedulerException;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
+
 import com.google.common.base.Strings;
 import com.google.gson.Gson;
 /**
@@ -125,6 +127,9 @@ public class NotebookServer extends WebSocketServlet 
implements
           case DEL_NOTE:
             removeNote(conn, notebook, messagereceived);
             break;
+          case CLONE_NOTE:
+            cloneNote(conn, notebook, messagereceived);
+            break;
           case COMMIT_PARAGRAPH:
             updateParagraph(conn, notebook, messagereceived);
             break;
@@ -429,6 +434,30 @@ public class NotebookServer extends WebSocketServlet 
implements
     note.persist();
     broadcast(note.id(), new Message(OP.PARAGRAPH).put("paragraph", p));
   }
+  
+  private void cloneNote(NotebookSocket conn, Notebook notebook, Message 
fromMessage)
+      throws IOException, CloneNotSupportedException {
+    String noteId = getOpenNoteId(conn);
+    String name = (String) fromMessage.get("name");
+    Note sourceNote = notebook.getNote(noteId);
+    Note newNote = notebook.createNote();
+    if (name != null) {
+      newNote.setName(name);
+    }
+    // Copy the interpreter bindings
+    List<String> boundInterpreterSettingsIds = notebook
+        .getBindedInterpreterSettingsIds(sourceNote.id());
+    notebook.bindInterpretersToNote(newNote.id(), boundInterpreterSettingsIds);
+
+    List<Paragraph> paragraphs = sourceNote.getParagraphs();
+    for (Paragraph para : paragraphs) {
+      Paragraph p = (Paragraph) para.clone();
+      newNote.addParagraph(p);
+    }
+    newNote.persist();
+    broadcastNote(newNote);
+    broadcastNoteList();
+  }
 
   private void removeParagraph(NotebookSocket conn, Notebook notebook,
       Message fromMessage) throws IOException {

http://git-wip-us.apache.org/repos/asf/incubator-zeppelin/blob/7f7a7c41/zeppelin-web/src/app/notebook/notebook.controller.js
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/app/notebook/notebook.controller.js 
b/zeppelin-web/src/app/notebook/notebook.controller.js
index 0eb4b77..07d8325 100644
--- a/zeppelin-web/src/app/notebook/notebook.controller.js
+++ b/zeppelin-web/src/app/notebook/notebook.controller.js
@@ -70,6 +70,15 @@ angular.module('zeppelinWebApp').controller('NotebookCtrl', 
function($scope, $ro
     }
   };
 
+  //Clone note
+  $scope.cloneNote = function(noteId) {
+    var result = confirm('Do you want to clone this notebook?');
+    if (result) {
+      websocketMsgSrv.cloneNotebook(noteId);
+      $location.path('/#');
+    }
+  };
+  
   $scope.runNote = function() {
     var result = confirm('Run all paragraphs?');
     if (result) {

http://git-wip-us.apache.org/repos/asf/incubator-zeppelin/blob/7f7a7c41/zeppelin-web/src/app/notebook/notebook.html
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/app/notebook/notebook.html 
b/zeppelin-web/src/app/notebook/notebook.html
index d618057..5079685 100644
--- a/zeppelin-web/src/app/notebook/notebook.html
+++ b/zeppelin-web/src/app/notebook/notebook.html
@@ -47,6 +47,13 @@ limitations under the License.
                     ng-hide="viewOnly"
                     tooltip-placement="top" tooltip="Remove the notebook">
               <i class="icon-trash"></i></button>
+            <button type="button"
+                    class="btn btn-default btn-xs"
+                    ng-hide="viewOnly"
+                    tooltip-placement="top" tooltip="Clone the notebook"
+                    data-toggle="modal" data-target="#noteNameModal" 
data-clone="true"
+                    >
+              <i class="fa fa-copy"></i></button>
           </span>
 
           <span ng-hide="viewOnly">

http://git-wip-us.apache.org/repos/asf/incubator-zeppelin/blob/7f7a7c41/zeppelin-web/src/components/noteName-create/note-name-dialog.html
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/components/noteName-create/note-name-dialog.html 
b/zeppelin-web/src/components/noteName-create/note-name-dialog.html
index e7670f9..d1bcd49 100644
--- a/zeppelin-web/src/components/noteName-create/note-name-dialog.html
+++ b/zeppelin-web/src/components/noteName-create/note-name-dialog.html
@@ -11,8 +11,7 @@ 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.
 -->
-<div ng-controller="NotenameCtrl as notenamectrl">
-  <div id="noteNameModal" class="modal fade" role="dialog" modalvisible 
previsiblecallback="notenamectrl.preVisible()" 
+  <div id="noteNameModal" class="modal fade" role="dialog" modalvisible 
previsiblecallback="notenamectrl.preVisible" 
     targetinput="noteName" tabindex='-1'>
     <div class="modal-dialog">
 
@@ -20,22 +19,28 @@ limitations under the License.
       <div class="modal-content" id="NotenameCtrl">
         <div class="modal-header">
           <button type="button" class="close" 
data-dismiss="modal">&times;</button>
-          <h4 class="modal-title">Create new note</h4>
+          <h4 class="modal-title" ng-show="!notenamectrl.clone">Create new 
note</h4>
+          <h4 class="modal-title" ng-show="notenamectrl.clone">Clone note</h4>
         </div>
         <div class="modal-body">
           <div class="form-group">
             <label for="noteName">Note Name</label> <input
               placeholder="Note name" type="text" class="form-control"
-              id="noteName" ng-model="notename">
+              id="noteName" ng-model="note.notename">
           </div>
         </div>
         <div class="modal-footer">
           <button type="button" id="createNoteButton"
             class="btn btn-default"
+            ng-show="!notenamectrl.clone"
             data-dismiss="modal" ng-click="notenamectrl.createNote()">Create
             Note</button>
+            <button type="button" id="createNoteButton"
+            class="btn btn-default"
+            ng-show="notenamectrl.clone"
+            data-dismiss="modal" ng-click="notenamectrl.createNote()">Clone
+            Note</button>
         </div>
       </div>
     </div>
   </div>
-</div>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-zeppelin/blob/7f7a7c41/zeppelin-web/src/components/noteName-create/notename.controller.js
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/components/noteName-create/notename.controller.js 
b/zeppelin-web/src/components/noteName-create/notename.controller.js
index 3f0e82d..342cad7 100644
--- a/zeppelin-web/src/components/noteName-create/notename.controller.js
+++ b/zeppelin-web/src/components/noteName-create/notename.controller.js
@@ -14,16 +14,22 @@
 
 'use strict';
 
-angular.module('zeppelinWebApp').controller('NotenameCtrl', function($scope, 
$rootScope, websocketMsgSrv) {
+angular.module('zeppelinWebApp').controller('NotenameCtrl', function($scope, 
$rootScope, $routeParams, websocketMsgSrv) {
   var vm = this;
   vm.websocketMsgSrv = websocketMsgSrv;
-  
+  $scope.note = {};
   vm.createNote = function(){
-         vm.websocketMsgSrv.createNotebook($scope.notename);
+         if(!vm.clone){
+                 vm.websocketMsgSrv.createNotebook($scope.note.notename);
+         }else{
+                var noteId = $routeParams.noteId;
+                vm.websocketMsgSrv.cloneNotebook(noteId, $scope.note.notename);
+         }
   };
-  vm.preVisible = function(){
+  vm.preVisible = function(clone){
                var generatedName = vm.generateName();
-               $scope.notename = 'Note ' + generatedName;
+               $scope.note.notename = 'Note ' + generatedName;
+               vm.clone = clone;
                $scope.$apply();
   };
   vm.generateName = function () {

http://git-wip-us.apache.org/repos/asf/incubator-zeppelin/blob/7f7a7c41/zeppelin-web/src/components/noteName-create/visible.directive.js
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/components/noteName-create/visible.directive.js 
b/zeppelin-web/src/components/noteName-create/visible.directive.js
index de3fc35..3c15dcb 100644
--- a/zeppelin-web/src/components/noteName-create/visible.directive.js
+++ b/zeppelin-web/src/components/noteName-create/visible.directive.js
@@ -26,7 +26,10 @@ angular.module('zeppelinWebApp').directive('modalvisible', 
function () {
                var previsibleMethod = scope.preVisibleCallback;
                var postVisibleMethod = scope.postVisibleCallback;
                elem.on('show.bs.modal',function(e) {
-                       previsibleMethod();
+                       var relatedTgt = angular.element(e.relatedTarget);
+                       var clone = relatedTgt.data('clone');
+                       var cloneNote = clone ? true : false;
+                       previsibleMethod()(cloneNote);
                });
                elem.on('shown.bs.modal', function(e) {
                        if(scope.targetinput) {

http://git-wip-us.apache.org/repos/asf/incubator-zeppelin/blob/7f7a7c41/zeppelin-web/src/components/websocketEvents/websocketMsg.service.js
----------------------------------------------------------------------
diff --git 
a/zeppelin-web/src/components/websocketEvents/websocketMsg.service.js 
b/zeppelin-web/src/components/websocketEvents/websocketMsg.service.js
index a30905c..e14c17a 100644
--- a/zeppelin-web/src/components/websocketEvents/websocketMsg.service.js
+++ b/zeppelin-web/src/components/websocketEvents/websocketMsg.service.js
@@ -29,6 +29,9 @@ angular.module('zeppelinWebApp').service('websocketMsgSrv', 
function($rootScope,
       websocketEvents.sendNewEvent({op: 'DEL_NOTE', data: {id: noteId}});
     },
 
+       cloneNotebook: function(noteIdToClone, newNoteName ) {
+      websocketEvents.sendNewEvent({op: 'CLONE_NOTE', data: {id: 
noteIdToClone, name: newNoteName}});
+    },
     getNotebookList: function() {
       websocketEvents.sendNewEvent({op: 'LIST_NOTES'});
     },

http://git-wip-us.apache.org/repos/asf/incubator-zeppelin/blob/7f7a7c41/zeppelin-web/src/index.html
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/index.html b/zeppelin-web/src/index.html
index 6fdc2f2..90b3f0a 100644
--- a/zeppelin-web/src/index.html
+++ b/zeppelin-web/src/index.html
@@ -67,7 +67,9 @@ limitations under the License.
     </div>
     <!-- Modal ::  Keyboard shortcuts -->
     <div ng-include 
src="'components/modal-shortcut/modal-shortcut.html'"></div>
-    <div id="note-modal-container" ng-include 
src="'components/noteName-create/note-name-dialog.html'"></div>
+    <div ng-controller="NotenameCtrl as notenamectrl">
+                <div id="note-modal-container" ng-include 
src="'components/noteName-create/note-name-dialog.html'"></div>
+    </div>
     <!-- build:js(.) scripts/oldieshim.js -->
     <!--[if lt IE 9]>
 <script src="bower_components/es5-shim/es5-shim.js"></script>

http://git-wip-us.apache.org/repos/asf/incubator-zeppelin/blob/7f7a7c41/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/Note.java
----------------------------------------------------------------------
diff --git 
a/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/Note.java 
b/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/Note.java
index 20cfa96..ac267fb 100644
--- a/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/Note.java
+++ b/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/Note.java
@@ -140,6 +140,17 @@ public class Note implements Serializable, JobListener {
   }
 
   /**
+   * Add the paragraph p to the list of paras in note.
+   *
+   * @param p
+   */
+  public void addParagraph(Paragraph p) {
+    synchronized (paragraphs) {
+      paragraphs.add(p);
+    }
+  }
+  
+  /**
    * Insert paragraph in given index.
    *
    * @param index

http://git-wip-us.apache.org/repos/asf/incubator-zeppelin/blob/7f7a7c41/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/Paragraph.java
----------------------------------------------------------------------
diff --git 
a/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/Paragraph.java 
b/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/Paragraph.java
index 50756e8..33a427c 100644
--- a/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/Paragraph.java
+++ b/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/Paragraph.java
@@ -35,7 +35,7 @@ import java.util.*;
  *
  * @author Leemoonsoo
  */
-public class Paragraph extends Job implements Serializable {
+public class Paragraph extends Job implements Serializable, Cloneable {
   private static final transient long serialVersionUID = -6328572073497992016L;
   private transient NoteInterpreterLoader replLoader;
   private transient Note note;
@@ -272,6 +272,23 @@ public class Paragraph extends Job implements Serializable 
{
   public void setReturn(InterpreterResult value, Throwable t) {
     setResult(value);
     setException(t);
-
+  }
+  
+  @Override
+  public Object clone() throws CloneNotSupportedException {
+    Paragraph paraClone = (Paragraph) super.clone();
+    Map<String, Object> config = new HashMap<>(this.getConfig());
+    // Show the editor by default
+    String hideEditorKey = "editorHide";
+    Object object = config.get(hideEditorKey);
+    if (object != null && object == Boolean.TRUE) {
+      config.put(hideEditorKey, Boolean.FALSE);
+    }
+    Map<String, Object> param = new HashMap<>(this.settings.getParams());
+    paraClone.setConfig(config);
+    paraClone.settings.setParams(param);
+    paraClone.setTitle(this.getTitle());
+    paraClone.setText(this.getText());
+    return paraClone;
   }
 }

Reply via email to