Commit a26034ff authored by Jannis Leidel's avatar Jannis Leidel
Browse files

Fixes #15270 -- Moved back the serve view to django.views.static due to...

Fixes #15270 -- Moved back the serve view to django.views.static due to dependency conflicts with the contrib app staticfiles (reverts parts of r14293). Added a helper function that generates URL patterns for serving static and media files during development. Thanks to Carl for reviewing the patch.

git-svn-id: http://code.djangoproject.com/svn/django/trunk@15530 bcc190cf-cafb-0310-a4f2-bffc1f526a37
parent 6b1191b1
Loading
Loading
Loading
Loading
+26 −0
Original line number Diff line number Diff line
import re
from django.conf import settings
from django.conf.urls.defaults import patterns, url
from django.core.exceptions import ImproperlyConfigured

def static(prefix, view='django.views.static.serve', **kwargs):
    """
    Helper function to return a URL pattern for serving files in debug mode.

    from django.conf import settings
    from django.conf.urls.static import static

    urlpatterns = patterns('',
        # ... the rest of your URLconf goes here ...
    ) + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

    """
    if not settings.DEBUG:
        return []
    elif not prefix:
        raise ImproperlyConfigured("Empty static prefix not permitted")
    elif '://' in prefix:
        raise ImproperlyConfigured("URL '%s' not allowed as static prefix" % prefix)
    return patterns('',
        url(r'^%s(?P<path>.*)$' % re.escape(prefix.lstrip('/')), view, **kwargs),
    )
+6 −18
Original line number Diff line number Diff line
import re
from django.conf import settings
from django.conf.urls.defaults import patterns, url, include
from django.core.exceptions import ImproperlyConfigured
from django.conf.urls.static import static

urlpatterns = []

# only serve non-fqdn URLs
if settings.DEBUG:
    urlpatterns += patterns('',
        url(r'^(?P<path>.*)$', 'django.contrib.staticfiles.views.serve'),
    )

def staticfiles_urlpatterns(prefix=None):
    """
    Helper function to return a URL pattern for serving static files.
    """
    if not settings.DEBUG:
        return []
    if prefix is None:
        prefix = settings.STATIC_URL
    if not prefix or '://' in prefix:
        raise ImproperlyConfigured(
            "The prefix for the 'staticfiles_urlpatterns' helper is invalid.")
    if prefix.startswith("/"):
        prefix = prefix[1:]
    return patterns('',
        url(r'^%s' % re.escape(prefix), include(urlpatterns)),)
    return static(prefix, view='django.contrib.staticfiles.views.serve')

# Only append if urlpatterns are empty
if settings.DEBUG and not urlpatterns:
    urlpatterns += staticfiles_urlpatterns()
+13 −140
Original line number Diff line number Diff line
@@ -3,24 +3,18 @@ Views and functions for serving static files. These are only to be used during
development, and SHOULD NOT be used in a production setting.

"""
import mimetypes
import os
import posixpath
import re
import stat
import urllib
from email.Utils import parsedate_tz, mktime_tz

from django.conf import settings
from django.core.exceptions import ImproperlyConfigured
from django.http import Http404, HttpResponse, HttpResponseRedirect, HttpResponseNotModified
from django.template import loader, Template, Context, TemplateDoesNotExist
from django.utils.http import http_date
from django.http import Http404
from django.views import static

from django.contrib.staticfiles import finders, utils
from django.contrib.staticfiles import finders


def serve(request, path, document_root=None, show_indexes=False, insecure=False):
def serve(request, path, document_root=None, insecure=False, **kwargs):
    """
    Serve static files below a given point in the directory structure or
    from locations inferred from the staticfiles finders.
