[fix bug 1029829][fix bug 1029838][bug 1081917] Thunderbird RNA

* Created releasenotes bedrock app
* Migrated firefox release notes views to releasenotes app.
* Added thunderbird releasenotes urls
* Added global.conf RewriteRules for Thunderbird release notes.
* Thunderbird base, release notes, system requirements templates
This commit is contained in:
Giorgos Logiotatidis 2014-10-01 14:58:09 +03:00
Родитель d9ed3b1b69
Коммит 1b1381e328
21 изменённых файлов: 1003 добавлений и 514 удалений

Просмотреть файл

@ -1,13 +0,0 @@
# 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/.
# Adapted from django-mozilla-product-details
version_re = (r"\d+" # major (x in x.y)
"\.\d+" # minor1 (y in x.y)
"\.?(?:\d+)?" # minor2 (z in x.y.z)
"\.?(?:\d+)?" # minor3 (w in x.y.z.w)
"(?:a|b(?:eta)?)?" # alpha/beta
"(?:\d*)" # alpha/beta version
"(?:pre)?" # pre release
"(?:\d)?") # pre release version

Просмотреть файл

@ -1,5 +1,4 @@
# -*- coding: utf-8 -*-
# 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/.
@ -450,7 +449,7 @@ class TestWhatsNew(TestCase):
def test_fx_australis_29(self, render_mock):
"""Should use australis template for 29.0."""
req = self.rf.get('/en-US/firefox/whatsnew/')
self.view(req, fx_version='29.0')
self.view(req, version='29.0')
template = render_mock.call_args[0][1]
eq_(template, ['firefox/australis/whatsnew-no-tour.html'])
@ -458,7 +457,7 @@ class TestWhatsNew(TestCase):
def test_fx_australis_29_0_1(self, render_mock):
"""Should use australis template for 29.0.1"""
req = self.rf.get('/en-US/firefox/whatsnew/')
self.view(req, fx_version='29.0.1')
self.view(req, version='29.0.1')
template = render_mock.call_args[0][1]
eq_(template, ['firefox/australis/whatsnew-no-tour.html'])
@ -466,7 +465,7 @@ class TestWhatsNew(TestCase):
def test_fx_30(self, render_mock):
"""Should use australis template for 30.0."""
req = self.rf.get('/en-US/firefox/whatsnew/')
self.view(req, fx_version='30.0')
self.view(req, version='30.0')
template = render_mock.call_args[0][1]
eq_(template, ['firefox/australis/whatsnew-no-tour.html'])
@ -474,7 +473,7 @@ class TestWhatsNew(TestCase):
def test_fx_31(self, render_mock):
"""Should use australis template for 31.0."""
req = self.rf.get('/en-US/firefox/whatsnew/')
self.view(req, fx_version='31.0')
self.view(req, version='31.0')
template = render_mock.call_args[0][1]
eq_(template, ['firefox/australis/whatsnew-no-tour.html'])
@ -482,7 +481,7 @@ class TestWhatsNew(TestCase):
def test_fx_33_0(self, render_mock):
"""Should use australis template for 33.0."""
req = self.rf.get('/en-US/firefox/whatsnew/')
self.view(req, fx_version='33.0')
self.view(req, version='33.0')
template = render_mock.call_args[0][1]
eq_(template, ['firefox/australis/whatsnew-no-tour.html'])
@ -490,7 +489,7 @@ class TestWhatsNew(TestCase):
def test_fx_33_0_1(self, render_mock):
"""Should use australis template for 33.0.1"""
req = self.rf.get('/en-US/firefox/whatsnew/')
self.view(req, fx_version='33.0.1')
self.view(req, version='33.0.1')
template = render_mock.call_args[0][1]
eq_(template, ['firefox/australis/whatsnew-no-tour.html'])
@ -498,7 +497,7 @@ class TestWhatsNew(TestCase):
def test_fx_33_1(self, render_mock):
"""Should use privacy tour template for 33.1"""
req = self.rf.get('/en-US/firefox/whatsnew/')
self.view(req, fx_version='33.1')
self.view(req, version='33.1')
template = render_mock.call_args[0][1]
eq_(template, ['firefox/privacy_tour/no-tour.html'])
@ -506,7 +505,7 @@ class TestWhatsNew(TestCase):
def test_rv_prefix(self, render_mock):
"""Prefixed oldversion shouldn't impact version sniffing."""
req = self.rf.get('/en-US/firefox/whatsnew/?oldversion=rv:10.0')
self.view(req, fx_version='33.1')
self.view(req, version='33.1')
template = render_mock.call_args[0][1]
eq_(template, ['firefox/privacy_tour/tour.html'])
@ -516,7 +515,7 @@ class TestWhatsNew(TestCase):
url = '/en-US/firefox/whatsnew/'
req = self.rf.get(url)
with patch.object(req, 'is_secure', return_value=False):
resp = self.view(req, fx_version='29.0')
resp = self.view(req, version='29.0')
eq_(resp['location'], 'https://testserver' + url)
@override_settings(DEV=True)
@ -525,7 +524,7 @@ class TestWhatsNew(TestCase):
url = '/en-US/firefox/whatsnew/'
req = self.rf.get(url)
with patch.object(req, 'is_secure', return_value=False):
resp = self.view(req, fx_version='29.0')
resp = self.view(req, version='29.0')
eq_(resp.status_code, 200)
@override_settings(DEV=True)
@ -534,7 +533,7 @@ class TestWhatsNew(TestCase):
url = '/en-US/firefox/whatsnew/'
req = self.rf.get(url)
with patch.object(req, 'is_secure', return_value=True):
resp = self.view(req, fx_version='29.0')
resp = self.view(req, version='29.0')
eq_(resp.status_code, 200)
@ -549,7 +548,7 @@ class TestTourView(TestCase):
def test_fx_tour_template(self, render_mock):
"""Should use firstrun tour template"""
req = self.rf.get('/en-US/firefox/tour/')
self.view(req, fx_version='29.0')
self.view(req, version='29.0')
template = render_mock.call_args[0][1]
eq_(template, ['firefox/australis/help-menu-tour.html'])
@ -557,7 +556,7 @@ class TestTourView(TestCase):
def test_fx_dev_browser_35_0_a2(self, render_mock):
"""Should use dev browser firstrun template for 35.0a2"""
req = self.rf.get('/en-US/firefox/tour/')
self.view(req, fx_version='35.0a2')
self.view(req, version='35.0a2')
template = render_mock.call_args[0][1]
eq_(template, ['firefox/dev-firstrun.html'])
@ -565,7 +564,7 @@ class TestTourView(TestCase):
def test_fx_dev_browser_35_1_a2(self, render_mock):
"""Should use dev browser firstrun template for 35.1a2"""
req = self.rf.get('/en-US/firefox/tour/')
self.view(req, fx_version='35.1a2')
self.view(req, version='35.1a2')
template = render_mock.call_args[0][1]
eq_(template, ['firefox/dev-firstrun.html'])
@ -573,7 +572,7 @@ class TestTourView(TestCase):
def test_fx_dev_browser_36_0_a2(self, render_mock):
"""Should use dev browser firstrun template for 36.0a2"""
req = self.rf.get('/en-US/firefox/tour/')
self.view(req, fx_version='36.0a2')
self.view(req, version='36.0a2')
template = render_mock.call_args[0][1]
eq_(template, ['firefox/dev-firstrun.html'])
@ -581,7 +580,7 @@ class TestTourView(TestCase):
def test_fx_dev_browser_34_0_a2(self, render_mock):
"""Should use standard firstrun template for older aurora"""
req = self.rf.get('/en-US/firefox/tour/')
self.view(req, fx_version='34.0a2')
self.view(req, version='34.0a2')
template = render_mock.call_args[0][1]
eq_(template, ['firefox/australis/help-menu-tour.html'])
@ -591,7 +590,7 @@ class TestTourView(TestCase):
url = '/en-US/firefox/tour/'
req = self.rf.get(url)
with patch.object(req, 'is_secure', return_value=False):
resp = self.view(req, fx_version='29.0')
resp = self.view(req, version='29.0')
eq_(resp['location'], 'https://testserver' + url)
@override_settings(DEV=True)
@ -600,7 +599,7 @@ class TestTourView(TestCase):
url = '/en-US/firefox/tour/'
req = self.rf.get(url)
with patch.object(req, 'is_secure', return_value=False):
resp = self.view(req, fx_version='29.0')
resp = self.view(req, version='29.0')
eq_(resp.status_code, 200)
@override_settings(DEV=True)
@ -609,7 +608,7 @@ class TestTourView(TestCase):
url = '/en-US/firefox/tour/'
req = self.rf.get(url)
with patch.object(req, 'is_secure', return_value=True):
resp = self.view(req, fx_version='29.0')
resp = self.view(req, version='29.0')
eq_(resp.status_code, 200)
@ -633,7 +632,7 @@ class TestFirstRun(TestCase):
def test_fx_australis_29(self, render_mock):
"""Should use firstrun tour template"""
req = self.rf.get('/en-US/firefox/firstrun/')
self.view(req, fx_version='29.0')
self.view(req, version='29.0')
template = render_mock.call_args[0][1]
eq_(template, ['firefox/australis/firstrun-tour.html'])
@ -641,7 +640,7 @@ class TestFirstRun(TestCase):
def test_fx_dev_browser_35_0_a2(self, render_mock):
"""Should use dev browser firstrun template for 35.0a2"""
req = self.rf.get('/en-US/firefox/firstrun/')
self.view(req, fx_version='35.0a2')
self.view(req, version='35.0a2')
template = render_mock.call_args[0][1]
eq_(template, ['firefox/dev-firstrun.html'])
@ -649,7 +648,7 @@ class TestFirstRun(TestCase):
def test_fx_dev_browser_35_1_a2(self, render_mock):
"""Should use dev browser firstrun template for 35.1a2"""
req = self.rf.get('/en-US/firefox/firstrun/')
self.view(req, fx_version='35.1a2')
self.view(req, version='35.1a2')
template = render_mock.call_args[0][1]
eq_(template, ['firefox/dev-firstrun.html'])
@ -657,7 +656,7 @@ class TestFirstRun(TestCase):
def test_fx_dev_browser_36_0_a2(self, render_mock):
"""Should use dev browser firstrun template for 36.0a2"""
req = self.rf.get('/en-US/firefox/firstrun/')
self.view(req, fx_version='36.0a2')
self.view(req, version='36.0a2')
template = render_mock.call_args[0][1]
eq_(template, ['firefox/dev-firstrun.html'])
@ -665,7 +664,7 @@ class TestFirstRun(TestCase):
def test_fx_dev_browser_34_0_a2(self, render_mock):
"""Should use standard firstrun template for older aurora"""
req = self.rf.get('/en-US/firefox/firstrun/')
self.view(req, fx_version='34.0a2')
self.view(req, version='34.0a2')
template = render_mock.call_args[0][1]
eq_(template, ['firefox/australis/firstrun-tour.html'])
@ -675,7 +674,7 @@ class TestFirstRun(TestCase):
url = '/en-US/firefox/firstrun/'
req = self.rf.get(url)
with patch.object(req, 'is_secure', return_value=False):
resp = self.view(req, fx_version='29.0')
resp = self.view(req, version='29.0')
eq_(resp['location'], 'https://testserver' + url)
@override_settings(DEV=True)
@ -684,7 +683,7 @@ class TestFirstRun(TestCase):
url = '/en-US/firefox/firstrun/'
req = self.rf.get(url)
with patch.object(req, 'is_secure', return_value=False):
resp = self.view(req, fx_version='29.0')
resp = self.view(req, version='29.0')
eq_(resp.status_code, 200)
@override_settings(DEV=True)
@ -693,7 +692,7 @@ class TestFirstRun(TestCase):
url = '/en-US/firefox/firstrun/'
req = self.rf.get(url)
with patch.object(req, 'is_secure', return_value=True):
resp = self.view(req, fx_version='29.0')
resp = self.view(req, version='29.0')
eq_(resp.status_code, 200)
@ -833,117 +832,3 @@ class TestWhatsnewRedirect(FxVersionRedirectsMixin, TestCase):
# if there's no oldversion parameter, show no tour
response = self.client.get(self.url, HTTP_USER_AGENT=self.user_agent)
self.assertNotIn(self.expected, response.content)
@patch.object(fx_views, 'firefox_details', firefox_details)
class TestReleaseNotesIndex(TestCase):
def test_relnotes_index(self):
with self.activate('en-US'):
response = self.client.get(reverse('firefox.releases.index'))
doc = pq(response.content)
eq_(len(doc('a[href="0.1.html"]')), 1)
eq_(len(doc('a[href="0.10.html"]')), 1)
eq_(len(doc('a[href="1.0.html"]')), 1)
eq_(len(doc('a[href="1.0.8.html"]')), 1)
eq_(len(doc('a[href="1.5.html"]')), 1)
eq_(len(doc('a[href="1.5.0.12.html"]')), 1)
eq_(len(doc('a[href="../2.0/releasenotes/"]')), 1)
eq_(len(doc('a[href="../2.0.0.20/releasenotes/"]')), 1)
eq_(len(doc('a[href="../3.6/releasenotes/"]')), 1)
eq_(len(doc('a[href="../3.6.28/releasenotes/"]')), 1)
eq_(len(doc('a[href="../17.0/releasenotes/"]')), 1)
eq_(len(doc('a[href="../17.0.11/releasenotes/"]')), 1)
eq_(len(doc('a[href="../24.0/releasenotes/"]')), 1)
eq_(len(doc('a[href="../24.1.0/releasenotes/"]')), 1)
eq_(len(doc('a[href="../24.1.1/releasenotes/"]')), 1)
eq_(len(doc('a[href="../25.0/releasenotes/"]')), 1)
eq_(len(doc('a[href="../25.0.1/releasenotes/"]')), 1)
@patch.object(fx_views, 'firefox_details', firefox_details)
@patch.object(fx_views, 'mobile_details', mobile_details)
class TestNotesRedirects(TestCase):
def _test(self, url_from, url_to):
with self.activate('en-US'):
url = '/en-US' + url_from
response = self.client.get(url)
eq_(response.status_code, 302)
eq_(response['Location'], 'http://testserver/en-US' + url_to)
@patch.dict(product_details.firefox_versions,
LATEST_FIREFOX_VERSION='22.0')
def test_desktop_release_version(self):
self._test('/firefox/notes/',
'/firefox/22.0/releasenotes/')
self._test('/firefox/latest/releasenotes/',
'/firefox/22.0/releasenotes/')
@patch.dict(product_details.firefox_versions,
LATEST_FIREFOX_DEVEL_VERSION='23.0b1')
def test_desktop_beta_version(self):
self._test('/firefox/beta/notes/',
'/firefox/23.0beta/releasenotes/')
@patch.dict(product_details.firefox_versions,
FIREFOX_AURORA='24.0a2')
def test_desktop_aurora_version(self):
self._test('/firefox/aurora/notes/',
'/firefox/24.0a2/auroranotes/')
@patch.dict(product_details.firefox_versions,
FIREFOX_ESR='24.2.0esr')
def test_desktop_esr_version(self):
self._test('/firefox/organizations/notes/',
'/firefox/24.2.0/releasenotes/')
@patch.dict(product_details.mobile_details,
version='22.0')
def test_mobile_release_version(self):
self._test('/mobile/notes/',
'/mobile/22.0/releasenotes/')
@patch.dict(product_details.mobile_details,
beta_version='23.0b1')
def test_mobile_beta_version(self):
self._test('/mobile/beta/notes/',
'/mobile/23.0beta/releasenotes/')
@patch.dict(product_details.mobile_details,
alpha_version='24.0a2')
def test_mobile_aurora_version(self):
self._test('/mobile/aurora/notes/',
'/mobile/24.0a2/auroranotes/')
@patch.object(fx_views, 'firefox_details', firefox_details)
class TestSysreqRedirect(TestCase):
def _test(self, url_from, url_to):
with self.activate('en-US'):
url = '/en-US' + url_from
response = self.client.get(url)
eq_(response.status_code, 302)
eq_(response['Location'], 'http://testserver/en-US' + url_to)
@patch.dict(product_details.firefox_versions,
LATEST_FIREFOX_VERSION='22.0')
def test_desktop_release_version(self):
self._test('/firefox/system-requirements/',
'/firefox/22.0/system-requirements/')
@patch.dict(product_details.firefox_versions,
LATEST_FIREFOX_DEVEL_VERSION='23.0b1')
def test_desktop_beta_version(self):
self._test('/firefox/beta/system-requirements/',
'/firefox/23.0beta/system-requirements/')
@patch.dict(product_details.firefox_versions,
FIREFOX_AURORA='24.0a2')
def test_desktop_aurora_version(self):
self._test('/firefox/aurora/system-requirements/',
'/firefox/24.0a2/system-requirements/')
@patch.dict(product_details.firefox_versions,
FIREFOX_ESR='24.2.0esr')
def test_desktop_esr_version(self):
self._test('/firefox/organizations/system-requirements/',
'/firefox/24.0/system-requirements/')

