diff --git a/notebook/notebookapp.py b/notebook/notebookapp.py index ba8b5d59f..2be2b92b9 100755 --- a/notebook/notebookapp.py +++ b/notebook/notebookapp.py @@ -273,6 +273,7 @@ class NotebookWebApplication(web.Application): websocket_url=jupyter_app.websocket_url, mathjax_url=jupyter_app.mathjax_url, mathjax_config=jupyter_app.mathjax_config, + shutdown_button=jupyter_app.quit_button, config=jupyter_app.config, config_dir=jupyter_app.config_dir, allow_password_change=jupyter_app.allow_password_change, @@ -1023,6 +1024,11 @@ class NotebookApp(JupyterApp): @observe('mathjax_config') def _update_mathjax_config(self, change): self.log.info(_("Using MathJax configuration file: %s"), change['new']) + + quit_button = Bool(True, config=True, + help="""If True, display a button in the dashboard to quit + (shutdown the notebook server).""" + ) contents_manager_class = Type( default_value=LargeFileManager, diff --git a/notebook/static/base/less/page.less b/notebook/static/base/less/page.less index 85e7f825a..d2720b386 100644 --- a/notebook/static/base/less/page.less +++ b/notebook/static/base/less/page.less @@ -124,9 +124,10 @@ span#login_widget { } span#login_widget > .button, -#logout +#logout, #shutdown { .btn-default(); + margin-left: 10px; } .nav-header { diff --git a/notebook/static/tree/js/main.js b/notebook/static/tree/js/main.js index ebced6b6f..5dcf8d806 100644 --- a/notebook/static/tree/js/main.js +++ b/notebook/static/tree/js/main.js @@ -35,6 +35,7 @@ requirejs([ 'tree/js/kernellist', 'tree/js/terminallist', 'tree/js/newnotebook', + 'tree/js/shutdownbutton', 'auth/js/loginwidget', 'bidi/bidi', ], function( @@ -52,6 +53,7 @@ requirejs([ kernellist, terminallist, newnotebook, + shutdownbutton, loginwidget, bidi){ "use strict"; @@ -209,4 +211,6 @@ requirejs([ if (window.location.hash) { $("#tabs").find("a[href=" + window.location.hash + "]").click(); } + + shutdownbutton.activate(); }); diff --git a/notebook/static/tree/js/shutdownbutton.js b/notebook/static/tree/js/shutdownbutton.js new file mode 100644 index 000000000..e8b789fd1 --- /dev/null +++ b/notebook/static/tree/js/shutdownbutton.js @@ -0,0 +1,48 @@ +// Copyright (c) Jupyter Development Team. +// Distributed under the terms of the Modified BSD License. + +define([ + 'jquery', + 'base/js/dialog', + 'base/js/i18n', + 'base/js/utils' +], function( + $, + dialog, + i18n, + utils +){ + "use strict"; + + function display_shutdown_dialog() { + var body = $('
').append( + $('').text(i18n.msg._("You have shut down Jupyter. You can now close this tab.")) + ).append( + $('').text(i18n.msg._("To use Jupyter again, you will need to relaunch it.")) + ); + + dialog.modal({ + title: i18n.msg._("Server stopped"), + body: body + }) + } + + function activate() { + // Add shutdown button + $("button#shutdown").click(function () { + utils.ajax(utils.url_path_join( + utils.get_body_data("baseUrl"), + "api", + "shutdown" + ), { + type: "POST", + success: display_shutdown_dialog, + error: function (error) { + console.log(error); + } + }); + }); + } + + return {activate: activate} +}); diff --git a/notebook/templates/tree.html b/notebook/templates/tree.html index 1e2377b93..e37f08d3c 100644 --- a/notebook/templates/tree.html +++ b/notebook/templates/tree.html @@ -12,7 +12,15 @@ data-server-root="{{server_root}}" {% endblock %} {% block headercontainer %} - + + {% if shutdown_button %} + + + + {% endif %} {% endblock %} {% block site %} diff --git a/notebook/tree/handlers.py b/notebook/tree/handlers.py index ef4252761..4cc4c8062 100644 --- a/notebook/tree/handlers.py +++ b/notebook/tree/handlers.py @@ -51,6 +51,7 @@ class TreeHandler(IPythonHandler): breadcrumbs=breadcrumbs, terminals_available=self.settings['terminals_available'], server_root=self.settings['server_root_dir'], + shutdown_button=self.settings.get('shutdown_button', False) )) elif cm.file_exists(path): # it's not a directory, we have redirecting to do