Merge lp:~elachuni/ubuntu-webcatalog/navigation into lp:ubuntu-webcatalog

Proposed by Anthony Lenton
Status: Merged
Approved by: Danny Tamez
Approved revision: 42
Merged at revision: 32
Proposed branch: lp:~elachuni/ubuntu-webcatalog/navigation
Merge into: lp:ubuntu-webcatalog
Diff against target: 432 lines (+176/-24)
9 files modified
src/webcatalog/admin.py (+2/-0)
src/webcatalog/models/applications.py (+5/-0)
src/webcatalog/static/css/webcatalog.css (+53/-2)
src/webcatalog/templates/webcatalog/application_detail.html (+16/-2)
src/webcatalog/templates/webcatalog/application_overview_snippet.html (+9/-5)
src/webcatalog/templates/webcatalog/department_overview.html (+18/-0)
src/webcatalog/tests/test_views.py (+45/-10)
src/webcatalog/urls.py (+3/-1)
src/webcatalog/views.py (+25/-4)
To merge this branch: bzr merge lp:~elachuni/ubuntu-webcatalog/navigation
Reviewer Review Type Date Requested Status
Danny Tamez (community) Approve
Review via email: mp+66636@code.launchpad.net

Commit message

Add a right sidebar navigation portlet to switch between distroseries

Description of the change

Overview
========
Add a right sidebar navigation portlet to switch between distroseries

Details
=======

The portlet was added both on the application details and the department overview screens. For that we needed to provide a distroseries-specific department overview url.
For consistency, I made the distroseries-specific url for apps specify the distroseries before the package name.

While I was there, I
- Fixed the breadcrumbs background to work with apps with extra-long names (like "A tool for the blackbox/fluxbox window managers that runs commands" -- package bbrun)

- Registered DistroSeries with the admin

To post a comment you must log in.
41. By Anthony Lenton

Test fix.

42. By Anthony Lenton

Just bumping rev.

Revision history for this message
Danny Tamez (zematynnad) wrote :

