From defabcc7725cd402e48123453d7003197c90f1cc Mon Sep 17 00:00:00 2001 From: Jeffrey Bush <jeff@coderforlife.com> Date: Wed, 13 Aug 2014 17:16:05 -0700 Subject: [PATCH 1/5] Minor improvements to file upload. Updated file upload so that the icon of the new upload item reflects if it is a file or notebook, added the ability to use the "Enter" key in the filename text box to cause the upload to happen, and make it so that the list of files refreshes immediately upon successful upload. --- IPython/html/static/tree/js/notebooklist.js | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/IPython/html/static/tree/js/notebooklist.js b/IPython/html/static/tree/js/notebooklist.js index 59a1b0a6d..396365ff6 100644 --- a/IPython/html/static/tree/js/notebooklist.js +++ b/IPython/html/static/tree/js/notebooklist.js @@ -87,7 +87,7 @@ define([ } var item = that.new_item(0); item.addClass('new-file'); - that.add_name_input(f.name, item); + that.add_name_input(f.name, item, file_ext == '.ipynb' ? 'notebook' : 'file'); // Store the list item in the reader so we can use it later // to know which item it belongs to. $(reader).data('item', item); @@ -260,15 +260,16 @@ define([ }; - NotebookList.prototype.add_name_input = function (name, item) { + NotebookList.prototype.add_name_input = function (name, item, icon_type) { item.data('name', name); - item.find(".item_icon").addClass('notebook_icon').addClass('icon-fixed-width'); + item.find(".item_icon").addClass(NotebookList.icons[icon_type]).addClass('icon-fixed-width'); item.find(".item_name").empty().append( $('<input/>') .addClass("filename_input") .attr('value', name) .attr('size', '30') .attr('type', 'text') + .keyup(function(event){if(event.keyCode == 13){item.find('.upload_button').click();}}) ); }; @@ -415,6 +416,7 @@ define([ item.removeClass('new-file'); that.add_link(model, item); that.add_delete_button(item); + that.session_list.load_sessions(); }, error : utils.log_ajax_error, }; @@ -485,7 +487,6 @@ define([ buttons : {'OK' : {'class' : 'btn-primary'}} }); }; - // Backwards compatability. IPython.NotebookList = NotebookList; From 5fff038965d1efb3c6835bcfe36f653f995a9908 Mon Sep 17 00:00:00 2001 From: Jeffrey Bush <jeff@coderforlife.com> Date: Wed, 13 Aug 2014 20:57:35 -0700 Subject: [PATCH 2/5] Added ESC keep to upload textbox to cancel. --- IPython/html/static/tree/js/notebooklist.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/IPython/html/static/tree/js/notebooklist.js b/IPython/html/static/tree/js/notebooklist.js index 396365ff6..002fae6dc 100644 --- a/IPython/html/static/tree/js/notebooklist.js +++ b/IPython/html/static/tree/js/notebooklist.js @@ -269,7 +269,10 @@ define([ .attr('value', name) .attr('size', '30') .attr('type', 'text') - .keyup(function(event){if(event.keyCode == 13){item.find('.upload_button').click();}}) + .keyup(function(event){ + if(event.keyCode == 13){item.find('.upload_button').click();} + else if(event.keyCode == 27){item.remove();} + }) ); }; From 305c734830260aa19b8de450b6588421cc8b74ba Mon Sep 17 00:00:00 2001 From: Jeffrey Bush <jeff@coderforlife.com> Date: Wed, 13 Aug 2014 22:07:57 -0700 Subject: [PATCH 3/5] Uploading a file with a name that already exists asks the user if they want to overwrite. This is not perfect (it doesn't check against the real filesystem but the current list in the browser which may be stale) but it is better than nothing. --- IPython/html/static/tree/js/notebooklist.js | 24 ++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/IPython/html/static/tree/js/notebooklist.js b/IPython/html/static/tree/js/notebooklist.js index 002fae6dc..3ae562d26 100644 --- a/IPython/html/static/tree/js/notebooklist.js +++ b/IPython/html/static/tree/js/notebooklist.js @@ -430,7 +430,29 @@ define([ that.notebook_path, filename ); - $.ajax(url, settings); + + var exists = false; + $.each(that.element.find('.list_item:not(.new-file)'), function(k,v){ + if ($(v).data('name') === filename) { exists = true; return false; } + }); + if (exists) { + dialog.modal({ + title : "Replace file", + body : 'There is already a file named ' + filename + ', do you want to replace it?', + buttons : { + Overwrite : { + class: "btn-danger", + click: function() { $.ajax(url, settings); } + }, + Cancel : { + click: function() { item.remove(); } + } + } + }); + } else { + $.ajax(url, settings); + } + return false; }); var cancel_button = $('<button/>').text("Cancel") From 1f68e22486cadf71720f16c9a68f8277dfe484ca Mon Sep 17 00:00:00 2001 From: Jeffrey Bush <jeff@coderforlife.com> Date: Wed, 13 Aug 2014 22:55:53 -0700 Subject: [PATCH 4/5] Fixed many edge cases in file uploads. When there is an error reading a file, a message is shown (I could reproduce this easily by dragging a folder onto the list), added a missing "return false;" after an illegal notebook was detected, and we prevent no-name files and files starting with dots (which will not be visible in dashboard and automatically overwritten). --- IPython/html/static/tree/js/notebooklist.js | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/IPython/html/static/tree/js/notebooklist.js b/IPython/html/static/tree/js/notebooklist.js index 3ae562d26..80fd3c562 100644 --- a/IPython/html/static/tree/js/notebooklist.js +++ b/IPython/html/static/tree/js/notebooklist.js @@ -96,6 +96,16 @@ define([ that.add_file_data(event.target.result, item); that.add_upload_button(item); }; + reader.onerror = function (event) { + var item = $(event.target).data('item'); + var name = item.data('name') + item.remove(); + dialog.modal({ + title : 'Failed to read file', + body : "Failed to read file '" + name + "'", + buttons : {'OK' : { 'class' : 'btn-primary' }} + }); + }; } // Replace the file input form wth a clone of itself. This is required to // reset the form. Otherwise, if you upload a file, delete it and try to @@ -364,6 +374,14 @@ define([ var filename = item.find('.item_name > input').val(); var filedata = item.data('filedata'); var format = 'text'; + if (filename.length === 0 || filename[0] === '.') { + dialog.modal({ + title : 'Invalid file name', + body : "File names must be at least one character and not start with a dot", + buttons : {'OK' : { 'class' : 'btn-primary' }} + }); + return false; + } if (filedata instanceof ArrayBuffer) { // base64-encode binary file data var bytes = ''; @@ -399,6 +417,7 @@ define([ } }} }); + return false; } content_type = 'application/json'; } else { From f141a6926ac607c7197fc7e757ed6ec0602d29f7 Mon Sep 17 00:00:00 2001 From: Jeffrey Bush <jeff@coderforlife.com> Date: Wed, 13 Aug 2014 23:58:20 -0700 Subject: [PATCH 5/5] File list refreshes no longer move the upload filename boxes. The new file list is always placed after the upload filename boxes. I also removed the autorefresh guards against refreshing while having an upload box since this is no longer a problem. --- IPython/html/static/tree/js/main.js | 14 ++++---------- IPython/html/static/tree/js/notebooklist.js | 7 ++++--- 2 files changed, 8 insertions(+), 13 deletions(-) diff --git a/IPython/html/static/tree/js/main.js b/IPython/html/static/tree/js/main.js index b321b34b3..ab0e08c03 100644 --- a/IPython/html/static/tree/js/main.js +++ b/IPython/html/static/tree/js/main.js @@ -57,18 +57,12 @@ require([ var enable_autorefresh = function(){ //refresh immediately , then start interval - if($('.upload_button').length === 0) - { - session_list.load_sessions(); - cluster_list.load_list(); - } + session_list.load_sessions(); + cluster_list.load_list(); if (!interval_id){ interval_id = setInterval(function(){ - if($('.upload_button').length === 0) - { - session_list.load_sessions(); - cluster_list.load_list(); - } + session_list.load_sessions(); + cluster_list.load_list(); }, time_refresh*1000); } }; diff --git a/IPython/html/static/tree/js/notebooklist.js b/IPython/html/static/tree/js/notebooklist.js index 80fd3c562..d74a154f0 100644 --- a/IPython/html/static/tree/js/notebooklist.js +++ b/IPython/html/static/tree/js/notebooklist.js @@ -172,6 +172,7 @@ define([ var list = data.content; var len = list.length; this.clear_list(); + var n_uploads = this.element.children('.list_item').length; if (len === 0) { item = this.new_item(0); var span12 = item.children().first(); @@ -179,16 +180,16 @@ define([ span12.append($('<div style="margin:auto;text-align:center;color:grey"/>').text(message)); } var path = this.notebook_path; - var offset = 0; + var offset = n_uploads; if (path !== '') { - item = this.new_item(0); + item = this.new_item(offset); model = { type: 'directory', name: '..', path: path, }; this.add_link(model, item); - offset = 1; + offset += 1; } for (var i=0; i<len; i++) { model = list[i];