""" 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 posixpath from pathlib import Path from django.http import FileResponse, Http404, HttpResponse, HttpResponseNotModified from django.template import Context, Engine, TemplateDoesNotExist, loader from django.utils._os import safe_join from django.utils.http import http_date, parse_http_date from django.utils.translation import gettext as _ from django.utils.translation import gettext_lazy def serve(request, path, document_root=None, show_indexes=False): """ Serve static files below a given point in the directory structure. To use, put a URL pattern such as:: from django.views.static import serve path('', serve, {'document_root': '/path/to/my/files/'}) in your URLconf. You must provide the ``document_root`` param. 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``. """ path = posixpath.normpath(path).lstrip("/") fullpath = Path(safe_join(document_root, path)) if fullpath.is_dir(): if show_indexes: return directory_index(path, fullpath) raise Http404(_("Directory indexes are not allowed here.")) if not fullpath.exists(): raise Http404(_("ā€œ%(path)sā€ does not exist") % {"path": fullpath}) # Respect the If-Modified-Since header. statobj = fullpath.stat() if not was_modified_since( request.META.get("HTTP_IF_MODIFIED_SINCE"), statobj.st_mtime ): return HttpResponseNotModified() content_type, encoding = mimetypes.guess_type(str(fullpath)) content_type = content_type or "application/octet-stream" response = FileResponse(fullpath.open("rb"), content_type=content_type) response.headers["Last-Modified"] = http_date(statobj.st_mtime) if encoding: response.headers["Content-Encoding"] = encoding return response DEFAULT_DIRECTORY_INDEX_TEMPLATE = """ {% load i18n %} {% blocktranslate %}Index of {{ directory }}{% endblocktranslate %}

{% blocktranslate %}Index of {{ directory }}{% endblocktranslate %}

""" template_translatable = gettext_lazy("Index of %(directory)s") def directory_index(path, fullpath): try: t = loader.select_template( [ "static/directory_index.html", "static/directory_index", ] ) except TemplateDoesNotExist: t = Engine(libraries={"i18n": "django.templatetags.i18n"}).from_string( DEFAULT_DIRECTORY_INDEX_TEMPLATE ) c = Context() else: c = {} files = [] for f in fullpath.iterdir(): if not f.name.startswith("."): url = str(f.relative_to(fullpath)) if f.is_dir(): url += "/" files.append(url) c.update( { "directory": path + "/", "file_list": files, } ) return HttpResponse(t.render(c)) def was_modified_since(header=None, mtime=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. """ try: if header is None: raise ValueError header_mtime = parse_http_date(header) if int(mtime) > header_mtime: raise ValueError except (ValueError, OverflowError): return True return False