Просмотреть файл

@ -1,16 +1,9 @@
# 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.http import Http404
from django.test.client import RequestFactory
from django.test.utils import override_settings
from funfactory.urlresolvers import reverse
from mock import patch, Mock
from nose.tools import eq_, ok_
from rna.models import Release
from nose.tools import ok_
from bedrock.firefox import views
from bedrock.mozorg.tests import TestCase
@ -25,190 +18,3 @@ class TestFirefoxNew(TestCase):
resp = self.client.get(reverse('firefox.new'))
ok_('x-frame-options' not in resp)
class TestRNAViews(TestCase):
def setUp(self):
self.factory = RequestFactory()
self.request = self.factory.get('/')
self.render_patch = patch('bedrock.firefox.views.l10n_utils.render')
self.mock_render = self.render_patch.start()
self.mock_render.return_value.has_header.return_value = False
def tearDown(self):
self.render_patch.stop()
@property
def last_ctx(self):
"""
Convenient way to access the context of the last rendered
response.
"""
return self.mock_render.call_args[0][2]
@patch('bedrock.firefox.views.get_object_or_404')
@patch('bedrock.firefox.views.Q')
def test_get_release_or_404(self, Q, get_object_or_404):
eq_(views.get_release_or_404('version', 'product'),
get_object_or_404.return_value)
get_object_or_404.assert_called_with(
Release, Q.return_value, version='version')
Q.assert_called_with(product='product')
@patch('bedrock.firefox.views.get_object_or_404')
@patch('bedrock.firefox.views.Q')
def test_get_release_or_404_esr(self, Q, get_object_or_404):
eq_(views.get_release_or_404('24.5.0', 'Firefox'),
get_object_or_404.return_value)
Q.assert_any_call(product='Firefox')
Q.assert_any_call(product='Firefox Extended Support Release')
Q.__or__.assert_called()
@override_settings(DEV=False)
@patch('bedrock.firefox.views.release_notes_template')
@patch('bedrock.firefox.views.get_release_or_404')
@patch('bedrock.firefox.views.equivalent_release_url')
def test_release_notes(self, mock_equiv_rel_url, get_release_or_404,
mock_release_notes_template):
"""
Should use release returned from get_release_or_404 with the
correct params and pass the correct context variables and
template to l10n_utils.render.
"""
mock_release = get_release_or_404.return_value
mock_release.major_version.return_value = '34'
mock_release.notes.return_value = ([Release(id=1), Release(id=2)],
[Release(id=3), Release(id=4)])
views.release_notes(self.request, '27.0')
get_release_or_404.assert_called_with('27.0', 'Firefox')
mock_release.notes.assert_called_with(public_only=True)
eq_(self.last_ctx['version'], '27.0')
eq_(self.last_ctx['release'], mock_release)
eq_(self.last_ctx['new_features'], [Release(id=1), Release(id=2)])
eq_(self.last_ctx['known_issues'], [Release(id=3), Release(id=4)])
eq_(self.mock_render.call_args[0][1],
mock_release_notes_template.return_value)
mock_equiv_rel_url.assert_called_with(mock_release)
mock_release_notes_template.assert_called_with(
mock_release.channel, 'Firefox', 34)
@patch('bedrock.firefox.views.get_release_or_404')
@patch('bedrock.firefox.views.releasenotes_url')
def test_release_notes_beta_redirect(self, releasenotes_url,
get_release_or_404):
"""
Should redirect to url for beta release
"""
get_release_or_404.side_effect = [Http404, 'mock release']
releasenotes_url.return_value = '/firefox/27.0beta/releasenotes/'
response = views.release_notes(self.request, '27.0')
eq_(response.status_code, 302)
eq_(response['location'], '/firefox/27.0beta/releasenotes/')
get_release_or_404.assert_called_with('27.0beta', 'Firefox')
releasenotes_url.assert_called_with('mock release')
@patch('bedrock.firefox.views.get_release_or_404')
def test_system_requirements(self, get_release_or_404):
"""
Should use release returned from get_release_or_404, with a
default channel of Release and default product of Firefox,
and pass the version to l10n_utils.render
"""
views.system_requirements(self.request, '27.0.1')
get_release_or_404.assert_called_with('27.0.1', 'Firefox')
eq_(self.last_ctx['release'], get_release_or_404.return_value)
eq_(self.last_ctx['version'], '27.0.1')
eq_(self.mock_render.call_args[0][1],
'firefox/releases/system_requirements.html')
def test_release_notes_template(self):
"""
Should return correct template name based on channel
and product
"""
eq_(views.release_notes_template('', 'Firefox OS'),
'firefox/releases/os-notes.html')
eq_(views.release_notes_template('Nightly', 'Firefox'),
'firefox/releases/nightly-notes.html')
eq_(views.release_notes_template('Aurora', 'Firefox'),
'firefox/releases/aurora-notes.html')
eq_(views.release_notes_template('Aurora', 'Firefox', 35),
'firefox/releases/dev-browser-notes.html')
eq_(views.release_notes_template('Aurora', 'Firefox', 34),
'firefox/releases/aurora-notes.html')
eq_(views.release_notes_template('Beta', 'Firefox'),
'firefox/releases/beta-notes.html')
eq_(views.release_notes_template('Release', 'Firefox'),
'firefox/releases/release-notes.html')
eq_(views.release_notes_template('ESR', 'Firefox'),
'firefox/releases/esr-notes.html')
eq_(views.release_notes_template('', ''),
'firefox/releases/release-notes.html')
@patch('bedrock.firefox.views.get_release_or_404')
def test_firefox_os_manual_template(self, get_release_or_404):
"""
Should render from pre-RNA template without querying DB
"""
views.release_notes(self.request, '1.0.1', product='Firefox OS')
get_release_or_404.assert_never_called()
eq_(self.mock_render.call_args[0][1],
'firefox/os/notes-1.0.1.html')
@override_settings(DEV=False)
@patch('bedrock.firefox.views.get_object_or_404')
def test_non_public_release(self, get_object_or_404):
"""
Should raise 404 if not release.is_public and not settings.DEV
"""
get_object_or_404.return_value = Release(is_public=False)
with self.assertRaises(Http404):
views.get_release_or_404('42', 'Firefox')
@patch('bedrock.firefox.views.releasenotes_url')
def test_no_equivalent_release_url(self, mock_releasenotes_url):
"""
Should return None without calling releasenotes_url
"""
release = Mock()
release.equivalent_android_release.return_value = None
release.equivalent_desktop_release.return_value = None
eq_(views.equivalent_release_url(release), None)
eq_(mock_releasenotes_url.called, 0)
@patch('bedrock.firefox.views.releasenotes_url')
def test_android_equivalent_release_url(self, mock_releasenotes_url):
"""
Should return the url for the equivalent android release
"""
release = Mock()
eq_(views.equivalent_release_url(release),
mock_releasenotes_url.return_value)
mock_releasenotes_url.assert_called_with(
release.equivalent_android_release.return_value)
@patch('bedrock.firefox.views.releasenotes_url')
def test_desktop_equivalent_release_url(self, mock_releasenotes_url):
"""
Should return the url for the equivalent desktop release
"""
release = Mock()
release.equivalent_android_release.return_value = None
eq_(views.equivalent_release_url(release),
mock_releasenotes_url.return_value)
mock_releasenotes_url.assert_called_with(
release.equivalent_desktop_release.return_value)
@patch('bedrock.firefox.views.android_builds')
def test_get_download_url_android(self, mock_android_builds):
"""
Shoud return the download link for the release.channel from
android_builds
"""
mock_android_builds.return_value = [{'download_link': '/download'}]
release = Mock(product='Firefox for Android')
link = views.get_download_url(release)
eq_(link, '/download')
mock_android_builds.assert_called_with(release.channel)

