diff --git a/IPython/html/services/notebooks/filenbmanager.py b/IPython/html/services/notebooks/filenbmanager.py index 974696a..07feefc 100644 --- a/IPython/html/services/notebooks/filenbmanager.py +++ b/IPython/html/services/notebooks/filenbmanager.py @@ -97,17 +97,17 @@ class FileNotebookManager(NotebookManager): changes = data.keys() response = 200 for change in changes: - full_path = self.get_path(notebook_name, notebook_path) + full_path = self.get_os_path(notebook_name, notebook_path) if change == "name": - new_path = self.get_path(data['name'], notebook_path) + new_path = self.get_os_path(data['name'], notebook_path) if not os.path.isfile(new_path): os.rename(full_path, - self.get_path(data['name'], notebook_path)) + self.get_os_path(data['name'], notebook_path)) notebook_name = data['name'] else: response = 409 if change == "path": - new_path = self.get_path(data['name'], data['path']) + new_path = self.get_os_path(data['name'], data['path']) stutil.move(full_path, new_path) notebook_path = data['path'] if change == "content": @@ -115,21 +115,45 @@ class FileNotebookManager(NotebookManager): model = self.notebook_model(notebook_name, notebook_path) return model, response - def notebook_exists(self, notebook_path): - """Does a notebook exist?""" - return os.path.isfile(notebook_path) - - def get_path(self, notebook_name, notebook_path=None): - """Return a full path to a notebook given its notebook_name.""" - return self.get_path_by_name(notebook_name, notebook_path) + def notebook_exists(self, name, path): + """Returns a True if the notebook exists. Else, returns False. + + Parameters + ---------- + name : string + The name of the notebook you are checking. + path : string + The relative path to the notebook (with '/' as separator) + + Returns + ------- + bool + """ + path = self.get_os_path(name, path) + return os.path.isfile(path) - def get_path_by_name(self, name, notebook_path=None): - """Return a full path to a notebook given its name.""" - filename = name #+ self.filename_ext - if notebook_path == None: - path = os.path.join(self.notebook_dir, filename) - else: - path = os.path.join(self.notebook_dir, notebook_path, filename) + def get_os_path(self, fname, path=None): + """Return a full path to a notebook with the os.sep as the separator. + + Parameters + ---------- + fname : string + The name of a notebook file with the .ipynb extension + path : string + The relative path (with '/' as separator) to the named notebook. + + Returns + ------- + path : + A path that combines notebook_dir (location where server started), + the relative path, and the filename with the current operating + system's url. + """ + if path is None: + path = '/' + parts = path.split('/') + parts = [p for p in parts if p != ''] # remove duplicate splits + path = os.sep.join([self.notebook_dir] + parts + [fname]) return path def read_notebook_object_from_path(self, path): @@ -148,7 +172,7 @@ class FileNotebookManager(NotebookManager): def read_notebook_object(self, notebook_name, notebook_path=None): """Get the Notebook representation of a notebook by notebook_name.""" - path = self.get_path(notebook_name, notebook_path) + path = self.get_os_path(notebook_name, notebook_path) if not os.path.isfile(path): raise web.HTTPError(404, u'Notebook does not exist: %s' % notebook_name) last_modified, nb = self.read_notebook_object_from_path(path) @@ -172,7 +196,7 @@ class FileNotebookManager(NotebookManager): old_name = notebook_name old_checkpoints = self.list_checkpoints(old_name) - path = self.get_path_by_name(new_name, new_path) + path = self.get_os_path(new_name, new_path) # Right before we save the notebook, we write an empty string as the # notebook name in the metadata. This is to prepare for removing @@ -201,7 +225,7 @@ class FileNotebookManager(NotebookManager): # remove old files if the name changed if old_name != new_name: # remove renamed original, if it exists - old_path = self.get_path_by_name(old_name, notebook_path) + old_path = self.get_os_path(old_name, notebook_path) if os.path.isfile(old_path): self.log.debug("unlinking notebook %s", old_path) os.unlink(old_path) @@ -226,7 +250,7 @@ class FileNotebookManager(NotebookManager): def delete_notebook(self, notebook_name, notebook_path): """Delete notebook by notebook_name.""" - nb_path = self.get_path(notebook_name, notebook_path) + nb_path = self.get_os_path(notebook_name, notebook_path) if not os.path.isfile(nb_path): raise web.HTTPError(404, u'Notebook does not exist: %s' % notebook_name) @@ -252,7 +276,7 @@ class FileNotebookManager(NotebookManager): i = 0 while True: name = u'%s%i.ipynb' % (basename,i) - path = self.get_path_by_name(name, notebook_path) + path = self.get_os_path(name, notebook_path) if not os.path.isfile(path): break else: @@ -295,7 +319,7 @@ class FileNotebookManager(NotebookManager): def create_checkpoint(self, notebook_name, notebook_path=None): """Create a checkpoint from the current state of a notebook""" - nb_path = self.get_path(notebook_name, notebook_path) + nb_path = self.get_os_path(notebook_name, notebook_path) # only the one checkpoint ID: checkpoint_id = u"checkpoint" cp_path = self.get_checkpoint_path(notebook_name, checkpoint_id, notebook_path) @@ -323,7 +347,7 @@ class FileNotebookManager(NotebookManager): def restore_checkpoint(self, notebook_name, checkpoint_id, notebook_path=None): """restore a notebook to a checkpointed state""" self.log.info("restoring Notebook %s from checkpoint %s", notebook_name, checkpoint_id) - nb_path = self.get_path(notebook_name, notebook_path) + nb_path = self.get_os_path(notebook_name, notebook_path) cp_path = self.get_checkpoint_path(notebook_name, checkpoint_id, notebook_path) if not os.path.isfile(cp_path): self.log.debug("checkpoint file does not exist: %s", cp_path) diff --git a/IPython/html/services/notebooks/nbmanager.py b/IPython/html/services/notebooks/nbmanager.py index 318ebbc..0979062 100644 --- a/IPython/html/services/notebooks/nbmanager.py +++ b/IPython/html/services/notebooks/nbmanager.py @@ -74,12 +74,10 @@ class NotebookManager(LoggingConfigurable): def url_encode(self, path): parts = os.path.split(path) - #parts = path.split('/') return os.path.join(*[quote(p) for p in parts]) def url_decode(self, path): parts = os.path.split(path) - #parts = path.split('/') return os.path.join(*[unquote(p) for p in parts]) def _notebook_dir_changed(self, new): diff --git a/IPython/html/services/notebooks/tests/test_nbmanager.py b/IPython/html/services/notebooks/tests/test_nbmanager.py index b4accf5..91294bb 100644 --- a/IPython/html/services/notebooks/tests/test_nbmanager.py +++ b/IPython/html/services/notebooks/tests/test_nbmanager.py @@ -32,6 +32,28 @@ class TestFileNotebookManager(TestCase): with NamedTemporaryFile() as tf: self.assertRaises(TraitError, FileNotebookManager, notebook_dir=tf.name) + def test_get_os_path(self): + # full filesystem path should be returned with correct operating system + # separators. + with TemporaryDirectory() as td: + nbdir = os.path.join(td, 'notebooks') + km = FileNotebookManager(notebook_dir=nbdir) + path = km.get_os_path('test.ipynb', '/path/to/notebook/') + self.assertEqual(path, km.notebook_dir+os.sep+'path'+os.sep+'to'+os.sep+ + 'notebook'+os.sep+'test.ipynb') + + with TemporaryDirectory() as td: + nbdir = os.path.join(td, 'notebooks') + km = FileNotebookManager(notebook_dir=nbdir) + path = km.get_os_path('test.ipynb', None) + self.assertEqual(path, km.notebook_dir+os.sep+'test.ipynb') + + with TemporaryDirectory() as td: + nbdir = os.path.join(td, 'notebooks') + km = FileNotebookManager(notebook_dir=nbdir) + path = km.get_os_path('test.ipynb', '////') + self.assertEqual(path, km.notebook_dir+os.sep+'test.ipynb') + class TestNotebookManager(TestCase): def test_named_notebook_path(self): nm = NotebookManager() diff --git a/IPython/html/static/notebook/js/notebook.js b/IPython/html/static/notebook/js/notebook.js index 740c460..b50937b 100644 --- a/IPython/html/static/notebook/js/notebook.js +++ b/IPython/html/static/notebook/js/notebook.js @@ -50,7 +50,6 @@ var IPython = (function (IPython) { // single worksheet for now this.worksheet_metadata = {}; this.control_key_active = false; - this.notebook_name = null; this.notebook_name_blacklist_re = /[\/\\:]/; this.nbformat = 3 // Increment this when changing the nbformat this.nbformat_minor = 0 // Increment this when changing the nbformat @@ -87,14 +86,7 @@ var IPython = (function (IPython) { Notebook.prototype.notebookPath = function() { var path = $('body').data('notebookPath'); path = decodeURIComponent(path); - if (path != 'None') { - if (path[path.length-1] != '/') { - path = path.substring(0,path.length); - }; - return path; - } else { - return ''; - } + return path }; /** @@ -1368,7 +1360,7 @@ var IPython = (function (IPython) { var cells = this.get_cells(); for (var i=0; i 1) { + if (content.worksheets.length > 1) { IPython.dialog.modal({ title : "Multiple worksheets", body : "This notebook has " + data.worksheets.length + " worksheets, " + @@ -1672,10 +1663,13 @@ var IPython = (function (IPython) { Notebook.prototype.save_notebook = function () { // We may want to move the name/id/nbformat logic inside toJSON? var data = this.toJSON(); - data.metadata.name = this.notebook_name; - data.nbformat = this.nbformat; - data.nbformat_minor = this.nbformat_minor; - + var model = {}; + // Create a JSON model to be sent to the server. + model['name'] = this.notebook_name; + model['path'] = this.notebook_path; + model['content'] = data + model.content.nbformat = this.nbformat; + model.content.nbformat_minor = this.nbformat_minor; // time the ajax call for autosave tuning purposes. var start = new Date().getTime(); // We do the call with settings so we can set cache to false. @@ -1683,13 +1677,13 @@ var IPython = (function (IPython) { processData : false, cache : false, type : "PUT", - data : JSON.stringify(data), + data : JSON.stringify(model), headers : {'Content-Type': 'application/json'}, success : $.proxy(this.save_notebook_success, this, start), error : $.proxy(this.save_notebook_error, this) }; $([IPython.events]).trigger('notebook_saving.Notebook'); - var url = this.baseProjectUrl() + 'api/notebooks/' + this.notebookPath()+ this.notebook_name; + var url = this.baseProjectUrl() + 'api/notebooks' + this.notebookPath()+ this.notebook_name; $.ajax(url, settings); }; @@ -1752,14 +1746,15 @@ var IPython = (function (IPython) { type : "POST", dataType : "json", success:$.proxy(function (data, status, xhr){ - notebook_name = data.name; - window.open(this._baseProjectUrl +'notebooks/' + this.notebookPath()+ notebook_name); + var notebook_name = data.name; + window.open(this.baseProjectUrl() +'notebooks' + this.notebookPath()+ notebook_name, '_blank'); }, this) }; - var url = this._baseProjectUrl + 'notebooks/' + path; + var url = this.baseProjectUrl() + 'api/notebooks' + path; $.ajax(url,settings); }; + Notebook.prototype.copy_notebook = function(){ var path = this.notebookPath(); var name = {'name': this.notebook_name} @@ -1771,10 +1766,10 @@ var IPython = (function (IPython) { dataType : "json", success:$.proxy(function (data, status, xhr){ notebook_name = data.name; - window.open(this._baseProjectUrl +'notebooks/' + this.notebookPath()+ notebook_name); + window.open(this._baseProjectUrl +'notebooks' + this.notebookPath()+ notebook_name); }, this) }; - var url = this._baseProjectUrl + 'notebooks/' + path; + var url = this._baseProjectUrl + 'notebooks' + path; $.ajax(url,settings); }; @@ -1793,15 +1788,16 @@ var IPython = (function (IPython) { error : $.proxy(that.rename_error, this) }; $([IPython.events]).trigger('notebook_rename.Notebook'); - var url = this.baseProjectUrl() + 'api/notebooks/' + this.notebookPath()+ this.notebook_name; + var url = this.baseProjectUrl() + 'api/notebooks' + this.notebookPath()+ this.notebook_name; $.ajax(url, settings); }; Notebook.prototype.rename_success = function (json, status, xhr) { this.notebook_name = json.name - var notebook_path = this.notebookPath() + this.notebook_name; - this.session.notebook_rename(notebook_path); + var name = this.notebook_name + var path = json.path + this.session.notebook_rename(name, path); $([IPython.events]).trigger('notebook_renamed.Notebook'); } @@ -1855,7 +1851,7 @@ var IPython = (function (IPython) { error : $.proxy(this.load_notebook_error,this), }; $([IPython.events]).trigger('notebook_loading.Notebook'); - var url = this.baseProjectUrl() + 'api/notebooks/' + this.notebookPath() + this.notebook_name; + var url = this.baseProjectUrl() + 'api/notebooks' + this.notebookPath() + this.notebook_name; $.ajax(url, settings); }; @@ -1916,7 +1912,7 @@ var IPython = (function (IPython) { // Create the session after the notebook is completely loaded to prevent // code execution upon loading, which is a security risk. if (this.session == null) { - this.start_session(this.notebook_path); + this.start_session(); } // load our checkpoint list IPython.notebook.list_checkpoints(); @@ -1988,7 +1984,7 @@ var IPython = (function (IPython) { * @method list_checkpoints */ Notebook.prototype.list_checkpoints = function () { - var url = this.baseProjectUrl() + 'api/notebooks/' + this.notebookPath() + this.notebook_name + '/checkpoints'; + var url = this.baseProjectUrl() + 'api/notebooks' + this.notebookPath() + this.notebook_name + '/checkpoints'; $.get(url).done( $.proxy(this.list_checkpoints_success, this) ).fail( @@ -2033,7 +2029,7 @@ var IPython = (function (IPython) { * @method create_checkpoint */ Notebook.prototype.create_checkpoint = function () { - var url = this.baseProjectUrl() + 'api/notebooks/' + this.notebookPath() + this.notebook_name + '/checkpoints'; + var url = this.baseProjectUrl() + 'api/notebooks' + this.notebookPath() + this.notebook_name + '/checkpoints'; $.post(url).done( $.proxy(this.create_checkpoint_success, this) ).fail( @@ -2113,18 +2109,8 @@ var IPython = (function (IPython) { * @param {String} checkpoint ID */ Notebook.prototype.restore_checkpoint = function (checkpoint) { -<<<<<<< HEAD - $([IPython.events]).trigger('checkpoint_restoring.Notebook', checkpoint); - if (this.notebook_path != "") { - var url = this.baseProjectUrl() + 'api/notebooks/' + this.notebook_path + this.notebook_name + '/checkpoints/' + checkpoint; - } - else { - var url = this.baseProjectUrl() + 'api/notebooks/' +this.notebook_name + '/checkpoints/' + checkpoint; - } -======= $([IPython.events]).trigger('notebook_restoring.Notebook', checkpoint); - var url = this.baseProjectUrl() + 'api/notebooks/' + this.notebookPath() + this.notebook_name + '/checkpoints/' + checkpoint; ->>>>>>> fixing path redirects, cleaning path logic + var url = this.baseProjectUrl() + 'api/notebooks' + this.notebookPath() + this.notebook_name + '/checkpoints/' + checkpoint; $.post(url).done( $.proxy(this.restore_checkpoint_success, this) ).fail( @@ -2165,7 +2151,7 @@ var IPython = (function (IPython) { */ Notebook.prototype.delete_checkpoint = function (checkpoint) { $([IPython.events]).trigger('notebook_restoring.Notebook', checkpoint); - var url = this.baseProjectUrl() + 'api/notebooks/' + this.notebookPath() + this.notebook_name + '/checkpoints/' + checkpoint; + var url = this.baseProjectUrl() + 'api/notebooks' + this.notebookPath() + this.notebook_name + '/checkpoints/' + checkpoint; $.ajax(url, { type: 'DELETE', success: $.proxy(this.delete_checkpoint_success, this), @@ -2205,4 +2191,3 @@ var IPython = (function (IPython) { return IPython; }(IPython)); -