From 80e7338b90f3dce4b60b03bd43f0dd66cf65ffcc 2011-11-20 23:10:48
From: Fernando Perez <Fernando.Perez@berkeley.edu>
Date: 2011-11-20 23:10:48
Subject: [PATCH] Merge branch 'logout_button' of https://github.com/stefanv/ipython into stefanv-logout_button

Add a logout button to authenticated notebooks.  This has no impact on
regular (non-authenticated) notebooks.

---

diff --git a/IPython/frontend/html/notebook/handlers.py b/IPython/frontend/html/notebook/handlers.py
index 7822118..ca92d59 100644
--- a/IPython/frontend/html/notebook/handlers.py
+++ b/IPython/frontend/html/notebook/handlers.py
@@ -116,7 +116,14 @@ def authenticate_unless_readonly(f, self, *args, **kwargs):
 # Top-level handlers
 #-----------------------------------------------------------------------------
 
-class AuthenticatedHandler(web.RequestHandler):
+class RequestHandler(web.RequestHandler):
+    """RequestHandler with default variable setting."""
+
+    def render(*args, **kwargs):
+        kwargs.setdefault('message', '')
+        return web.RequestHandler.render(*args, **kwargs)
+
+class AuthenticatedHandler(RequestHandler):
     """A RequestHandler with an authenticated user."""
 
     def get_current_user(self):
@@ -167,7 +174,7 @@ class ProjectDashboardHandler(AuthenticatedHandler):
 
 class LoginHandler(AuthenticatedHandler):
 
-    def _render(self, message=''):
+    def _render(self, message=None):
         self.render('login.html',
                 next=self.get_argument('next', default='/'),
                 read_only=self.read_only,
@@ -175,7 +182,10 @@ class LoginHandler(AuthenticatedHandler):
         )
 
     def get(self):
-        self._render()
+        if self.current_user:
+            self.redirect(self.get_argument('next', default='/'))
+        else:
+            self._render()
 
     def post(self):
         pwd = self.get_argument('password', default=u'')
@@ -183,12 +193,19 @@ class LoginHandler(AuthenticatedHandler):
             if passwd_check(self.application.password, pwd):
                 self.set_secure_cookie('username', str(uuid.uuid4()))
             else:
-                self._render(message='Invalid password')
+                self._render(message={'error': 'Invalid password'})
                 return
 
         self.redirect(self.get_argument('next', default='/'))
 
 
+class LogoutHandler(AuthenticatedHandler):
+
+    def get(self):
+        self.clear_cookie('username')
+        self.render('logout.html', message={'info': 'Successfully logged out.'})
+
+
 class NewHandler(AuthenticatedHandler):
 
     @web.authenticated
diff --git a/IPython/frontend/html/notebook/notebookapp.py b/IPython/frontend/html/notebook/notebookapp.py
index e507607..bdef5ad 100644
--- a/IPython/frontend/html/notebook/notebookapp.py
+++ b/IPython/frontend/html/notebook/notebookapp.py
@@ -40,7 +40,7 @@ from tornado import web
 
 # Our own libraries
 from .kernelmanager import MappingKernelManager
