From a50ac36bd554366a80f907d4d16b552d53c69ddf 2012-01-06 10:00:14
From: Min RK <benjaminrk@gmail.com>
Date: 2012-01-06 10:00:14
Subject: [PATCH] Merge pull request #1211 from minrk/localfiles

Files in notebook-dir are served by the nbserver as `files/<relativepath>`.
---

diff --git a/IPython/frontend/html/notebook/handlers.py b/IPython/frontend/html/notebook/handlers.py
index df3b65c..62bc795 100644
--- a/IPython/frontend/html/notebook/handlers.py
+++ b/IPython/frontend/html/notebook/handlers.py
@@ -108,6 +108,7 @@ def authenticate_unless_readonly(f, self, *args, **kwargs):
     @web.authenticated
     def auth_f(self, *args, **kwargs):
         return f(self, *args, **kwargs)
+
     if self.application.read_only:
         return f(self, *args, **kwargs)
     else:
@@ -175,6 +176,14 @@ class AuthenticatedHandler(RequestHandler):
         return "%s://%s" % (proto, self.request.host)
 
 
+class AuthenticatedFileHandler(AuthenticatedHandler, web.StaticFileHandler):
+    """static files should only be accessible when logged in"""
+
+    @authenticate_unless_readonly
+    def get(self, path):
+        return web.StaticFileHandler.get(self, path)
+
+
 class ProjectDashboardHandler(AuthenticatedHandler):
 
     @authenticate_unless_readonly
diff --git a/IPython/frontend/html/notebook/notebookapp.py b/IPython/frontend/html/notebook/notebookapp.py
index beb356b..b0360e5 100644
--- a/IPython/frontend/html/notebook/notebookapp.py
+++ b/IPython/frontend/html/notebook/notebookapp.py
@@ -48,7 +48,8 @@ from .kernelmanager import MappingKernelManager
 from .handlers import (LoginHandler, LogoutHandler,
     ProjectDashboardHandler, NewHandler, NamedNotebookHandler,
     MainKernelHandler, KernelHandler, KernelActionHandler, IOPubHandler,
-    ShellHandler, NotebookRootHandler, NotebookHandler, RSTHandler
+    ShellHandler, NotebookRootHandler, NotebookHandler, RSTHandler,
+    AuthenticatedFileHandler,
 )
 from .notebookmanager import NotebookManager
 
@@ -103,7 +104,8 @@ class NotebookWebApplication(web.Application):
             (r"/kernels/%s/shell" % _kernel_id_regex, ShellHandler),
             (r"/notebooks", NotebookRootHandler),
             (r"/notebooks/%s" % _notebook_id_regex, NotebookHandler),
-            (r"/rstservice/render", RSTHandler)
+            (r"/rstservice/render", RSTHandler),
+            (r"/files/(.*)", AuthenticatedFileHandler, {'path' : notebook_manager.notebook_dir}),
         ]
         settings = dict(
             template_path=os.path.join(os.path.dirname(__file__), "templates"),
@@ -280,7 +282,7 @@ class NotebookApp(BaseIPythonApplication):
         static_path = self.webapp_settings.get("static_path", os.path.join(os.path.dirname(__file__), "static"))
         if os.path.exists(os.path.join(static_path, 'mathjax', "MathJax.js")):
             self.log.info("Using local MathJax")
-            return u"static/mathjax/MathJax.js"
+            return u"/static/mathjax/MathJax.js"
         else:
             self.log.info("Using MathJax from CDN")
             return u"http://cdn.mathjax.org/mathjax/latest/MathJax.js"
diff --git a/IPython/frontend/html/notebook/templates/layout.html b/IPython/frontend/html/notebook/templates/layout.html
index 8d5e4d2..407e2ab 100644
--- a/IPython/frontend/html/notebook/templates/layout.html
+++ b/IPython/frontend/html/notebook/templates/layout.html
@@ -6,10 +6,10 @@
 
     <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"/>
+    <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 %}
 
@@ -21,7 +21,7 @@
 <body {% block params %}{% end %}>
 
 <div id="header">
-    <span id="ipython_notebook"><h1><img src='static/ipynblogo.png' alt='IPython Notebook'/></h1></span>
+    <span id="ipython_notebook"><h1><img src='/static/ipynblogo.png' alt='IPython Notebook'/></h1></span>
 
     {% block login_widget %}
 
@@ -72,11 +72,11 @@
 
 </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>
+<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 %}
 
diff --git a/IPython/frontend/html/notebook/templates/notebook.html b/IPython/frontend/html/notebook/templates/notebook.html
index ab160a1..11daece 100644
--- a/IPython/frontend/html/notebook/templates/notebook.html
+++ b/IPython/frontend/html/notebook/templates/notebook.html
@@ -15,20 +15,20 @@
     window.mathjax_url = "{{mathjax_url}}";
     </script>
 
