##// END OF EJS Templates
Simplifying logic on login page.
Brian E. Granger -
Show More
@@ -0,0 +1,29 b''
1 //----------------------------------------------------------------------------
2 // Copyright (C) 2008-2011 The IPython Development Team
3 //
4 // Distributed under the terms of the BSD License. The full license is in
5 // the file COPYING, distributed as part of this software.
6 //----------------------------------------------------------------------------
7
8 //============================================================================
9 // On document ready
10 //============================================================================
11
12
13 $(document).ready(function () {
14
15 $('div#header').addClass('border-box-sizing');
16 $('div#header_border').addClass('border-box-sizing ui-widget ui-widget-content');
17
18 $('div#main_app').addClass('border-box-sizing ui-widget');
19 $('div#app_hbox').addClass('hbox');
20
21 $('div#left_panel').addClass('box-flex');
22 $('div#right_panel').addClass('box-flex');
23
24 // These have display: none in the css file and are made visible here to prevent FLOUC.
25 $('div#header').css('display','block');
26 $('div#main_app').css('display','block');
27
28 });
29
@@ -1,446 +1,446 b''
1 """Tornado handlers for the notebook.
1 """Tornado handlers for the notebook.
2
2
3 Authors:
3 Authors:
4
4
5 * Brian Granger
5 * Brian Granger
6 """
6 """
7
7
8 #-----------------------------------------------------------------------------
8 #-----------------------------------------------------------------------------
9 # Copyright (C) 2008-2011 The IPython Development Team
9 # Copyright (C) 2008-2011 The IPython Development Team
10 #
10 #
11 # Distributed under the terms of the BSD License. The full license is in
11 # Distributed under the terms of the BSD License. The full license is in
12 # the file COPYING, distributed as part of this software.
12 # the file COPYING, distributed as part of this software.
13 #-----------------------------------------------------------------------------
13 #-----------------------------------------------------------------------------
14
14
15 #-----------------------------------------------------------------------------
15 #-----------------------------------------------------------------------------
16 # Imports
16 # Imports
17 #-----------------------------------------------------------------------------
17 #-----------------------------------------------------------------------------
18
18
19 import logging
19 import logging
20 import Cookie
20 import Cookie
21 import uuid
21 import uuid
22
22
23 from tornado import web
23 from tornado import web
24 from tornado import websocket
24 from tornado import websocket
25
25
26 from zmq.eventloop import ioloop
26 from zmq.eventloop import ioloop
27 from zmq.utils import jsonapi
27 from zmq.utils import jsonapi
28
28
29 from IPython.zmq.session import Session
29 from IPython.zmq.session import Session
30
30
31 try:
31 try:
32 from docutils.core import publish_string
32 from docutils.core import publish_string
33 except ImportError:
33 except ImportError:
34 publish_string = None
34 publish_string = None
35
35
36
36
37
37
38 #-----------------------------------------------------------------------------
38 #-----------------------------------------------------------------------------
39 # Top-level handlers
39 # Top-level handlers
40 #-----------------------------------------------------------------------------
40 #-----------------------------------------------------------------------------
41
41
42 class AuthenticatedHandler(web.RequestHandler):
42 class AuthenticatedHandler(web.RequestHandler):
43 """A RequestHandler with an authenticated user."""
43 """A RequestHandler with an authenticated user."""
44
44
45 def get_current_user(self):
45 def get_current_user(self):
46 user_id = self.get_secure_cookie("user")
46 user_id = self.get_secure_cookie("user")
47 # For now the user_id should not return empty, but it could eventually
47 # For now the user_id should not return empty, but it could eventually
48 if user_id == '':
48 if user_id == '':
49 user_id = 'anonymous'
49 user_id = 'anonymous'
50 if user_id is None:
50 if user_id is None:
51 # prevent extra Invalid cookie sig warnings:
51 # prevent extra Invalid cookie sig warnings:
52 self.clear_cookie('user')
52 self.clear_cookie('user')
53 if not self.application.password:
53 if not self.application.password:
54 user_id = 'anonymous'
54 user_id = 'anonymous'
55 return user_id
55 return user_id
56
56
57
57
58 class NBBrowserHandler(AuthenticatedHandler):
58 class NBBrowserHandler(AuthenticatedHandler):
59
59
60 @web.authenticated
60 @web.authenticated
61 def get(self):
61 def get(self):
62 nbm = self.application.notebook_manager
62 nbm = self.application.notebook_manager
63 project = nbm.notebook_dir
63 project = nbm.notebook_dir
64 self.render(
64 self.render(
65 'nbbrowser.html', project=project,
65 'nbbrowser.html', project=project,
66 base_project_url=u'/', base_kernel_url=u'/'
66 base_project_url=u'/', base_kernel_url=u'/'
67 )
67 )
68
68
69
69
70 class LoginHandler(AuthenticatedHandler):
70 class LoginHandler(AuthenticatedHandler):
71
71
72 def get(self):
72 def get(self):
73 self.render('login.html')
73 self.render('login.html', next='/')
74
74
75 def post(self):
75 def post(self):
76 pwd = self.get_argument("password", default=u'')
76 pwd = self.get_argument('password', default=u'')
77 if self.application.password and pwd == self.application.password:
77 if self.application.password and pwd == self.application.password:
78 self.set_secure_cookie("user", str(uuid.uuid4()))
78 self.set_secure_cookie('user', str(uuid.uuid4()))
79 url = self.get_argument("next", default="/")
79 url = self.get_argument('next', default='/')
80 self.redirect(url)
80 self.redirect(url)
81
81
82
82
83 class NewHandler(AuthenticatedHandler):
83 class NewHandler(AuthenticatedHandler):
84
84
85 @web.authenticated
85 @web.authenticated
86 def get(self):
86 def get(self):
87 nbm = self.application.notebook_manager
87 nbm = self.application.notebook_manager
88 project = nbm.notebook_dir
88 project = nbm.notebook_dir
89 notebook_id = nbm.new_notebook()
89 notebook_id = nbm.new_notebook()
90 self.render(
90 self.render(
91 'notebook.html', project=project,
91 'notebook.html', project=project,
92 notebook_id=notebook_id,
92 notebook_id=notebook_id,
93 base_project_url=u'/', base_kernel_url=u'/'
93 base_project_url=u'/', base_kernel_url=u'/'
94 )
94 )
95
95
96
96
97 class NamedNotebookHandler(AuthenticatedHandler):
97 class NamedNotebookHandler(AuthenticatedHandler):
98
98
99 @web.authenticated
99 @web.authenticated
100 def get(self, notebook_id):
100 def get(self, notebook_id):
101 nbm = self.application.notebook_manager
101 nbm = self.application.notebook_manager
102 project = nbm.notebook_dir
102 project = nbm.notebook_dir
103 if not nbm.notebook_exists(notebook_id):
103 if not nbm.notebook_exists(notebook_id):
104 raise web.HTTPError(404, u'Notebook does not exist: %s' % notebook_id)
104 raise web.HTTPError(404, u'Notebook does not exist: %s' % notebook_id)
105 self.render(
105 self.render(
106 'notebook.html', project=project,
106 'notebook.html', project=project,
107 notebook_id=notebook_id,
107 notebook_id=notebook_id,
108 base_project_url=u'/', base_kernel_url=u'/'
108 base_project_url=u'/', base_kernel_url=u'/'
109 )
109 )
110
110
111
111
112 #-----------------------------------------------------------------------------
112 #-----------------------------------------------------------------------------
113 # Kernel handlers
113 # Kernel handlers
114 #-----------------------------------------------------------------------------
114 #-----------------------------------------------------------------------------
115
115
116
116
117 class MainKernelHandler(AuthenticatedHandler):
117 class MainKernelHandler(AuthenticatedHandler):
118
118
119 @web.authenticated
119 @web.authenticated
120 def get(self):
120 def get(self):
121 km = self.application.kernel_manager
121 km = self.application.kernel_manager
122 self.finish(jsonapi.dumps(km.kernel_ids))
122 self.finish(jsonapi.dumps(km.kernel_ids))
123
123
124 @web.authenticated
124 @web.authenticated
125 def post(self):
125 def post(self):
126 km = self.application.kernel_manager
126 km = self.application.kernel_manager
127 notebook_id = self.get_argument('notebook', default=None)
127 notebook_id = self.get_argument('notebook', default=None)
128 kernel_id = km.start_kernel(notebook_id)
128 kernel_id = km.start_kernel(notebook_id)
129 ws_url = self.application.ipython_app.get_ws_url()
129 ws_url = self.application.ipython_app.get_ws_url()
130 data = {'ws_url':ws_url,'kernel_id':kernel_id}
130 data = {'ws_url':ws_url,'kernel_id':kernel_id}
131 self.set_header('Location', '/'+kernel_id)
131 self.set_header('Location', '/'+kernel_id)
132 self.finish(jsonapi.dumps(data))
132 self.finish(jsonapi.dumps(data))
133
133
134
134
135 class KernelHandler(AuthenticatedHandler):
135 class KernelHandler(AuthenticatedHandler):
136
136
137 SUPPORTED_METHODS = ('DELETE')
137 SUPPORTED_METHODS = ('DELETE')
138
138
139 @web.authenticated
139 @web.authenticated
140 def delete(self, kernel_id):
140 def delete(self, kernel_id):
141 km = self.application.kernel_manager
141 km = self.application.kernel_manager
142 km.kill_kernel(kernel_id)
142 km.kill_kernel(kernel_id)
143 self.set_status(204)
143 self.set_status(204)
144 self.finish()
144 self.finish()
145
145
146
146
147 class KernelActionHandler(AuthenticatedHandler):
147 class KernelActionHandler(AuthenticatedHandler):
148
148
149 @web.authenticated
149 @web.authenticated
150 def post(self, kernel_id, action):
150 def post(self, kernel_id, action):
151 km = self.application.kernel_manager
151 km = self.application.kernel_manager
152 if action == 'interrupt':
152 if action == 'interrupt':
153 km.interrupt_kernel(kernel_id)
153 km.interrupt_kernel(kernel_id)
154 self.set_status(204)
154 self.set_status(204)
155 if action == 'restart':
155 if action == 'restart':
156 new_kernel_id = km.restart_kernel(kernel_id)
156 new_kernel_id = km.restart_kernel(kernel_id)
157 ws_url = self.application.ipython_app.get_ws_url()
157 ws_url = self.application.ipython_app.get_ws_url()
158 data = {'ws_url':ws_url,'kernel_id':new_kernel_id}
158 data = {'ws_url':ws_url,'kernel_id':new_kernel_id}
159 self.set_header('Location', '/'+new_kernel_id)
159 self.set_header('Location', '/'+new_kernel_id)
160 self.write(jsonapi.dumps(data))
160 self.write(jsonapi.dumps(data))
161 self.finish()
161 self.finish()
162
162
163
163
164 class ZMQStreamHandler(websocket.WebSocketHandler):
164 class ZMQStreamHandler(websocket.WebSocketHandler):
165
165
166 def _reserialize_reply(self, msg_list):
166 def _reserialize_reply(self, msg_list):
167 """Reserialize a reply message using JSON.
167 """Reserialize a reply message using JSON.
168
168
169 This takes the msg list from the ZMQ socket, unserializes it using
169 This takes the msg list from the ZMQ socket, unserializes it using
170 self.session and then serializes the result using JSON. This method
170 self.session and then serializes the result using JSON. This method
171 should be used by self._on_zmq_reply to build messages that can
171 should be used by self._on_zmq_reply to build messages that can
172 be sent back to the browser.
172 be sent back to the browser.
173 """
173 """
174 idents, msg_list = self.session.feed_identities(msg_list)
174 idents, msg_list = self.session.feed_identities(msg_list)
175 msg = self.session.unserialize(msg_list)
175 msg = self.session.unserialize(msg_list)
176 try:
176 try:
177 msg['header'].pop('date')
177 msg['header'].pop('date')
178 except KeyError:
178 except KeyError:
179 pass
179 pass
180 try:
180 try:
181 msg['parent_header'].pop('date')
181 msg['parent_header'].pop('date')
182 except KeyError:
182 except KeyError:
183 pass
183 pass
184 msg.pop('buffers')
184 msg.pop('buffers')
185 return jsonapi.dumps(msg)
185 return jsonapi.dumps(msg)
186
186
187 def _on_zmq_reply(self, msg_list):
187 def _on_zmq_reply(self, msg_list):
188 try:
188 try:
189 msg = self._reserialize_reply(msg_list)
189 msg = self._reserialize_reply(msg_list)
190 except:
190 except:
191 self.application.kernel_manager.log.critical("Malformed message: %r" % msg_list)
191 self.application.kernel_manager.log.critical("Malformed message: %r" % msg_list)
192 else:
192 else:
193 self.write_message(msg)
193 self.write_message(msg)
194
194
195 class AuthenticatedZMQStreamHandler(ZMQStreamHandler):
195 class AuthenticatedZMQStreamHandler(ZMQStreamHandler):
196 def open(self, kernel_id):
196 def open(self, kernel_id):
197 self.kernel_id = kernel_id.decode('ascii')
197 self.kernel_id = kernel_id.decode('ascii')
198 try:
198 try:
199 cfg = self.application.ipython_app.config
199 cfg = self.application.ipython_app.config
200 except AttributeError:
200 except AttributeError:
201 # protect from the case where this is run from something other than
201 # protect from the case where this is run from something other than
202 # the notebook app:
202 # the notebook app:
203 cfg = None
203 cfg = None
204 self.session = Session(config=cfg)
204 self.session = Session(config=cfg)
205 self.save_on_message = self.on_message
205 self.save_on_message = self.on_message
206 self.on_message = self.on_first_message
206 self.on_message = self.on_first_message
207
207
208 def get_current_user(self):
208 def get_current_user(self):
209 user_id = self.get_secure_cookie("user")
209 user_id = self.get_secure_cookie("user")
210 if user_id == '' or (user_id is None and not self.application.password):
210 if user_id == '' or (user_id is None and not self.application.password):
211 user_id = 'anonymous'
211 user_id = 'anonymous'
212 return user_id
212 return user_id
213
213
214 def _inject_cookie_message(self, msg):
214 def _inject_cookie_message(self, msg):
215 """Inject the first message, which is the document cookie,
215 """Inject the first message, which is the document cookie,
216 for authentication."""
216 for authentication."""
217 if isinstance(msg, unicode):
217 if isinstance(msg, unicode):
218 # Cookie can't constructor doesn't accept unicode strings for some reason
218 # Cookie can't constructor doesn't accept unicode strings for some reason
219 msg = msg.encode('utf8', 'replace')
219 msg = msg.encode('utf8', 'replace')
220 try:
220 try:
221 self._cookies = Cookie.SimpleCookie(msg)
221 self._cookies = Cookie.SimpleCookie(msg)
222 except:
222 except:
223 logging.warn("couldn't parse cookie string: %s",msg, exc_info=True)
223 logging.warn("couldn't parse cookie string: %s",msg, exc_info=True)
224
224
225 def on_first_message(self, msg):
225 def on_first_message(self, msg):
226 self._inject_cookie_message(msg)
226 self._inject_cookie_message(msg)
227 if self.get_current_user() is None:
227 if self.get_current_user() is None:
228 logging.warn("Couldn't authenticate WebSocket connection")
228 logging.warn("Couldn't authenticate WebSocket connection")
229 raise web.HTTPError(403)
229 raise web.HTTPError(403)
230 self.on_message = self.save_on_message
230 self.on_message = self.save_on_message
231
231
232
232
233 class IOPubHandler(AuthenticatedZMQStreamHandler):
233 class IOPubHandler(AuthenticatedZMQStreamHandler):
234
234
235 def initialize(self, *args, **kwargs):
235 def initialize(self, *args, **kwargs):
236 self._kernel_alive = True
236 self._kernel_alive = True
237 self._beating = False
237 self._beating = False
238 self.iopub_stream = None
238 self.iopub_stream = None
239 self.hb_stream = None
239 self.hb_stream = None
240
240
241 def on_first_message(self, msg):
241 def on_first_message(self, msg):
242 try:
242 try:
243 super(IOPubHandler, self).on_first_message(msg)
243 super(IOPubHandler, self).on_first_message(msg)
244 except web.HTTPError:
244 except web.HTTPError:
245 self.close()
245 self.close()
246 return
246 return
247 km = self.application.kernel_manager
247 km = self.application.kernel_manager
248 self.time_to_dead = km.time_to_dead
248 self.time_to_dead = km.time_to_dead
249 kernel_id = self.kernel_id
249 kernel_id = self.kernel_id
250 try:
250 try:
251 self.iopub_stream = km.create_iopub_stream(kernel_id)
251 self.iopub_stream = km.create_iopub_stream(kernel_id)
252 self.hb_stream = km.create_hb_stream(kernel_id)
252 self.hb_stream = km.create_hb_stream(kernel_id)
253 except web.HTTPError:
253 except web.HTTPError:
254 # WebSockets don't response to traditional error codes so we
254 # WebSockets don't response to traditional error codes so we
255 # close the connection.
255 # close the connection.
256 if not self.stream.closed():
256 if not self.stream.closed():
257 self.stream.close()
257 self.stream.close()
258 self.close()
258 self.close()
259 else:
259 else:
260 self.iopub_stream.on_recv(self._on_zmq_reply)
260 self.iopub_stream.on_recv(self._on_zmq_reply)
261 self.start_hb(self.kernel_died)
261 self.start_hb(self.kernel_died)
262
262
263 def on_message(self, msg):
263 def on_message(self, msg):
264 pass
264 pass
265
265
266 def on_close(self):
266 def on_close(self):
267 # This method can be called twice, once by self.kernel_died and once
267 # This method can be called twice, once by self.kernel_died and once
268 # from the WebSocket close event. If the WebSocket connection is
268 # from the WebSocket close event. If the WebSocket connection is
269 # closed before the ZMQ streams are setup, they could be None.
269 # closed before the ZMQ streams are setup, they could be None.
270 self.stop_hb()
270 self.stop_hb()
271 if self.iopub_stream is not None and not self.iopub_stream.closed():
271 if self.iopub_stream is not None and not self.iopub_stream.closed():
272 self.iopub_stream.on_recv(None)
272 self.iopub_stream.on_recv(None)
273 self.iopub_stream.close()
273 self.iopub_stream.close()
274 if self.hb_stream is not None and not self.hb_stream.closed():
274 if self.hb_stream is not None and not self.hb_stream.closed():
275 self.hb_stream.close()
275 self.hb_stream.close()
276
276
277 def start_hb(self, callback):
277 def start_hb(self, callback):
278 """Start the heartbeating and call the callback if the kernel dies."""
278 """Start the heartbeating and call the callback if the kernel dies."""
279 if not self._beating:
279 if not self._beating:
280 self._kernel_alive = True
280 self._kernel_alive = True
281
281
282 def ping_or_dead():
282 def ping_or_dead():
283 if self._kernel_alive:
283 if self._kernel_alive:
284 self._kernel_alive = False
284 self._kernel_alive = False
285 self.hb_stream.send(b'ping')
285 self.hb_stream.send(b'ping')
286 else:
286 else:
287 try:
287 try:
288 callback()
288 callback()
289 except:
289 except:
290 pass
290 pass
291 finally:
291 finally:
292 self._hb_periodic_callback.stop()
292 self._hb_periodic_callback.stop()
293
293
294 def beat_received(msg):
294 def beat_received(msg):
295 self._kernel_alive = True
295 self._kernel_alive = True
296
296
297 self.hb_stream.on_recv(beat_received)
297 self.hb_stream.on_recv(beat_received)
298 self._hb_periodic_callback = ioloop.PeriodicCallback(ping_or_dead, self.time_to_dead*1000)
298 self._hb_periodic_callback = ioloop.PeriodicCallback(ping_or_dead, self.time_to_dead*1000)
299 self._hb_periodic_callback.start()
299 self._hb_periodic_callback.start()
300 self._beating= True
300 self._beating= True
301
301
302 def stop_hb(self):
302 def stop_hb(self):
303 """Stop the heartbeating and cancel all related callbacks."""
303 """Stop the heartbeating and cancel all related callbacks."""
304 if self._beating:
304 if self._beating:
305 self._hb_periodic_callback.stop()
305 self._hb_periodic_callback.stop()
306 if not self.hb_stream.closed():
306 if not self.hb_stream.closed():
307 self.hb_stream.on_recv(None)
307 self.hb_stream.on_recv(None)
308
308
309 def kernel_died(self):
309 def kernel_died(self):
310 self.application.kernel_manager.delete_mapping_for_kernel(self.kernel_id)
310 self.application.kernel_manager.delete_mapping_for_kernel(self.kernel_id)
311 self.write_message(
311 self.write_message(
312 {'header': {'msg_type': 'status'},
312 {'header': {'msg_type': 'status'},
313 'parent_header': {},
313 'parent_header': {},
314 'content': {'execution_state':'dead'}
314 'content': {'execution_state':'dead'}
315 }
315 }
316 )
316 )
317 self.on_close()
317 self.on_close()
318
318
319
319
320 class ShellHandler(AuthenticatedZMQStreamHandler):
320 class ShellHandler(AuthenticatedZMQStreamHandler):
321
321
322 def initialize(self, *args, **kwargs):
322 def initialize(self, *args, **kwargs):
323 self.shell_stream = None
323 self.shell_stream = None
324
324
325 def on_first_message(self, msg):
325 def on_first_message(self, msg):
326 try:
326 try:
327 super(ShellHandler, self).on_first_message(msg)
327 super(ShellHandler, self).on_first_message(msg)
328 except web.HTTPError:
328 except web.HTTPError:
329 self.close()
329 self.close()
330 return
330 return
331 km = self.application.kernel_manager
331 km = self.application.kernel_manager
332 self.max_msg_size = km.max_msg_size
332 self.max_msg_size = km.max_msg_size
333 kernel_id = self.kernel_id
333 kernel_id = self.kernel_id
334 try:
334 try:
335 self.shell_stream = km.create_shell_stream(kernel_id)
335 self.shell_stream = km.create_shell_stream(kernel_id)
336 except web.HTTPError:
336 except web.HTTPError:
337 # WebSockets don't response to traditional error codes so we
337 # WebSockets don't response to traditional error codes so we
338 # close the connection.
338 # close the connection.
339 if not self.stream.closed():
339 if not self.stream.closed():
340 self.stream.close()
340 self.stream.close()
341 self.close()
341 self.close()
342 else:
342 else:
343 self.shell_stream.on_recv(self._on_zmq_reply)
343 self.shell_stream.on_recv(self._on_zmq_reply)
344
344
345 def on_message(self, msg):
345 def on_message(self, msg):
346 if len(msg) < self.max_msg_size:
346 if len(msg) < self.max_msg_size:
347 msg = jsonapi.loads(msg)
347 msg = jsonapi.loads(msg)
348 self.session.send(self.shell_stream, msg)
348 self.session.send(self.shell_stream, msg)
349
349
350 def on_close(self):
350 def on_close(self):
351 # Make sure the stream exists and is not already closed.
351 # Make sure the stream exists and is not already closed.
352 if self.shell_stream is not None and not self.shell_stream.closed():
352 if self.shell_stream is not None and not self.shell_stream.closed():
353 self.shell_stream.close()
353 self.shell_stream.close()
354
354
355
355
356 #-----------------------------------------------------------------------------
356 #-----------------------------------------------------------------------------
357 # Notebook web service handlers
357 # Notebook web service handlers
358 #-----------------------------------------------------------------------------
358 #-----------------------------------------------------------------------------
359
359
360 class NotebookRootHandler(AuthenticatedHandler):
360 class NotebookRootHandler(AuthenticatedHandler):
361
361
362 @web.authenticated
362 @web.authenticated
363 def get(self):
363 def get(self):
364 nbm = self.application.notebook_manager
364 nbm = self.application.notebook_manager
365 files = nbm.list_notebooks()
365 files = nbm.list_notebooks()
366 self.finish(jsonapi.dumps(files))
366 self.finish(jsonapi.dumps(files))
367
367
368 @web.authenticated
368 @web.authenticated
369 def post(self):
369 def post(self):
370 nbm = self.application.notebook_manager
370 nbm = self.application.notebook_manager
371 body = self.request.body.strip()
371 body = self.request.body.strip()
372 format = self.get_argument('format', default='json')
372 format = self.get_argument('format', default='json')
373 name = self.get_argument('name', default=None)
373 name = self.get_argument('name', default=None)
374 if body:
374 if body:
375 notebook_id = nbm.save_new_notebook(body, name=name, format=format)
375 notebook_id = nbm.save_new_notebook(body, name=name, format=format)
376 else:
376 else:
377 notebook_id = nbm.new_notebook()
377 notebook_id = nbm.new_notebook()
378 self.set_header('Location', '/'+notebook_id)
378 self.set_header('Location', '/'+notebook_id)
379 self.finish(jsonapi.dumps(notebook_id))
379 self.finish(jsonapi.dumps(notebook_id))
380
380
381
381
382 class NotebookHandler(AuthenticatedHandler):
382 class NotebookHandler(AuthenticatedHandler):
383
383
384 SUPPORTED_METHODS = ('GET', 'PUT', 'DELETE')
384 SUPPORTED_METHODS = ('GET', 'PUT', 'DELETE')
385
385
386 @web.authenticated
386 @web.authenticated
387 def get(self, notebook_id):
387 def get(self, notebook_id):
388 nbm = self.application.notebook_manager
388 nbm = self.application.notebook_manager
389 format = self.get_argument('format', default='json')
389 format = self.get_argument('format', default='json')
390 last_mod, name, data = nbm.get_notebook(notebook_id, format)
390 last_mod, name, data = nbm.get_notebook(notebook_id, format)
391 if format == u'json':
391 if format == u'json':
392 self.set_header('Content-Type', 'application/json')
392 self.set_header('Content-Type', 'application/json')
393 self.set_header('Content-Disposition','attachment; filename="%s.ipynb"' % name)
393 self.set_header('Content-Disposition','attachment; filename="%s.ipynb"' % name)
394 elif format == u'py':
394 elif format == u'py':
395 self.set_header('Content-Type', 'application/x-python')
395 self.set_header('Content-Type', 'application/x-python')
396 self.set_header('Content-Disposition','attachment; filename="%s.py"' % name)
396 self.set_header('Content-Disposition','attachment; filename="%s.py"' % name)
397 self.set_header('Last-Modified', last_mod)
397 self.set_header('Last-Modified', last_mod)
398 self.finish(data)
398 self.finish(data)
399
399
400 @web.authenticated
400 @web.authenticated
401 def put(self, notebook_id):
401 def put(self, notebook_id):
402 nbm = self.application.notebook_manager
402 nbm = self.application.notebook_manager
403 format = self.get_argument('format', default='json')
403 format = self.get_argument('format', default='json')
404 name = self.get_argument('name', default=None)
404 name = self.get_argument('name', default=None)
405 nbm.save_notebook(notebook_id, self.request.body, name=name, format=format)
405 nbm.save_notebook(notebook_id, self.request.body, name=name, format=format)
406 self.set_status(204)
406 self.set_status(204)
407 self.finish()
407 self.finish()
408
408
409 @web.authenticated
409 @web.authenticated
410 def delete(self, notebook_id):
410 def delete(self, notebook_id):
411 nbm = self.application.notebook_manager
411 nbm = self.application.notebook_manager
412 nbm.delete_notebook(notebook_id)
412 nbm.delete_notebook(notebook_id)
413 self.set_status(204)
413 self.set_status(204)
414 self.finish()
414 self.finish()
415
415
416 #-----------------------------------------------------------------------------
416 #-----------------------------------------------------------------------------
417 # RST web service handlers
417 # RST web service handlers
418 #-----------------------------------------------------------------------------
418 #-----------------------------------------------------------------------------
419
419
420
420
421 class RSTHandler(AuthenticatedHandler):
421 class RSTHandler(AuthenticatedHandler):
422
422
423 @web.authenticated
423 @web.authenticated
424 def post(self):
424 def post(self):
425 if publish_string is None:
425 if publish_string is None:
426 raise web.HTTPError(503, u'docutils not available')
426 raise web.HTTPError(503, u'docutils not available')
427 body = self.request.body.strip()
427 body = self.request.body.strip()
428 source = body
428 source = body
429 # template_path=os.path.join(os.path.dirname(__file__), u'templates', u'rst_template.html')
429 # template_path=os.path.join(os.path.dirname(__file__), u'templates', u'rst_template.html')
430 defaults = {'file_insertion_enabled': 0,
430 defaults = {'file_insertion_enabled': 0,
431 'raw_enabled': 0,
431 'raw_enabled': 0,
432 '_disable_config': 1,
432 '_disable_config': 1,
433 'stylesheet_path': 0
433 'stylesheet_path': 0
434 # 'template': template_path
434 # 'template': template_path
435 }
435 }
436 try:
436 try:
437 html = publish_string(source, writer_name='html',
437 html = publish_string(source, writer_name='html',
438 settings_overrides=defaults
438 settings_overrides=defaults
439 )
439 )
440 except:
440 except:
441 raise web.HTTPError(400, u'Invalid RST')
441 raise web.HTTPError(400, u'Invalid RST')
442 print html
442 print html
443 self.set_header('Content-Type', 'text/html')
443 self.set_header('Content-Type', 'text/html')
444 self.finish(html)
444 self.finish(html)
445
445
446
446
@@ -1,62 +1,53 b''
1 <!DOCTYPE HTML>
1 <!DOCTYPE HTML>
2 <html>
2 <html>
3
3
4 <head>
4 <head>
5 <meta charset="utf-8">
5 <meta charset="utf-8">
6
6
7 <title>IPython Notebook</title>
7 <title>IPython Notebook</title>
8
8
9 <link rel="stylesheet" href="static/jquery/css/themes/aristo/jquery-wijmo.css" type="text/css" />
9 <link rel="stylesheet" href="static/jquery/css/themes/aristo/jquery-wijmo.css" type="text/css" />
10 <link rel="stylesheet" href="static/css/boilerplate.css" type="text/css" />
10 <link rel="stylesheet" href="static/css/boilerplate.css" type="text/css" />
11 <link rel="stylesheet" href="static/css/layout.css" type="text/css" />
11 <link rel="stylesheet" href="static/css/layout.css" type="text/css" />
12 <link rel="stylesheet" href="static/css/base.css" type="text/css" />
12 <link rel="stylesheet" href="static/css/base.css" type="text/css" />
13 <script type="text/javascript" charset="utf-8">
13
14 function add_next_to_action(){
15 // add 'next' argument to action url, to preserve redirect
16 var query = location.search.substring(1);
17 var form = document.forms[0];
18 var action = form.getAttribute("action");
19 form.setAttribute("action", action + '?' + query);
20 }
21 </script>
22 </head>
14 </head>
23
15
24 <body onload="add_next_to_action()">
16 <body>
25
17
26 <div id="header">
18 <div id="header">
27 <span id="ipython_notebook"><h1>IPython Notebook</h1></span>
19 <span id="ipython_notebook"><h1>IPython Notebook</h1></span>
28 </div>
20 </div>
29
21
30 <div id="header_border"></div>
22 <div id="header_border"></div>
31
23
32 <div id="main_app">
24 <div id="main_app">
33
25
34 <div id="app_hbox">
26 <div id="app_hbox">
35
27
36 <div id="left_panel">
28 <div id="left_panel">
37 </div>
29 </div>
38
30
39 <div id="content_panel">
31 <div id="content_panel">
40 <form action="/login" method="post">
32 <form action="/login?next={{url_escape(next)}}" method="post">
41 Password: <input type="password" name="password">
33 Password: <input type="password" name="password">
42 <input type="submit" value="Sign in">
34 <input type="submit" value="Sign in">
43 </form>
35 </form>
44 </div>
36 </div>
45 <div id="right_panel">
37 <div id="right_panel">
46 </div>
38 </div>
47
39
48 </div>
40 </div>
49
41
50 </div>
42 </div>
51
43
52 <script src="static/jquery/js/jquery-1.6.2.min.js" type="text/javascript" charset="utf-8"></script>
44 <script src="static/jquery/js/jquery-1.6.2.min.js" type="text/javascript" charset="utf-8"></script>
53 <script src="static/jquery/js/jquery-ui-1.8.14.custom.min.js" type="text/javascript" charset="utf-8"></script>
45 <script src="static/jquery/js/jquery-ui-1.8.14.custom.min.js" type="text/javascript" charset="utf-8"></script>
54 <script src="static/js/namespace.js" type="text/javascript" charset="utf-8"></script>
46 <script src="static/js/namespace.js" type="text/javascript" charset="utf-8"></script>
55 <script src="static/js/notebooklist.js" type="text/javascript" charset="utf-8"></script>
47 <script src="static/js/login_main.js" type="text/javascript" charset="utf-8"></script>
56 <script src="static/js/nbbrowser_main.js" type="text/javascript" charset="utf-8"></script>
57
48
58 </body>
49 </body>
59
50
60 </html>
51 </html>
61
52
62
53
General Comments 0
You need to be logged in to leave comments. Login now