-from .handlers import (LoginHandler,
+from .handlers import (LoginHandler, LogoutHandler,
     ProjectDashboardHandler, NewHandler, NamedNotebookHandler,
     MainKernelHandler, KernelHandler, KernelActionHandler, IOPubHandler,
     ShellHandler, NotebookRootHandler, NotebookHandler, RSTHandler
@@ -87,6 +87,7 @@ class NotebookWebApplication(web.Application):
         handlers = [
             (r"/", ProjectDashboardHandler),
             (r"/login", LoginHandler),
+            (r"/logout", LogoutHandler),
             (r"/new", NewHandler),
             (r"/%s" % _notebook_id_regex, NamedNotebookHandler),
             (r"/kernels", MainKernelHandler),
diff --git a/IPython/frontend/html/notebook/static/css/layout.css b/IPython/frontend/html/notebook/static/css/layout.css
index b9e0b08..d8bec47 100644
--- a/IPython/frontend/html/notebook/static/css/layout.css
+++ b/IPython/frontend/html/notebook/static/css/layout.css
@@ -102,12 +102,27 @@
 	box-pack: center;
 }
 
-#message {
-    border: 1px solid red;
-    background-color: #FFD3D1;
+.message {
+    border-width: 1px;
+    border-style: solid;
     text-align: center;
     padding: 0.5em;
-    margin: 0.5em;
+    margin: 0.5em 0;
+}
+
+.message.error {
+    background-color: #FFD3D1;
+    border-color: red;
+}
+
+.message.warning {
+    background-color: #FFD09E;
+    border-color: orange;
+}
+
+.message.info {
+    background-color: #CBFFBA;
+    border-color: green;
 }
 
 #content_panel {
diff --git a/IPython/frontend/html/notebook/static/js/loginwidget.js b/IPython/frontend/html/notebook/static/js/loginwidget.js
index 17fcc58..6ba8075 100644
--- a/IPython/frontend/html/notebook/static/js/loginwidget.js
+++ b/IPython/frontend/html/notebook/static/js/loginwidget.js
@@ -21,12 +21,12 @@ var IPython = (function (IPython) {
     };
 
     LoginWidget.prototype.style = function () {
-        this.element.find('button#login').button();
+        this.element.find('button#logout').button();
     };
     LoginWidget.prototype.bind_events = function () {
         var that = this;
-        this.element.find("button#login").click(function () {
-            window.location = "/login?next="+location.pathname;
+        this.element.find("button#logout").click(function () {
+            window.location = "/logout";
         });
     };
 
diff --git a/IPython/frontend/html/notebook/templates/layout.html b/IPython/frontend/html/notebook/templates/layout.html
new file mode 100644
index 0000000..1f24a40
--- /dev/null
+++ b/IPython/frontend/html/notebook/templates/layout.html
@@ -0,0 +1,77 @@
+<!DOCTYPE HTML>
+<html>
+
+<head>
+    <meta charset="utf-8">
+
+    <title>{% block title %}IPython Notebook{% end %}</title>
+
+    <link rel="stylesheet" href="static/jquery/css/themes/aristo/jquery-wijmo.css" type="text/css" />
+    <link rel="stylesheet" href="static/css/boilerplate.css" type="text/css" />
+    <link rel="stylesheet" href="static/css/layout.css" type="text/css" />
+    <link rel="stylesheet" href="static/css/base.css" type="text/css"/>
+    {% block stylesheet %}
+    {% end %}
+
+    {% block meta %}
+    {% end %}
+
+</head>
+
+<body {% block params %}{% end %}>
+
+<div id="header">
+    <span id="ipython_notebook"><h1>IPython Notebook</h1></span>
+    <span id="login_widget">
+      {% if current_user and current_user != 'anonymous' %}
+        <button id="logout">Logout</button>
+      {% end %}
+    </span>
+    {% block header %}
+    {% end %}
+</div>
+
+<div id="header_border"></div>
+
+<div id="main_app">
+
+    <div id="app_hbox">
+
+    <div id="left_panel">
+    {% block left_panel %}
+    {% end %}
+    </div>
+
+    <div id="content_panel">
+        {% if message %}
+
+          {% for key in message %}
+            <div class="message {{key}}">
+               {{message[key]}}
+            </div>
+          {% end %}
+        {% end %}
+
+        {% block content_panel %}
+        {% end %}
+    </div>
+    <div id="right_panel">
+    {% block right_panel %}
+    {% end %}
+    </div>
+
+    </div>
+
+</div>
+
+<script src="static/jquery/js/jquery-1.6.2.min.js" type="text/javascript" charset="utf-8"></script>
+<script src="static/jquery/js/jquery-ui-1.8.14.custom.min.js" type="text/javascript" charset="utf-8"></script>
+<script src="static/js/namespace.js" type="text/javascript" charset="utf-8"></script>
+<script src="static/js/loginmain.js" type="text/javascript" charset="utf-8"></script>
+<script src="static/js/loginwidget.js" type="text/javascript" charset="utf-8"></script>
+{% block script %}
+{% end %}
+
+</body>
+
+</html>
diff --git a/IPython/frontend/html/notebook/templates/login.html b/IPython/frontend/html/notebook/templates/login.html
index b719c5b..08b71a6 100644
--- a/IPython/frontend/html/notebook/templates/login.html
+++ b/IPython/frontend/html/notebook/templates/login.html
@@ -1,61 +1,8 @@
-<!DOCTYPE HTML>
-<html>
-
-<head>
-    <meta charset="utf-8">
-
-    <title>IPython Notebook</title>
-
-    <link rel="stylesheet" href="static/jquery/css/themes/aristo/jquery-wijmo.css" type="text/css" />
-    <link rel="stylesheet" href="static/css/boilerplate.css" type="text/css" />
-    <link rel="stylesheet" href="static/css/layout.css" type="text/css" />
-    <link rel="stylesheet" href="static/css/base.css" type="text/css" />
-
-    <meta name="read_only" content="{{read_only}}"/>
-
-</head>
-
-<body>
-
-<div id="header">
-    <span id="ipython_notebook"><h1>IPython Notebook</h1></span>
-</div>
-
-<div id="header_border"></div>
-
-<div id="main_app">
-
-    <div id="app_hbox">
-
-    <div id="left_panel">
-    </div>
-    
-    <div id="content_panel">
-        {% if message %}
-        <div id="message">
-            {{message}}
-        </div>
-        {% end %}
-
-        <form action="/login?next={{url_escape(next)}}" method="post">
-            Password: <input type="password" name="password">
-            <input type="submit" value="Sign in" id="signin">
-        </form>
-    </div>
-    <div id="right_panel">
-    </div>
-    
-    </div>
-
-</div>
-
-<script src="static/jquery/js/jquery-1.6.2.min.js" type="text/javascript" charset="utf-8"></script>
-<script src="static/jquery/js/jquery-ui-1.8.14.custom.min.js" type="text/javascript" charset="utf-8"></script>
-<script src="static/js/namespace.js" type="text/javascript" charset="utf-8"></script>
-<script src="static/js/loginmain.js" type="text/javascript" charset="utf-8"></script>
-
-</body>
-
-</html>
-
-
+{% extends layout.html %}
+
+{% block content_panel %}
+    <form action="/login?next={{url_escape(next)}}" method="post">
+        Password: <input type="password" name="password">
+        <input type="submit" value="Sign in" id="signin">
+    </form>
+{% end %}
diff --git a/IPython/frontend/html/notebook/templates/logout.html b/IPython/frontend/html/notebook/templates/logout.html
new file mode 100644
index 0000000..f3c7b53
--- /dev/null
+++ b/IPython/frontend/html/notebook/templates/logout.html
@@ -0,0 +1,5 @@
+{% extends layout.html %}
+
+{% block content_panel %}
+Proceed to the <a href="/login">login page</a>.
+{% end %}
diff --git a/IPython/frontend/html/notebook/templates/notebook.html b/IPython/frontend/html/notebook/templates/notebook.html
index 0d924fc..e2fdc3b 100644
--- a/IPython/frontend/html/notebook/templates/notebook.html
+++ b/IPython/frontend/html/notebook/templates/notebook.html
@@ -59,9 +59,15 @@
     <span id="quick_help_area">
       <button id="quick_help">Quick<u>H</u>elp</button>
     </span>
-    <span id="login_widget" class="hidden">
-      <button id="login">Login</button>
+
+    <span id="login_widget">
+      {% comment  This is a temporary workaround to hide the logout button %}
+      {% comment  when appropriate until notebook.html is templated %}
+      {% if current_user and current_user != 'anonymous' %}
+        <button id="logout">Logout</button>
+      {% end %}
     </span>
+
     <span id="kernel_status">Idle</span>
 </div>
 
diff --git a/IPython/frontend/html/notebook/templates/projectdashboard.html b/IPython/frontend/html/notebook/templates/projectdashboard.html
index 386e261..a893848 100644
--- a/IPython/frontend/html/notebook/templates/projectdashboard.html
+++ b/IPython/frontend/html/notebook/templates/projectdashboard.html
@@ -1,69 +1,36 @@
-<!DOCTYPE HTML>
-<html>
+{% extends layout.html %}
 
-<head>
-    <meta charset="utf-8">
+{% block title %}
+IPython Dashboard
+{% end %}
 
-    <title>IPython Dashboard</title>
-
-    <link rel="stylesheet" href="static/jquery/css/themes/aristo/jquery-wijmo.css" type="text/css" />
-    <link rel="stylesheet" href="static/css/boilerplate.css" type="text/css" />
-    <link rel="stylesheet" href="static/css/layout.css" type="text/css" />
-    <link rel="stylesheet" href="static/css/base.css" type="text/css" />
+{% block stylesheet %}
     <link rel="stylesheet" href="static/css/projectdashboard.css" type="text/css" />
+{% end %}
 
+{% block meta %}
     <meta name="read_only" content="{{read_only}}"/>
-
-</head>
-
-<body data-project={{project}} data-base-project-url={{base_project_url}}
-      data-base-kernel-url={{base_kernel_url}}>
-
-<div id="header">
-    <span id="ipython_notebook"><h1>IPython Notebook</h1></span>
-    <span id="login_widget" class="hidden">
-      <button id="login">Login</button>
-    </span>
-</div>
-
-<div id="header_border"></div>
-
-<div id="main_app">
-
-    <div id="app_hbox">
-
-    <div id="left_panel">
-    </div>
-
-    <div id="content_panel">
-        <div id="content_toolbar">
-            <span id="drag_info">Drag files onto the list to import notebooks.</span>
-            <span id="notebooks_buttons">
-                <button id="new_notebook">New Notebook</button>
-            </span>
-        </div>
-        <div id="notebook_list">
-            <div id="project_name"><h2>{{project}}</h2></div>
-        </div>
-
+{% end %}
+
+{% block params %}
+data-project={{project}}
+data-base-project-url={{base_project_url}}
+data-base-kernel-url={{base_kernel_url}}
+{% end %}
+
+{% block content_panel %}
+    <div id="content_toolbar">
+        <span id="drag_info">Drag files onto the list to import notebooks.</span>
+        <span id="notebooks_buttons">
+            <button id="new_notebook">New Notebook</button>
+        </span>
     </div>
-
-    <div id="right_panel">
+    <div id="notebook_list">
+        <div id="project_name"><h2>{{project}}</h2></div>
     </div>
-    
-    </div>
-
-</div>
-
-<script src="static/jquery/js/jquery-1.6.2.min.js" type="text/javascript" charset="utf-8"></script>
-<script src="static/jquery/js/jquery-ui-1.8.14.custom.min.js" type="text/javascript" charset="utf-8"></script>
-<script src="static/js/namespace.js" type="text/javascript" charset="utf-8"></script>
-<script src="static/js/notebooklist.js" type="text/javascript" charset="utf-8"></script>
-<script src="static/js/loginwidget.js" type="text/javascript" charset="utf-8"></script>
-<script src="static/js/projectdashboardmain.js" type="text/javascript" charset="utf-8"></script>
-
-</body>
-
-</html>
-
+{% end %}
 
+{% block script %}
+    <script src="static/js/notebooklist.js" type="text/javascript" charset="utf-8"></script>
+    <script src="static/js/projectdashboardmain.js" type="text/javascript" charset="utf-8"></script>
+{% end %}