-    <link rel="stylesheet" href="static/jquery/css/themes/aristo/jquery-wijmo.css" type="text/css" />
-    <link rel="stylesheet" href="static/codemirror/lib/codemirror.css">
-    <link rel="stylesheet" href="static/codemirror/mode/markdown/markdown.css">
-    <link rel="stylesheet" href="static/codemirror/mode/rst/rst.css">
-    <link rel="stylesheet" href="static/codemirror/theme/ipython.css">
-    <link rel="stylesheet" href="static/codemirror/theme/default.css">
+    <link rel="stylesheet" href="/static/jquery/css/themes/aristo/jquery-wijmo.css" type="text/css" />
+    <link rel="stylesheet" href="/static/codemirror/lib/codemirror.css">
+    <link rel="stylesheet" href="/static/codemirror/mode/markdown/markdown.css">
+    <link rel="stylesheet" href="/static/codemirror/mode/rst/rst.css">
+    <link rel="stylesheet" href="/static/codemirror/theme/ipython.css">
+    <link rel="stylesheet" href="/static/codemirror/theme/default.css">
 
-    <link rel="stylesheet" href="static/prettify/prettify.css"/>
+    <link rel="stylesheet" href="/static/prettify/prettify.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" />
-    <link rel="stylesheet" href="static/css/notebook.css" type="text/css" />
-    <link rel="stylesheet" href="static/css/renderedhtml.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" />
+    <link rel="stylesheet" href="/static/css/notebook.css" type="text/css" />
+    <link rel="stylesheet" href="/static/css/renderedhtml.css" type="text/css" />
 
     {% comment  In the notebook, the read-only flag is used to determine %}
     {% comment  whether to hide the side panels and switch off input %}
@@ -42,7 +42,7 @@
 >
 
 <div id="header">
-    <span id="ipython_notebook"><h1><a href='..' alt='dashboard'><img src='static/ipynblogo.png' alt='IPython Notebook'/></a></h1></span>
+    <span id="ipython_notebook"><h1><a href='..' alt='dashboard'><img src='/static/ipynblogo.png' alt='IPython Notebook'/></a></h1></span>
     <span id="save_widget">
         <input type="text" id="notebook_name" size="20"></textarea>
         <button id="save_notebook"><u>S</u>ave</button>
@@ -258,40 +258,40 @@
 
 </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/jquery/js/jquery.autogrow.js" type="text/javascript" charset="utf-8"></script>
+<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/jquery/js/jquery.autogrow.js" type="text/javascript" charset="utf-8"></script>
 
-<script src="static/codemirror/lib/codemirror.js" charset="utf-8"></script>
-<script src="static/codemirror/mode/python/python.js" charset="utf-8"></script>
-<script src="static/codemirror/mode/htmlmixed/htmlmixed.js" charset="utf-8"></script>
-<script src="static/codemirror/mode/xml/xml.js" charset="utf-8"></script>
-<script src="static/codemirror/mode/javascript/javascript.js" charset="utf-8"></script>
-<script src="static/codemirror/mode/css/css.js" charset="utf-8"></script>
-<script src="static/codemirror/mode/rst/rst.js" charset="utf-8"></script>
-<script src="static/codemirror/mode/markdown/markdown.js" charset="utf-8"></script>
+<script src="/static/codemirror/lib/codemirror.js" charset="utf-8"></script>
+<script src="/static/codemirror/mode/python/python.js" charset="utf-8"></script>
+<script src="/static/codemirror/mode/htmlmixed/htmlmixed.js" charset="utf-8"></script>
+<script src="/static/codemirror/mode/xml/xml.js" charset="utf-8"></script>
+<script src="/static/codemirror/mode/javascript/javascript.js" charset="utf-8"></script>
+<script src="/static/codemirror/mode/css/css.js" charset="utf-8"></script>
+<script src="/static/codemirror/mode/rst/rst.js" charset="utf-8"></script>
+<script src="/static/codemirror/mode/markdown/markdown.js" charset="utf-8"></script>
 
-<script src="static/pagedown/Markdown.Converter.js" charset="utf-8"></script>
+<script src="/static/pagedown/Markdown.Converter.js" charset="utf-8"></script>
 
-<script src="static/prettify/prettify.js" charset="utf-8"></script>
+<script src="/static/prettify/prettify.js" charset="utf-8"></script>
 
