Enable cache-busting URLs for CSS and JS

This will allow us to increase the cache times in the browser for our
CSS, which almost never changes.

Enables a new value to be used in templates, {{gitrev}}, which can be
used to bust pretty much any URL. We could do this for all the images
in the templates as well, but since most of them almost never change,
we'll just enable it manually for each individual image as it becomes
necessray - or just use a ?1, ?2 etc for those.

Enabled by default for CSS and JavaScript links, since those are much
more likely to be changed without having the URL changed.

Cache times aren't increased yet - we'll do that later one we're sure
that all existing caches are expired first.
This commit is contained in:
Magnus Hagander
2014-01-01 15:24:51 +01:00
parent 34bd7b54a5
commit 48410d04fa
7 changed files with 37 additions and 22 deletions

View File

@ -63,7 +63,7 @@ def home(request):
'quote': quote, 'quote': quote,
'versions': versions, 'versions': versions,
'planet': planet, 'planet': planet,
}) }, RequestContext(request))
# Community main page (contains surveys and potentially more) # Community main page (contains surveys and potentially more)
def community(request): def community(request):
@ -244,7 +244,7 @@ def sync_timestamp(request):
def admin_pending(request): def admin_pending(request):
return render_to_response('core/admin_pending.html', { return render_to_response('core/admin_pending.html', {
'app_list': get_all_pending_moderations(), 'app_list': get_all_pending_moderations(),
}) }, RequestContext(request))
# Purge objects from varnish, for the admin pages # Purge objects from varnish, for the admin pages
@login_required @login_required

View File

@ -1,6 +1,7 @@
from django.shortcuts import render_to_response, get_object_or_404 from django.shortcuts import render_to_response, get_object_or_404
from django.http import HttpResponse, Http404, HttpResponseRedirect from django.http import HttpResponse, Http404, HttpResponseRedirect
from django.template import TemplateDoesNotExist, loader, Context from django.template import TemplateDoesNotExist, loader, Context
from django.template.context import RequestContext
from django.contrib.auth.decorators import login_required from django.contrib.auth.decorators import login_required
from django.db.models import Q from django.db.models import Q
from django.conf import settings from django.conf import settings
@ -67,7 +68,7 @@ def docpage(request, version, typ, filename):
'can_comment': (typ=="interactive" and ver==currver), 'can_comment': (typ=="interactive" and ver==currver),
'doc_index_filename': indexname, 'doc_index_filename': indexname,
'loaddate': loaddate, 'loaddate': loaddate,
}) }, RequestContext(request))
def docsrootpage(request, version, typ): def docsrootpage(request, version, typ):
return docpage(request, version, typ, 'index') return docpage(request, version, typ, 'index')

View File

@ -1,4 +1,5 @@
from django.shortcuts import render_to_response, get_object_or_404 from django.shortcuts import render_to_response, get_object_or_404
from django.template.context import RequestContext
from django.http import HttpResponse, Http404, HttpResponseRedirect from django.http import HttpResponse, Http404, HttpResponseRedirect
from django.views.decorators.csrf import csrf_exempt from django.views.decorators.csrf import csrf_exempt
from django.conf import settings from django.conf import settings
@ -125,18 +126,18 @@ def search(request):
'listid': listid, 'listid': listid,
'dates': dateoptions, 'dates': dateoptions,
'dateval': dateval, 'dateval': dateval,
}) }, RequestContext(request))
else: else:
return render_to_response('search/sitesearch.html', { return render_to_response('search/sitesearch.html', {
'search_error': "No search term specified.", 'search_error': "No search term specified.",
}) }, RequestContext(request))
query = request.REQUEST['q'] query = request.REQUEST['q']
# Anti-stefan prevention # Anti-stefan prevention
if len(query) > 1000: if len(query) > 1000:
return render_to_response('search/sitesearch.html', { return render_to_response('search/sitesearch.html', {
'search_error': "Search term too long.", 'search_error': "Search term too long.",
}) }, RequestContext(request))
# Is the request being paged? # Is the request being paged?
if request.REQUEST.has_key('p'): if request.REQUEST.has_key('p'):
@ -186,12 +187,12 @@ def search(request):
except socket.timeout: except socket.timeout:
return render_to_response('search/listsearch.html', { return render_to_response('search/listsearch.html', {
'search_error': 'Timeout when talking to search server. Please try your search again later, or with a more restrictive search terms.', 'search_error': 'Timeout when talking to search server. Please try your search again later, or with a more restrictive search terms.',
}) }, RequestContext(request))
if r.status != 200: if r.status != 200:
memc = None memc = None
return render_to_response('search/listsearch.html', { return render_to_response('search/listsearch.html', {
'search_error': 'Error talking to search server: %s' % r.reason, 'search_error': 'Error talking to search server: %s' % r.reason,
}) }, RequestContext(request))
hits = json.loads(r.read()) hits = json.loads(r.read())
if has_memcached and memc: if has_memcached and memc:
# Store them in memcached too! But only for 10 minutes... # Store them in memcached too! But only for 10 minutes...
@ -236,7 +237,7 @@ def search(request):
'listid': listid, 'listid': listid,
'dates': dateoptions, 'dates': dateoptions,
'dateval': dateval, 'dateval': dateval,
}) }, RequestContext(request))
else: else:
# Website search is still done by making a regular pgsql connection # Website search is still done by making a regular pgsql connection
@ -247,7 +248,7 @@ def search(request):
except: except:
return render_to_response('search/sitesearch.html', { return render_to_response('search/sitesearch.html', {
'search_error': 'Could not connect to search database.' 'search_error': 'Could not connect to search database.'
}) }, RequestContext(request))
# perform the query for general web search # perform the query for general web search
curs.execute("SELECT * FROM site_search(%(query)s, %(firsthit)s, %(hitsperpage)s, %(allsites)s, %(suburl)s)", { curs.execute("SELECT * FROM site_search(%(query)s, %(firsthit)s, %(hitsperpage)s, %(allsites)s, %(suburl)s)", {
@ -283,4 +284,4 @@ def search(request):
'url': "%s%s" % (h[1], h[2]), 'url': "%s%s" % (h[1], h[2]),
'abstract': h[4].replace("[[[[[[", "<b>").replace("]]]]]]","</b>"), 'abstract': h[4].replace("[[[[[[", "<b>").replace("]]]]]]","</b>"),
'rank': h[5]} for h in hits[:-1]], 'rank': h[5]} for h in hits[:-1]],
}) }, RequestContext(request))

