Merge lp:~elachuni/ubuntu-webcatalog/featured-apps into lp:ubuntu-webcatalog
- featured-apps
- Merge into trunk
Status: | Merged | ||||
---|---|---|---|---|---|
Approved by: | Danny Tamez | ||||
Approved revision: | 69 | ||||
Merged at revision: | 71 | ||||
Proposed branch: | lp:~elachuni/ubuntu-webcatalog/featured-apps | ||||
Merge into: | lp:ubuntu-webcatalog | ||||
Diff against target: |
568 lines (+312/-60) 12 files modified
src/webcatalog/managers.py (+38/-0) src/webcatalog/models/applications.py (+3/-0) src/webcatalog/schema.py (+2/-2) src/webcatalog/static/css/carousel.css (+131/-51) src/webcatalog/static/js/carousel.js (+13/-4) src/webcatalog/templates/webcatalog/exhibits_widget.html (+4/-3) src/webcatalog/templates/webcatalog/featured_apps_widget.html (+52/-0) src/webcatalog/templates/webcatalog/index.html (+6/-0) src/webcatalog/tests/__init__.py (+1/-0) src/webcatalog/tests/test_managers.py (+50/-0) src/webcatalog/tests/test_views.py (+8/-0) src/webcatalog/views.py (+4/-0) |
||||
To merge this branch: | bzr merge lp:~elachuni/ubuntu-webcatalog/featured-apps | ||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Danny Tamez (community) | Approve | ||
Review via email: mp+96640@code.launchpad.net |
Commit message
Implemented a "featured apps" widget for the front page, similar to the one on https:/
Description of the change
Overview
========
This branch implements a "featured apps" widget for the front page, similar to the one on https:/
Details
=======
No javascript was taken from developer.u.c as we already have a YUI carousel widget in place for the exhibits widget. Instead, I generalized that a bit so that the controls could be placed outside of the main carousel container.
The list of featured apps was added via a setting. The data for each app is taken straight from the database. If anything in the app seems wrong this would need to be customized on the app itself, and currently would be overridden the next time app-install-data is imported.
The tidy solution for this would be to allow per-app customizations, but this was left for another branch.
A very short video showing off the featured apps widget: http://
Preview Diff
1 | === added file 'src/webcatalog/managers.py' | |||
2 | --- src/webcatalog/managers.py 1970-01-01 00:00:00 +0000 | |||
3 | +++ src/webcatalog/managers.py 2012-03-08 18:33:37 +0000 | |||
4 | @@ -0,0 +1,38 @@ | |||
5 | 1 | # -*- coding: utf-8 -*- | ||
6 | 2 | # This file is part of the Apps Directory | ||
7 | 3 | # Copyright (C) 2011 Canonical Ltd. | ||
8 | 4 | # | ||
9 | 5 | # This program is free software: you can redistribute it and/or modify | ||
10 | 6 | # it under the terms of the GNU Affero General Public License as | ||
11 | 7 | # published by the Free Software Foundation, either version 3 of the | ||
12 | 8 | # License, or (at your option) any later version. | ||
13 | 9 | # | ||
14 | 10 | # This program is distributed in the hope that it will be useful, | ||
15 | 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
16 | 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
17 | 13 | # GNU Affero General Public License for more details. | ||
18 | 14 | # | ||
19 | 15 | # You should have received a copy of the GNU Affero General Public License | ||
20 | 16 | # along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
21 | 17 | |||
22 | 18 | """Django object managers.""" | ||
23 | 19 | |||
24 | 20 | from __future__ import ( | ||
25 | 21 | absolute_import, | ||
26 | 22 | with_statement, | ||
27 | 23 | ) | ||
28 | 24 | |||
29 | 25 | |||
30 | 26 | __metaclass__ = type | ||
31 | 27 | __all__ = [ | ||
32 | 28 | 'ApplicationManager', | ||
33 | 29 | ] | ||
34 | 30 | |||
35 | 31 | from django.db import models | ||
36 | 32 | |||
37 | 33 | |||
38 | 34 | class ApplicationManager(models.Manager): | ||
39 | 35 | def find_best(self, **kwargs): | ||
40 | 36 | options = self.filter(**kwargs).order_by('-distroseries__version') | ||
41 | 37 | if options.exists(): | ||
42 | 38 | return options[0] | ||
43 | 0 | 39 | ||
44 | === modified file 'src/webcatalog/models/applications.py' | |||
45 | --- src/webcatalog/models/applications.py 2012-03-01 21:38:31 +0000 | |||
46 | +++ src/webcatalog/models/applications.py 2012-03-08 18:33:37 +0000 | |||
47 | @@ -31,6 +31,7 @@ | |||
48 | 31 | from django.db import models | 31 | from django.db import models |
49 | 32 | 32 | ||
50 | 33 | from webcatalog.department_filters import department_filters | 33 | from webcatalog.department_filters import department_filters |
51 | 34 | from webcatalog.managers import ApplicationManager | ||
52 | 34 | 35 | ||
53 | 35 | __metaclass__ = type | 36 | __metaclass__ = type |
54 | 36 | __all__ = [ | 37 | __all__ = [ |
55 | @@ -103,6 +104,8 @@ | |||
56 | 103 | # series etc.) | 104 | # series etc.) |
57 | 104 | description = models.TextField(blank=True) | 105 | description = models.TextField(blank=True) |
58 | 105 | 106 | ||
59 | 107 | objects = ApplicationManager() | ||
60 | 108 | |||
61 | 106 | def __unicode__(self): | 109 | def __unicode__(self): |
62 | 107 | return u"{0} ({1})".format(self.name, self.package_name) | 110 | return u"{0} ({1})".format(self.name, self.package_name) |
63 | 108 | 111 | ||
64 | 109 | 112 | ||
65 | === modified file 'src/webcatalog/schema.py' | |||
66 | --- src/webcatalog/schema.py 2012-03-06 16:47:42 +0000 | |||
67 | +++ src/webcatalog/schema.py 2012-03-08 18:33:37 +0000 | |||
68 | @@ -17,8 +17,6 @@ | |||
69 | 17 | 17 | ||
70 | 18 | """configglue schema for the Apps Directory.""" | 18 | """configglue schema for the Apps Directory.""" |
71 | 19 | 19 | ||
72 | 20 | import django | ||
73 | 21 | |||
74 | 22 | from configglue.pyschema import ConfigSection | 20 | from configglue.pyschema import ConfigSection |
75 | 23 | from configglue.pyschema.options import ( | 21 | from configglue.pyschema.options import ( |
76 | 24 | BoolConfigOption, | 22 | BoolConfigOption, |
77 | @@ -63,6 +61,8 @@ | |||
78 | 63 | webcatalog.oauth_data_store = StringConfigOption( | 61 | webcatalog.oauth_data_store = StringConfigOption( |
79 | 64 | default='webcatalog.models.oauthtoken.DataStore') | 62 | default='webcatalog.models.oauthtoken.DataStore') |
80 | 65 | webcatalog.convoy_root = StringConfigOption() | 63 | webcatalog.convoy_root = StringConfigOption() |
81 | 64 | webcatalog.featured_apps = LinesConfigOption(item=StringConfigOption(), | ||
82 | 65 | default=[]) | ||
83 | 66 | 66 | ||
84 | 67 | google = ConfigSection() | 67 | google = ConfigSection() |
85 | 68 | google.google_analytics_id = StringConfigOption() | 68 | google.google_analytics_id = StringConfigOption() |
86 | 69 | 69 | ||
87 | === modified file 'src/webcatalog/static/css/carousel.css' | |||
88 | --- src/webcatalog/static/css/carousel.css 2012-03-06 16:55:20 +0000 | |||
89 | +++ src/webcatalog/static/css/carousel.css 2012-03-08 18:33:37 +0000 | |||
90 | @@ -1,96 +1,176 @@ | |||
91 | 1 | /* Base carousel */ | ||
92 | 1 | .carousel-wrapper { | 2 | .carousel-wrapper { |
93 | 2 | position: relative; | 3 | position: relative; |
94 | 3 | height: 200px; | ||
95 | 4 | overflow: hidden; | 4 | overflow: hidden; |
96 | 5 | } | 5 | } |
98 | 6 | #carousel { | 6 | .carousel { |
99 | 7 | width: 100%; | 7 | width: 100%; |
100 | 8 | height: 100%; | 8 | height: 100%; |
101 | 9 | top: 0; | 9 | top: 0; |
102 | 10 | left: 0; | 10 | left: 0; |
103 | 11 | position: absolute; | 11 | position: absolute; |
104 | 12 | background: #333; | ||
105 | 13 | display: block; | 12 | display: block; |
106 | 14 | } | 13 | } |
144 | 15 | #carousel .next, #carousel .prev { | 14 | .carousel .pagination { |
108 | 16 | background: url(/assets/images/arrow-sprite.png) no-repeat 0 0; | ||
109 | 17 | position: absolute; | ||
110 | 18 | z-index: 20; | ||
111 | 19 | width: 33px; | ||
112 | 20 | height: 66px; | ||
113 | 21 | margin-top: -33px; | ||
114 | 22 | } | ||
115 | 23 | #carousel .next:active, #carousel .prev:active { | ||
116 | 24 | margin-top: -32px; | ||
117 | 25 | outline: none; | ||
118 | 26 | } | ||
119 | 27 | #carousel .next span, #carousel .prev span { | ||
120 | 28 | position:absolute; | ||
121 | 29 | left: -9999em; | ||
122 | 30 | height: 0; | ||
123 | 31 | width: 0; | ||
124 | 32 | } | ||
125 | 33 | #carousel .prev { | ||
126 | 34 | left: 0; | ||
127 | 35 | top: 50%; | ||
128 | 36 | background-position:0 0; | ||
129 | 37 | } | ||
130 | 38 | #carousel .prev:hover, #carousel .prev:focus { | ||
131 | 39 | outline: none; | ||
132 | 40 | background-position: 0 -116px; | ||
133 | 41 | } | ||
134 | 42 | #carousel .next { | ||
135 | 43 | right: 0; | ||
136 | 44 | top: 50%; | ||
137 | 45 | background-position: 0 -232px; | ||
138 | 46 | } | ||
139 | 47 | #carousel .next:hover, #carousel .next:focus { | ||
140 | 48 | outline: none; | ||
141 | 49 | background-position: 0 -348px; | ||
142 | 50 | } | ||
143 | 51 | #carousel .pagination { | ||
145 | 52 | position: absolute; | 15 | position: absolute; |
146 | 53 | z-index: 20; | 16 | z-index: 20; |
147 | 54 | left: 50%; | 17 | left: 50%; |
148 | 55 | top: 87%; | 18 | top: 87%; |
149 | 56 | margin-left: -35px; | 19 | margin-left: -35px; |
150 | 57 | } | 20 | } |
152 | 58 | #carousel .pagination li { | 21 | .carousel .pagination li { |
153 | 59 | float: left; | 22 | float: left; |
154 | 60 | margin-right: 10px; | 23 | margin-right: 10px; |
155 | 61 | } | 24 | } |
157 | 62 | #carousel .pagination li a { | 25 | .carousel .pagination li a { |
158 | 63 | display: block; | 26 | display: block; |
159 | 64 | height: 10px; | 27 | height: 10px; |
160 | 65 | width: 10px; | 28 | width: 10px; |
161 | 66 | background: #aea79f; | 29 | background: #aea79f; |
162 | 67 | border-radius: 20px; | 30 | border-radius: 20px; |
163 | 68 | } | 31 | } |
166 | 69 | #carousel .pagination li a:hover, #carousel .pagination li a:focus, | 32 | .carousel .pagination li a:hover, .carousel .pagination li a:focus, |
167 | 70 | #carousel .pagination li a.active:hover, #carousel .pagination li a.active:focus { | 33 | .carousel .pagination li a.active:hover, .carousel .pagination li a.active:focus { |
168 | 71 | background-color: #dd4814; | 34 | background-color: #dd4814; |
169 | 72 | } | 35 | } |
171 | 73 | #carousel .pagination li a.active { | 36 | .carousel .pagination li a.active { |
172 | 74 | background-color: #fff; | 37 | background-color: #fff; |
173 | 75 | } | 38 | } |
175 | 76 | #carousel .pagination li a span { | 39 | .carousel .pagination li a span { |
176 | 77 | position: absolute; | 40 | position: absolute; |
177 | 78 | left: -999em; | 41 | left: -999em; |
178 | 79 | } | 42 | } |
180 | 80 | #carousel .carousel { | 43 | .carousel .carouselol { |
181 | 81 | width: 10000px; | 44 | width: 10000px; |
182 | 82 | overflow: hidden; | 45 | overflow: hidden; |
183 | 83 | position: relative; | 46 | position: relative; |
184 | 84 | } | 47 | } |
186 | 85 | #carousel .carousel li { | 48 | .carousel .carouselol li { |
187 | 86 | position: relative; | 49 | position: relative; |
188 | 87 | display: block; | 50 | display: block; |
189 | 88 | background: #333; | ||
190 | 89 | float: left; | 51 | float: left; |
191 | 90 | } | 52 | } |
193 | 91 | #carousel .carousel a { | 53 | .carousel .carouselol a { |
194 | 92 | cursor: hand; | 54 | cursor: hand; |
195 | 93 | } | 55 | } |
197 | 94 | #carousel .carousel .disabled { | 56 | .carousel .carouselol .disabled { |
198 | 95 | display: none; | 57 | display: none; |
199 | 96 | } | 58 | } |
200 | 59 | /* End base carousel */ | ||
201 | 60 | |||
202 | 61 | /* Exhibits carousel */ | ||
203 | 62 | .exhibits-widget { | ||
204 | 63 | margin-bottom: 32px; | ||
205 | 64 | } | ||
206 | 65 | .exhibits-widget .carousel-wrapper{ | ||
207 | 66 | height: 200px; | ||
208 | 67 | } | ||
209 | 68 | .exhibits-widget .carousel .next, .exhibits-widget .carousel .prev { | ||
210 | 69 | background: url(/assets/images/arrow-sprite.png) no-repeat 0 0; | ||
211 | 70 | position: absolute; | ||
212 | 71 | z-index: 20; | ||
213 | 72 | width: 33px; | ||
214 | 73 | height: 66px; | ||
215 | 74 | margin-top: -33px; | ||
216 | 75 | } | ||
217 | 76 | .exhibits-widget .carousel .next:active, .exhibits-widget .carousel .prev:active { | ||
218 | 77 | margin-top: -32px; | ||
219 | 78 | outline: none; | ||
220 | 79 | } | ||
221 | 80 | .exhibits-widget .carousel .next span, .exhibits-widget .carousel .prev span { | ||
222 | 81 | position:absolute; | ||
223 | 82 | left: -9999em; | ||
224 | 83 | height: 0; | ||
225 | 84 | width: 0; | ||
226 | 85 | } | ||
227 | 86 | .exhibits-widget .carousel .prev { | ||
228 | 87 | left: 0; | ||
229 | 88 | top: 50%; | ||
230 | 89 | background-position:0 0; | ||
231 | 90 | } | ||
232 | 91 | .exhibits-widget .carousel .prev:hover, .exhibits-widget .carousel .prev:focus { | ||
233 | 92 | outline: none; | ||
234 | 93 | background-position: 0 -116px; | ||
235 | 94 | } | ||
236 | 95 | .exhibits-widget .carousel .next { | ||
237 | 96 | right: 0; | ||
238 | 97 | top: 50%; | ||
239 | 98 | background-position: 0 -232px; | ||
240 | 99 | } | ||
241 | 100 | .exhibits-widget .carousel .next:hover, .exhibits-widget .carousel .next:focus { | ||
242 | 101 | outline: none; | ||
243 | 102 | background-position: 0 -348px; | ||
244 | 103 | } | ||
245 | 104 | |||
246 | 105 | /* End exhibits carousel */ | ||
247 | 106 | |||
248 | 107 | /* Featured apps carousel */ | ||
249 | 108 | .featured-widget { | ||
250 | 109 | background: url("/assets/images/pattern-featured.gif") repeat scroll 0 0 #ebe9e7; | ||
251 | 110 | border: 2px solid #aea79f; | ||
252 | 111 | border-radius: 4px 4px 4px 4px; | ||
253 | 112 | margin-bottom: 30px; | ||
254 | 113 | overflow: hidden; | ||
255 | 114 | padding: 5px; | ||
256 | 115 | } | ||
257 | 116 | .featured-widget .carousel-wrapper{ | ||
258 | 117 | height: 140px; | ||
259 | 118 | } | ||
260 | 119 | .featured-widget div.carousel-container { | ||
261 | 120 | background-color: #fff; | ||
262 | 121 | border: 1px solid #aea79f; | ||
263 | 122 | border-radius: 4px 4px 4px 4px; | ||
264 | 123 | overflow: hidden; | ||
265 | 124 | padding: 10px; | ||
266 | 125 | } | ||
267 | 126 | |||
268 | 127 | #content .featured-widget table { | ||
269 | 128 | width: 874px; | ||
270 | 129 | } | ||
271 | 130 | #content .featured-widget table td { | ||
272 | 131 | border-right: 1px dotted #AEA79F; | ||
273 | 132 | border-bottom: 0; | ||
274 | 133 | padding: 0 10px; | ||
275 | 134 | width: 202px; | ||
276 | 135 | } | ||
277 | 136 | #featured-carousel .pagination li a.active { | ||
278 | 137 | background-color: #333; | ||
279 | 138 | } | ||
280 | 139 | |||
281 | 140 | #featured-controls { | ||
282 | 141 | width: 70px; | ||
283 | 142 | height: 32px; | ||
284 | 143 | float: right; | ||
285 | 144 | } | ||
286 | 145 | |||
287 | 146 | #featured-controls .next, #featured-controls .prev { | ||
288 | 147 | background: url(/assets/images/arrow-sliders.png) no-repeat 0 0; | ||
289 | 148 | float: left; | ||
290 | 149 | z-index: 20; | ||
291 | 150 | width: 32px; | ||
292 | 151 | height: 29px; | ||
293 | 152 | } | ||
294 | 153 | #featured-controls .next span, #featured-controls .prev span { | ||
295 | 154 | position:absolute; | ||
296 | 155 | left: -9999em; | ||
297 | 156 | height: 0; | ||
298 | 157 | width: 0; | ||
299 | 158 | } | ||
300 | 159 | #featured-controls .prev { | ||
301 | 160 | background-position:0 0; | ||
302 | 161 | } | ||
303 | 162 | #featured-controls .next { | ||
304 | 163 | background-position: -32px 0; | ||
305 | 164 | } | ||
306 | 165 | #featured-controls .prev:hover, #featured-controls .prev:focus, | ||
307 | 166 | #featured-controls .next:hover, #featured-controls .next:focus { | ||
308 | 167 | outline: none; | ||
309 | 168 | } | ||
310 | 169 | |||
311 | 170 | .featured-widget img.icon64 { | ||
312 | 171 | margin: 0 8px 40px 0; | ||
313 | 172 | float: left; | ||
314 | 173 | } | ||
315 | 174 | .featured-widget h4 { | ||
316 | 175 | font-weight: bold; | ||
317 | 176 | } | ||
318 | 97 | 177 | ||
319 | === added file 'src/webcatalog/static/images/arrow-sliders.png' | |||
320 | 98 | Binary files src/webcatalog/static/images/arrow-sliders.png 1970-01-01 00:00:00 +0000 and src/webcatalog/static/images/arrow-sliders.png 2012-03-08 18:33:37 +0000 differ | 178 | Binary files src/webcatalog/static/images/arrow-sliders.png 1970-01-01 00:00:00 +0000 and src/webcatalog/static/images/arrow-sliders.png 2012-03-08 18:33:37 +0000 differ |
321 | === added file 'src/webcatalog/static/images/pattern-featured.gif' | |||
322 | 99 | Binary files src/webcatalog/static/images/pattern-featured.gif 1970-01-01 00:00:00 +0000 and src/webcatalog/static/images/pattern-featured.gif 2012-03-08 18:33:37 +0000 differ | 179 | Binary files src/webcatalog/static/images/pattern-featured.gif 1970-01-01 00:00:00 +0000 and src/webcatalog/static/images/pattern-featured.gif 2012-03-08 18:33:37 +0000 differ |
323 | === modified file 'src/webcatalog/static/js/carousel.js' | |||
324 | --- src/webcatalog/static/js/carousel.js 2012-03-05 23:14:54 +0000 | |||
325 | +++ src/webcatalog/static/js/carousel.js 2012-03-08 18:33:37 +0000 | |||
326 | @@ -91,12 +91,12 @@ | |||
327 | 91 | generateControls: function() { | 91 | generateControls: function() { |
328 | 92 | var prev = Y.Node.create('<a href="#" class="prev"><span>Previous Slide</span></a>'), | 92 | var prev = Y.Node.create('<a href="#" class="prev"><span>Previous Slide</span></a>'), |
329 | 93 | next = Y.Node.create('<a href="#" class="next"><span>Next Slide</span></a>'), | 93 | next = Y.Node.create('<a href="#" class="next"><span>Next Slide</span></a>'), |
331 | 94 | nodeContainer = this.get("nodeContainer"); | 94 | controlsContainer = this.get("controlsContainer"); |
332 | 95 | 95 | ||
333 | 96 | next.on("click", this.next, this); | 96 | next.on("click", this.next, this); |
334 | 97 | prev.on("click", this.prev, this); | 97 | prev.on("click", this.prev, this); |
337 | 98 | nodeContainer.appendChild(prev); | 98 | controlsContainer.appendChild(prev); |
338 | 99 | nodeContainer.appendChild(next); | 99 | controlsContainer.appendChild(next); |
339 | 100 | }, | 100 | }, |
340 | 101 | advance: function(e, val){ | 101 | advance: function(e, val){ |
341 | 102 | if (e) { | 102 | if (e) { |
342 | @@ -157,7 +157,7 @@ | |||
343 | 157 | }, { | 157 | }, { |
344 | 158 | NAME: "carousel", | 158 | NAME: "carousel", |
345 | 159 | ATTRS: { | 159 | ATTRS: { |
347 | 160 | carouselClassName: { value: "carousel"}, | 160 | carouselClassName: { value: "carouselol"}, |
348 | 161 | containerHeight: { value: null }, | 161 | containerHeight: { value: null }, |
349 | 162 | containerWidth: { value: null }, | 162 | containerWidth: { value: null }, |
350 | 163 | nodeContainer: { | 163 | nodeContainer: { |
351 | @@ -169,6 +169,15 @@ | |||
352 | 169 | return n; | 169 | return n; |
353 | 170 | } | 170 | } |
354 | 171 | }, | 171 | }, |
355 | 172 | controlsContainer: { | ||
356 | 173 | setter: function(sel) { | ||
357 | 174 | var n = Y.one(sel); | ||
358 | 175 | if (!n) { | ||
359 | 176 | Y.log('UWC:Carousel - invalid selector provided: ' + sel); | ||
360 | 177 | } | ||
361 | 178 | return n; | ||
362 | 179 | } | ||
363 | 180 | }, | ||
364 | 172 | slideAnimDuration: { value: 1 }, | 181 | slideAnimDuration: { value: 1 }, |
365 | 173 | slideAnimInterval: { value: 5000 }, | 182 | slideAnimInterval: { value: 5000 }, |
366 | 174 | slideEasing: { value: Y.Easing.easeBoth }, | 183 | slideEasing: { value: Y.Easing.easeBoth }, |
367 | 175 | 184 | ||
368 | === modified file 'src/webcatalog/templates/webcatalog/exhibits_widget.html' | |||
369 | --- src/webcatalog/templates/webcatalog/exhibits_widget.html 2012-03-06 16:55:20 +0000 | |||
370 | +++ src/webcatalog/templates/webcatalog/exhibits_widget.html 2012-03-08 18:33:37 +0000 | |||
371 | @@ -1,6 +1,6 @@ | |||
372 | 1 | <div class="carousel-wrapper"> | 1 | <div class="carousel-wrapper"> |
375 | 2 | <div id="carousel"> | 2 | <div id="exhibits-carousel" class="carousel"> |
376 | 3 | <ol class="carousel"> | 3 | <ol class="carouselol"> |
377 | 4 | {% autoescape off %} | 4 | {% autoescape off %} |
378 | 5 | {% comment %} | 5 | {% comment %} |
379 | 6 | All slides except the first one start off with a "disabled" class, which will | 6 | All slides except the first one start off with a "disabled" class, which will |
380 | @@ -22,7 +22,8 @@ | |||
381 | 22 | <script type="text/javascript"> | 22 | <script type="text/javascript"> |
382 | 23 | YUI({combine: true, comboBase: '{% url wc-combo %}?', root: 'yui/3.4.0/build/'}).use('uwc-carousel', function(Y) { | 23 | YUI({combine: true, comboBase: '{% url wc-combo %}?', root: 'yui/3.4.0/build/'}).use('uwc-carousel', function(Y) { |
383 | 24 | var caro = new Y.uwc.Carousel({ | 24 | var caro = new Y.uwc.Carousel({ |
385 | 25 | nodeContainer: "#carousel", | 25 | nodeContainer: "#exhibits-carousel", |
386 | 26 | controlsContainer: "#exhibits-carousel", | ||
387 | 26 | containerHeight: 200, | 27 | containerHeight: 200, |
388 | 27 | containerWidth: 912, | 28 | containerWidth: 912, |
389 | 28 | autoPlay: true | 29 | autoPlay: true |
390 | 29 | 30 | ||
391 | === added file 'src/webcatalog/templates/webcatalog/featured_apps_widget.html' | |||
392 | --- src/webcatalog/templates/webcatalog/featured_apps_widget.html 1970-01-01 00:00:00 +0000 | |||
393 | +++ src/webcatalog/templates/webcatalog/featured_apps_widget.html 2012-03-08 18:33:37 +0000 | |||
394 | @@ -0,0 +1,52 @@ | |||
395 | 1 | <div id="featured-controls"></div> | ||
396 | 2 | <h3>Featured apps on the Ubuntu Software Centre</h3> | ||
397 | 3 | <div class="carousel-container"> | ||
398 | 4 | <div class="carousel-wrapper"> | ||
399 | 5 | <div id="featured-carousel" class="carousel"> | ||
400 | 6 | <ol class="carouselol"> | ||
401 | 7 | {% autoescape off %} | ||
402 | 8 | {% comment %} | ||
403 | 9 | All slides except the first one start off with a "disabled" class, which will | ||
404 | 10 | make them "display: none". This will avoid loading pointless remote resources | ||
405 | 11 | (banners) if Javascript is disabled. The "disabled" class is then removed via | ||
406 | 12 | Javascript. | ||
407 | 13 | {% endcomment %} | ||
408 | 14 | <li class="slide{% if forloop.counter > 1%} disabled{% endif %}"> | ||
409 | 15 | <table><tr> | ||
410 | 16 | {% for app in featured_apps %} | ||
411 | 17 | <td> | ||
412 | 18 | {% if app.icon %} | ||
413 | 19 | <img class="icon64" src="{{ app.icon.url }}"/> | ||
414 | 20 | {% else %} | ||
415 | 21 | <img class="icon64" src="{{ STATIC_URL }}images/applications-other-64.png"/> | ||
416 | 22 | {% endif %} | ||
417 | 23 | <h4><a href="{% url wc-package-detail distro=app.distroseries.code_name package_name=app.package_name %}">{{ app.name }}</a></h4> | ||
418 | 24 | <p>{{ app.departments.all.0.name }} | <b>{% if app.price %}${{ app.price }}{% else %}FREE{% endif %}</b></p> | ||
419 | 25 | <p>{{ app.comment }}</p> | ||
420 | 26 | </td> | ||
421 | 27 | {% if forloop.counter|divisibleby:4 %} | ||
422 | 28 | </tr></table> | ||
423 | 29 | </li> | ||
424 | 30 | <li class="slide{% if forloop.counter > 1%} disabled{% endif %}"> | ||
425 | 31 | <table><tr> | ||
426 | 32 | {% endif %} | ||
427 | 33 | {% endfor %} | ||
428 | 34 | </tr></table> | ||
429 | 35 | </li> | ||
430 | 36 | {% endautoescape %} | ||
431 | 37 | </ol> | ||
432 | 38 | </div> | ||
433 | 39 | </div> | ||
434 | 40 | </div> | ||
435 | 41 | <script type="text/javascript"> | ||
436 | 42 | YUI({combine: true, comboBase: '{% url wc-combo %}?', root: 'yui/3.4.0/build/'}).use('uwc-carousel', function(Y) { | ||
437 | 43 | var caro = new Y.uwc.Carousel({ | ||
438 | 44 | nodeContainer: "#featured-carousel", | ||
439 | 45 | controlsContainer: "#featured-controls", | ||
440 | 46 | containerHeight: 200, | ||
441 | 47 | containerWidth: 912, | ||
442 | 48 | autoPlay: false | ||
443 | 49 | }); | ||
444 | 50 | Y.all('.slide').removeClass('disabled'); | ||
445 | 51 | }); | ||
446 | 52 | </script> | ||
447 | 0 | 53 | ||
448 | === modified file 'src/webcatalog/templates/webcatalog/index.html' | |||
449 | --- src/webcatalog/templates/webcatalog/index.html 2012-03-05 23:14:54 +0000 | |||
450 | +++ src/webcatalog/templates/webcatalog/index.html 2012-03-08 18:33:37 +0000 | |||
451 | @@ -19,6 +19,12 @@ | |||
452 | 19 | </div> | 19 | </div> |
453 | 20 | {% endif %} | 20 | {% endif %} |
454 | 21 | 21 | ||
455 | 22 | {% if featured_apps %} | ||
456 | 23 | <div class="featured-widget"> | ||
457 | 24 | {% include "webcatalog/featured_apps_widget.html" %} | ||
458 | 25 | </div> | ||
459 | 26 | {% endif %} | ||
460 | 27 | |||
461 | 22 | <h3>{% trans "Browse application departments" %}:</h3> | 28 | <h3>{% trans "Browse application departments" %}:</h3> |
462 | 23 | 29 | ||
463 | 24 | {% for dept in depts %} | 30 | {% for dept in depts %} |
464 | 25 | 31 | ||
465 | === modified file 'src/webcatalog/tests/__init__.py' | |||
466 | --- src/webcatalog/tests/__init__.py 2012-01-06 17:54:47 +0000 | |||
467 | +++ src/webcatalog/tests/__init__.py 2012-03-08 18:33:37 +0000 | |||
468 | @@ -23,6 +23,7 @@ | |||
469 | 23 | from .test_forms import * | 23 | from .test_forms import * |
470 | 24 | from .test_handlers import * | 24 | from .test_handlers import * |
471 | 25 | from .test_models import * | 25 | from .test_models import * |
472 | 26 | from .test_managers import * | ||
473 | 26 | from .test_pep8 import * | 27 | from .test_pep8 import * |
474 | 27 | from .test_templatetags import * | 28 | from .test_templatetags import * |
475 | 28 | from .test_utilities import * | 29 | from .test_utilities import * |
476 | 29 | 30 | ||
477 | === added file 'src/webcatalog/tests/test_managers.py' | |||
478 | --- src/webcatalog/tests/test_managers.py 1970-01-01 00:00:00 +0000 | |||
479 | +++ src/webcatalog/tests/test_managers.py 2012-03-08 18:33:37 +0000 | |||
480 | @@ -0,0 +1,50 @@ | |||
481 | 1 | # -*- coding: utf-8 -*- | ||
482 | 2 | # This file is part of the Apps Directory | ||
483 | 3 | # Copyright (C) 2011 Canonical Ltd. | ||
484 | 4 | # | ||
485 | 5 | # This program is free software: you can redistribute it and/or modify | ||
486 | 6 | # it under the terms of the GNU Affero General Public License as | ||
487 | 7 | # published by the Free Software Foundation, either version 3 of the | ||
488 | 8 | # License, or (at your option) any later version. | ||
489 | 9 | # | ||
490 | 10 | # This program is distributed in the hope that it will be useful, | ||
491 | 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
492 | 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
493 | 13 | # GNU Affero General Public License for more details. | ||
494 | 14 | # | ||
495 | 15 | # You should have received a copy of the GNU Affero General Public License | ||
496 | 16 | # along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
497 | 17 | |||
498 | 18 | """Test cases for object managers.""" | ||
499 | 19 | |||
500 | 20 | from __future__ import ( | ||
501 | 21 | absolute_import, | ||
502 | 22 | with_statement, | ||
503 | 23 | ) | ||
504 | 24 | |||
505 | 25 | |||
506 | 26 | from webcatalog.tests.factory import TestCaseWithFactory | ||
507 | 27 | from webcatalog.models import Application | ||
508 | 28 | |||
509 | 29 | __metaclass__ = type | ||
510 | 30 | __all__ = [ | ||
511 | 31 | 'ApplicationManagerTestCase', | ||
512 | 32 | ] | ||
513 | 33 | |||
514 | 34 | |||
515 | 35 | class ApplicationManagerTestCase(TestCaseWithFactory): | ||
516 | 36 | def test_find_best_returns_none(self): | ||
517 | 37 | self.assertIsNone(Application.objects.find_best(package_name='foo')) | ||
518 | 38 | |||
519 | 39 | def test_find_best_returns_latest(self): | ||
520 | 40 | latest = self.factory.make_distroseries(version='14.10') | ||
521 | 41 | older = self.factory.make_distroseries(version='14.04') | ||
522 | 42 | |||
523 | 43 | expected = self.factory.make_application(distroseries=latest) | ||
524 | 44 | self.factory.make_application(package_name=expected.package_name, | ||
525 | 45 | distroseries=older) | ||
526 | 46 | |||
527 | 47 | retrieved = Application.objects.find_best( | ||
528 | 48 | package_name=expected.package_name) | ||
529 | 49 | |||
530 | 50 | self.assertEqual(expected.id, retrieved.id) | ||
531 | 0 | 51 | ||
532 | === modified file 'src/webcatalog/tests/test_views.py' | |||
533 | --- src/webcatalog/tests/test_views.py 2012-03-05 23:14:54 +0000 | |||
534 | +++ src/webcatalog/tests/test_views.py 2012-03-08 18:33:37 +0000 | |||
535 | @@ -421,6 +421,14 @@ | |||
536 | 421 | 421 | ||
537 | 422 | self.assertContains(response, '<li class="slide', count=1) | 422 | self.assertContains(response, '<li class="slide', count=1) |
538 | 423 | 423 | ||
539 | 424 | def test_featured_apps(self): | ||
540 | 425 | app = self.factory.make_application(package_name='foobar') | ||
541 | 426 | |||
542 | 427 | with patch_settings(FEATURED_APPS=['foobar', 'baz']): | ||
543 | 428 | response = self.client.get(reverse('wc-index')) | ||
544 | 429 | |||
545 | 430 | self.assertEqual([app], response.context[0]['featured_apps']) | ||
546 | 431 | |||
547 | 424 | 432 | ||
548 | 425 | class OverviewTestCase(TestCaseWithFactory): | 433 | class OverviewTestCase(TestCaseWithFactory): |
549 | 426 | def test_department_contains_links_to_subdepartments(self): | 434 | def test_department_contains_links_to_subdepartments(self): |
550 | 427 | 435 | ||
551 | === modified file 'src/webcatalog/views.py' | |||
552 | --- src/webcatalog/views.py 2012-03-06 16:47:42 +0000 | |||
553 | +++ src/webcatalog/views.py 2012-03-08 18:33:37 +0000 | |||
554 | @@ -117,10 +117,14 @@ | |||
555 | 117 | exhibits = list(Exhibit.objects.filter(Q(display=True) | | 117 | exhibits = list(Exhibit.objects.filter(Q(display=True) | |
556 | 118 | Q(display=None, published=True,))) | 118 | Q(display=None, published=True,))) |
557 | 119 | shuffle(exhibits) | 119 | shuffle(exhibits) |
558 | 120 | featured_apps = [Application.objects.find_best(package_name=app) | ||
559 | 121 | for app in settings.FEATURED_APPS] | ||
560 | 122 | featured_apps = [x for x in featured_apps if x] | ||
561 | 120 | 123 | ||
562 | 121 | context = RequestContext(request, dict={ | 124 | context = RequestContext(request, dict={ |
563 | 122 | 'depts': depts, | 125 | 'depts': depts, |
564 | 123 | 'exhibits': exhibits, | 126 | 'exhibits': exhibits, |
565 | 127 | 'featured_apps': featured_apps, | ||
566 | 124 | }) | 128 | }) |
567 | 125 | return render_to_response('webcatalog/index.html', | 129 | return render_to_response('webcatalog/index.html', |
568 | 126 | context_instance=context) | 130 | context_instance=context) |
Nice stuff!