Dynamically merge CSSes into a single one

This will make CSS downloading a single request, instead of making
6-7 requests for each page as it is now. It also moves all the definitions
of URLs for CSS into the templates and not in the raw CSS itself, which
will make it possible to enable client side caching in the future.

Fixes #91
This commit is contained in:
Magnus Hagander
2013-12-28 17:22:36 +01:00
parent cd7c7449c8
commit 6dfd884f16
5 changed files with 64 additions and 10 deletions

View File

@ -1,9 +1,6 @@
/* PostgreSQL.org Documentation Style */
@import url("global.css");
@import url("table.css");
@import url("text.css");
/* requires global.css, table.css and text.css to be loaded before this file! */
body {
font-size: 76%;
}

View File

@ -1,14 +1,16 @@
from django.shortcuts import render_to_response, get_object_or_404
from django.shortcuts import render_to_response
from django.http import HttpResponse, Http404, HttpResponseRedirect
from django.template import TemplateDoesNotExist, loader, Context
from django.http import HttpResponseNotModified
from django.template import TemplateDoesNotExist, loader
from django.contrib.auth.decorators import login_required, user_passes_test
from django.views.decorators.csrf import csrf_exempt
from django.db.models import Count
from django.db import connection, transaction
from django.utils.http import http_date, parse_http_date
from django.conf import settings
from datetime import date, datetime
from os import uname
import os
import re
import urllib
@ -155,6 +157,60 @@ def sitemap(request):
x.endDocument()
return resp
# dynamic CSS serving, meaning we merge a number of different CSS into a
# single one, making sure it turns into a single http response. We do this
# dynamically, since the output will be cached (for all non-SSL users, which
# is the vast majority) anyway.
_dynamic_cssmap = {
'base': ['../media/css/global.css',
'../media/css/layout.css',
'../media/css/text.css',
'../media/css/navigation.css',
'../media/css/table.css',
'../media/css/iefixes.css'],
'docs': ['../media/css/global.css',
'../media/css/table.css',
'../media/css/text.css',
'../media/css/docs.css'],
}
@cache(hours=6)
def dynamic_css(request, css):
if not _dynamic_cssmap.has_key(css):
raise Http404('CSS not found')
files = _dynamic_cssmap[css]
resp = HttpResponse(content_type='text/css')
# We honor if-modified-since headers by looking at the most recently
# touched CSS file.
latestmod = 0
for fn in files:
try:
stime = os.stat(fn).st_mtime
if latestmod < stime:
latestmod = stime
except OSError:
# If we somehow referred to a file that didn't exist, or
# one that we couldn't access.
raise Http404('CSS (sub) not found')
if request.META.has_key('HTTP_IF_MODIFIED_SINCE'):
# This code is mostly stolen from django :)
matches = re.match(r"^([^;]+)(; length=([0-9]+))?$",
request.META.get('HTTP_IF_MODIFIED_SINCE'),
re.IGNORECASE)
header_mtime = parse_http_date(matches.group(1))
# We don't do length checking, just the date
if int(latestmod) <= header_mtime:
return HttpResponseNotModified(mimetype='text/css')
resp['Last-Modified'] = http_date(latestmod)
for fn in files:
with open(fn) as f:
resp.write("/* %s */\n" % fn)
resp.write(f.read())
resp.write("\n")
return resp
@nocache
def csrf_failure(request, reason=''):
resp = render_to_response('errors/csrf_failure.html', {
@ -167,7 +223,7 @@ def csrf_failure(request, reason=''):
@cache(seconds=30)
def system_information(request):
return render_to_response('core/system_information.html', {
'server': uname()[1],
'server': os.uname()[1],
'behind_cache': is_behind_cache(request),
'cache_server': is_behind_cache(request) and request.META['REMOTE_ADDR'] or None,
'client_ip': get_client_ip(request),

View File

@ -18,6 +18,7 @@ from pwn.feeds import PwnFeed
urlpatterns = patterns('',
(r'^$', 'pgweb.core.views.home'),
(r'^dyncss/(?P<css>base|docs).css$', 'pgweb.core.views.dynamic_css'),
(r'^about/newsarchive/$', 'news.views.archive'),
(r'^about/news/(\d+)(-.*)?/$', 'news.views.item'),

View File

@ -6,7 +6,7 @@
<meta http-equiv="Content-Type" content="text/xhtml; charset=utf-8" />
{%block meta%}{%endblock%} {# used for custom meta tags such as description which we don't want for every page #}
<meta name="copyright" content="The PostgreSQL Global Development Group" />
<style type="text/css" media="screen" title="Normal Text">@import url("/media/css/base.css");</style>
<style type="text/css" media="screen" title="Normal Text">@import url("/dyncss/base.css");</style>
<link rel="shortcut icon" href="/favicon.ico" />
<link rel="alternate stylesheet" type="text/css" media="screen" title="Large Text" href="/media/css/docs_large.css" />
<link rel="alternate" type="application/rss+xml" title="PostgreSQL News" href="{{link_root}}/news.rss" />

View File

@ -2,7 +2,7 @@
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en" dir="ltr">
<head>
<title>PostgreSQL: Documentation: {{page.display_version}}: {{page.title}}</title>
<style type="text/css" media="screen" title="Normal Text">@import url("/media/css/docs.css");</style>
<style type="text/css" media="screen" title="Normal Text">@import url("/dyncss/docs.css");</style>
<link rel="alternate stylesheet" media="screen" href="/media/css/docs_large.css" type="text/css" title="Large Text" />
<script type="text/javascript" src="/media/js/styleswitcher.js"></script>
<script type="text/javascript" src="/media/js/monospacefix.js"></script>