View File

@ -79,7 +79,7 @@ TEMPLATE_CONTEXT_PROCESSORS = (
'django.contrib.auth.context_processors.auth', 'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages', 'django.contrib.messages.context_processors.messages',
'django.core.context_processors.media', 'django.core.context_processors.media',
'util.contexts.RootLinkContextProcessor', 'util.contexts.PGWebContextProcessor',
) )
LOGIN_URL='/account/login/' LOGIN_URL='/account/login/'

View File

@ -1,4 +1,5 @@
from django.template import RequestContext from django.template import RequestContext
from django.utils.functional import SimpleLazyObject
from django.conf import settings from django.conf import settings
# This is the whole site navigation structure. Stick in a smarter file? # This is the whole site navigation structure. Stick in a smarter file?
@ -100,12 +101,24 @@ class NavContext(RequestContext):
self.update({'navmenu': navsection}) self.update({'navmenu': navsection})
# Template context processor to add information about the root link def _get_gitrev():
def RootLinkContextProcessor(request): # Return the current git revision, that is used for
# cache-busting URLs.
with open('../.git/refs/heads/master') as f:
return f.readline()[:8]
# Template context processor to add information about the root link and
# the current git revision. git revision is returned as a lazy object so
# we don't spend effort trying to load it if we don't need it (though
# all general pages will need it since it's used to render the css urls)
def PGWebContextProcessor(request):
gitrev = SimpleLazyObject(_get_gitrev)
if request.is_secure(): if request.is_secure():
return { return {
'link_root': settings.SITE_ROOT, 'link_root': settings.SITE_ROOT,
'gitrev': gitrev,
} }
else: else:
return {} return {
'gitrev': gitrev,
}

View File

@ -6,12 +6,12 @@
<meta http-equiv="Content-Type" content="text/xhtml; charset=utf-8" /> <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 #} {%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" /> <meta name="copyright" content="The PostgreSQL Global Development Group" />
<style type="text/css" media="screen" title="Normal Text">@import url("/dyncss/base.css");</style> <style type="text/css" media="screen" title="Normal Text">@import url("/dyncss/base.css?{{gitrev}}");</style>
<link rel="shortcut icon" href="/favicon.ico" /> <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 stylesheet" type="text/css" media="screen" title="Large Text" href="/media/css/docs_large.css?{{gitrev}}" />
<link rel="alternate" type="application/rss+xml" title="PostgreSQL News" href="{{link_root}}/news.rss" /> <link rel="alternate" type="application/rss+xml" title="PostgreSQL News" href="{{link_root}}/news.rss" />
<link rel="alternate" type="application/rss+xml" title="PostgreSQL Events" href="{{link_root}}/events.rss" /> <link rel="alternate" type="application/rss+xml" title="PostgreSQL Events" href="{{link_root}}/events.rss" />
<script async type="text/javascript" src="/media/js/styleswitcher.js"></script> <script async type="text/javascript" src="/media/js/styleswitcher.js?{{gitrev}}"></script>
<script type="text/javascript"> <script type="text/javascript">
var _gaq = _gaq || []; var _gaq = _gaq || [];
_gaq.push(['_setAccount', 'UA-1345454-1']); _gaq.push(['_setAccount', 'UA-1345454-1']);

View File

@ -2,10 +2,10 @@
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en" dir="ltr"> <html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en" dir="ltr">
<head> <head>
<title>PostgreSQL: Documentation: {{page.display_version}}: {{page.title}}</title> <title>PostgreSQL: Documentation: {{page.display_version}}: {{page.title}}</title>
<style type="text/css" media="screen" title="Normal Text">@import url("/dyncss/docs.css");</style> <style type="text/css" media="screen" title="Normal Text">@import url("/dyncss/docs.css?{{gitrev}}");</style>
<link rel="alternate stylesheet" media="screen" href="/media/css/docs_large.css" type="text/css" title="Large Text" /> <link rel="alternate stylesheet" media="screen" href="/media/css/docs_large.css" type="text/css" title="Large Text" />
<script async type="text/javascript" src="/media/js/styleswitcher.js"></script> <script async type="text/javascript" src="/media/js/styleswitcher.js?{{gitrev}}"></script>
<script type="text/javascript" src="/media/js/monospacefix.js"></script> <script type="text/javascript" src="/media/js/monospacefix.js?{{gitrev}}"></script>
<script type="text/javascript"> <script type="text/javascript">
var _gaq = _gaq || []; var _gaq = _gaq || [];
_gaq.push(['_setAccount', 'UA-1345454-1']); _gaq.push(['_setAccount', 'UA-1345454-1']);