Skip to content

Commit

Permalink
Merge pull request #851 from pmclanahan/redirect-tabzilla-cdn-bug-859572
Browse files Browse the repository at this point in the history
Fix bug 859572: Redirect tabzilla.js to CDN.
  • Loading branch information
pmac committed May 14, 2013
2 parents d54c084 + 01436d6 commit b822b7e
Show file tree
Hide file tree
Showing 9 changed files with 135 additions and 16 deletions.
3 changes: 3 additions & 0 deletions apps/tabzilla/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
27 changes: 27 additions & 0 deletions apps/tabzilla/middleware.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.

from django.conf import settings

from funfactory.middleware import LocaleURLMiddleware


class TabzillaLocaleURLMiddleware(LocaleURLMiddleware):
def process_request(self, request):
resp = super(TabzillaLocaleURLMiddleware, self).process_request(request)

# no locale redirect happening
if resp is None:
return resp

is_enabled = not settings.TEMPLATE_DEBUG and settings.CDN_BASE_URL
is_interesting = 'tabzilla.js' in resp.get('location', '')
if is_enabled and is_interesting:
# CDN URL should be protocol relative, but that won't work
# in a Location header.
protocol = 'https:' if request.is_secure() else 'http:'
cdn_base = protocol + settings.CDN_BASE_URL
resp['location'] = cdn_base + resp['location']

return resp
6 changes: 3 additions & 3 deletions apps/tabzilla/models.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
from django.db import models

# Create your models here.
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
63 changes: 61 additions & 2 deletions apps/tabzilla/tests.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,21 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.

from math import floor
import time
from hashlib import md5

from django.conf import settings
from django.test import Client
from django.test import Client, RequestFactory
from django.utils.http import parse_http_date

from funfactory.urlresolvers import reverse
from mock import patch
from nose.tools import eq_
from nose.tools import eq_, ok_

from mozorg.tests import TestCase
from tabzilla.middleware import TabzillaLocaleURLMiddleware


class TabzillaViewTests(TestCase):
Expand Down Expand Up @@ -45,6 +50,12 @@ class TabzillaRedirectTests(TestCase):
def setUp(self):
self.client = Client()

def _process_request(self, url):
rf = RequestFactory()
with self.activate('en-US'):
req = rf.get(url)
return TabzillaLocaleURLMiddleware().process_request(req)

def test_locale_preserved(self):
"""The tabzilla URL should preserve the locale through redirects."""
resp = self.client.get('/de/tabzilla/media/js/tabzilla.js')
Expand Down Expand Up @@ -88,3 +99,51 @@ def test_tabzilla_css_less_processing(self, less_mock):
with self.activate('en-US'):
self.client.get(tabzilla_css_url)
eq_(less_mock.call_count, 1)

@patch('tabzilla.middleware.settings.CDN_BASE_URL', '//example.com')
@patch('tabzilla.middleware.settings.TEMPLATE_DEBUG', True)
def test_no_cdn_redirect_middleware_template_debug(self):
"""
Tabzilla should NOT redirect to a CDN when it redirects to a locale
when TEMPLATE_DEBUG = True.
"""
resp = self._process_request('/tabzilla/tabzilla.js')
eq_(resp['location'], '/en-US/tabzilla/tabzilla.js')

@patch('tabzilla.middleware.settings.CDN_BASE_URL', '//example.com')
@patch('tabzilla.middleware.settings.TEMPLATE_DEBUG', False)
def test_no_cdn_redirect_middleware_specified_locale(self):
"""
Tabzilla should NOT redirect to a CDN when it doesn't need to redirect
to a locale.
"""
resp = self._process_request('/en-US/tabzilla/tabzilla.js')
ok_(resp is None)

@patch('tabzilla.middleware.settings.CDN_BASE_URL', '')
@patch('tabzilla.middleware.settings.TEMPLATE_DEBUG', True)
def test_no_cdn_redirect_middleware_no_cdn(self):
"""
Tabzilla should NOT redirect to a CDN when it redirects to a locale
when no CDN is configured.
"""
resp = self._process_request('/tabzilla/tabzilla.js')
eq_(resp['location'], '/en-US/tabzilla/tabzilla.js')

@patch('tabzilla.middleware.settings.CDN_BASE_URL', '//example.com')
@patch('tabzilla.middleware.settings.TEMPLATE_DEBUG', False)
def test_cdn_redirect_middleware(self):
"""
Tabzilla should redirect to a CDN when it redirects to a locale
"""
resp = self._process_request('/tabzilla/tabzilla.js')
eq_(resp['location'], 'http://example.com/en-US/tabzilla/tabzilla.js')

