Show More
@@ -176,6 +176,7 b' class FileContentsManager(ContentsManager):' | |||||
176 | model['created'] = created |
|
176 | model['created'] = created | |
177 | model['content'] = None |
|
177 | model['content'] = None | |
178 | model['format'] = None |
|
178 | model['format'] = None | |
|
179 | model['message'] = None | |||
179 | return model |
|
180 | return model | |
180 |
|
181 | |||
181 | def _dir_model(self, name, path='', content=True): |
|
182 | def _dir_model(self, name, path='', content=True): | |
@@ -258,6 +259,7 b' class FileContentsManager(ContentsManager):' | |||||
258 | self.mark_trusted_cells(nb, name, path) |
|
259 | self.mark_trusted_cells(nb, name, path) | |
259 | model['content'] = nb |
|
260 | model['content'] = nb | |
260 | model['format'] = 'json' |
|
261 | model['format'] = 'json' | |
|
262 | self.validate_notebook_model(model) | |||
261 | return model |
|
263 | return model | |
262 |
|
264 | |||
263 | def get_model(self, name, path='', content=True): |
|
265 | def get_model(self, name, path='', content=True): | |
@@ -366,7 +368,14 b' class FileContentsManager(ContentsManager):' | |||||
366 | except Exception as e: |
|
368 | except Exception as e: | |
367 | raise web.HTTPError(400, u'Unexpected error while saving file: %s %s' % (os_path, e)) |
|
369 | raise web.HTTPError(400, u'Unexpected error while saving file: %s %s' % (os_path, e)) | |
368 |
|
370 | |||
|
371 | validation_message = None | |||
|
372 | if model['type'] == 'notebook': | |||
|
373 | self.validate_notebook_model(model) | |||
|
374 | validation_message = model.get('message', None) | |||
|
375 | ||||
369 | model = self.get_model(new_name, new_path, content=False) |
|
376 | model = self.get_model(new_name, new_path, content=False) | |
|
377 | if validation_message: | |||
|
378 | model['message'] = validation_message | |||
370 | return model |
|
379 | return model | |
371 |
|
380 | |||
372 | def update(self, model, name, path=''): |
|
381 | def update(self, model, name, path=''): |
@@ -5,6 +5,7 b'' | |||||
5 |
|
5 | |||
6 | from fnmatch import fnmatch |
|
6 | from fnmatch import fnmatch | |
7 | import itertools |
|
7 | import itertools | |
|
8 | import json | |||
8 | import os |
|
9 | import os | |
9 |
|
10 | |||
10 | from tornado.web import HTTPError |
|
11 | from tornado.web import HTTPError | |
@@ -216,6 +217,16 b' class ContentsManager(LoggingConfigurable):' | |||||
216 | break |
|
217 | break | |
217 | return name |
|
218 | return name | |
218 |
|
219 | |||
|
220 | def validate_notebook_model(self, model): | |||
|
221 | """Add failed-validation message to model""" | |||
|
222 | try: | |||
|
223 | current.validate(model['content']) | |||
|
224 | except current.ValidationError as e: | |||
|
225 | model['message'] = 'Notebook Validation failed: {}:\n{}'.format( | |||
|
226 | e.message, json.dumps(e.instance, indent=1, default=lambda obj: '<UNKNOWN>'), | |||
|
227 | ) | |||
|
228 | return model | |||
|
229 | ||||
219 | def create_file(self, model=None, path='', ext='.ipynb'): |
|
230 | def create_file(self, model=None, path='', ext='.ipynb'): | |
220 | """Create a new file or directory and return its model with no content.""" |
|
231 | """Create a new file or directory and return its model with no content.""" | |
221 | path = path.strip('/') |
|
232 | path = path.strip('/') |
@@ -1826,6 +1826,7 b' define([' | |||||
1826 | * @param {Object} data JSON representation of a notebook |
|
1826 | * @param {Object} data JSON representation of a notebook | |
1827 | */ |
|
1827 | */ | |
1828 | Notebook.prototype.fromJSON = function (data) { |
|
1828 | Notebook.prototype.fromJSON = function (data) { | |
|
1829 | ||||
1829 | var content = data.content; |
|
1830 | var content = data.content; | |
1830 | var ncells = this.ncells(); |
|
1831 | var ncells = this.ncells(); | |
1831 | var i; |
|
1832 | var i; | |
@@ -1975,6 +1976,7 b' define([' | |||||
1975 | type : "PUT", |
|
1976 | type : "PUT", | |
1976 | data : JSON.stringify(model), |
|
1977 | data : JSON.stringify(model), | |
1977 | headers : {'Content-Type': 'application/json'}, |
|
1978 | headers : {'Content-Type': 'application/json'}, | |
|
1979 | dataType : "json", | |||
1978 | success : $.proxy(this.save_notebook_success, this, start), |
|
1980 | success : $.proxy(this.save_notebook_success, this, start), | |
1979 | error : $.proxy(this.save_notebook_error, this) |
|
1981 | error : $.proxy(this.save_notebook_error, this) | |
1980 | }; |
|
1982 | }; | |
@@ -2004,6 +2006,30 b' define([' | |||||
2004 | */ |
|
2006 | */ | |
2005 | Notebook.prototype.save_notebook_success = function (start, data, status, xhr) { |
|
2007 | Notebook.prototype.save_notebook_success = function (start, data, status, xhr) { | |
2006 | this.set_dirty(false); |
|
2008 | this.set_dirty(false); | |
|
2009 | if (data.message) { | |||
|
2010 | // save succeeded, but validation failed. | |||
|
2011 | var body = $("<div>"); | |||
|
2012 | var title = "Notebook validation failed"; | |||
|
2013 | ||||
|
2014 | body.append($("<p>").text( | |||
|
2015 | "The save operation succeeded," + | |||
|
2016 | " but the notebook does not appear to be valid." + | |||
|
2017 | " The validation error was:" | |||
|
2018 | )).append($("<div>").addClass("validation-error").append( | |||
|
2019 | $("<pre>").text(data.message) | |||
|
2020 | )); | |||
|
2021 | dialog.modal({ | |||
|
2022 | notebook: this, | |||
|
2023 | keyboard_manager: this.keyboard_manager, | |||
|
2024 | title: title, | |||
|
2025 | body: body, | |||
|
2026 | buttons : { | |||
|
2027 | OK : { | |||
|
2028 | "class" : "btn-primary" | |||
|
2029 | } | |||
|
2030 | } | |||
|
2031 | }); | |||
|
2032 | } | |||
2007 | this.events.trigger('notebook_saved.Notebook'); |
|
2033 | this.events.trigger('notebook_saved.Notebook'); | |
2008 | this._update_autosave_interval(start); |
|
2034 | this._update_autosave_interval(start); | |
2009 | if (this._checkpoint_after_save) { |
|
2035 | if (this._checkpoint_after_save) { | |
@@ -2278,7 +2304,57 b' define([' | |||||
2278 | * @param {jqXHR} xhr jQuery Ajax object |
|
2304 | * @param {jqXHR} xhr jQuery Ajax object | |
2279 | */ |
|
2305 | */ | |
2280 | Notebook.prototype.load_notebook_success = function (data, status, xhr) { |
|
2306 | Notebook.prototype.load_notebook_success = function (data, status, xhr) { | |
2281 | this.fromJSON(data); |
|
2307 | var failed; | |
|
2308 | try { | |||
|
2309 | this.fromJSON(data); | |||
|
2310 | } catch (e) { | |||
|
2311 | failed = e; | |||
|
2312 | console.log("Notebook failed to load from JSON:", e); | |||
|
2313 | } | |||
|
2314 | if (failed || data.message) { | |||
|
2315 | // *either* fromJSON failed or validation failed | |||
|
2316 | var body = $("<div>"); | |||
|
2317 | var title; | |||
|
2318 | if (failed) { | |||
|
2319 | title = "Notebook failed to load"; | |||
|
2320 | body.append($("<p>").text( | |||
|
2321 | "The error was: " | |||
|
2322 | )).append($("<div>").addClass("js-error").text( | |||
|
2323 | failed.toString() | |||
|
2324 | )).append($("<p>").text( | |||
|
2325 | "See the error console for details." | |||
|
2326 | )); | |||
|
2327 | } else { | |||
|
2328 | title = "Notebook validation failed"; | |||
|
2329 | } | |||
|
2330 | ||||
|
2331 | if (data.message) { | |||
|
2332 | var msg; | |||
|
2333 | if (failed) { | |||
|
2334 | msg = "The notebook also failed validation:" | |||
|
2335 | } else { | |||
|
2336 | msg = "An invalid notebook may not function properly." + | |||
|
2337 | " The validation error was:" | |||
|
2338 | } | |||
|
2339 | body.append($("<p>").text( | |||
|
2340 | msg | |||
|
2341 | )).append($("<div>").addClass("validation-error").append( | |||
|
2342 | $("<pre>").text(data.message) | |||
|
2343 | )); | |||
|
2344 | } | |||
|
2345 | ||||
|
2346 | dialog.modal({ | |||
|
2347 | notebook: this, | |||
|
2348 | keyboard_manager: this.keyboard_manager, | |||
|
2349 | title: title, | |||
|
2350 | body: body, | |||
|
2351 | buttons : { | |||
|
2352 | OK : { | |||
|
2353 | "class" : "btn-primary" | |||
|
2354 | } | |||
|
2355 | } | |||
|
2356 | }); | |||
|
2357 | } | |||
2282 | if (this.ncells() === 0) { |
|
2358 | if (this.ncells() === 0) { | |
2283 | this.insert_cell_below('code'); |
|
2359 | this.insert_cell_below('code'); | |
2284 | this.edit_mode(0); |
|
2360 | this.edit_mode(0); |
General Comments 0
You need to be logged in to leave comments.
Login now