-<script src="static/js/namespace.js" type="text/javascript" charset="utf-8"></script>
-<script src="static/js/utils.js" type="text/javascript" charset="utf-8"></script>
-<script src="static/js/cell.js" type="text/javascript" charset="utf-8"></script>
-<script src="static/js/codecell.js" type="text/javascript" charset="utf-8"></script>
-<script src="static/js/textcell.js" type="text/javascript" charset="utf-8"></script>
-<script src="static/js/kernel.js" type="text/javascript" charset="utf-8"></script>
-<script src="static/js/kernelstatus.js" type="text/javascript" charset="utf-8"></script>
-<script src="static/js/layout.js" type="text/javascript" charset="utf-8"></script>
-<script src="static/js/savewidget.js" type="text/javascript" charset="utf-8"></script>
-<script src="static/js/quickhelp.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/pager.js" type="text/javascript" charset="utf-8"></script>
-<script src="static/js/panelsection.js" type="text/javascript" charset="utf-8"></script>
-<script src="static/js/printwidget.js" type="text/javascript" charset="utf-8"></script>
-<script src="static/js/leftpanel.js" type="text/javascript" charset="utf-8"></script>
-<script src="static/js/notebook.js" type="text/javascript" charset="utf-8"></script>
-<script src="static/js/notebookmain.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/utils.js" type="text/javascript" charset="utf-8"></script>
+<script src="/static/js/cell.js" type="text/javascript" charset="utf-8"></script>
+<script src="/static/js/codecell.js" type="text/javascript" charset="utf-8"></script>
+<script src="/static/js/textcell.js" type="text/javascript" charset="utf-8"></script>
+<script src="/static/js/kernel.js" type="text/javascript" charset="utf-8"></script>
+<script src="/static/js/kernelstatus.js" type="text/javascript" charset="utf-8"></script>
+<script src="/static/js/layout.js" type="text/javascript" charset="utf-8"></script>
+<script src="/static/js/savewidget.js" type="text/javascript" charset="utf-8"></script>
+<script src="/static/js/quickhelp.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/pager.js" type="text/javascript" charset="utf-8"></script>
+<script src="/static/js/panelsection.js" type="text/javascript" charset="utf-8"></script>
+<script src="/static/js/printwidget.js" type="text/javascript" charset="utf-8"></script>
+<script src="/static/js/leftpanel.js" type="text/javascript" charset="utf-8"></script>
+<script src="/static/js/notebook.js" type="text/javascript" charset="utf-8"></script>
+<script src="/static/js/notebookmain.js" type="text/javascript" charset="utf-8"></script>
 
 </body>
 
diff --git a/IPython/frontend/html/notebook/templates/projectdashboard.html b/IPython/frontend/html/notebook/templates/projectdashboard.html
index d3036af..3dfc52b 100644
--- a/IPython/frontend/html/notebook/templates/projectdashboard.html
+++ b/IPython/frontend/html/notebook/templates/projectdashboard.html
@@ -5,7 +5,7 @@ IPython Dashboard
 {% end %}
 
 {% block stylesheet %}
-    <link rel="stylesheet" href="static/css/projectdashboard.css" type="text/css" />
+    <link rel="stylesheet" href="/static/css/projectdashboard.css" type="text/css" />
 {% end %}
 
 {% block meta %}
@@ -38,6 +38,6 @@ data-base-kernel-url={{base_kernel_url}}
 {% 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>
+    <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 %}
diff --git a/docs/examples/notebooks/00_notebook_tour.ipynb b/docs/examples/notebooks/00_notebook_tour.ipynb
index dc99083..a62c2de 100644
--- a/docs/examples/notebooks/00_notebook_tour.ipynb
+++ b/docs/examples/notebooks/00_notebook_tour.ipynb
@@ -778,6 +778,45 @@
     {
      "cell_type": "markdown", 
      "source": [
+      "## Local Files", 
+      "", 
+      "The above examples embed images and video from the notebook filesystem in the output", 
+      "areas of code cells.  It is also possible to request these files directly in markdown cells", 
+      "if they reside in the notebook directory via relative urls prefixed with `files/`:", 
+      "", 
+      "    files/[subdirectory/]<filename>", 
+      "", 
+      "", 
+      "For example, in the example notebook folder, we have the Python logo, addressed as:", 
+      "", 
+      "    <img src=\"files/python-logo.svg\" />", 
+      "", 
+      "<img src=\"files/python-logo.svg\" />", 
+      "", 
+      "and a video with the HTML5 video tag:", 
+      "", 
+      "    <video controls src=\"files/animation.m4v\" />", 
+      "", 
+      "<video controls src=\"files/animation.m4v\" />", 
+      "", 
+      "These do not embed the data into the notebook file,", 
+      "and require that the files exist when you are viewing the notebook.", 
+      "", 
+      "### Security of local files", 
+      "", 
+      "Note that this means that the IPython notebook server also acts as a generic file server", 
+      "for files inside the same tree as your notebooks.  Access is not granted outside the", 
+      "notebook folder so you have strict control over what files are visible, but for this", 
+      "reason it is highly recommended that you do not run the notebook server with a notebook", 
+      "directory at a high level in your filesystem (e.g. your home directory).", 
+      "", 
+      "When you run the notebook in a password-protected manner, local file access is restricted", 
+      "to authenticated users unless read-only views are active."
+     ]
+    }, 
+    {
+     "cell_type": "markdown", 
+     "source": [
       "### External sites", 
       "", 
       "You can even embed an entire page from another site in an iframe; for example this is today's Wikipedia",