Просмотреть файл

@ -5,13 +5,15 @@ from django.conf.urls import patterns, url
from commonware.response.decorators import xframe_allow
from bedrock.firefox import version_re
from bedrock.redirects.util import redirect
from bedrock.mozorg.util import page
import views
import bedrock.releasenotes.views
from bedrock.releasenotes import version_re
latest_re = r'^firefox(?:/(?P<fx_version>%s))?/%s/$'
latest_re = r'^firefox(?:/(?P<version>%s))?/%s/$'
firstrun_re = latest_re % (version_re, 'firstrun')
whatsnew_re = latest_re % (version_re, 'whatsnew')
tour_re = latest_re % (version_re, 'tour')
@ -36,11 +38,6 @@ urlpatterns = patterns('',
page('firefox/developer', 'firefox/developer.html'),
page('firefox/geolocation', 'firefox/geolocation.html'),
page('firefox/interest-dashboard', 'firefox/interest-dashboard.html'),
url('^(?:%s)/(?:%s/)?notes/$' % (product_re, channel_re),
views.latest_notes, name='firefox.notes'),
url('^firefox/latest/releasenotes/$', views.latest_notes),
url('^firefox/(?:%s/)?system-requirements/$' % channel_re,
views.latest_sysreq, name='firefox.sysreq'),
page('firefox/android', 'firefox/android/index.html'),
page('firefox/android/faq', 'firefox/android/faq.html'),
page('firefox/os/faq', 'firefox/os/faq.html'),
@ -53,8 +50,6 @@ urlpatterns = patterns('',
page('firefox/organizations/faq', 'firefox/organizations/faq.html'),
page('firefox/organizations', 'firefox/organizations/organizations.html'),
page('firefox/nightly/firstrun', 'firefox/nightly_firstrun.html'),
url('^firefox/releases/$', views.releases_index,
name='firefox.releases.index'),
url(r'^firefox/installer-help/$', views.installer_help,
name='firefox.installer-help'),
@ -77,19 +72,30 @@ urlpatterns = patterns('',
page('firefox/os', 'firefox/os/index.html'),
page('firefox/os/releases', 'firefox/os/releases.html'),
# firefox/os/notes/ should redirect to the latest version; update this in /redirects/urls.py
url('^firefox/os/notes/(?P<fx_version>%s)/$' % version_re,
views.release_notes, {'product': 'Firefox OS'},
name='firefox.os.releasenotes'),
page('mwc', 'firefox/os/mwc-2014-preview.html'),
page('firefox/os/devices', 'firefox/os/devices.html'),
page('firefox/independent', 'firefox/independent.html'),
url(releasenotes_re, views.release_notes, name='firefox.releasenotes'),
url(mobile_releasenotes_re, views.release_notes,
# Release notes
url('^(?:%s)/(?:%s/)?notes/$' % (product_re, channel_re),
bedrock.releasenotes.views.latest_notes, name='firefox.notes'),
url('firefox/latest/releasenotes/$', bedrock.releasenotes.views.latest_notes,
{'product': 'firefox'}),
url('^firefox/(?:%s/)?system-requirements/$' % channel_re,
bedrock.releasenotes.views.latest_sysreq,
{'product': 'firefox'}, name='firefox.sysreq'),
url(releasenotes_re, bedrock.releasenotes.views.release_notes, name='firefox.releasenotes'),
url(mobile_releasenotes_re, bedrock.releasenotes.views.release_notes,
{'product': 'Firefox for Android'}, name='mobile.releasenotes'),
url(sysreq_re, views.system_requirements,
url(sysreq_re, bedrock.releasenotes.views.system_requirements,
name='firefox.system_requirements'),
# firefox/os/notes/ should redirect to the latest version; update this in /redirects/urls.py
url('^firefox/os/notes/(?P<version>%s)/$' % version_re,
bedrock.releasenotes.views.release_notes, {'product': 'Firefox OS'},
name='firefox.os.releasenotes'),
url('^firefox/releases/$', bedrock.releasenotes.views.releases_index,
{'product': 'Firefox'}, name='firefox.releases.index'),
)

Просмотреть файл

@ -8,10 +8,7 @@ import json
import re
from django.conf import settings
from django.db.models import Q
from django.http import (
Http404, HttpResponsePermanentRedirect, HttpResponseRedirect)
from django.shortcuts import get_object_or_404
from django.http import HttpResponsePermanentRedirect, HttpResponseRedirect
from django.views.decorators.csrf import csrf_exempt, csrf_protect
from django.views.decorators.vary import vary_on_headers
from django.views.generic.base import TemplateView
@ -20,14 +17,10 @@ import basket
from funfactory.urlresolvers import reverse
from jingo_minify.helpers import BUILD_ID_JS, BUNDLE_HASHES
from lib import l10n_utils
from rna.models import Release
from bedrock.firefox import version_re
from bedrock.releasenotes import version_re
from bedrock.firefox.forms import SMSSendForm
from bedrock.mozorg.decorators import cache_control_expires
from bedrock.mozorg.views import process_partnership_form
from bedrock.mozorg.helpers.misc import releasenotes_url
from bedrock.mozorg.helpers.download_buttons import android_builds
from bedrock.firefox.firefox_details import firefox_details, mobile_details
from lib.l10n_utils.dotlang import _
from product_details.version_compare import Version
@ -102,12 +95,6 @@ INSTALLER_CHANNElS = [
# 'nightly', # soon
]
SUPPORT_URLS = {
'Firefox for Android': 'https://support.mozilla.org/products/mobile',
'Firefox OS': 'https://support.mozilla.org/products/firefox-os',
'Firefox': 'https://support.mozilla.org/products/firefox'
}
def get_js_bundle_files(bundle):
"""
@ -260,62 +247,6 @@ def firefox_partners(request):
request, 'firefox/partners/index.html', 'firefox.partners.index', template_vars, form_kwargs)
def releases_index(request):
releases = {}
major_releases = firefox_details.firefox_history_major_releases
minor_releases = firefox_details.firefox_history_stability_releases
for release in major_releases:
major_verion = float(re.findall(r'^\d+\.\d+', release)[0])
# The version numbering scheme of Firefox changes sometimes. The second
# number has not been used since Firefox 4, then reintroduced with
# Firefox ESR 24 (Bug 870540). On this index page, 24.1.x should be
# fallen under 24.0. This patter is a tricky part.
major_pattern = r'^' + \
re.escape(
('%s' if major_verion < 4 else '%g') % round(major_verion, 1))
releases[major_verion] = {
'major': release,
'minor': sorted(filter(lambda x: re.findall(major_pattern, x),
minor_releases),
key=lambda x: int(re.findall(r'\d+$', x)[0]))
}
return l10n_utils.render(request, 'firefox/releases/index.html',
{'releases': sorted(releases.items(), reverse=True)})
def latest_notes(request, product='firefox', channel='release'):
version = get_latest_version(product, channel)
if channel == 'beta':
version = re.sub(r'b\d+$', 'beta', version)
if channel == 'organizations':
version = re.sub(r'esr$', '', version)
dir = 'auroranotes' if channel == 'aurora' else 'releasenotes'
path = [product, version, dir]
locale = getattr(request, 'locale', None)
if locale:
path.insert(0, locale)
return HttpResponseRedirect('/' + '/'.join(path) + '/')
def latest_sysreq(request, channel='release'):
version = get_latest_version('firefox', channel)
if channel == 'beta':
version = re.sub(r'b\d+$', 'beta', version)
if channel == 'organizations':
version = re.sub(r'^(\d+).+', r'\1.0', version)
path = ['firefox', version, 'system-requirements']
locale = getattr(request, 'locale', None)
if locale:
path.insert(0, locale)
return HttpResponseRedirect('/' + '/'.join(path) + '/')
def show_devbrowser_firstrun(version):
match = re.match(r'\d{1,2}', version)
if match:
@ -401,7 +332,7 @@ class FirstrunView(LatestFxView):
return super(FirstrunView, self).get(request, *args, **kwargs)
def get_template_names(self):
version = self.kwargs.get('fx_version') or ''
version = self.kwargs.get('version') or ''
if show_devbrowser_firstrun(version):
template = 'firefox/dev-firstrun.html'
@ -448,7 +379,7 @@ class WhatsnewView(LatestFxView):
return ctx
def get_template_names(self):
version = self.kwargs.get('fx_version') or ''
version = self.kwargs.get('version') or ''
locale = l10n_utils.get_locale(self.request)
oldversion = self.request.GET.get('oldversion', '')
# old versions of Firefox sent a prefixed version
@ -487,7 +418,7 @@ class TourView(LatestFxView):
return super(TourView, self).get(request, *args, **kwargs)
def get_template_names(self):
version = self.kwargs.get('fx_version') or ''
version = self.kwargs.get('version') or ''
if show_devbrowser_firstrun(version):
template = 'firefox/dev-firstrun.html'
@ -496,77 +427,3 @@ class TourView(LatestFxView):
# return a list to conform with original intention
return [template]
def release_notes_template(channel, product, version=None):
if product == 'Firefox OS':
return 'firefox/releases/os-notes.html'
prefix = dict((c, c.lower()) for c in Release.CHANNELS)
if product == 'Firefox' and channel == 'Aurora' and version >= 35:
return 'firefox/releases/dev-browser-notes.html'
return 'firefox/releases/%s-notes.html' % prefix.get(channel, 'release')
def equivalent_release_url(release):
equivalent_release = (release.equivalent_android_release() or
release.equivalent_desktop_release())
if equivalent_release:
return releasenotes_url(equivalent_release)
def get_release_or_404(version, product):
if product == 'Firefox' and len(version.split('.')) == 3:
product_query = Q(product='Firefox') | Q(
product='Firefox Extended Support Release')
else:
product_query = Q(product=product)
release = get_object_or_404(Release, product_query, version=version)
if not release.is_public and not settings.DEV:
raise Http404
return release
def get_download_url(release):
if release.product == 'Firefox for Android':
return android_builds(release.channel)[0]['download_link']
else:
if release.channel == 'Aurora':
return reverse('firefox.channel') + '#aurora'
elif release.channel == 'Beta':
return reverse('firefox.channel') + '#beta'
else:
return reverse('firefox')
@cache_control_expires(1)
def release_notes(request, fx_version, product='Firefox'):
if product == 'Firefox OS' and fx_version in ('1.0.1', '1.1', '1.2'):
return l10n_utils.render(
request, 'firefox/os/notes-%s.html' % fx_version)
try:
release = get_release_or_404(fx_version, product)
except Http404:
release = get_release_or_404(fx_version + 'beta', product)
return HttpResponseRedirect(releasenotes_url(release))
new_features, known_issues = release.notes(public_only=not settings.DEV)
return l10n_utils.render(
request, release_notes_template(release.channel, product,
int(release.major_version())), {
'version': fx_version,
'download_url': get_download_url(release),
'support_url': SUPPORT_URLS.get(product, 'https://support.mozilla.org/'),
'release': release,
'equivalent_release_url': equivalent_release_url(release),
'new_features': new_features,
'known_issues': known_issues})
@cache_control_expires(1)
def system_requirements(request, fx_version, product='Firefox'):
release = get_release_or_404(fx_version, product)
return l10n_utils.render(
request, 'firefox/releases/system_requirements.html',
{'release': release, 'version': fx_version})

Просмотреть файл

@ -0,0 +1,13 @@
# 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/.
# Adapted from django-mozilla-product-details
version_re = (r"\d+" # major (x in x.y)
"\.\d+" # minor1 (y in x.y)
"\.?(?:\d+)?" # minor2 (z in x.y.z)
"\.?(?:\d+)?" # minor3 (w in x.y.z.w)
"(?:a|b(?:eta)?)?" # alpha/beta
"(?:\d*)" # alpha/beta version
"(?:pre)?" # pre release
"(?:\d)?") # pre release version

Просмотреть файл

Просмотреть файл

@ -0,0 +1,338 @@
# 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.http import Http404
from django.test.client import RequestFactory
from django.test.utils import override_settings
from funfactory.urlresolvers import reverse
from mock import patch, Mock
from nose.tools import eq_
from rna.models import Release
from pyquery import PyQuery as pq
from bedrock.mozorg.tests import TestCase
from bedrock.releasenotes import views
from bedrock.firefox.utils import product_details
class TestRNAViews(TestCase):
def setUp(self):
self.factory = RequestFactory()
self.request = self.factory.get('/')
self.render_patch = patch('bedrock.releasenotes.views.l10n_utils.render')
self.mock_render = self.render_patch.start()
self.mock_render.return_value.has_header.return_value = False
def tearDown(self):
self.render_patch.stop()
@property
def last_ctx(self):
"""
Convenient way to access the context of the last rendered
response.
"""
return self.mock_render.call_args[0][2]
@patch('bedrock.releasenotes.views.get_object_or_404')
@patch('bedrock.releasenotes.views.Q')
def test_get_release_or_404(self, Q, get_object_or_404):
eq_(views.get_release_or_404('version', 'product'),
get_object_or_404.return_value)
get_object_or_404.assert_called_with(
Release, Q.return_value, version='version')
Q.assert_called_with(product='product')
@patch('bedrock.releasenotes.views.get_object_or_404')
@patch('bedrock.releasenotes.views.Q')
def test_get_release_or_404_esr(self, Q, get_object_or_404):
eq_(views.get_release_or_404('24.5.0', 'Firefox'),
get_object_or_404.return_value)
Q.assert_any_call(product='Firefox')
Q.assert_any_call(product='Firefox Extended Support Release')
Q.__or__.assert_called()
@override_settings(DEV=False)
@patch('bedrock.releasenotes.views.get_release_or_404')
@patch('bedrock.releasenotes.views.equivalent_release_url')
def test_release_notes(self, mock_equiv_rel_url, get_release_or_404):
"""
Should use release returned from get_release_or_404 with the
correct params and pass the correct context variables and
template to l10n_utils.render.
"""
mock_release = get_release_or_404.return_value
mock_release.notes.return_value = ([Release(id=1), Release(id=2)],
[Release(id=3), Release(id=4)])
views.release_notes(self.request, '27.0')
get_release_or_404.assert_called_with('27.0', 'Firefox')
mock_release.notes.assert_called_with(public_only=True)
eq_(self.last_ctx['version'], '27.0')
eq_(self.last_ctx['release'], mock_release)
eq_(self.last_ctx['new_features'], [Release(id=1), Release(id=2)])
eq_(self.last_ctx['known_issues'], [Release(id=3), Release(id=4)])
eq_(self.mock_render.call_args[0][1],
'firefox/releases/release-notes.html')
mock_equiv_rel_url.assert_called_with(mock_release)
@patch('bedrock.releasenotes.views.get_release_or_404')
@patch('bedrock.releasenotes.views.releasenotes_url')
def test_release_notes_beta_redirect(self, releasenotes_url,
get_release_or_404):
"""
Should redirect to url for beta release
"""
get_release_or_404.side_effect = [Http404, 'mock release']
releasenotes_url.return_value = '/firefox/27.0beta/releasenotes/'
response = views.release_notes(self.request, '27.0')
eq_(response.status_code, 302)
eq_(response['location'], '/firefox/27.0beta/releasenotes/')
get_release_or_404.assert_called_with('27.0beta', 'Firefox')
releasenotes_url.assert_called_with('mock release')
@patch('bedrock.releasenotes.views.get_release_or_404')
def test_system_requirements(self, get_release_or_404):
"""
Should use release returned from get_release_or_404, with a
default channel of Release and default product of Firefox,
and pass the version to l10n_utils.render
"""
views.system_requirements(self.request, '27.0.1')
get_release_or_404.assert_called_with('27.0.1', 'Firefox')
eq_(self.last_ctx['release'], get_release_or_404.return_value)
eq_(self.last_ctx['version'], '27.0.1')
eq_(self.mock_render.call_args[0][1],
'firefox/releases/system_requirements.html')
def test_release_notes_template(self):
"""
Should return correct template name based on channel
and product
"""
eq_(views.release_notes_template('', 'Firefox OS'),
'firefox/releases/os-notes.html')
eq_(views.release_notes_template('Nightly', 'Firefox'),
'firefox/releases/nightly-notes.html')
eq_(views.release_notes_template('Aurora', 'Firefox'),
'firefox/releases/aurora-notes.html')
eq_(views.release_notes_template('Aurora', 'Firefox', 35),
'firefox/releases/dev-browser-notes.html')
eq_(views.release_notes_template('Aurora', 'Firefox', 34),
'firefox/releases/aurora-notes.html')
eq_(views.release_notes_template('Beta', 'Firefox'),
'firefox/releases/beta-notes.html')
eq_(views.release_notes_template('Release', 'Firefox'),
'firefox/releases/release-notes.html')
eq_(views.release_notes_template('ESR', 'Firefox'),
'firefox/releases/esr-notes.html')
eq_(views.release_notes_template('Release', 'Thunderbird'),
'thunderbird/releases/release-notes.html')
eq_(views.release_notes_template('Beta', 'Thunderbird'),
'thunderbird/releases/beta-notes.html')
eq_(views.release_notes_template('', ''),
'firefox/releases/release-notes.html')
@patch('bedrock.releasenotes.views.get_release_or_404')
def test_firefox_os_manual_template(self, get_release_or_404):
"""
Should render from pre-RNA template without querying DB
"""
views.release_notes(self.request, '1.0.1', product='Firefox OS')
get_release_or_404.assert_never_called()
eq_(self.mock_render.call_args[0][1],
'firefox/os/notes-1.0.1.html')
@override_settings(DEV=False)
@patch('bedrock.releasenotes.views.get_object_or_404')
def test_non_public_release(self, get_object_or_404):
"""
Should raise 404 if not release.is_public and not settings.DEV
"""
get_object_or_404.return_value = Release(is_public=False)
with self.assertRaises(Http404):
views.get_release_or_404('42', 'Firefox')
@patch('bedrock.releasenotes.views.releasenotes_url')
def test_no_equivalent_release_url(self, mock_releasenotes_url):
"""
Should return None without calling releasenotes_url
"""
release = Mock()
release.equivalent_android_release.return_value = None
release.equivalent_desktop_release.return_value = None
eq_(views.equivalent_release_url(release), None)
eq_(mock_releasenotes_url.called, 0)
@patch('bedrock.releasenotes.views.releasenotes_url')
def test_android_equivalent_release_url(self, mock_releasenotes_url):
"""
Should return the url for the equivalent android release
"""
release = Mock()
eq_(views.equivalent_release_url(release),
mock_releasenotes_url.return_value)
mock_releasenotes_url.assert_called_with(
release.equivalent_android_release.return_value)
@patch('bedrock.releasenotes.views.releasenotes_url')
def test_desktop_equivalent_release_url(self, mock_releasenotes_url):
"""
Should return the url for the equivalent desktop release
"""
release = Mock()
release.equivalent_android_release.return_value = None
eq_(views.equivalent_release_url(release),
mock_releasenotes_url.return_value)
mock_releasenotes_url.assert_called_with(
release.equivalent_desktop_release.return_value)
@patch('bedrock.releasenotes.views.android_builds')
def test_get_download_url_android(self, mock_android_builds):
"""
Shoud return the download link for the release.channel from
android_builds
"""
mock_android_builds.return_value = [{'download_link': '/download'}]
release = Mock(product='Firefox for Android')
link = views.get_download_url(release)
eq_(link, '/download')
mock_android_builds.assert_called_with(release.channel)
def test_get_download_url_thunderbird(self):
release = Mock(product='Thunderbird')
link = views.get_download_url(release)
eq_(link, 'https://www.mozilla.org/thunderbird/')
class TestReleaseNotesIndex(TestCase):
def test_relnotes_index_firefox(self):
with self.activate('en-US'):
response = self.client.get(reverse('firefox.releases.index'))
doc = pq(response.content)
eq_(len(doc('a[href="0.1.html"]')), 1)
eq_(len(doc('a[href="0.10.html"]')), 1)
eq_(len(doc('a[href="1.0.html"]')), 1)
eq_(len(doc('a[href="1.0.8.html"]')), 1)
eq_(len(doc('a[href="1.5.html"]')), 1)
eq_(len(doc('a[href="1.5.0.12.html"]')), 1)
eq_(len(doc('a[href="../2.0/releasenotes/"]')), 1)
eq_(len(doc('a[href="../2.0.0.20/releasenotes/"]')), 1)
eq_(len(doc('a[href="../3.6/releasenotes/"]')), 1)
eq_(len(doc('a[href="../3.6.28/releasenotes/"]')), 1)
eq_(len(doc('a[href="../17.0/releasenotes/"]')), 1)
eq_(len(doc('a[href="../17.0.11/releasenotes/"]')), 1)
eq_(len(doc('a[href="../24.0/releasenotes/"]')), 1)
eq_(len(doc('a[href="../24.1.0/releasenotes/"]')), 1)
eq_(len(doc('a[href="../24.1.1/releasenotes/"]')), 1)
eq_(len(doc('a[href="../25.0/releasenotes/"]')), 1)
eq_(len(doc('a[href="../25.0.1/releasenotes/"]')), 1)
def test_relnotes_index_thunderbird(self):
with self.activate('en-US'):
response = self.client.get(reverse('thunderbird.releases.index'))
doc = pq(response.content)
eq_(len(doc('a[href="0.1.html"]')), 1)
eq_(len(doc('a[href="1.5.0.2.html"]')), 1)
eq_(len(doc('a[href="../2.0.0.0/releasenotes/"]')), 1)
eq_(len(doc('a[href="../3.0.1/releasenotes/"]')), 1)
class TestNotesRedirects(TestCase):
def _test(self, url_from, url_to):
with self.activate('en-US'):
url = '/en-US' + url_from
response = self.client.get(url)
eq_(response.status_code, 302)
eq_(response['Location'], 'http://testserver/en-US' + url_to)
@patch.dict(product_details.firefox_versions,
LATEST_FIREFOX_VERSION='22.0')
def test_desktop_release_version(self):
self._test('/firefox/notes/',
'/firefox/22.0/releasenotes/')
self._test('/firefox/latest/releasenotes/',
'/firefox/22.0/releasenotes/')
@patch.dict(product_details.firefox_versions,
LATEST_FIREFOX_DEVEL_VERSION='23.0b1')
def test_desktop_beta_version(self):
self._test('/firefox/beta/notes/',
'/firefox/23.0beta/releasenotes/')
@patch.dict(product_details.firefox_versions,
FIREFOX_AURORA='24.0a2')
def test_desktop_aurora_version(self):
self._test('/firefox/aurora/notes/',
'/firefox/24.0a2/auroranotes/')
@patch.dict(product_details.firefox_versions,
FIREFOX_ESR='24.2.0esr')
def test_desktop_esr_version(self):
self._test('/firefox/organizations/notes/',
'/firefox/24.2.0/releasenotes/')
@patch.dict(product_details.mobile_details,
version='22.0')
def test_mobile_release_version(self):
self._test('/mobile/notes/',
'/mobile/22.0/releasenotes/')
@patch.dict(product_details.mobile_details,
beta_version='23.0b1')
def test_mobile_beta_version(self):
self._test('/mobile/beta/notes/',
'/mobile/23.0beta/releasenotes/')
@patch.dict(product_details.mobile_details,
alpha_version='24.0a2')
def test_mobile_aurora_version(self):
self._test('/mobile/aurora/notes/',
'/mobile/24.0a2/auroranotes/')
@patch.dict(product_details.thunderbird_versions,
LATEST_THUNDERBIRD_VERSION='22.0')
def test_thunderbird_release_version(self):
self._test('/thunderbird/latest/releasenotes/',
'/thunderbird/22.0/releasenotes/')
class TestSysreqRedirect(TestCase):
def _test(self, url_from, url_to):
with self.activate('en-US'):
url = '/en-US' + url_from
response = self.client.get(url)
eq_(response.status_code, 302)
eq_(response['Location'], 'http://testserver/en-US' + url_to)
@patch.dict(product_details.firefox_versions,
LATEST_FIREFOX_VERSION='22.0')
def test_desktop_release_version(self):
self._test('/firefox/system-requirements/',
'/firefox/22.0/system-requirements/')
@patch.dict(product_details.firefox_versions,
LATEST_FIREFOX_DEVEL_VERSION='23.0b1')
def test_desktop_beta_version(self):
self._test('/firefox/beta/system-requirements/',
'/firefox/23.0beta/system-requirements/')
@patch.dict(product_details.firefox_versions,
FIREFOX_AURORA='24.0a2')
def test_desktop_aurora_version(self):
self._test('/firefox/aurora/system-requirements/',
'/firefox/24.0a2/system-requirements/')
@patch.dict(product_details.firefox_versions,
FIREFOX_ESR='24.2.0esr')
def test_desktop_esr_version(self):
self._test('/firefox/organizations/system-requirements/',
'/firefox/24.0/system-requirements/')
@patch.dict(product_details.thunderbird_versions,
LATEST_THUNDERBIRD_VERSION='22.0')
def test_thunderbird_release_version(self):
self._test('/thunderbird/latest/system-requirements/',
'/thunderbird/22.0/system-requirements/')

Просмотреть файл

@ -0,0 +1,183 @@
# 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 re
from django.conf import settings
from django.db.models import Q
from django.http import Http404, HttpResponseRedirect
from django.shortcuts import get_object_or_404
from funfactory.urlresolvers import reverse
from lib import l10n_utils
from rna.models import Release
from product_details import product_details
from bedrock.firefox.firefox_details import firefox_details
from bedrock.firefox.views import get_latest_version as firefox_get_latest_version
from bedrock.mozorg.decorators import cache_control_expires
from bedrock.mozorg.helpers.misc import releasenotes_url
from bedrock.mozorg.helpers.download_buttons import android_builds
from bedrock.thunderbird.utils import get_latest_version as thunderbird_get_latest_version
SUPPORT_URLS = {
'Firefox for Android': 'https://support.mozilla.org/products/mobile',
'Firefox OS': 'https://support.mozilla.org/products/firefox-os',
'Firefox': 'https://support.mozilla.org/products/firefox',
'Thunderbird': 'https://support.mozilla.org/products/thunderbird/',
}
def release_notes_template(channel, product, version=None):
if product == 'Firefox OS':
return 'firefox/releases/os-notes.html'
prefix = dict((c, c.lower()) for c in Release.CHANNELS)
if product == 'Firefox' and channel == 'Aurora' and version >= 35:
return 'firefox/releases/dev-browser-notes.html'
dir = 'firefox'
if product == 'Thunderbird':
dir = 'thunderbird'
return ('{dir}/releases/{channel}-notes.html'
.format(dir=dir, channel=prefix.get(channel, 'release')))
def equivalent_release_url(release):
equivalent_release = (release.equivalent_android_release() or
release.equivalent_desktop_release())
if equivalent_release:
return releasenotes_url(equivalent_release)
def get_release_or_404(version, product):
if product == 'Firefox' and len(version.split('.')) == 3:
product_query = Q(product='Firefox') | Q(
product='Firefox Extended Support Release')
else:
product_query = Q(product=product)
release = get_object_or_404(Release, product_query, version=version)
if not release.is_public and not settings.DEV:
raise Http404
return release
def get_download_url(release):
if release.product == 'Thunderbird':
return 'https://www.mozilla.org/thunderbird/'
elif release.product == 'Firefox for Android':
return android_builds(release.channel)[0]['download_link']
else:
if release.channel == 'Aurora':
return reverse('firefox.channel') + '#aurora'
elif release.channel == 'Beta':
return reverse('firefox.channel') + '#beta'
else:
return reverse('firefox')
@cache_control_expires(1)
def release_notes(request, version, product='Firefox'):
if product == 'Firefox OS' and version in ('1.0.1', '1.1', '1.2'):
return l10n_utils.render(
request, 'firefox/os/notes-%s.html' % version)
try:
release = get_release_or_404(version, product)
except Http404:
release = get_release_or_404(version + 'beta', product)
return HttpResponseRedirect(releasenotes_url(release))
new_features, known_issues = release.notes(public_only=not settings.DEV)
return l10n_utils.render(
request, release_notes_template(release.channel, product), {
'version': version,
'download_url': get_download_url(release),
'support_url': SUPPORT_URLS.get(product, 'https://support.mozilla.org/'),
'release': release,
'equivalent_release_url': equivalent_release_url(release),
'new_features': new_features,
'known_issues': known_issues})
@cache_control_expires(1)
def system_requirements(request, version, product='Firefox'):
release = get_release_or_404(version, product)
dir = 'firefox'
if product == 'Thunderbird':
dir = 'thunderbird'
return l10n_utils.render(
request, '{dir}/releases/system_requirements.html'.format(dir=dir),
{'release': release, 'version': version})
def latest_notes(request, product='firefox', channel='release'):
if product == 'thunderbird':
version = thunderbird_get_latest_version(product, channel)
else:
version = firefox_get_latest_version(product, channel)
if channel == 'beta':
version = re.sub(r'b\d+$', 'beta', version)
if channel == 'organizations':
version = re.sub(r'esr$', '', version)
dir = 'auroranotes' if channel == 'aurora' else 'releasenotes'
path = [product, version, dir]
locale = getattr(request, 'locale', None)
if locale:
path.insert(0, locale)
return HttpResponseRedirect('/' + '/'.join(path) + '/')
def latest_sysreq(request, channel, product):
if product == 'thunderbird':
version = thunderbird_get_latest_version(product, channel)
else:
version = firefox_get_latest_version(product, channel)
if channel == 'beta':
version = re.sub(r'b\d+$', 'beta', version)
if channel == 'organizations':
version = re.sub(r'^(\d+).+', r'\1.0', version)
dir = 'system-requirements'
path = [product, version, dir]
locale = getattr(request, 'locale', None)
if locale:
path.insert(0, locale)
return HttpResponseRedirect('/' + '/'.join(path) + '/')
def releases_index(request, product):
releases = {}
if product == 'Firefox':
major_releases = firefox_details.firefox_history_major_releases
minor_releases = firefox_details.firefox_history_stability_releases
elif product == 'Thunderbird':
major_releases = product_details.thunderbird_history_major_releases
minor_releases = product_details.thunderbird_history_stability_releases
for release in major_releases:
major_version = float(re.findall(r'^\d+\.\d+', release)[0])
# The version numbering scheme of Firefox changes sometimes. The second
# number has not been used since Firefox 4, then reintroduced with
# Firefox ESR 24 (Bug 870540). On this index page, 24.1.x should be
# fallen under 24.0. This patter is a tricky part.
major_pattern = r'^' + \
re.escape(
('%s' if major_version < 4 else '%g') % round(major_version, 1))
releases[major_version] = {
'major': release,
'minor': sorted(filter(lambda x: re.findall(major_pattern, x),
minor_releases),
key=lambda x: int(re.findall(r'\d+$', x)[0]))
}
return l10n_utils.render(
request, '{product}/releases/index.html'.format(product=product.lower()),
{'releases': sorted(releases.items(), reverse=True)}
)

Просмотреть файл

@ -982,6 +982,8 @@ INSTALLED_APPS = get_apps(exclude=(
'%s.externalfiles' % PROJECT_MODULE,
'%s.security' % PROJECT_MODULE,
'%s.events' % PROJECT_MODULE,
'%s.releasenotes' % PROJECT_MODULE,
'%s.thunderbird' % PROJECT_MODULE,
# libs
'django_extensions',

Просмотреть файл

Просмотреть файл

@ -0,0 +1,86 @@
{# 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/. -#}
{% extends "base-resp.html" %}
{% block body_class %}sky{% endblock %}
{% block site_css %}
{{ css('firefox-resp') }}
{% endblock %}
{% block site_header_nav %}
<nav id="nav-main" role="navigation">
<span class="toggle" role="button" aria-controls="nav-main-menu" aria-expanded="false" tabindex="0">{{_('Menu')}}</span>
<ul id="nav-main-menu" class="has-submenus">
<li class="first" id="nav-main-features"><a href="/thunderbird/" tabindex="0" aria-owns="nav-main-features-submenu" aria-haspopup="true">{{_('Thunderbird')}}</a>
<ul aria-expanded="false" id="nav-main-features-submenu" class="submenu">
<li class="first">
<a href="/thunderbird/channel/" tabindex="-1">
{{_('Releases')}}
</a>
</li>
<li>
<a href="/thunderbird/features/" tabindex="-1">
{{_('Features')}}
</a>
</li>
<li class="last">
<a href="/thunderbird/organizations/" tabindex="-1">
{{_('Thunderbird for Organizations')}}
</a>
</li>
</ul>
</li>
<li id="nav-main-addons">
<a href="https://addons.mozilla.org/" tabindex="0" aria-owns="nav-main-addons-submenu" aria-haspopup="true">
{{_('Add-ons')}}
</a>
<ul aria-expanded="false" id="nav-main-addons-submenu" class="submenu">
<li class="first">
<a href="https://addons.mozilla.org/thunderbird/extensions/" tabindex="-1">
{{_('Add-ons')}}
</a>
</li>
<li class="last">
<a href="https://addons.mozilla.org/thunderbird/themes/" tabindex="-1">
{{_('Themes')}}
</a>
</li>
</ul>
</li>
<li class="last" id="nav-main-support">
<a href="https://support.mozilla.org/products/thunderbird/" tabindex="0"
aria-owns="nav-main-addons-submenu" aria-haspopup="true">
{{_('Support')}}
</a>
<ul aria-expanded="false" id="nav-main-addons-submenu" class="submenu">
<li class="first last">
<a href="https://support.mozilla.org/products/thunderbird" tabindex="-1">
{{_('Thunderbird Support')}}
</a>
</li>
</ul>
</li>
</ul>
</nav>
{% endblock %}
{% block email_form %}
{% endblock email_form %}
{% block site_header_logo %}
<h2>
<a href="/thunderbird/">
<img src="{{ media('img/thunderbird/template/thunderbird.png?2014-10') }}"
alt="Mozilla Thunderbird"
height="70"
width="260">
</a>
</h2>
{% endblock %}
{% block site_js %}
{{ js('firefox-resp') }}
{% endblock %}

Просмотреть файл

@ -0,0 +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/. #}
{% extends "thunderbird/releases/release-notes.html" %}
{% set channel_name = 'Beta' %}

Просмотреть файл

@ -0,0 +1,72 @@
{# 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/. #}
{% extends "thunderbird/base-resp.html" %}
{% block page_title %}{{ _('Mozilla Thunderbird Release Notes') }}{% endblock %}
{% block page_desc %}{{ _('Release notes for each version of Thunderbird.') }}{% endblock %}
{% block body_id %}thunderbird-releases-index{% endblock %}
{% block site_css %}
{{ css('firefox_releases_index') }}
{% endblock %}
{% block breadcrumbs %}
<nav class="breadcrumbs">
<a href="{{ url('mozorg.home') }}">{{ _('Home') }}</a> >
<a href="/thunderbird/">{{ _('Thunderbird') }}</a> >
<span>{{ _('Release Notes') }}</span>
</nav>
{% endblock %}
{% block content %}
<div id="main-feature">
<h1>{{ _('Thunderbird Releases') }}</h1>
<p>{{ _('Thunderbird release notes are specific to each version of the application. Select your version from the list below to see the release notes for it.') }}</p>
</div>
<div id="main-content">
<ol reversed>
{% for int_version, versions in releases -%}
<li>
<strong>{{ get_link(int_version, versions.major) }}</strong>
{% if versions.minor -%}
<ol>
{% for version in versions.minor -%}
<li>{{ get_link(int_version, version) }}</li>
{% endfor -%}
</ol>
{% endif -%}
</li>
{% endfor -%}
<li>
<strong><a href="0.1.html">0.1</a></strong>
<ol>
<li><a href="0.2.html">0.2</a></li>
<li><a href="0.3.html">0.3</a></li>
<li><a href="0.4.html">0.4</a></li>
<li><a href="0.5.html">0.5</a></li>
<li><a href="0.6.html">0.6</a></li>
<li><a href="0.7.html">0.7</a></li>
<li><a href="0.8.html">0.8</a></li>
<li><a href="0.9.html">0.9</a></li>
</ol>
</li>
</ol>
</div>
{% endblock %}
{% macro get_link(int_version, version) %}
{%- if int_version < 2 -%}
<a href="{{ version }}.html">{{ version }}</a>
{%- elif version == '2.0' -%}
<a href="../2.0.0.0/releasenotes/">{{ version }}</a>
{%- elif version == '17.0.9' -%}
<a href="../17.0.9esr/releasenotes/">{{ version }}</a>
{%- elif version == '17.0.10' -%}
<a href="../17.0.10esr/releasenotes/">{{ version }}</a>
{%- else -%}
<a href="../{{ version }}/releasenotes/">{{ version }}</a>
{%- endif -%}
{% endmacro %}

Просмотреть файл

@ -0,0 +1,161 @@
{# 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/. #}
{% extends "thunderbird/base-resp.html" %}
{% block page_title_prefix %}{% endblock %}
{% block page_title %}{{ _('Thunderbird — {channel} Notes ({version})')|f(channel=channel_name, version=release.version) }}{% endblock %}
{% block body_id %}notes{% endblock %}
{% block body_class %}thunderbird-notes sky{% endblock %}
{% block site_css %}
{{ css('firefox_releasenotes') }}
{% endblock %}
{# channel_name is for display purposes where needed. #}
{% set channel_name = channel_name|default('') %}
{% block content %}
<main role="main">
<article id="main-content">
{% block notes_header %}
<header class="notes-head">
<h1>{{ _('Thunderbird {channel} Notes')|f(channel=channel_name) }}</h1>
<h2>{{ _('Version {version}, first offered to {channel} channel users on {date}')|f(channel=release.channel, date=release.release_date|l10n_format_date, version=release.version) }}</h2>
<p>
{{ _('Check out "Whats New" and "Known Issues" for this version of Thunderbird below.') }}
{% trans feedback='https://support.mozilla.org/questions/new/thunderbird',
bugzilla='https://bugzilla.mozilla.org/' %}
As always, youre encouraged to
<a href="{{ feedback }}">tell us what you think</a>,
or <a href="{{ bugzilla }}">file a bug in Bugzilla</a>.
{% endtrans %}
{% if release.bug_list %}
<form action="https://bugzilla.mozilla.org/buglist.cgi" method="post">
<input type="hidden" name="limit" value="0">
<input type="hidden" name="bug_id" value="{{ release.bug_list }}">
<p>If interested, please see the <button type="submit">complete list of changes</button> in this release.</p>
</form>
{% else %}
If interested, please see the <a href="{{ release.get_bug_search_url() }}">complete list of changes</a> in this release.
{% endif %}
{{ release.text|markdown|safe }}
</p>
</header>
{% endblock %}
<div class="main-column">
{% if new_features %}
<section class="notes-section" id="new-features">
<h3>{{ _('Whats New') }}</h3>
<ul class="section-items tagged">
{% for note in new_features %}
<li {% if not note.tag %}class="untagged"{% endif %} id="note-{{ note.id }}">
{% if note.tag %}
<b class="tag tag-{{ note.tag.lower() }}">{{ note.tag }}</b>
{% endif %}
{{ note.note|markdown|safe }}
</li>
{% endfor %}
</ul>
</section>
{% endif %}
{% if known_issues %}
<section class="notes-section" id="known-issues">
<h3>{{ _('Known Issues') }}</h3>
<ul class="section-items tagged">
{% for note in known_issues %}
<li id="note-{{ note.id }}">
<b class="tag tag-unresolved">{{ _('unresolved') }}</b>
{{ note.note|markdown|safe }}
{% if note.fixed_in_release %}
<p class="note">
<a href="{{ releasenotes_url(note.fixed_in_release) }}">
{{ _('Resolved in v{version_number}')|f(version_number=note.fixed_in_release.version) }}
</a>
</p>
{% endif %}
</li>
{% endfor %}
</ul>
</section>
{% endif %}
</div>
{% block notes_sidebar %}
<aside id="sidebar" class="sidebar">
<section id="try">
<h3>{{ _('Try Thunderbird %s')|format(channel_name) }}</h3>
<ol>
<li class="try1">
<a href="{{ url('thunderbird.system_requirements', version) }}">
{{ _('Check') }}
</a>
</li>
<li class="try2">
<a href="{{ download_url }}">
{{ _('Download') }}
</a>
</li>
<li class="try3">
<a href="https://support.mozilla.org/kb/Installing%20Thunderbird">
{{ _('Install') }}
</a>
</li>
<li class="try4">
<a href="https://addons.mozilla.org/thunderbird/">
{{ _('Customize') }}
</a>
</li>
</ol>
</section>
<section id="get-involved">
<h3>{{ _('Want to get involved?') }}</h3>
<p>{{ _('Did you know that most of the content in Thunderbird Support was written by volunteers?') }}<br>
<a href="{{ url('mozorg.contribute') }}">{{ _('Find out more') }}</a></p>
</section>
<section id="problems">
<h3>{{ _('Having Problems?') }}</h3>
<ol>
<li>
<a href="{{ support_url }}">
{{ _('Search for answers on the Thunderbird Support page') }}
</a>
</li>
{% if known_issues %}
<li>
<a href="#known-issues">
{{ _('Look at the known issues list and see if we already know about the problem') }}
</a>
</li>
{% endif %}
<li>
<a href="https://bugzilla.mozilla.org/enter_bug.cgi">
{{ _('If you cant find reference to your issue, please consider filing a technical bug report') }}
</a>
</li>
</ol>
</section>
<section id="other">
<h3>{{ _('Other Resources') }}</h3>
<p>
<a href="https://blog.mozilla.com/thunderbird/">
{{ _('The Thunderbird Blog') }}
</a>
</p>
</section>
</aside>
{% endblock %}
</article>
</main>
{% endblock %}
{% block js %}{% endblock %}

Просмотреть файл

@ -0,0 +1,33 @@
{# 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/. #}
{% extends "thunderbird/base-resp.html" %}
{% block page_title_prefix %}{% endblock %}
{% block page_title %}{{ _('Thunderbird — {version} System Requirements')|f(version=version) }}{% endblock %}
{% block body_id %}notes{% endblock %}
{% block body_class %}thunderbird-notes sky{% endblock %}
{% block site_css %}
{{ css('firefox_releasenotes') }}
{% endblock %}
{% block extrahead %}{% endblock %}
{% block content %}
<header id="main-feature">
<h1>{{ _('Thunderbird {version} System Requirements')|f(version=version) }}</h1>
</header>
<div id="main-content">
<article class="main-column">
{{ release.system_requirements|markdown|safe }}
</article>
</div>
{% block sidebar %}{% endblock %}
{% endblock %}
{% block js %}{% endblock %}

Просмотреть файл

@ -0,0 +1,32 @@
# 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 import patterns, url
import bedrock.releasenotes.views
from bedrock.releasenotes import version_re
latest_re = r'^thunderbird(?:/(?P<version>%s))?/%s/$'
thunderbird_releasenotes_re = latest_re % (version_re, r'releasenotes')
sysreq_re = latest_re % (version_re, 'system-requirements')
urlpatterns = patterns('',
url('^thunderbird/releases/$', bedrock.releasenotes.views.releases_index,
{'product': 'Thunderbird'}, name='thunderbird.releases.index'),
url(thunderbird_releasenotes_re, bedrock.releasenotes.views.release_notes,
{'product': 'Thunderbird'}, name='thunderbird.releasenotes'),
url(sysreq_re, bedrock.releasenotes.views.system_requirements,
{'product': 'Thunderbird'}, name='thunderbird.system_requirements'),
url('^thunderbird/latest/system-requirements/$',
bedrock.releasenotes.views.latest_sysreq,
{'product': 'thunderbird', 'channel': 'release'}, name='thunderbird.sysreq'),
url('^thunderbird/latest/releasenotes/$',
bedrock.releasenotes.views.latest_notes,
{'product': 'thunderbird'}),
)

Просмотреть файл

@ -0,0 +1,10 @@
# -*- coding: utf-8 -*-
# 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 product_details import product_details
def get_latest_version(product='Thunderbird', channel='release'):
return product_details.thunderbird_versions.get('LATEST_THUNDERBIRD_VERSION')

Просмотреть файл

@ -34,6 +34,7 @@ urlpatterns = patterns(
(r'^tabzilla/', include('bedrock.tabzilla.urls')),
(r'^security/', include('bedrock.security.urls')),
(r'', include('bedrock.firefox.urls')),
(r'', include('bedrock.thunderbird.urls')),
(r'', include('bedrock.mozorg.urls')),
(r'', include('bedrock.newsletter.urls')),
(r'', include('bedrock.redirects.urls')),

Просмотреть файл

@ -566,6 +566,16 @@ RewriteRule ^/(\w{2,3}(?:-\w{2}(?:-mac)?)?/)?(firefox|mobile)/(2[38]\.0(?:\.\d)?
RewriteRule ^/(\w{2,3}(?:-\w{2}(?:-mac)?)?/)?(firefox|mobile)/(29\.0(?:beta|\.\d)?)/releasenotes(/)?$ /b/$1$2/$3/releasenotes$4 [L,PT]
RewriteRule ^/(\w{2,3}(?:-\w{2}(?:-mac)?)?/)?(firefox|mobile)/([3-9]\d\.\d(?:a2|beta|\.\d)?)/(aurora|release)notes(/)?$ /b/$1$2/$3/$4notes$5 [L,PT]
# bug 1081917, 1029829, 1029838
RewriteRule ^/(\w{2,3}(?:-\w{2}(?:-mac)?)?/)?thunderbird/([3-9]\d\.\d(?:a2|beta|\.\d)?)/releasenotes(/)?$ /b/$1thunderbird/$2/releasenotes$3 [L,PT]
RewriteRule ^/(\w{2,3}(?:-\w{2}(?:-mac)?)?/)?thunderbird/([3-9]\d\.\d(?:a2|beta|\.\d)?)/system-requirements(/)?$ /b/$1thunderbird/$2/system-requirements$3 [L,PT]
RewriteRule ^/thunderbird/releases/(.*)$ http://www-archive.mozilla.org/thunderbird_releasenotes/en-US/thunderbird/releases/$1 [L,R=301]
RewriteRule ^/(\w{2,3}(?:-\w{2}(?:-mac)?)?/)thunderbird/(.*)/releasenotes(.*)$ http://www-archive.mozilla.org/thunderbird_releasenotes/en-US/thunderbird/$2/releasenotes$4 [L,R=301]
RewriteRule ^/(\w{2,3}(?:-\w{2}(?:-mac)?)?/)thunderbird/(.*)/system-requirements(.*)$ http://www-archive.mozilla.org/thunderbird_releasenotes/en-US/thunderbird/$2/system-requirements$4 [L,R=301]
# bug 1001238, 1025056
RewriteRule ^/(\w{2,3}(?:-\w{2}(?:-mac)?)?/)?firefox/(24\.[5678]\.\d)/releasenotes(/)?$ /b/$1firefox/$2/releasenotes$3 [L,PT]

Двоичные данные
media/img/thunderbird/template/thunderbird.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 14 KiB