@@ -31,135 +25,14 @@ def serve(request, path, document_root=None, show_indexes=False, insecure=False)

    in your URLconf.

    If you provide the ``document_root`` parameter, the file won't be looked
    up with the staticfiles finders, but in the given filesystem path, e.g.::

    (r'^(?P<path>.*)$', 'django.contrib.staticfiles.views.serve', {'document_root' : '/path/to/my/files/'})

    You may also set ``show_indexes`` to ``True`` if you'd like to serve a
    basic index of the directory.  This index view will use the
    template hardcoded below, but if you'd like to override it, you can create
    a template called ``static/directory_index.html``.
    It automatically falls back to django.views.static
    """
    if not settings.DEBUG and not insecure:
        raise ImproperlyConfigured("The view to serve static files can only "
                                   "be used if the DEBUG setting is True or "
                                   "the --insecure option of 'runserver' is "
                                   "used")
    if not document_root:
        path = os.path.normpath(path)
        absolute_path = finders.find(path)
        if not absolute_path:
            raise Http404('"%s" could not be found' % path)
        raise ImproperlyConfigured("The staticfiles view can only be used in "
                                   "debug mode or if the the --insecure "
                                   "option of 'runserver' is used")
    normalized_path = posixpath.normpath(urllib.unquote(path)).lstrip('/')
    absolute_path = finders.find(normalized_path)
    if absolute_path:
        document_root, path = os.path.split(absolute_path)
    # Clean up given path to only allow serving files below document_root.
    path = posixpath.normpath(urllib.unquote(path))
    path = path.lstrip('/')
    newpath = ''
    for part in path.split('/'):
        if not part:
            # Strip empty path components.
            continue
        drive, part = os.path.splitdrive(part)
        head, part = os.path.split(part)
        if part in (os.curdir, os.pardir):
            # Strip '.' and '..' in path.
            continue
        newpath = os.path.join(newpath, part).replace('\\', '/')
    if newpath and path != newpath:
        return HttpResponseRedirect(newpath)
    fullpath = os.path.join(document_root, newpath)
    if os.path.isdir(fullpath):
        if show_indexes:
            return directory_index(newpath, fullpath)
        raise Http404("Directory indexes are not allowed here.")
    if not os.path.exists(fullpath):
        raise Http404('"%s" does not exist' % fullpath)
    # Respect the If-Modified-Since header.
    statobj = os.stat(fullpath)
    mimetype, encoding = mimetypes.guess_type(fullpath)
    mimetype = mimetype or 'application/octet-stream'
    if not was_modified_since(request.META.get('HTTP_IF_MODIFIED_SINCE'),
                              statobj[stat.ST_MTIME], statobj[stat.ST_SIZE]):
        return HttpResponseNotModified(mimetype=mimetype)
    contents = open(fullpath, 'rb').read()
    response = HttpResponse(contents, mimetype=mimetype)
    response["Last-Modified"] = http_date(statobj[stat.ST_MTIME])
    response["Content-Length"] = len(contents)
    if encoding:
        response["Content-Encoding"] = encoding
    return response


DEFAULT_DIRECTORY_INDEX_TEMPLATE = """
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
  <head>
    <meta http-equiv="Content-type" content="text/html; charset=utf-8" />
    <meta http-equiv="Content-Language" content="en-us" />
    <meta name="robots" content="NONE,NOARCHIVE" />
    <title>Index of {{ directory }}</title>
  </head>
  <body>
    <h1>Index of {{ directory }}</h1>
    <ul>
      {% ifnotequal directory "/" %}
      <li><a href="../">../</a></li>
      {% endifnotequal %}
      {% for f in file_list %}
      <li><a href="{{ f|urlencode }}">{{ f }}</a></li>
      {% endfor %}
    </ul>
  </body>
</html>
"""

def directory_index(path, fullpath):
    try:
        t = loader.select_template(['static/directory_index.html',
                'static/directory_index'])
    except TemplateDoesNotExist:
        t = Template(DEFAULT_DIRECTORY_INDEX_TEMPLATE, name='Default directory index template')
    files = []
    for f in os.listdir(fullpath):
        if not f.startswith('.'):
            if os.path.isdir(os.path.join(fullpath, f)):
                f += '/'
            files.append(f)
    c = Context({
        'directory' : path + '/',
        'file_list' : files,
    })
    return HttpResponse(t.render(c))

def was_modified_since(header=None, mtime=0, size=0):
    """
    Was something modified since the user last downloaded it?

    header
      This is the value of the If-Modified-Since header.  If this is None,
      I'll just return True.

    mtime
      This is the modification time of the item we're talking about.

    size
      This is the size of the item we're talking about.
    """
    try:
        if header is None:
            raise ValueError
        matches = re.match(r"^([^;]+)(; length=([0-9]+))?$", header,
                           re.IGNORECASE)
        header_date = parsedate_tz(matches.group(1))
        if header_date is None:
            raise ValueError
        header_mtime = mktime_tz(header_date)
        header_len = matches.group(3)
        if header_len and int(header_len) != size:
            raise ValueError
        if mtime > header_mtime:
            raise ValueError
    except (AttributeError, ValueError, OverflowError):
        return True
    return False
    return static.serve(request, path, document_root=document_root, **kwargs)
+3 −3
Original line number Diff line number Diff line
@@ -18,8 +18,9 @@ import warnings
from django.core.management.color import color_style
from django.utils.http import http_date
from django.utils._os import safe_join
from django.views import static

from django.contrib.staticfiles import handlers, views as static
from django.contrib.staticfiles import handlers

__version__ = "0.1"
__all__ = ['WSGIServer','WSGIRequestHandler']
@@ -677,8 +678,7 @@ class AdminMediaHandler(handlers.StaticFilesHandler):

    def serve(self, request):
        document_root, path = os.path.split(self.file_path(request.path))
        return static.serve(request, path,
            document_root=document_root, insecure=True)
        return static.serve(request, path, document_root=document_root)

    def _should_handle(self, path):
        """
