##// END OF EJS Templates
Minor cleanups in the contents API....
Scott Sanderson -
Show More
@@ -1,265 +1,265 b''
1 """Tornado handlers for the contents web service."""
1 """Tornado handlers for the contents web service."""
2
2
3 # Copyright (c) IPython Development Team.
3 # Copyright (c) IPython Development Team.
4 # Distributed under the terms of the Modified BSD License.
4 # Distributed under the terms of the Modified BSD License.
5
5
6 import json
6 import json
7
7
8 from tornado import web
8 from tornado import web
9
9
10 from IPython.html.utils import url_path_join, url_escape
10 from IPython.html.utils import url_path_join, url_escape
11 from IPython.utils.jsonutil import date_default
11 from IPython.utils.jsonutil import date_default
12
12
13 from IPython.html.base.handlers import (
13 from IPython.html.base.handlers import (
14 IPythonHandler, json_errors, path_regex,
14 IPythonHandler, json_errors, path_regex,
15 )
15 )
16
16
17
17
18 def sort_key(model):
18 def sort_key(model):
19 """key function for case-insensitive sort by name and type"""
19 """key function for case-insensitive sort by name and type"""
20 iname = model['name'].lower()
20 iname = model['name'].lower()
21 type_key = {
21 type_key = {
22 'directory' : '0',
22 'directory' : '0',
23 'notebook' : '1',
23 'notebook' : '1',
24 'file' : '2',
24 'file' : '2',
25 }.get(model['type'], '9')
25 }.get(model['type'], '9')
26 return u'%s%s' % (type_key, iname)
26 return u'%s%s' % (type_key, iname)
27
27
28 class ContentsHandler(IPythonHandler):
28 class ContentsHandler(IPythonHandler):
29
29
30 SUPPORTED_METHODS = (u'GET', u'PUT', u'PATCH', u'POST', u'DELETE')
30 SUPPORTED_METHODS = (u'GET', u'PUT', u'PATCH', u'POST', u'DELETE')
31
31
32 def location_url(self, path):
32 def location_url(self, path):
33 """Return the full URL location of a file.
33 """Return the full URL location of a file.
34
34
35 Parameters
35 Parameters
36 ----------
36 ----------
37 path : unicode
37 path : unicode
38 The API path of the file, such as "foo/bar.txt".
38 The API path of the file, such as "foo/bar.txt".
39 """
39 """
40 return url_escape(url_path_join(
40 return url_escape(url_path_join(
41 self.base_url, 'api', 'contents', path
41 self.base_url, 'api', 'contents', path
42 ))
42 ))
43
43
44 def _finish_model(self, model, location=True):
44 def _finish_model(self, model, location=True):
45 """Finish a JSON request with a model, setting relevant headers, etc."""
45 """Finish a JSON request with a model, setting relevant headers, etc."""
46 if location:
46 if location:
47 location = self.location_url(model['path'])
47 location = self.location_url(model['path'])
48 self.set_header('Location', location)
48 self.set_header('Location', location)
49 self.set_header('Last-Modified', model['last_modified'])
49 self.set_header('Last-Modified', model['last_modified'])
50 self.finish(json.dumps(model, default=date_default))
50 self.finish(json.dumps(model, default=date_default))
51
51
52 @web.authenticated
52 @web.authenticated
53 @json_errors
53 @json_errors
54 def get(self, path=''):
54 def get(self, path=''):
55 """Return a model for a file or directory.
55 """Return a model for a file or directory.
56
56
57 A directory model contains a list of models (without content)
57 A directory model contains a list of models (without content)
58 of the files and directories it contains.
58 of the files and directories it contains.
59 """
59 """
60 path = path or ''
60 path = path or ''
61 type_ = self.get_query_argument('type', default=None)
61 type_ = self.get_query_argument('type', default=None)
62 if type_ not in {None, 'directory', 'file', 'notebook'}:
62 if type_ not in {None, 'directory', 'file', 'notebook'}:
63 raise web.HTTPError(400, u'Type %r is invalid' % type_)
63 raise web.HTTPError(400, u'Type %r is invalid' % type_)
64
64
65 format = self.get_query_argument('format', default=None)#
65 format = self.get_query_argument('format', default=None)
66 if format not in {None, 'text', 'base64'}:
66 if format not in {None, 'text', 'base64'}:
67 raise web.HTTPError(400, u'Format %r is invalid' % format)
67 raise web.HTTPError(400, u'Format %r is invalid' % format)
68
68
69 model = self.contents_manager.get(path=path, type_=type_, format=format)
69 model = self.contents_manager.get(path=path, type_=type_, format=format)
70 if model['type'] == 'directory':
70 if model['type'] == 'directory':
71 # group listing by type, then by name (case-insensitive)
71 # group listing by type, then by name (case-insensitive)
72 # FIXME: sorting should be done in the frontends
72 # FIXME: sorting should be done in the frontends
73 model['content'].sort(key=sort_key)
73 model['content'].sort(key=sort_key)
74 self._finish_model(model, location=False)
74 self._finish_model(model, location=False)
75
75
76 @web.authenticated
76 @web.authenticated
77 @json_errors
77 @json_errors
78 def patch(self, path=''):
78 def patch(self, path=''):
79 """PATCH renames a file or directory without re-uploading content."""
79 """PATCH renames a file or directory without re-uploading content."""
80 cm = self.contents_manager
80 cm = self.contents_manager
81 model = self.get_json_body()
81 model = self.get_json_body()
82 if model is None:
82 if model is None:
83 raise web.HTTPError(400, u'JSON body missing')
83 raise web.HTTPError(400, u'JSON body missing')
84 model = cm.update(model, path)
84 model = cm.update(model, path)
85 self._finish_model(model)
85 self._finish_model(model)
86
86
87 def _copy(self, copy_from, copy_to=None):
87 def _copy(self, copy_from, copy_to=None):
88 """Copy a file, optionally specifying a target directory."""
88 """Copy a file, optionally specifying a target directory."""
89 self.log.info(u"Copying {copy_from} to {copy_to}".format(
89 self.log.info(u"Copying {copy_from} to {copy_to}".format(
90 copy_from=copy_from,
90 copy_from=copy_from,
91 copy_to=copy_to or '',
91 copy_to=copy_to or '',
92 ))
92 ))
93 model = self.contents_manager.copy(copy_from, copy_to)
93 model = self.contents_manager.copy(copy_from, copy_to)
94 self.set_status(201)
94 self.set_status(201)
95 self._finish_model(model)
95 self._finish_model(model)
96
96
97 def _upload(self, model, path):
97 def _upload(self, model, path):
98 """Handle upload of a new file to path"""
98 """Handle upload of a new file to path"""
99 self.log.info(u"Uploading file to %s", path)
99 self.log.info(u"Uploading file to %s", path)
100 model = self.contents_manager.new(model, path)
100 model = self.contents_manager.new(model, path)
101 self.set_status(201)
101 self.set_status(201)
102 self._finish_model(model)
102 self._finish_model(model)
103
103
104 def _new_untitled(self, path, type='', ext=''):
104 def _new_untitled(self, path, type='', ext=''):
105 """Create a new, empty untitled entity"""
105 """Create a new, empty untitled entity"""
106 self.log.info(u"Creating new %s in %s", type or 'file', path)
106 self.log.info(u"Creating new %s in %s", type or 'file', path)
107 model = self.contents_manager.new_untitled(path=path, type=type, ext=ext)
107 model = self.contents_manager.new_untitled(path=path, type=type, ext=ext)
108 self.set_status(201)
108 self.set_status(201)
109 self._finish_model(model)
109 self._finish_model(model)
110
110
111 def _save(self, model, path):
111 def _save(self, model, path):
112 """Save an existing file."""
112 """Save an existing file."""
113 self.log.info(u"Saving file at %s", path)
113 self.log.info(u"Saving file at %s", path)
114 model = self.contents_manager.save(model, path)
114 model = self.contents_manager.save(model, path)
115 self._finish_model(model)
115 self._finish_model(model)
116
116
117 @web.authenticated
117 @web.authenticated
118 @json_errors
118 @json_errors
119 def post(self, path=''):
119 def post(self, path=''):
120 """Create a new file in the specified path.
120 """Create a new file in the specified path.
121
121
122 POST creates new files. The server always decides on the name.
122 POST creates new files. The server always decides on the name.
123
123
124 POST /api/contents/path
124 POST /api/contents/path
125 New untitled, empty file or directory.
125 New untitled, empty file or directory.
126 POST /api/contents/path
126 POST /api/contents/path
127 with body {"copy_from" : "/path/to/OtherNotebook.ipynb"}
127 with body {"copy_from" : "/path/to/OtherNotebook.ipynb"}
128 New copy of OtherNotebook in path
128 New copy of OtherNotebook in path
129 """
129 """
130
130
131 cm = self.contents_manager
131 cm = self.contents_manager
132
132
133 if cm.file_exists(path):
133 if cm.file_exists(path):
134 raise web.HTTPError(400, "Cannot POST to files, use PUT instead.")
134 raise web.HTTPError(400, "Cannot POST to files, use PUT instead.")
135
135
136 if not cm.dir_exists(path):
136 if not cm.dir_exists(path):
137 raise web.HTTPError(404, "No such directory: %s" % path)
137 raise web.HTTPError(404, "No such directory: %s" % path)
138
138
139 model = self.get_json_body()
139 model = self.get_json_body()
140
140
141 if model is not None:
141 if model is not None:
142 copy_from = model.get('copy_from')
142 copy_from = model.get('copy_from')
143 ext = model.get('ext', '')
143 ext = model.get('ext', '')
144 type = model.get('type', '')
144 type = model.get('type', '')
145 if copy_from:
145 if copy_from:
146 self._copy(copy_from, path)
146 self._copy(copy_from, path)
147 else:
147 else:
148 self._new_untitled(path, type=type, ext=ext)
148 self._new_untitled(path, type=type, ext=ext)
149 else:
149 else:
150 self._new_untitled(path)
150 self._new_untitled(path)
151
151
152 @web.authenticated
152 @web.authenticated
153 @json_errors
153 @json_errors
154 def put(self, path=''):
154 def put(self, path=''):
155 """Saves the file in the location specified by name and path.
155 """Saves the file in the location specified by name and path.
156
156
157 PUT is very similar to POST, but the requester specifies the name,
157 PUT is very similar to POST, but the requester specifies the name,
158 whereas with POST, the server picks the name.
158 whereas with POST, the server picks the name.
159
159
160 PUT /api/contents/path/Name.ipynb
160 PUT /api/contents/path/Name.ipynb
161 Save notebook at ``path/Name.ipynb``. Notebook structure is specified
161 Save notebook at ``path/Name.ipynb``. Notebook structure is specified
162 in `content` key of JSON request body. If content is not specified,
162 in `content` key of JSON request body. If content is not specified,
163 create a new empty notebook.
163 create a new empty notebook.
164 """
164 """
165 model = self.get_json_body()
165 model = self.get_json_body()
166 if model:
166 if model:
167 if model.get('copy_from'):
167 if model.get('copy_from'):
168 raise web.HTTPError(400, "Cannot copy with PUT, only POST")
168 raise web.HTTPError(400, "Cannot copy with PUT, only POST")
169 if self.contents_manager.file_exists(path):
169 if self.contents_manager.file_exists(path):
170 self._save(model, path)
170 self._save(model, path)
171 else:
171 else:
172 self._upload(model, path)
172 self._upload(model, path)
173 else:
173 else:
174 self._new_untitled(path)
174 self._new_untitled(path)
175
175
176 @web.authenticated
176 @web.authenticated
177 @json_errors
177 @json_errors
178 def delete(self, path=''):
178 def delete(self, path=''):
179 """delete a file in the given path"""
179 """delete a file in the given path"""
180 cm = self.contents_manager
180 cm = self.contents_manager
181 self.log.warn('delete %s', path)
181 self.log.warn('delete %s', path)
182 cm.delete(path)
182 cm.delete(path)
183 self.set_status(204)
183 self.set_status(204)
184 self.finish()
184 self.finish()
185
185
186
186
187 class CheckpointsHandler(IPythonHandler):
187 class CheckpointsHandler(IPythonHandler):
188
188
189 SUPPORTED_METHODS = ('GET', 'POST')
189 SUPPORTED_METHODS = ('GET', 'POST')
190
190
191 @web.authenticated
191 @web.authenticated
192 @json_errors
192 @json_errors
193 def get(self, path=''):
193 def get(self, path=''):
194 """get lists checkpoints for a file"""
194 """get lists checkpoints for a file"""
195 cm = self.contents_manager
195 cm = self.contents_manager
196 checkpoints = cm.list_checkpoints(path)
196 checkpoints = cm.list_checkpoints(path)
197 data = json.dumps(checkpoints, default=date_default)
197 data = json.dumps(checkpoints, default=date_default)
198 self.finish(data)
198 self.finish(data)
199
199
200 @web.authenticated
200 @web.authenticated
201 @json_errors
201 @json_errors
202 def post(self, path=''):
202 def post(self, path=''):
203 """post creates a new checkpoint"""
203 """post creates a new checkpoint"""
204 cm = self.contents_manager
204 cm = self.contents_manager
205 checkpoint = cm.create_checkpoint(path)
205 checkpoint = cm.create_checkpoint(path)
206 data = json.dumps(checkpoint, default=date_default)
206 data = json.dumps(checkpoint, default=date_default)
207 location = url_path_join(self.base_url, 'api/contents',
207 location = url_path_join(self.base_url, 'api/contents',
208 path, 'checkpoints', checkpoint['id'])
208 path, 'checkpoints', checkpoint['id'])
209 self.set_header('Location', url_escape(location))
209 self.set_header('Location', url_escape(location))
210 self.set_status(201)
210 self.set_status(201)
211 self.finish(data)
211 self.finish(data)
212
212
213
213
214 class ModifyCheckpointsHandler(IPythonHandler):
214 class ModifyCheckpointsHandler(IPythonHandler):
215
215
216 SUPPORTED_METHODS = ('POST', 'DELETE')
216 SUPPORTED_METHODS = ('POST', 'DELETE')
217
217
218 @web.authenticated
218 @web.authenticated
219 @json_errors
219 @json_errors
220 def post(self, path, checkpoint_id):
220 def post(self, path, checkpoint_id):
221 """post restores a file from a checkpoint"""
221 """post restores a file from a checkpoint"""
222 cm = self.contents_manager
222 cm = self.contents_manager
223 cm.restore_checkpoint(checkpoint_id, path)
223 cm.restore_checkpoint(checkpoint_id, path)
224 self.set_status(204)
224 self.set_status(204)
225 self.finish()
225 self.finish()
226
226
227 @web.authenticated
227 @web.authenticated
228 @json_errors
228 @json_errors
229 def delete(self, path, checkpoint_id):
229 def delete(self, path, checkpoint_id):
230 """delete clears a checkpoint for a given file"""
230 """delete clears a checkpoint for a given file"""
231 cm = self.contents_manager
231 cm = self.contents_manager
232 cm.delete_checkpoint(checkpoint_id, path)
232 cm.delete_checkpoint(checkpoint_id, path)
233 self.set_status(204)
233 self.set_status(204)
234 self.finish()
234 self.finish()
235
235
236
236
237 class NotebooksRedirectHandler(IPythonHandler):
237 class NotebooksRedirectHandler(IPythonHandler):
238 """Redirect /api/notebooks to /api/contents"""
238 """Redirect /api/notebooks to /api/contents"""
239 SUPPORTED_METHODS = ('GET', 'PUT', 'PATCH', 'POST', 'DELETE')
239 SUPPORTED_METHODS = ('GET', 'PUT', 'PATCH', 'POST', 'DELETE')
240
240
241 def get(self, path):
241 def get(self, path):
242 self.log.warn("/api/notebooks is deprecated, use /api/contents")
242 self.log.warn("/api/notebooks is deprecated, use /api/contents")
243 self.redirect(url_path_join(
243 self.redirect(url_path_join(
244 self.base_url,
244 self.base_url,
245 'api/contents',
245 'api/contents',
246 path
246 path
247 ))
247 ))
248
248
249 put = patch = post = delete = get
249 put = patch = post = delete = get
250
250
251
251
252 #-----------------------------------------------------------------------------
252 #-----------------------------------------------------------------------------
253 # URL to handler mappings
253 # URL to handler mappings
254 #-----------------------------------------------------------------------------
254 #-----------------------------------------------------------------------------
255
255
256
256
257 _checkpoint_id_regex = r"(?P<checkpoint_id>[\w-]+)"
257 _checkpoint_id_regex = r"(?P<checkpoint_id>[\w-]+)"
258
258
259 default_handlers = [
259 default_handlers = [
260 (r"/api/contents%s/checkpoints" % path_regex, CheckpointsHandler),
260 (r"/api/contents%s/checkpoints" % path_regex, CheckpointsHandler),
261 (r"/api/contents%s/checkpoints/%s" % (path_regex, _checkpoint_id_regex),
261 (r"/api/contents%s/checkpoints/%s" % (path_regex, _checkpoint_id_regex),
262 ModifyCheckpointsHandler),
262 ModifyCheckpointsHandler),
263 (r"/api/contents%s" % path_regex, ContentsHandler),
263 (r"/api/contents%s" % path_regex, ContentsHandler),
264 (r"/api/notebooks/?(.*)", NotebooksRedirectHandler),
264 (r"/api/notebooks/?(.*)", NotebooksRedirectHandler),
265 ]
265 ]
@@ -1,67 +1,68 b''
1 // Copyright (c) IPython Development Team.
1 // Copyright (c) IPython Development Team.
2 // Distributed under the terms of the Modified BSD License.
2 // Distributed under the terms of the Modified BSD License.
3
3
4 define([
4 define([
5 'jquery',
5 'jquery',
6 'base/js/utils',
6 'base/js/utils',
7 ],
7 ],
8 function($, utils) {
8 function($, utils) {
9 "use strict";
9 var ConfigSection = function(section_name, options) {
10 var ConfigSection = function(section_name, options) {
10 this.section_name = section_name;
11 this.section_name = section_name;
11 this.base_url = options.base_url;
12 this.base_url = options.base_url;
12 this.data = {};
13 this.data = {};
13
14
14 var that = this;
15 var that = this;
15
16
16 /* .loaded is a promise, fulfilled the first time the config is loaded
17 /* .loaded is a promise, fulfilled the first time the config is loaded
17 * from the server. Code can do:
18 * from the server. Code can do:
18 * conf.loaded.then(function() { ... using conf.data ... });
19 * conf.loaded.then(function() { ... using conf.data ... });
19 */
20 */
20 this._one_load_finished = false;
21 this._one_load_finished = false;
21 this.loaded = new Promise(function(resolve, reject) {
22 this.loaded = new Promise(function(resolve, reject) {
22 that._finish_firstload = resolve;
23 that._finish_firstload = resolve;
23 });
24 });
24 };
25 };
25
26
26 ConfigSection.prototype.api_url = function() {
27 ConfigSection.prototype.api_url = function() {
27 return utils.url_join_encode(this.base_url, 'api/config', this.section_name);
28 return utils.url_join_encode(this.base_url, 'api/config', this.section_name);
28 };
29 };
29
30
30 ConfigSection.prototype._load_done = function() {
31 ConfigSection.prototype._load_done = function() {
31 if (!this._one_load_finished) {
32 if (!this._one_load_finished) {
32 this._one_load_finished = true;
33 this._one_load_finished = true;
33 this._finish_firstload();
34 this._finish_firstload();
34 }
35 }
35 };
36 };
36
37
37 ConfigSection.prototype.load = function() {
38 ConfigSection.prototype.load = function() {
38 var that = this;
39 var that = this;
39 return utils.promising_ajax(this.api_url(), {
40 return utils.promising_ajax(this.api_url(), {
40 cache: false,
41 cache: false,
41 type: "GET",
42 type: "GET",
42 dataType: "json",
43 dataType: "json",
43 }).then(function(data) {
44 }).then(function(data) {
44 that.data = data;
45 that.data = data;
45 that._load_done();
46 that._load_done();
46 return data;
47 return data;
47 });
48 });
48 };
49 };
49
50
50 ConfigSection.prototype.update = function(newdata) {
51 ConfigSection.prototype.update = function(newdata) {
51 var that = this;
52 var that = this;
52 return utils.promising_ajax(this.api_url(), {
53 return utils.promising_ajax(this.api_url(), {
53 processData: false,
54 processData: false,
54 type : "PATCH",
55 type : "PATCH",
55 data: JSON.stringify(newdata),
56 data: JSON.stringify(newdata),
56 dataType : "json",
57 dataType : "json",
57 contentType: 'application/json',
58 contentType: 'application/json',
58 }).then(function(data) {
59 }).then(function(data) {
59 that.data = data;
60 that.data = data;
60 that._load_done();
61 that._load_done();
61 return data;
62 return data;
62 });
63 });
63 };
64 };
64
65
65 return {ConfigSection: ConfigSection};
66 return {ConfigSection: ConfigSection};
66
67
67 });
68 });
@@ -1,250 +1,251 b''
1 // Copyright (c) IPython Development Team.
1 // Copyright (c) IPython Development Team.
2 // Distributed under the terms of the Modified BSD License.
2 // Distributed under the terms of the Modified BSD License.
3
3
4 define([
4 define([
5 'base/js/namespace',
5 'base/js/namespace',
6 'jquery',
6 'jquery',
7 'base/js/utils',
7 'base/js/utils',
8 ], function(IPython, $, utils) {
8 ], function(IPython, $, utils) {
9 "use strict";
9 var Contents = function(options) {
10 var Contents = function(options) {
10 /**
11 /**
11 * Constructor
12 * Constructor
12 *
13 *
13 * A contents handles passing file operations
14 * A contents handles passing file operations
14 * to the back-end. This includes checkpointing
15 * to the back-end. This includes checkpointing
15 * with the normal file operations.
16 * with the normal file operations.
16 *
17 *
17 * Parameters:
18 * Parameters:
18 * options: dictionary
19 * options: dictionary
19 * Dictionary of keyword arguments.
20 * Dictionary of keyword arguments.
20 * base_url: string
21 * base_url: string
21 */
22 */
22 this.base_url = options.base_url;
23 this.base_url = options.base_url;
23 };
24 };
24
25
25 /** Error type */
26 /** Error type */
26 Contents.DIRECTORY_NOT_EMPTY_ERROR = 'DirectoryNotEmptyError';
27 Contents.DIRECTORY_NOT_EMPTY_ERROR = 'DirectoryNotEmptyError';
27
28
28 Contents.DirectoryNotEmptyError = function() {
29 Contents.DirectoryNotEmptyError = function() {
29 // Constructor
30 // Constructor
30 //
31 //
31 // An error representing the result of attempting to delete a non-empty
32 // An error representing the result of attempting to delete a non-empty
32 // directory.
33 // directory.
33 this.message = 'A directory must be empty before being deleted.';
34 this.message = 'A directory must be empty before being deleted.';
34 };
35 };
35
36
36 Contents.DirectoryNotEmptyError.prototype = Object.create(Error.prototype);
37 Contents.DirectoryNotEmptyError.prototype = Object.create(Error.prototype);
37 Contents.DirectoryNotEmptyError.prototype.name =
38 Contents.DirectoryNotEmptyError.prototype.name =
38 Contents.DIRECTORY_NOT_EMPTY_ERROR;
39 Contents.DIRECTORY_NOT_EMPTY_ERROR;
39
40
40
41
41 Contents.prototype.api_url = function() {
42 Contents.prototype.api_url = function() {
42 var url_parts = [this.base_url, 'api/contents'].concat(
43 var url_parts = [this.base_url, 'api/contents'].concat(
43 Array.prototype.slice.apply(arguments));
44 Array.prototype.slice.apply(arguments));
44 return utils.url_join_encode.apply(null, url_parts);
45 return utils.url_join_encode.apply(null, url_parts);
45 };
46 };
46
47
47 /**
48 /**
48 * Creates a basic error handler that wraps a jqXHR error as an Error.
49 * Creates a basic error handler that wraps a jqXHR error as an Error.
49 *
50 *
50 * Takes a callback that accepts an Error, and returns a callback that can
51 * Takes a callback that accepts an Error, and returns a callback that can
51 * be passed directly to $.ajax, which will wrap the error from jQuery
52 * be passed directly to $.ajax, which will wrap the error from jQuery
52 * as an Error, and pass that to the original callback.
53 * as an Error, and pass that to the original callback.
53 *
54 *
54 * @method create_basic_error_handler
55 * @method create_basic_error_handler
55 * @param{Function} callback
56 * @param{Function} callback
56 * @return{Function}
57 * @return{Function}
57 */
58 */
58 Contents.prototype.create_basic_error_handler = function(callback) {
59 Contents.prototype.create_basic_error_handler = function(callback) {
59 if (!callback) {
60 if (!callback) {
60 return utils.log_ajax_error;
61 return utils.log_ajax_error;
61 }
62 }
62 return function(xhr, status, error) {
63 return function(xhr, status, error) {
63 callback(utils.wrap_ajax_error(xhr, status, error));
64 callback(utils.wrap_ajax_error(xhr, status, error));
64 };
65 };
65 };
66 };
66
67
67 /**
68 /**
68 * File Functions (including notebook operations)
69 * File Functions (including notebook operations)
69 */
70 */
70
71
71 /**
72 /**
72 * Get a file.
73 * Get a file.
73 *
74 *
74 * Calls success with file JSON model, or error with error.
75 * Calls success with file JSON model, or error with error.
75 *
76 *
76 * @method get
77 * @method get
77 * @param {String} path
78 * @param {String} path
78 * @param {Object} options
79 * @param {Object} options
79 * type : 'notebook', 'file', or 'directory'
80 * type : 'notebook', 'file', or 'directory'
80 * format: 'text' or 'base64'; only relevant for type: 'file'
81 * format: 'text' or 'base64'; only relevant for type: 'file'
81 */
82 */
82 Contents.prototype.get = function (path, options) {
83 Contents.prototype.get = function (path, options) {
83 /**
84 /**
84 * We do the call with settings so we can set cache to false.
85 * We do the call with settings so we can set cache to false.
85 */
86 */
86 var settings = {
87 var settings = {
87 processData : false,
88 processData : false,
88 cache : false,
89 cache : false,
89 type : "GET",
90 type : "GET",
90 dataType : "json",
91 dataType : "json",
91 };
92 };
92 var url = this.api_url(path);
93 var url = this.api_url(path);
93 params = {};
94 var params = {};
94 if (options.type) { params.type = options.type; }
95 if (options.type) { params.type = options.type; }
95 if (options.format) { params.format = options.format; }
96 if (options.format) { params.format = options.format; }
96 return utils.promising_ajax(url + '?' + $.param(params), settings);
97 return utils.promising_ajax(url + '?' + $.param(params), settings);
97 };
98 };
98
99
99
100
100 /**
101 /**
101 * Creates a new untitled file or directory in the specified directory path.
102 * Creates a new untitled file or directory in the specified directory path.
102 *
103 *
103 * @method new
104 * @method new
104 * @param {String} path: the directory in which to create the new file/directory
105 * @param {String} path: the directory in which to create the new file/directory
105 * @param {Object} options:
106 * @param {Object} options:
106 * ext: file extension to use
107 * ext: file extension to use
107 * type: model type to create ('notebook', 'file', or 'directory')
108 * type: model type to create ('notebook', 'file', or 'directory')
108 */
109 */
109 Contents.prototype.new_untitled = function(path, options) {
110 Contents.prototype.new_untitled = function(path, options) {
110 var data = JSON.stringify({
111 var data = JSON.stringify({
111 ext: options.ext,
112 ext: options.ext,
112 type: options.type
113 type: options.type
113 });
114 });
114
115
115 var settings = {
116 var settings = {
116 processData : false,
117 processData : false,
117 type : "POST",
118 type : "POST",
118 data: data,
119 data: data,
119 dataType : "json",
120 dataType : "json",
120 };
121 };
121 return utils.promising_ajax(this.api_url(path), settings);
122 return utils.promising_ajax(this.api_url(path), settings);
122 };
123 };
123
124
124 Contents.prototype.delete = function(path) {
125 Contents.prototype.delete = function(path) {
125 var settings = {
126 var settings = {
126 processData : false,
127 processData : false,
127 type : "DELETE",
128 type : "DELETE",
128 dataType : "json",
129 dataType : "json",
129 };
130 };
130 var url = this.api_url(path);
131 var url = this.api_url(path);
131 return utils.promising_ajax(url, settings).catch(
132 return utils.promising_ajax(url, settings).catch(
132 // Translate certain errors to more specific ones.
133 // Translate certain errors to more specific ones.
133 function(error) {
134 function(error) {
134 // TODO: update IPEP27 to specify errors more precisely, so
135 // TODO: update IPEP27 to specify errors more precisely, so
135 // that error types can be detected here with certainty.
136 // that error types can be detected here with certainty.
136 if (error.xhr.status === 400) {
137 if (error.xhr.status === 400) {
137 throw new Contents.DirectoryNotEmptyError();
138 throw new Contents.DirectoryNotEmptyError();
138 }
139 }
139 throw error;
140 throw error;
140 }
141 }
141 );
142 );
142 };
143 };
143
144
144 Contents.prototype.rename = function(path, new_path) {
145 Contents.prototype.rename = function(path, new_path) {
145 var data = {path: new_path};
146 var data = {path: new_path};
146 var settings = {
147 var settings = {
147 processData : false,
148 processData : false,
148 type : "PATCH",
149 type : "PATCH",
149 data : JSON.stringify(data),
150 data : JSON.stringify(data),
150 dataType: "json",
151 dataType: "json",
151 contentType: 'application/json',
152 contentType: 'application/json',
152 };
153 };
153 var url = this.api_url(path);
154 var url = this.api_url(path);
154 return utils.promising_ajax(url, settings);
155 return utils.promising_ajax(url, settings);
155 };
156 };
156
157
157 Contents.prototype.save = function(path, model) {
158 Contents.prototype.save = function(path, model) {
158 /**
159 /**
159 * We do the call with settings so we can set cache to false.
160 * We do the call with settings so we can set cache to false.
160 */
161 */
161 var settings = {
162 var settings = {
162 processData : false,
163 processData : false,
163 type : "PUT",
164 type : "PUT",
164 data : JSON.stringify(model),
165 data : JSON.stringify(model),
165 contentType: 'application/json',
166 contentType: 'application/json',
166 };
167 };
167 var url = this.api_url(path);
168 var url = this.api_url(path);
168 return utils.promising_ajax(url, settings);
169 return utils.promising_ajax(url, settings);
169 };
170 };
170
171
171 Contents.prototype.copy = function(from_file, to_dir) {
172 Contents.prototype.copy = function(from_file, to_dir) {
172 /**
173 /**
173 * Copy a file into a given directory via POST
174 * Copy a file into a given directory via POST
174 * The server will select the name of the copied file
175 * The server will select the name of the copied file
175 */
176 */
176 var url = this.api_url(to_dir);
177 var url = this.api_url(to_dir);
177
178
178 var settings = {
179 var settings = {
179 processData : false,
180 processData : false,
180 type: "POST",
181 type: "POST",
181 data: JSON.stringify({copy_from: from_file}),
182 data: JSON.stringify({copy_from: from_file}),
182 dataType : "json",
183 dataType : "json",
183 };
184 };
184 return utils.promising_ajax(url, settings);
185 return utils.promising_ajax(url, settings);
185 };
186 };
186
187
187 /**
188 /**
188 * Checkpointing Functions
189 * Checkpointing Functions
189 */
190 */
190
191
191 Contents.prototype.create_checkpoint = function(path) {
192 Contents.prototype.create_checkpoint = function(path) {
192 var url = this.api_url(path, 'checkpoints');
193 var url = this.api_url(path, 'checkpoints');
193 var settings = {
194 var settings = {
194 type : "POST",
195 type : "POST",
195 dataType : "json",
196 dataType : "json",
196 };
197 };
197 return utils.promising_ajax(url, settings);
198 return utils.promising_ajax(url, settings);
198 };
199 };
199
200
200 Contents.prototype.list_checkpoints = function(path) {
201 Contents.prototype.list_checkpoints = function(path) {
201 var url = this.api_url(path, 'checkpoints');
202 var url = this.api_url(path, 'checkpoints');
202 var settings = {
203 var settings = {
203 type : "GET",
204 type : "GET",
204 cache: false,
205 cache: false,
205 dataType: "json",
206 dataType: "json",
206 };
207 };
207 return utils.promising_ajax(url, settings);
208 return utils.promising_ajax(url, settings);
208 };
209 };
209
210
210 Contents.prototype.restore_checkpoint = function(path, checkpoint_id) {
211 Contents.prototype.restore_checkpoint = function(path, checkpoint_id) {
211 var url = this.api_url(path, 'checkpoints', checkpoint_id);
212 var url = this.api_url(path, 'checkpoints', checkpoint_id);
212 var settings = {
213 var settings = {
213 type : "POST",
214 type : "POST",
214 };
215 };
215 return utils.promising_ajax(url, settings);
216 return utils.promising_ajax(url, settings);
216 };
217 };
217
218
218 Contents.prototype.delete_checkpoint = function(path, checkpoint_id) {
219 Contents.prototype.delete_checkpoint = function(path, checkpoint_id) {
219 var url = this.api_url(path, 'checkpoints', checkpoint_id);
220 var url = this.api_url(path, 'checkpoints', checkpoint_id);
220 var settings = {
221 var settings = {
221 type : "DELETE",
222 type : "DELETE",
222 };
223 };
223 return utils.promising_ajax(url, settings);
224 return utils.promising_ajax(url, settings);
224 };
225 };
225
226
226 /**
227 /**
227 * File management functions
228 * File management functions
228 */
229 */
229
230
230 /**
231 /**
231 * List notebooks and directories at a given path
232 * List notebooks and directories at a given path
232 *
233 *
233 * On success, load_callback is called with an array of dictionaries
234 * On success, load_callback is called with an array of dictionaries
234 * representing individual files or directories. Each dictionary has
235 * representing individual files or directories. Each dictionary has
235 * the keys:
236 * the keys:
236 * type: "notebook" or "directory"
237 * type: "notebook" or "directory"
237 * created: created date
238 * created: created date
238 * last_modified: last modified dat
239 * last_modified: last modified dat
239 * @method list_notebooks
240 * @method list_notebooks
240 * @param {String} path The path to list notebooks in
241 * @param {String} path The path to list notebooks in
241 */
242 */
242 Contents.prototype.list_contents = function(path) {
243 Contents.prototype.list_contents = function(path) {
243 return this.get(path, {type: 'directory'});
244 return this.get(path, {type: 'directory'});
244 };
245 };
245
246
246
247
247 IPython.Contents = Contents;
248 IPython.Contents = Contents;
248
249
249 return {'Contents': Contents};
250 return {'Contents': Contents};
250 });
251 });
General Comments 0
You need to be logged in to leave comments. Login now