@patch('tabzilla.middleware.settings.CDN_BASE_URL', '//example.com')
@patch('tabzilla.middleware.settings.TEMPLATE_DEBUG', False)
def test_no_cdn_redirect_middleware(self):
"""
Middleware should NOT redirect to a CDN when it's not tabzilla
"""
resp = self._process_request('/')
eq_(resp['location'], '/en-US/')
8 changes: 6 additions & 2 deletions apps/tabzilla/urls.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
from django.conf.urls.defaults import *
from mozorg.util import page
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.

from django.conf.urls.defaults import * # noqa

import views


urlpatterns = patterns('',
url(r'^tabzilla\.js$', views.tabzilla_js, name='tabzilla'),
)
4 changes: 4 additions & 0 deletions apps/tabzilla/views.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.

import l10n_utils

from mozorg.decorators import cache_control_expires
Expand Down
25 changes: 20 additions & 5 deletions docs/tabzilla.rst
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
Tabzilla
========

*tabzilla* is the universal tab displayed on Mozilla websites.
*Tabzilla* is the universal tab displayed on Mozilla websites.

Adding the universal tab to a site requires:

Expand All @@ -18,16 +18,31 @@ Adding the universal tab to a site requires:
<a href="http://www.mozilla.org/" id="tabzilla">mozilla</a>

2. Include the tabzilla.css CSS file either as a CSS include or built in to your minified styles::
<link href="//www.mozilla.org/media/css/tabzilla-min.css" rel="stylesheet" />

<link href="//mozorg.cdn.mozilla.net/media/css/tabzilla-min.css" rel="stylesheet" />

3. Include the tabzilla.js file in your template (preferably in the footer)::

<script src="//www.mozilla.org/tabzilla/tabzilla.js"></script>
<script src="//mozorg.cdn.mozilla.net/tabzilla/tabzilla.js"></script>

This will choose the best locale for your visitor. If you prefer to force the locale, you can use::

<script src="//www.mozilla.org/{locale}/tabzilla/tabzilla.js"></script>
<script src="//mozorg.cdn.mozilla.net/{locale}/tabzilla/tabzilla.js"></script>

Where ``{locale}`` is the language in which you'd like Tabzilla to be loaded (e.g. fr or de).
If Tabzilla is not yet translated into said locale the user will get the en-US version.

.. note:: Tabzilla uses jQuery. If your site already includes jQuery be sure to
place the Tabzilla script tag **after** the one for jQuery. Tabzilla will
use the existing jQuery if available and a supported version, otherwise
it will load its own version of jQuery.

That the source file URLs begin with ``//`` is not a typo. This is a
protocol-relative URL which allows the resource to be loaded via
whichever protocol (http or https) the page itself is loaded. This
removes the need to add any logic to support loading Tabzilla over
both secure and insecure connections, thereby avoiding mixed-content
warnings from the browser.


Requirements
Expand Down
7 changes: 5 additions & 2 deletions settings/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,16 @@
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.

from .base import *
from .base import * # noqa
try:
from .local import *
from .local import * # noqa
except ImportError, exc:
exc.args = tuple(['%s (did you rename settings/local.py-dist?)' % exc.args[0]])
raise exc


if DEV:
ALLOWED_HOSTS = ['*']


MEDIA_URL = CDN_BASE_URL + MEDIA_URL
8 changes: 6 additions & 2 deletions settings/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

from django.utils.functional import lazy

from funfactory.settings_base import *
from funfactory.settings_base import * # noqa

# Make file paths relative to settings.
ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
Expand Down Expand Up @@ -574,7 +574,7 @@ def JINJA_CONFIG():
MIDDLEWARE_CLASSES = (
'mozorg.middleware.MozorgRequestTimingMiddleware',
'django_statsd.middleware.GraphiteMiddleware',
'funfactory.middleware.LocaleURLMiddleware',
'tabzilla.middleware.TabzillaLocaleURLMiddleware',
'django.middleware.common.CommonMiddleware',
'commonware.middleware.FrameOptionsHeader',
'mozorg.middleware.CacheMiddleware',
Expand Down Expand Up @@ -705,3 +705,7 @@ def facebook_tab_url_lazy():
from django.conf import settings
return '//www.facebook.com/{page}/app_{id}'.format(page=settings.FACEBOOK_PAGE_NAMESPACE, id=settings.FACEBOOK_APP_ID)
FACEBOOK_TAB_URL = lazy(facebook_tab_url_lazy, str)()

# Prefix for media. No trailing slash.
# e.g. '//mozorg.cdn.mozilla.net'
CDN_BASE_URL = ''

0 comments on commit b822b7e

Please sign in to comment.