You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@zeppelin.apache.org by mo...@apache.org on 2016/11/13 14:55:13 UTC
zeppelin git commit: [ZEPPELIN-1628] Enable renaming note from the
main page
Repository: zeppelin
Updated Branches:
refs/heads/master 5b1b81154 -> c1254f7b0
[ZEPPELIN-1628] Enable renaming note from the main page
### What is this PR for?
Now users can rename a note from the main page! This new feature will improve UX.
I divided [ZEPPELIN-1598](https://issues.apache.org/jira/browse/ZEPPELIN-1598) into sub-tasks since renaming folder gonna be huge. I will open PR for [ZEPPELIN-1629](https://issues.apache.org/jira/browse/ZEPPELIN-1629) after merging this PR.
By the way, I have a question! Does a `writer` can rename a note? Currently, only an owner can rename a note.
### What type of PR is it?
[Feature]
### What is the Jira issue?
https://issues.apache.org/jira/browse/ZEPPELIN-1628
### Screenshots (if appropriate)
![rename_note](https://cloud.githubusercontent.com/assets/8201019/20057051/a16b221c-a52c-11e6-900e-f88031ff1246.gif)
### Questions:
* Does the licenses files need update? No
* Is there breaking changes for older versions? No
* Does this needs documentation? No
Author: Jun <i2...@gmail.com>
Closes #1609 from tae-jun/ZEPPELIN-1628 and squashes the following commits:
7d3f7bb [Jun] Fix factory/noteList.js test errors.
54bae60 [Jun] Add rename modal input validation
3eadcd8 [Jun] Add folder id
fb8b35f [Jun] Correct indent to pass style check
c019b9e [Jun] Rename a note from the main page
Project: http://git-wip-us.apache.org/repos/asf/zeppelin/repo
Commit: http://git-wip-us.apache.org/repos/asf/zeppelin/commit/c1254f7b
Tree: http://git-wip-us.apache.org/repos/asf/zeppelin/tree/c1254f7b
Diff: http://git-wip-us.apache.org/repos/asf/zeppelin/diff/c1254f7b
Branch: refs/heads/master
Commit: c1254f7b0d29d311e7759af3054e5e5ac7c77c33
Parents: 5b1b811
Author: Jun <i2...@gmail.com>
Authored: Thu Nov 10 02:18:02 2016 +0900
Committer: Lee moon soo <mo...@apache.org>
Committed: Sun Nov 13 06:55:10 2016 -0800
----------------------------------------------------------------------
.../apache/zeppelin/socket/NotebookServer.java | 39 ++++++++++++---
zeppelin-web/src/app/home/home.controller.js | 4 ++
zeppelin-web/src/app/home/home.html | 14 ++++--
.../components/noteAction/noteAction.service.js | 14 +++++-
.../noteListDataFactory/noteList.datafactory.js | 4 +-
.../src/components/rename/rename.controller.js | 50 ++++++++++++++++++++
zeppelin-web/src/components/rename/rename.html | 42 ++++++++++++++++
.../src/components/rename/rename.service.js | 35 ++++++++++++++
.../websocketEvents/websocketMsg.service.js | 4 ++
zeppelin-web/src/index.html | 5 ++
zeppelin-web/test/spec/factory/noteList.js | 8 ++--
.../zeppelin/notebook/socket/Message.java | 2 +
12 files changed, 203 insertions(+), 18 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/zeppelin/blob/c1254f7b/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 6cba536..8a84587 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
@@ -48,13 +48,7 @@ import org.apache.zeppelin.interpreter.InterpreterSetting;
import org.apache.zeppelin.interpreter.remote.RemoteAngularObjectRegistry;
import org.apache.zeppelin.interpreter.remote.RemoteInterpreterProcessListener;
import org.apache.zeppelin.interpreter.thrift.InterpreterCompletion;
-import org.apache.zeppelin.notebook.JobListenerFactory;
-import org.apache.zeppelin.notebook.Note;
-import org.apache.zeppelin.notebook.Notebook;
-import org.apache.zeppelin.notebook.NotebookAuthorization;
-import org.apache.zeppelin.notebook.NotebookEventListener;
-import org.apache.zeppelin.notebook.Paragraph;
-import org.apache.zeppelin.notebook.ParagraphJobListener;
+import org.apache.zeppelin.notebook.*;
import org.apache.zeppelin.notebook.repo.NotebookRepo.Revision;
import org.apache.zeppelin.notebook.socket.Message;
import org.apache.zeppelin.notebook.socket.Message.OP;
@@ -234,6 +228,9 @@ public class NotebookServer extends WebSocketServlet implements
case NOTE_UPDATE:
updateNote(conn, userAndRoles, notebook, messagereceived);
break;
+ case NOTE_RENAME:
+ renameNote(conn, userAndRoles, notebook, messagereceived);
+ break;
case COMPLETION:
completion(conn, userAndRoles, notebook, messagereceived);
break;
@@ -701,6 +698,34 @@ public class NotebookServer extends WebSocketServlet implements
}
}
+ private void renameNote(NotebookSocket conn, HashSet<String> userAndRoles,
+ Notebook notebook, Message fromMessage)
+ throws SchedulerException, IOException {
+ String noteId = (String) fromMessage.get("id");
+ String name = (String) fromMessage.get("name");
+
+ if (noteId == null) {
+ return;
+ }
+
+ NotebookAuthorization notebookAuthorization = notebook.getNotebookAuthorization();
+ if (!notebookAuthorization.isOwner(noteId, userAndRoles)) {
+ permissionError(conn, "renameNote", fromMessage.principal,
+ userAndRoles, notebookAuthorization.getOwners(noteId));
+ return;
+ }
+
+ Note note = notebook.getNote(noteId);
+ if (note != null) {
+ note.setName(name);
+
+ AuthenticationInfo subject = new AuthenticationInfo(fromMessage.principal);
+ note.persist(subject);
+ broadcastNote(note);
+ broadcastNoteList(subject, userAndRoles);
+ }
+ }
+
private boolean isCronUpdated(Map<String, Object> configA,
Map<String, Object> configB) {
boolean cronUpdated = false;
http://git-wip-us.apache.org/repos/asf/zeppelin/blob/c1254f7b/zeppelin-web/src/app/home/home.controller.js
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/app/home/home.controller.js b/zeppelin-web/src/app/home/home.controller.js
index 1d11c79..839978f 100644
--- a/zeppelin-web/src/app/home/home.controller.js
+++ b/zeppelin-web/src/app/home/home.controller.js
@@ -88,6 +88,10 @@
}
});
+ $scope.renameNote = function(node) {
+ noteActionSrv.renameNote(node.id, node.path);
+ };
+
$scope.removeNote = function(noteId) {
noteActionSrv.removeNote(noteId, false);
};
http://git-wip-us.apache.org/repos/asf/zeppelin/blob/c1254f7b/zeppelin-web/src/app/home/home.html
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/app/home/home.html b/zeppelin-web/src/app/home/home.html
index a8d5e56..0b9883e 100644
--- a/zeppelin-web/src/app/home/home.html
+++ b/zeppelin-web/src/app/home/home.html
@@ -14,20 +14,26 @@ limitations under the License.
<script type="text/ng-template" id="notebook_folder_renderer.html">
<div ng-if="node.children == null"
- ng-mouseenter="showButton=true"
- ng-mouseleave="showButton=false">
+ ng-mouseenter="showNoteButton=true"
+ ng-mouseleave="showNoteButton=false">
<a style="text-decoration: none;" href="#/notebook/{{node.id}}">
<i style="font-size: 10px;" class="icon-doc"/> {{noteName(node)}}
</a>
<a style="text-decoration: none;">
<i style="font-size: 13px; margin-left: 10px; cursor: pointer; text-decoration: none;"
- class="fa fa-eraser" ng-show="showButton" ng-click="clearAllParagraphOutput(node.id)"
+ class="fa fa-pencil" ng-show="showNoteButton" ng-click="renameNote(node)"
+ tooltip-placement="bottom" tooltip="Rename note">
+ </i>
+ </a>
+ <a style="text-decoration: none;">
+ <i style="font-size: 13px; margin-left: 2px; cursor: pointer; text-decoration: none;"
+ class="fa fa-eraser" ng-show="showNoteButton" ng-click="clearAllParagraphOutput(node.id)"
tooltip-placement="bottom" tooltip="Clear output">
</i>
</a>
<a style="text-decoration: none;">
<i style="font-size: 13px; margin-left: 2px; cursor: pointer; text-decoration: none;"
- class="fa fa-trash-o" ng-show="showButton" ng-click="removeNote(node.id)"
+ class="fa fa-trash-o" ng-show="showNoteButton" ng-click="removeNote(node.id)"
tooltip-placement="bottom" tooltip="Remove note">
</i>
</a>
http://git-wip-us.apache.org/repos/asf/zeppelin/blob/c1254f7b/zeppelin-web/src/components/noteAction/noteAction.service.js
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/components/noteAction/noteAction.service.js b/zeppelin-web/src/components/noteAction/noteAction.service.js
index 33e5722..3630fc8 100644
--- a/zeppelin-web/src/components/noteAction/noteAction.service.js
+++ b/zeppelin-web/src/components/noteAction/noteAction.service.js
@@ -16,9 +16,9 @@
angular.module('zeppelinWebApp').service('noteActionSrv', noteActionSrv);
- noteActionSrv.$inject = ['websocketMsgSrv', '$location'];
+ noteActionSrv.$inject = ['websocketMsgSrv', '$location', 'renameSrv'];
- function noteActionSrv(websocketMsgSrv, $location) {
+ function noteActionSrv(websocketMsgSrv, $location, renameSrv) {
this.removeNote = function(noteId, redirectToHome) {
BootstrapDialog.confirm({
closable: true,
@@ -47,5 +47,15 @@
}
});
};
+
+ this.renameNote = function(noteId, notePath) {
+ renameSrv.openRenameModal({
+ title: 'Rename note',
+ oldName: notePath,
+ callback: function(newName) {
+ websocketMsgSrv.renameNote(noteId, newName);
+ }
+ });
+ };
}
})();
http://git-wip-us.apache.org/repos/asf/zeppelin/blob/c1254f7b/zeppelin-web/src/components/noteListDataFactory/noteList.datafactory.js
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/components/noteListDataFactory/noteList.datafactory.js b/zeppelin-web/src/components/noteListDataFactory/noteList.datafactory.js
index 24ddca7..0f700b6 100644
--- a/zeppelin-web/src/components/noteListDataFactory/noteList.datafactory.js
+++ b/zeppelin-web/src/components/noteListDataFactory/noteList.datafactory.js
@@ -43,7 +43,8 @@
if (nodes.length === 1) { // the leaf
curDir.children.push({
name: nodes[0],
- id: noteId
+ id: noteId,
+ path: curDir.id ? curDir.id + '/' + nodes[0] : nodes[0]
});
} else { // a folder node
var node = nodes.shift();
@@ -53,6 +54,7 @@
addNode(dir, nodes, noteId);
} else {
var newDir = {
+ id: curDir.id ? curDir.id + '/' + node : node,
name: node,
hidden: true,
children: []
http://git-wip-us.apache.org/repos/asf/zeppelin/blob/c1254f7b/zeppelin-web/src/components/rename/rename.controller.js
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/components/rename/rename.controller.js b/zeppelin-web/src/components/rename/rename.controller.js
new file mode 100644
index 0000000..ad27bec
--- /dev/null
+++ b/zeppelin-web/src/components/rename/rename.controller.js
@@ -0,0 +1,50 @@
+/*
+ * Licensed 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.
+ */
+'use strict';
+(function() {
+
+ angular.module('zeppelinWebApp').controller('RenameCtrl', RenameCtrl);
+
+ RenameCtrl.$inject = ['$scope'];
+
+ function RenameCtrl($scope) {
+ var self = this;
+
+ $scope.params = {newName: ''};
+ $scope.isValid = true;
+
+ $scope.rename = function() {
+ angular.element('#renameModal').modal('hide');
+ self.callback($scope.params.newName);
+ };
+
+ $scope.$on('openRenameModal', function(event, options) {
+ self.validator = options.validator || defaultValidator;
+ self.callback = options.callback || function() {};
+
+ $scope.title = options.title || 'Rename';
+ $scope.params.newName = options.oldName || '';
+ $scope.validate = function() {
+ $scope.isValid = self.validator($scope.params.newName);
+ };
+
+ angular.element('#renameModal').modal('show');
+ });
+
+ function defaultValidator(str) {
+ return !!str.trim();
+ }
+ }
+
+})();
http://git-wip-us.apache.org/repos/asf/zeppelin/blob/c1254f7b/zeppelin-web/src/components/rename/rename.html
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/components/rename/rename.html b/zeppelin-web/src/components/rename/rename.html
new file mode 100644
index 0000000..723c6aa
--- /dev/null
+++ b/zeppelin-web/src/components/rename/rename.html
@@ -0,0 +1,42 @@
+<!--
+Licensed 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.
+-->
+<div id="renameModal" class="modal fade" role="dialog"
+ tabindex="-1">
+ <div class="modal-dialog">
+ <div class="modal-content">
+ <div class="modal-header">
+ <button type="button" class="close" data-dismiss="modal">×</button>
+ <h4 class="modal-title">{{title}}</h4>
+ </div>
+ <div class="modal-body">
+ <label ng-if="isValid">Please enter a new name</label>
+ <label class="text-danger" ng-if="!isValid">Please enter a valid name</label>
+ <div class="form-group" ng-class="{'has-error': !isValid}">
+ <input type="text" class="form-control"
+ ng-model="params.newName" ng-change="validate()"
+ ng-keyup="$event.keyCode == 13 && isValid && rename()" />
+ </div>
+
+ </div>
+ <div class="modal-footer">
+ <div>
+ <button type="button" class="btn btn-default btn-primary"
+ ng-click="rename()" ng-class="{'disabled': !isValid}">
+ Rename
+ </button>
+ </div>
+ </div>
+ </div>
+ </div>
+</div>
http://git-wip-us.apache.org/repos/asf/zeppelin/blob/c1254f7b/zeppelin-web/src/components/rename/rename.service.js
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/components/rename/rename.service.js b/zeppelin-web/src/components/rename/rename.service.js
new file mode 100644
index 0000000..1e19b24
--- /dev/null
+++ b/zeppelin-web/src/components/rename/rename.service.js
@@ -0,0 +1,35 @@
+/*
+ * Licensed 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.
+ */
+'use strict';
+(function() {
+
+ angular.module('zeppelinWebApp').service('renameSrv', renameSrv);
+
+ renameSrv.$inject = ['$rootScope'];
+
+ function renameSrv($rootScope) {
+ var self = this;
+
+ /**
+ * <options schema>
+ * title: string - Modal title
+ * oldName: string - name to initialize input
+ * callback: (newName: string)=>void - callback onButtonClick
+ * validator: (str: string)=>boolean - input validator
+ */
+ self.openRenameModal = function(options) {
+ $rootScope.$broadcast('openRenameModal', options);
+ };
+ }
+})();
http://git-wip-us.apache.org/repos/asf/zeppelin/blob/c1254f7b/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 8c025cc..5ffdaf7 100644
--- a/zeppelin-web/src/components/websocketEvents/websocketMsg.service.js
+++ b/zeppelin-web/src/components/websocketEvents/websocketMsg.service.js
@@ -59,6 +59,10 @@
websocketEvents.sendNewEvent({op: 'NOTE_UPDATE', data: {id: noteId, name: noteName, config: noteConfig}});
},
+ renameNote: function(noteId, noteName) {
+ websocketEvents.sendNewEvent({op: 'NOTE_RENAME', data: {id: noteId, name: noteName}});
+ },
+
moveParagraph: function(paragraphId, newIndex) {
websocketEvents.sendNewEvent({op: 'MOVE_PARAGRAPH', data: {id: paragraphId, index: newIndex}});
},
http://git-wip-us.apache.org/repos/asf/zeppelin/blob/c1254f7b/zeppelin-web/src/index.html
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/index.html b/zeppelin-web/src/index.html
index 3208860..fc000a9 100644
--- a/zeppelin-web/src/index.html
+++ b/zeppelin-web/src/index.html
@@ -93,6 +93,9 @@ limitations under the License.
<div ng-controller="LoginCtrl as noteimportctrl">
<div id="login-container" ng-include src="'components/login/login.html'"></div>
</div>
+ <div ng-controller="RenameCtrl">
+ <div ng-include src="'components/rename/rename.html'"></div>
+ </div>
<!-- build:js(.) scripts/oldieshim.js -->
<!--[if lt IE 9]>
<script src="bower_components/es5-shim/es5-shim.js"></script>
@@ -212,6 +215,8 @@ limitations under the License.
<script src="components/login/login.controller.js"></script>
<script src="components/elasticInputCtrl/elasticInput.controller.js"></script>
<script src="components/noteAction/noteAction.service.js"></script>
+ <script src="components/rename/rename.controller.js"></script>
+ <script src="components/rename/rename.service.js"></script>
<!-- endbuild -->
</body>
</html>
http://git-wip-us.apache.org/repos/asf/zeppelin/blob/c1254f7b/zeppelin-web/test/spec/factory/noteList.js
----------------------------------------------------------------------
diff --git a/zeppelin-web/test/spec/factory/noteList.js b/zeppelin-web/test/spec/factory/noteList.js
index 07eac9c..e1c9a76 100644
--- a/zeppelin-web/test/spec/factory/noteList.js
+++ b/zeppelin-web/test/spec/factory/noteList.js
@@ -46,7 +46,7 @@ describe('Factory: NoteList', function() {
expect(folderList[1].name).toBe('B');
expect(folderList[2].name).toBe('000003');
expect(folderList[3].name).toBe('C');
- expect(folderList[3].id).toBeUndefined();
+ expect(folderList[3].id).toBe('C');
expect(folderList[3].children.length).toBe(3);
expect(folderList[3].children[0].name).toBe('CA');
expect(folderList[3].children[0].id).toBe('000004');
@@ -55,7 +55,7 @@ describe('Factory: NoteList', function() {
expect(folderList[3].children[1].id).toBe('000005');
expect(folderList[3].children[1].children).toBeUndefined();
expect(folderList[3].children[2].name).toBe('CB');
- expect(folderList[3].children[2].id).toBeUndefined();
+ expect(folderList[3].children[2].id).toBe('C/CB');
expect(folderList[3].children[2].children.length).toBe(3);
expect(folderList[3].children[2].children[0].name).toBe('CBA');
expect(folderList[3].children[2].children[0].id).toBe('000006');
@@ -67,10 +67,10 @@ describe('Factory: NoteList', function() {
expect(folderList[3].children[2].children[2].id).toBe('000008');
expect(folderList[3].children[2].children[2].children).toBeUndefined();
expect(folderList[4].name).toBe('D');
- expect(folderList[4].id).toBeUndefined();
+ expect(folderList[4].id).toBe('D');
expect(folderList[4].children.length).toBe(1);
expect(folderList[4].children[0].name).toBe('D[A');
- expect(folderList[4].children[0].id).toBeUndefined();
+ expect(folderList[4].children[0].id).toBe('D/D[A');
expect(folderList[4].children[0].children[0].name).toBe('DA]B');
expect(folderList[4].children[0].children[0].id).toBe('000009');
expect(folderList[4].children[0].children[0].children).toBeUndefined();
http://git-wip-us.apache.org/repos/asf/zeppelin/blob/c1254f7b/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/socket/Message.java
----------------------------------------------------------------------
diff --git a/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/socket/Message.java b/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/socket/Message.java
index b4da1e1..9fe9636 100644
--- a/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/socket/Message.java
+++ b/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/socket/Message.java
@@ -53,6 +53,8 @@ public class Message {
// @param object notebook
NOTE_UPDATE,
+ NOTE_RENAME,
+
RUN_PARAGRAPH, // [c-s] run paragraph
// @param id paragraph id
// @param paragraph paragraph content.ie. script