+111 −10
Original line number Diff line number Diff line
@@ -9,7 +9,6 @@ import posixpath
import re
import stat
import urllib
import warnings
from email.Utils import parsedate_tz, mktime_tz

from django.template import loader
@@ -17,11 +16,7 @@ from django.http import Http404, HttpResponse, HttpResponseRedirect, HttpRespons
from django.template import Template, Context, TemplateDoesNotExist
from django.utils.http import http_date

from django.contrib.staticfiles.views import (directory_index,
    was_modified_since, serve as staticfiles_serve)


def serve(request, path, document_root=None, show_indexes=False, insecure=False):
def serve(request, path, document_root=None, show_indexes=False):
    """
    Serve static files below a given point in the directory structure.

@@ -35,7 +30,113 @@ def serve(request, path, document_root=None, show_indexes=False, insecure=False)
    but if you'd like to override it, you can create a template called
    ``static/directory_index.html``.
    """
    warnings.warn("The view at `django.views.static.serve` is deprecated; "
                  "use the path `django.contrib.staticfiles.views.serve` "
                  "instead.", PendingDeprecationWarning)
    return staticfiles_serve(request, path, document_root, show_indexes, insecure)
    path = posixpath.normpath(urllib.unquote(path))
    path = path.lstrip('/')
    newpath = ''
    for part in path.split('/'):
        if not part:
            # Strip empty path components.
            continue
        drive, part = os.path.splitdrive(part)
        head, part = os.path.split(part)
        if part in (os.curdir, os.pardir):
            # Strip '.' and '..' in path.
            continue
        newpath = os.path.join(newpath, part).replace('\\', '/')
    if newpath and path != newpath:
        return HttpResponseRedirect(newpath)
    fullpath = os.path.join(document_root, newpath)
    if os.path.isdir(fullpath):
        if show_indexes:
            return directory_index(newpath, fullpath)
        raise Http404("Directory indexes are not allowed here.")
    if not os.path.exists(fullpath):
        raise Http404('"%s" does not exist' % fullpath)
    # Respect the If-Modified-Since header.
    statobj = os.stat(fullpath)
    mimetype, encoding = mimetypes.guess_type(fullpath)
    mimetype = mimetype or 'application/octet-stream'
    if not was_modified_since(request.META.get('HTTP_IF_MODIFIED_SINCE'),
                              statobj[stat.ST_MTIME], statobj[stat.ST_SIZE]):
        return HttpResponseNotModified(mimetype=mimetype)
    contents = open(fullpath, 'rb').read()
    response = HttpResponse(contents, mimetype=mimetype)
    response["Last-Modified"] = http_date(statobj[stat.ST_MTIME])
    response["Content-Length"] = len(contents)
    if encoding:
        response["Content-Encoding"] = encoding
    return response


DEFAULT_DIRECTORY_INDEX_TEMPLATE = """
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
  <head>
    <meta http-equiv="Content-type" content="text/html; charset=utf-8" />
    <meta http-equiv="Content-Language" content="en-us" />
    <meta name="robots" content="NONE,NOARCHIVE" />
    <title>Index of {{ directory }}</title>
  </head>
  <body>
    <h1>Index of {{ directory }}</h1>
    <ul>
      {% ifnotequal directory "/" %}
      <li><a href="../">../</a></li>
      {% endifnotequal %}
      {% for f in file_list %}
      <li><a href="{{ f|urlencode }}">{{ f }}</a></li>
      {% endfor %}
    </ul>
  </body>
</html>
"""

def directory_index(path, fullpath):
    try:
        t = loader.select_template(['static/directory_index.html',
                'static/directory_index'])
    except TemplateDoesNotExist:
        t = Template(DEFAULT_DIRECTORY_INDEX_TEMPLATE, name='Default directory index template')
    files = []
    for f in os.listdir(fullpath):
        if not f.startswith('.'):
            if os.path.isdir(os.path.join(fullpath, f)):
                f += '/'
            files.append(f)
    c = Context({
        'directory' : path + '/',
        'file_list' : files,
    })
    return HttpResponse(t.render(c))

def was_modified_since(header=None, mtime=0, size=0):
    """
    Was something modified since the user last downloaded it?

    header
      This is the value of the If-Modified-Since header.  If this is None,
      I'll just return True.

    mtime
      This is the modification time of the item we're talking about.

    size
      This is the size of the item we're talking about.
    """
    try:
        if header is None:
            raise ValueError
        matches = re.match(r"^([^;]+)(; length=([0-9]+))?$", header,
                           re.IGNORECASE)
        header_date = parsedate_tz(matches.group(1))
        if header_date is None:
            raise ValueError
        header_mtime = mktime_tz(header_date)
        header_len = matches.group(3)
        if header_len and int(header_len) != size:
            raise ValueError
        if mtime > header_mtime:
            raise ValueError
    except (AttributeError, ValueError, OverflowError):
        return True
    return False
Loading