diff --git a/apps/addons/templates/addons/details.html b/apps/addons/templates/addons/details.html
index 82ddc7c5bf..2d3fcaa7b1 100644
--- a/apps/addons/templates/addons/details.html
+++ b/apps/addons/templates/addons/details.html
@@ -180,62 +180,7 @@
{% if not addon.is_selfhosted() and version %}
- {# TODO reverse URL #}
- {% set base = remora_url('/addons/versions/{0}'|f(addon.id)) %}
-
- {{ _('Version {0}', 'addon_display_header_version')|f(version.version) }}
- —
- {{
- version.created|datetime }}
- {% if version.has_files %}
- —
- {{ _('{0} KB')|f(version.all_files[0].size|int|numberfmt) }}
- {% endif %}
-
-
-
-
- {# License #}
- {% if version.license %}
-
- {# TODO reverse URL #}
-
-
- {{ version.license.name }}
-
- {% endif %}
-
-
-
+ {{ version_detail(addon, version, src="addon-detail-version") }}
{% endif %}
{# /primary #}
diff --git a/apps/addons/urls.py b/apps/addons/urls.py
index 7f8f24214b..d171cab625 100644
--- a/apps/addons/urls.py
+++ b/apps/addons/urls.py
@@ -23,6 +23,7 @@ detail_patterns = patterns('',
('^reviews/', include('reviews.urls')),
('^statistics/', include('stats.urls')),
+ ('^versions/', include('versions.urls')),
)
diff --git a/apps/amo/tests/test_redirects.py b/apps/amo/tests/test_redirects.py
index 3348c0a90d..bed8efaf10 100644
--- a/apps/amo/tests/test_redirects.py
+++ b/apps/amo/tests/test_redirects.py
@@ -23,12 +23,12 @@ class TestRedirects(test.TestCase):
"""`/persona/\d+` should go to `/addon/\d+`."""
r = self.client.get(u'persona/4', follow=True)
assert r.redirect_chain[-1][0].endswith('/en-US/firefox/addon/4/')
-
+
def test_contribute_installed(self):
"""`/addon/\d+/about` should go to `/addon/\d+/contribute/installed`."""
r = self.client.get(u'addon/5326/about', follow=True)
assert r.redirect_chain[-1][0].endswith('/en-US/firefox/addon/5326/contribute/installed/')
-
+
def test_utf8(self):
"""Without proper unicode handling this will fail."""
response = self.client.get(u'/api/1.5/search/ツールバー',
@@ -156,3 +156,8 @@ class TestRedirects(test.TestCase):
url, code = r.redirect_chain[-1]
eq_(code, 301)
assert url.endswith('/en-US/firefox/extensions/woo/?sort=rating')
+
+ def test_addons_versions(self):
+ r = self.client.get('/addons/versions/4', follow=True)
+ self.assertRedirects(r, '/en-US/firefox/addon/4/versions/',
+ status_code=301)
diff --git a/apps/versions/helpers.py b/apps/versions/helpers.py
new file mode 100644
index 0000000000..c9ed77dc21
--- /dev/null
+++ b/apps/versions/helpers.py
@@ -0,0 +1,11 @@
+import jingo
+import jinja2
+
+from addons.helpers import new_context
+
+
+@jingo.register.inclusion_tag('versions/version.html')
+@jinja2.contextfunction
+def version_detail(context, addon, version, src,
+ show_versions_link=True, itemclass='article'):
+ return new_context(**locals())
diff --git a/apps/versions/templates/versions/version.html b/apps/versions/templates/versions/version.html
new file mode 100644
index 0000000000..d9077b040d
--- /dev/null
+++ b/apps/versions/templates/versions/version.html
@@ -0,0 +1,61 @@
+{# addon, version, request #}
+
+ {% set versions_url = url('addons.versions', addon.id) %}
+
+
+ {{ install_button(addon, version=version) }}
+
+ {% if addon.compatible_apps %}
+
{{ _('Works with:') }}
+
+ {% for compat in addon.compatible_apps.values() %}
+ - {{ compat }}
+ {% endfor %}
+
+ {% endif %}
+
+
+ {{ version.releasenotes|nl2br }}
+
+
+ {# License #}
+ {% if version.license %}
+
+ {# TODO reverse URL #}
+
+ {{ version.license.name }}
+ {% endif %}
+
+
+
diff --git a/apps/versions/templates/versions/version_list.html b/apps/versions/templates/versions/version_list.html
new file mode 100644
index 0000000000..d5850c319d
--- /dev/null
+++ b/apps/versions/templates/versions/version_list.html
@@ -0,0 +1,42 @@
+{% extends "base.html" %}
+
+{# L10n: {0} is an add-on name. #}
+{% block title %}{{ page_title('{0} :: Versions')|f(addon.name) }}{% endblock %}
+
+{% block bodyclass %}versions inverse{% endblock %}
+
+{% block content %}
+
+
+ {{ breadcrumbs([(addon.type_url(), amo.ADDON_TYPES[addon.type]),
+ (url('addons.detail', addon.id), addon.name),
+ (None, _('Versions'))]) }}
+ {# L10n: {0} is an add-on name. #}
+
+ {{ _('{0} Version History')|f(addon.name) }}
+ {% with cnt=versions.paginator.count %}
+ {# L10n: {0} is a number. #}
+ {{ ngettext('{0} Version', '{0} Versions', cnt)|f(cnt) }}
+ {% endwith %}
+
+
+
+
{{ _('Be careful with old versions!') }}
+
{% trans url=url('addons.detail', addon.id) %}
+ These versions are displayed for reference and testing purposes.
+ You should always use the latest version of an add-on.
+ {% endtrans %}
+
+
+
+ {% for version in versions.object_list %}
+ {{ version_detail(addon, version, show_versions_link=False,
+ src="version-history", itemclass="item") }}
+ {% endfor %}
+
+
+
+
+{% endblock %}
diff --git a/apps/versions/urls.py b/apps/versions/urls.py
new file mode 100644
index 0000000000..0510a98295
--- /dev/null
+++ b/apps/versions/urls.py
@@ -0,0 +1,7 @@
+from django.conf.urls.defaults import patterns, url
+
+from . import views
+
+urlpatterns = patterns('',
+ url('^$', views.versions_list, name='addons.versions'),
+)
diff --git a/apps/versions/views.py b/apps/versions/views.py
new file mode 100644
index 0000000000..e310deda3b
--- /dev/null
+++ b/apps/versions/views.py
@@ -0,0 +1,18 @@
+from django.shortcuts import get_object_or_404
+
+import jingo
+
+import amo
+from addons.models import Addon
+from versions.models import Version
+
+
+def versions_list(request, addon_id):
+ addon = get_object_or_404(Addon.objects.valid(), pk=addon_id)
+ qs = (addon.versions.filter(files__status__in=amo.VALID_STATUSES)
+ .distinct().order_by('-created'))
+ versions = amo.utils.paginate(request, qs)
+ versions.object_list = list(versions.object_list)
+ Version.transformer(versions.object_list)
+ return jingo.render(request, 'versions/version_list.html',
+ {'addon': addon, 'versions': versions})
diff --git a/urls.py b/urls.py
index b10ccaa9f7..d607359148 100644
--- a/urls.py
+++ b/urls.py
@@ -71,6 +71,9 @@ urlpatterns = patterns('',
('^personas/film and tv/?$',
lambda r: redirect('browse.personas', 'film-and-tv', permanent=True)),
+ ('^addons/versions/(\d+)/?$',
+ lambda r, id: redirect('addons.versions', id, permanent=True)),
+
# Firefox Cup page, /firefoxcup
('^firefoxcup/', include('firefoxcup.urls'))
)