Looks fine.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'src/webcatalog/admin.py'
2--- src/webcatalog/admin.py 2011-06-17 16:21:59 +0000
3+++ src/webcatalog/admin.py 2011-07-01 17:17:27 +0000
4@@ -25,6 +25,7 @@
5 from webcatalog.models import (
6 Application,
7 Department,
8+ DistroSeries,
9 )
10
11 __metaclass__ = type
12@@ -41,3 +42,4 @@
13
14 admin.site.register(Application, ApplicationAdmin)
15 admin.site.register(Department)
16+admin.site.register(DistroSeries)
17
18=== modified file 'src/webcatalog/models/applications.py'
19--- src/webcatalog/models/applications.py 2011-06-29 14:42:24 +0000
20+++ src/webcatalog/models/applications.py 2011-07-01 17:17:27 +0000
21@@ -99,6 +99,11 @@
22 stripped = [x.strip() for x in self.categories.split(';')]
23 return set(x for x in stripped if x)
24
25+ def available_distroseries(self):
26+ """Return the set of distroseries for which this app is available"""
27+ return DistroSeries.objects.filter(
28+ application__package_name=self.package_name).order_by('version')
29+
30 @classmethod
31 def from_json(cls, data):
32 app = Application()
33
34=== modified file 'src/webcatalog/static/css/webcatalog.css'
35--- src/webcatalog/static/css/webcatalog.css 2011-06-23 14:49:53 +0000
36+++ src/webcatalog/static/css/webcatalog.css 2011-07-01 17:17:27 +0000
37@@ -16,6 +16,8 @@
38 -webkit-box-shadow: #bbb 2px 2px 5px 1px;
39 margin: 12px 0 20px;
40 font-size: 14px;
41+ width: 684px;
42+ float: left;
43 }
44
45 #sc-mockup h2 {
46@@ -44,7 +46,7 @@
47 }
48 .screenshot {
49 float: right;
50- margin-right: 16px;
51+ margin: 0 16px;
52 }
53
54 .noscreenshot {
55@@ -73,14 +75,63 @@
56
57 /* End software centre mockup */
58
59+/* Right sidebar */
60+#rightbar {
61+ float: right;
62+ width: 206px;
63+ margin-top: 28px;
64+}
65+
66+.portletheader {
67+ padding: 10px 8px;
68+ font-size: 14px;
69+ background: #DD4814;
70+ color: white;
71+ -moz-border-radius: 4px 4px 0 0;
72+ -webkit-border-radius: 4px 4px 0 0;
73+ -khtml-border-radius: 4px 4px 0 0;
74+ border-radius: 4px 4px 0 0;
75+}
76+
77+ul.portletactions {
78+ font-size: 12px;
79+ list-style-image: none;
80+}
81+
82+ul.portletactions li {
83+ border-bottom: 1px dotted gray;
84+ margin: 0;
85+}
86+
87+ul.portletactions li a, ul.portletactions li span {
88+ padding: 8px;
89+ display: block;
90+ color: black;
91+}
92+
93+ul.portletactions li.active span, ul.portletactions a:hover {
94+ background: transparent url("/assets/images/subnav_active_bg.png");
95+ color: #E24912;
96+ text-decoration: none;
97+}
98+/* End right sidebar */
99+
100+.main-column {
101+ width: 684px;
102+ float: left;
103+}
104 .app-overview-row {
105- height: 36px;
106+ min-height: 36px;
107 padding: 2px;
108 }
109 .app-overview-row img {
110 float: left;
111 margin-right: 10px;
112 }
113+.app-overview-text {
114+ float: left;
115+ width: 634px;
116+}
117 .app-overview-row h3 {
118 padding: 0;
119 margin: 0;
120
121=== added file 'src/webcatalog/static/images/applications-other-64.png'
122Binary files src/webcatalog/static/images/applications-other-64.png 1970-01-01 00:00:00 +0000 and src/webcatalog/static/images/applications-other-64.png 2011-07-01 17:17:27 +0000 differ
123=== added file 'src/webcatalog/static/images/applications-other.png'
124Binary files src/webcatalog/static/images/applications-other.png 1970-01-01 00:00:00 +0000 and src/webcatalog/static/images/applications-other.png 2011-07-01 17:17:27 +0000 differ
125=== modified file 'src/webcatalog/static/images/breadcrumbs_bg.png'
126Binary files src/webcatalog/static/images/breadcrumbs_bg.png 2011-06-23 14:20:52 +0000 and src/webcatalog/static/images/breadcrumbs_bg.png 2011-07-01 17:17:27 +0000 differ
127=== added file 'src/webcatalog/static/images/subnav_active_bg.png'
128Binary files src/webcatalog/static/images/subnav_active_bg.png 1970-01-01 00:00:00 +0000 and src/webcatalog/static/images/subnav_active_bg.png 2011-07-01 17:17:27 +0000 differ
129=== modified file 'src/webcatalog/templates/webcatalog/application_detail.html'
130--- src/webcatalog/templates/webcatalog/application_detail.html 2011-06-29 09:24:48 +0000
131+++ src/webcatalog/templates/webcatalog/application_detail.html 2011-07-01 17:17:27 +0000
132@@ -7,13 +7,27 @@
133
134 {% block content %}
135 {% include "webcatalog/breadcrumbs_snippet.html" %}
136-
137+ <div id="rightbar">
138+ <div class="portlet">
139+ <div class="portletheader">Available versions</div>
140+ <ul class="portletactions">
141+ {% for ds in available_distroseries %}
142+ <li class="item{% ifequal ds.code_name distroseries %} active{% endifequal %}{% if forloop.last %} last{% endif %}">
143+ {% ifequal ds.code_name distroseries %}
144+ <span>Ubuntu {{ ds.version }} ({{ ds.code_name }})</span>
145+ {% else %}
146+ <a href="{% url wc-package-detail ds.code_name application.package_name %}">Ubuntu {{ ds.version }} ({{ ds.code_name }})</a>
147+ {% endifequal %}</li>
148+ {% endfor %}
149+ </ul>
150+ </div>
151+ </div>
152 <div id="sc-mockup">
153 <div class="header">
154 {% if application.icon %}
155 <img class="icon64" src="{{ application.icon.url }}"/>
156 {% else %}
157- <img class="icon64" src="{{ STATIC_URL }}images/noicon_32.png"/>
158+ <img class="icon64" src="{{ STATIC_URL }}images/applications-other-64.png"/>
159 {% endif %}
160
161 <h2>{{ application.name }}</h2>
162
163=== modified file 'src/webcatalog/templates/webcatalog/application_overview_snippet.html'
164--- src/webcatalog/templates/webcatalog/application_overview_snippet.html 2011-06-28 15:03:12 +0000
165+++ src/webcatalog/templates/webcatalog/application_overview_snippet.html 2011-07-01 17:17:27 +0000
166@@ -1,11 +1,15 @@
167 <div class="app-overview-row">
168+ <div>
169 {% if app.icon %}
170 <img src="{{ app.icon.url }}" width="32" height="32" />
171 {% else %}
172- <img src="{{ STATIC_URL }}images/noicon_32.png"/>
173+ <img src="{{ STATIC_URL }}images/applications-other.png"/>
174 {% endif %}
175- <h3>
176- <a href="{% url wc-package-detail app.distroseries.code_name app.package_name %}">{{ app.name }}</a>
177- </h3>
178- <p>{{ app.comment }}</p>
179+ </div>
180+ <div class="app-overview-text">
181+ <h3>
182+ <a href="{% url wc-package-detail app.distroseries.code_name app.package_name %}">{{ app.name }}</a>
183+ </h3>
184+ <p>{{ app.comment }}</p>
185+ </div>
186 </div>
187
188=== modified file 'src/webcatalog/templates/webcatalog/department_overview.html'
189--- src/webcatalog/templates/webcatalog/department_overview.html 2011-06-28 15:03:12 +0000
190+++ src/webcatalog/templates/webcatalog/department_overview.html 2011-07-01 17:17:27 +0000
191@@ -7,6 +7,23 @@
192 {% block content %}
193 {% include "webcatalog/breadcrumbs_snippet.html" %}
194
195+ <div id="rightbar">
196+ <div class="portlet">
197+ <div class="portletheader">Available versions</div>
198+ <ul class="portletactions">
199+ {% for ds in available_distroseries %}
200+ <li class="item{% ifequal ds.code_name distroseries %} active{% endifequal %}{% if forloop.last %} last{% endif %}">
201+ {% ifequal ds.code_name distroseries %}
202+ <span>Ubuntu {{ ds.version }} ({{ ds.code_name }})</span>
203+ {% else %}
204+ <a href="{% url wc-department ds.code_name dept.id %}">Ubuntu {{ ds.version }} ({{ ds.code_name }})</a>
205+ {% endifequal %}</li>
206+ {% endfor %}
207+ </ul>
208+ </div>
209+ </div>
210+
211+<div class="main-column">
212 {% if subdepts %}
213 <h3>{% trans "Subsections" %}:</h3>
214
215@@ -26,5 +43,6 @@
216 {% else %}
217 <p>{% trans "No applications found." %}</p>
218 {% endif %}
219+</div>
220
221 {% endblock %}
222
223=== modified file 'src/webcatalog/tests/test_views.py'
224--- src/webcatalog/tests/test_views.py 2011-06-29 15:24:23 +0000
225+++ src/webcatalog/tests/test_views.py 2011-07-01 17:17:27 +0000
226@@ -68,7 +68,8 @@
227 if not detail_package:
228 detail_package = app.package_name
229
230- url = reverse('wc-package-detail', args=[detail_package, detail_distro])
231+ url = reverse('wc-package-detail', args=[detail_distro,
232+ detail_package])
233
234 if useragent:
235 response = self.client.get(url, HTTP_USER_AGENT=useragent)
236@@ -134,6 +135,22 @@
237 '<a href="{lucid_app_url}">your version of Ubuntu</a>.'.format(
238 lucid_app_url=lucid_app_url))
239
240+ def test_includes_right_navigation(self):
241+ lucid = self.factory.make_distroseries(code_name='lucid',
242+ version='10.04')
243+ maverick = self.factory.make_distroseries(code_name='maverick',
244+ version='10.10')
245+ lucid_app = self.factory.make_application(package_name='pkgfoo',
246+ distroseries=lucid)
247+ maverick_app = self.factory.make_application(package_name='pkgfoo',
248+ distroseries=maverick)
249+
250+ response, app = self.get_app_and_response(name="Foobar")
251+
252+ for ds in ['lucid', 'maverick']:
253+ url = reverse('wc-package-detail', args=[ds, 'pkgfoo'])
254+ self.assertContains(response, '<a href="{0}">Ubuntu'.format(url))
255+
256
257 class ApplicationDetailNoSeriesTestCase(TestCaseWithFactory):
258
259@@ -155,7 +172,7 @@
260
261 self.assertRedirects(
262 response, reverse(
263- 'wc-package-detail', args=['pkgfoo', default_distro]))
264+ 'wc-package-detail', args=[default_distro, 'pkgfoo']))
265
266 def test_redirects_to_ua_distroseries(self):
267 # If a distroseries is not included in the url, but we
268@@ -175,7 +192,7 @@
269
270 self.assertRedirects(
271 response, reverse(
272- 'wc-package-detail', args=['pkgfoo', 'lucid']))
273+ 'wc-package-detail', args=['lucid', 'pkgfoo']))
274
275
276 class SearchTestCase(TestCaseWithFactory):
277@@ -297,7 +314,8 @@
278 dept = self.factory.make_department('foo')
279 subdept = self.factory.make_department('bar', parent=dept)
280
281- response = self.client.get(reverse('wc-department', args=[dept.id]))
282+ response = self.client.get(reverse('wc-department', args=[
283+ settings.DEFAULT_DISTRO, dept.id]))
284
285 self.assertContains(response, reverse('wc-department',
286 args=[subdept.id]))
287@@ -307,7 +325,8 @@
288 dept = self.factory.make_department('bar')
289 app.departments.add(dept)
290
291- response = self.client.get(reverse('wc-department', args=[dept.id]))
292+ response = self.client.get(reverse('wc-department', args=[
293+ app.distroseries.code_name, dept.id]))
294
295 self.assertContains(response, reverse('wc-package-detail',
296 args=[app.distroseries.code_name, app.package_name]))
297@@ -315,17 +334,19 @@
298 def test_department_with_no_subdepts_doesnt_contain_header(self):
299 dept = self.factory.make_department('bar')
300
301- response = self.client.get(reverse('wc-department', args=[dept.id]))
302+ response = self.client.get(reverse('wc-department', args=[
303+ settings.DEFAULT_DISTRO, dept.id]))
304
305 self.assertNotContains(response, 'Subsections')
306
307 def test_department_shows_paginated_results(self):
308 dept = self.factory.make_department('bar')
309+ lucid = self.factory.make_distroseries(code_name='lucid')
310 for count in range(3):
311- app = self.factory.make_application()
312+ app = self.factory.make_application(distroseries=lucid)
313 app.departments.add(dept)
314
315- url = reverse('wc-department', args=[dept.id])
316+ url = reverse('wc-department', args=['lucid', dept.id])
317 with patch_settings(PAGE_BATCH_SIZE=2):
318 response = self.client.get(url)
319
320@@ -340,8 +361,7 @@
321
322 def test_invalid_page_doesnt_error(self):
323 dept = self.factory.make_department('bar')
324- url = reverse('wc-department', args=[dept.id])
325-
326+ url = reverse('wc-department', args=[settings.DEFAULT_DISTRO, dept.id])
327 response = self.client.get(url, data={'page': 'aef8'})
328 page = response.context['page']
329 self.assertEqual(1, page.number)
330@@ -353,3 +373,18 @@
331 response = self.client.get(url, data={'page': '999'})
332 page = response.context['page']
333 self.assertEqual(1, page.number)
334+
335+ def test_department_includes_right_navigation(self):
336+ dept = self.factory.make_department('bar')
337+ lucid = self.factory.make_distroseries(code_name='lucid',
338+ version='10.04')
339+ maverick = self.factory.make_distroseries(code_name='maverick',
340+ version='10.10')
341+
342+ response = self.client.get(reverse('wc-department', args=[
343+ settings.DEFAULT_DISTRO, dept.id]))
344+
345+ for ds in ['lucid', 'maverick']:
346+ url = reverse('wc-department', args=[ds, dept.id])
347+ self.assertContains(response, '<a href="{0}">Ubuntu'.format(url))
348+
349
350=== modified file 'src/webcatalog/urls.py'
351--- src/webcatalog/urls.py 2011-06-29 12:47:09 +0000
352+++ src/webcatalog/urls.py 2011-07-01 17:17:27 +0000
353@@ -31,9 +31,11 @@
354
355 urlpatterns = patterns('webcatalog.views',
356 url(r'^$', 'index', name='wc-index'),
357+ url(r'^department/(?P<distro>[-.+\w]+)/(?P<dept_id>\d+)/$',
358+ 'department_overview', name='wc-department'),
359 url(r'^department/(?P<dept_id>\d+)/$', 'department_overview',
360 name='wc-department'),
361- url(r'^applications/(?P<package_name>[-.+\w]+)/(?P<distro>[-.+\w]+)/$',
362+ url(r'^applications/(?P<distro>[-.+\w]+)/(?P<package_name>[-.+\w]+)/$',
363 'application_detail', name="wc-package-detail"),
364 url(r'^applications/(?P<package_name>[-.+\w]+)/$', 'application_detail',
365 name="wc-package-detail"),
366
367=== modified file 'src/webcatalog/views.py'
368--- src/webcatalog/views.py 2011-06-30 16:38:56 +0000
369+++ src/webcatalog/views.py 2011-07-01 17:17:27 +0000
370@@ -39,6 +39,7 @@
371 from webcatalog.models import (
372 Application,
373 Department,
374+ DistroSeries,
375 )
376 from webcatalog.utilities import UserAgentString
377
378@@ -95,11 +96,24 @@
379 context_instance=context)
380
381
382-def department_overview(request, dept_id):
383+def department_overview(request, dept_id, distro=None):
384+ if distro is None:
385+ useragent = UserAgentString(request.META.get('HTTP_USER_AGENT', ''))
386+ # Check for the distroseries in the useragent, if we have it,
387+ # redirect there.
388+ if useragent.distroseries:
389+ distro = useragent.distroseries
390+ else:
391+ distro = settings.DEFAULT_DISTRO
392+ return HttpResponseRedirect(
393+ reverse('wc-department', args=[distro, dept_id]))
394+
395 dept = get_object_or_404(Department, pk=dept_id)
396 subdepts = Department.objects.filter(parent=dept)
397 subdepts = subdepts.order_by('name')
398- apps = Application.objects.filter(departments=dept)
399+
400+ apps = Application.objects.filter(departments=dept,
401+ distroseries__code_name=distro)
402 apps = apps.order_by('name')
403 paginator = Paginator(apps, settings.PAGE_BATCH_SIZE)
404 page_num = _get_page_num_from_request(request, paginator)
405@@ -108,6 +122,8 @@
406 'subdepts': subdepts,
407 'page': paginator.page(page_num),
408 'breadcrumbs': dept.crumbs(),
409+ 'available_distroseries': DistroSeries.objects.all(),
410+ 'distroseries': distro,
411 })
412 return render_to_response('webcatalog/department_overview.html',
413 context_instance=context)
414@@ -125,11 +141,16 @@
415 distro = settings.DEFAULT_DISTRO
416 return HttpResponseRedirect(
417 reverse('wc-package-detail',
418- args=[package_name, distro]))
419+ args=[distro, package_name]))
420
421 app = get_object_or_404(Application, package_name=package_name,
422 distroseries__code_name=distro)
423+ atts = {'application': app,
424+ 'available_distroseries': app.available_distroseries(),
425+ 'breadcrumbs': app.crumbs(),
426+ 'distroseries': distro,
427+ }
428
429 return render_to_response(
430 'webcatalog/application_detail.html', RequestContext(
431- request, dict(application=app, breadcrumbs=app.crumbs())))
432+ request, atts))

Subscribers

People subscribed via source and target branches