Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
ca4e160
add renaming folder from the main page
tae-jun Nov 14, 2016
e7e41c4
Fix ConcurrentModificationException of FolderView, and add more comments
tae-jun Nov 15, 2016
4a3d722
Fix awkward insufficient privileges error message
tae-jun Nov 19, 2016
cb0ec4f
Make permission error message more natural
tae-jun Nov 19, 2016
e5cd770
Include note name in permission error message
tae-jun Nov 19, 2016
7f7e6bd
Change notebook -> note
AhyoungRyu Nov 20, 2016
90182a6
Install Zeppelin to local maven repo after building
tae-jun Nov 20, 2016
11b95eb
Revert "Install Zeppelin to local maven repo after building"
tae-jun Nov 20, 2016
757bb10
Address @AhyoungRyu's suggestions
tae-jun Nov 20, 2016
2f55d05
Fix cannot find symbol error
tae-jun Nov 23, 2016
ad84c4c
Revert "Fix cannot find symbol error"
tae-jun Nov 24, 2016
30ccea9
Fix “cannot find symbol” error by importing `Folder`
tae-jun Nov 24, 2016
0f729b7
Fix error that not working when a note’s name changed
tae-jun Nov 24, 2016
01e6bc9
Fix the error @AhyoungRyu addressed and add more test cases
tae-jun Nov 28, 2016
27fe1ea
Fix the folder test error
tae-jun Nov 29, 2016
b719d63
Fix concurrent modification error
tae-jun Nov 30, 2016
af71edd
Add warning sign if a renamed folder will be merged
tae-jun Nov 30, 2016
5dee99c
Check permission of every notes under the folder which is target of r…
tae-jun Nov 30, 2016
613b453
Remove folder from a parent when folder is renamed only if it’s not c…
tae-jun Dec 2, 2016
7d2dc57
Add loggers & fix bugs
tae-jun Dec 3, 2016
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@
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.Folder;
import org.apache.zeppelin.notebook.Note;
import org.apache.zeppelin.notebook.Notebook;
import org.apache.zeppelin.notebook.NotebookAuthorization;
Expand Down Expand Up @@ -248,6 +249,9 @@ public void onMessage(NotebookSocket conn, String msg) {
case NOTE_RENAME:
renameNote(conn, userAndRoles, notebook, messagereceived);
break;
case FOLDER_RENAME:
renameFolder(conn, userAndRoles, notebook, messagereceived);
break;
case COMPLETION:
completion(conn, userAndRoles, notebook, messagereceived);
break;
Expand Down Expand Up @@ -635,7 +639,7 @@ void permissionError(NotebookSocket conn, String op,
op, userAndRoles, allowed);

conn.send(serializeMessage(new Message(OP.AUTH_INFO).put("info",
"Insufficient privileges to " + op + " notebook.\n\n" +
"Insufficient privileges to " + op + "note.\n\n" +
"Allowed users or roles: " + allowed.toString() + "\n\n" +
"But the user " + userName + " belongs to: " + userAndRoles.toString())));
}
Expand Down Expand Up @@ -749,7 +753,7 @@ private void renameNote(NotebookSocket conn, HashSet<String> userAndRoles,

NotebookAuthorization notebookAuthorization = notebook.getNotebookAuthorization();
if (!notebookAuthorization.isOwner(noteId, userAndRoles)) {
permissionError(conn, "renameNote", fromMessage.principal,
permissionError(conn, "rename", fromMessage.principal,
userAndRoles, notebookAuthorization.getOwners(noteId));
return;
}
Expand All @@ -765,6 +769,41 @@ private void renameNote(NotebookSocket conn, HashSet<String> userAndRoles,
}
}

private void renameFolder(NotebookSocket conn, HashSet<String> userAndRoles,
Notebook notebook, Message fromMessage)
throws SchedulerException, IOException {
String oldFolderId = (String) fromMessage.get("id");
String newFolderId = (String) fromMessage.get("name");

if (oldFolderId == null) {
return;
}

NotebookAuthorization notebookAuthorization = notebook.getNotebookAuthorization();
for (Note note : notebook.getNotesUnderFolder(oldFolderId)) {
String noteId = note.getId();
if (!notebookAuthorization.isOwner(noteId, userAndRoles)) {
permissionError(conn, "rename folder of '" + note.getName() + "'", fromMessage.principal,
userAndRoles, notebookAuthorization.getOwners(noteId));
return;
}
}

Folder oldFolder = notebook.renameFolder(oldFolderId, newFolderId);

if (oldFolder != null) {
AuthenticationInfo subject = new AuthenticationInfo(fromMessage.principal);

List<Note> renamedNotes = oldFolder.getNotesRecursively();
for (Note note : renamedNotes) {
note.persist(subject);
broadcastNote(note);
}

broadcastNoteList(subject, userAndRoles);
}
}

private boolean isCronUpdated(Map<String, Object> configA,
Map<String, Object> configB) {
boolean cronUpdated = false;
Expand Down
4 changes: 4 additions & 0 deletions zeppelin-web/src/app/home/home.controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,10 @@
noteActionSrv.renameNote(node.id, node.path);
};

$scope.renameFolder = function(node) {
noteActionSrv.renameFolder(node.id);
};

$scope.removeNote = function(noteId) {
noteActionSrv.removeNote(noteId, false);
};
Expand Down
15 changes: 12 additions & 3 deletions zeppelin-web/src/app/home/home.html
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,18 @@
</a>
</div>
<div ng-if="node.children != null">
<a style="text-decoration: none; cursor: pointer;" ng-click="toggleFolderNode(node)">
<i style="font-size: 10px;" ng-class="node.hidden ? 'icon-folder' : 'icon-folder-alt'" /> {{noteName(node)}}
</a>
<div ng-mouseenter="showFolderButton=true"
ng-mouseleave="showFolderButton=false">
<a style="text-decoration: none; cursor: pointer;" ng-click="toggleFolderNode(node)">
<i style="font-size: 10px;" ng-class="node.hidden ? 'icon-folder' : 'icon-folder-alt'" /> {{noteName(node)}}
</a>
<a style="text-decoration: none;">
<i style="font-size: 13px; margin-left: 10px; cursor: pointer; text-decoration: none;"
class="fa fa-pencil" ng-show="showFolderButton" ng-click="renameFolder(node)"
tooltip-placement="bottom" tooltip="Rename folder">
</i>
</a>
</div>
<div ng-if="!node.hidden">
<ul style="list-style-type: none; padding-left:15px;">
<li ng-repeat="node in node.children" ng-include="'notebook_folder_renderer.html'" />
Expand Down
57 changes: 55 additions & 2 deletions zeppelin-web/src/components/noteAction/noteAction.service.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,9 @@

angular.module('zeppelinWebApp').service('noteActionSrv', noteActionSrv);

noteActionSrv.$inject = ['websocketMsgSrv', '$location', 'renameSrv'];
noteActionSrv.$inject = ['websocketMsgSrv', '$location', 'renameSrv', 'noteListDataFactory'];

function noteActionSrv(websocketMsgSrv, $location, renameSrv) {
function noteActionSrv(websocketMsgSrv, $location, renameSrv, noteListDataFactory) {
this.removeNote = function(noteId, redirectToHome) {
BootstrapDialog.confirm({
closable: true,
Expand Down Expand Up @@ -57,5 +57,58 @@
}
});
};

this.renameFolder = function(folderId) {
renameSrv.openRenameModal({
title: 'Rename folder',
oldName: folderId,
callback: function(newName) {
var newFolderId = normalizeFolderId(newName);
if (_.has(noteListDataFactory.flatFolderMap, newFolderId)) {
BootstrapDialog.confirm({
type: BootstrapDialog.TYPE_WARNING,
closable: true,
title: 'WARNING! The folder will be MERGED',
message: 'The folder will be merged into <strong>' + newFolderId + '</strong>. Are you sure?',
callback: function(result) {
if (result) {
websocketMsgSrv.renameFolder(folderId, newFolderId);
}
}
});
} else {
websocketMsgSrv.renameFolder(folderId, newFolderId);
}
}
});
};

function normalizeFolderId(folderId) {
folderId = folderId.trim();

while (folderId.contains('\\')) {
folderId = folderId.replace('\\', '/');
}

while (folderId.contains('///')) {
folderId = folderId.replace('///', '/');
}

folderId = folderId.replace('//', '/');

if (folderId === '/') {
return '/';
}

if (folderId[0] === '/') {
folderId = folderId.substring(1);
}

if (folderId.slice(-1) === '/') {
folderId = folderId.slice(0, -1);
}

return folderId;
}
}
})();
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,15 @@
var notes = {
root: {children: []},
flatList: [],
flatFolderMap: {},

setNotes: function(notesList) {
// a flat list to boost searching
notes.flatList = angular.copy(notesList);

// construct the folder-based tree
notes.root = {children: []};
notes.flatFolderMap = {};
_.reduce(notesList, function(root, note) {
var noteName = note.name || note.id;
var nodes = noteName.match(/([^\/][^\/]*)/g);
Expand Down Expand Up @@ -59,6 +61,10 @@
hidden: true,
children: []
};

// add the folder to flat folder map
notes.flatFolderMap[newDir.id] = newDir;

curDir.children.push(newDir);
addNode(newDir, nodes, noteId);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ <h4 class="modal-title" ng-show="notenamectrl.clone">Clone note</h4>
</select>
</div>
</div>
Use '/' to create folders. Example: /NoteDirA/Notebook1
Use '/' to create folders. Example: /NoteDirA/Note1
</div>
<div class="modal-footer">
<button type="button" id="createNoteButton"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,10 @@
websocketEvents.sendNewEvent({op: 'NOTE_RENAME', data: {id: noteId, name: noteName}});
},

renameFolder: function(folderId, folderName) {
websocketEvents.sendNewEvent({op: 'FOLDER_RENAME', data: {id: folderId, name: folderName}});
},

moveParagraph: function(paragraphId, newIndex) {
websocketEvents.sendNewEvent({op: 'MOVE_PARAGRAPH', data: {id: paragraphId, index: newIndex}});
},
Expand Down
Loading