]> git.ipfire.org Git - people/shoehn/ipfire.org.git/blob - webapp/ui_modules.py
Improve design of the download page.
[people/shoehn/ipfire.org.git] / webapp / ui_modules.py
1 #!/usr/bin/python
2
3 from __future__ import division
4
5 import hashlib
6 import logging
7 import operator
8 import re
9 import socket
10 import textile
11 import tornado.escape
12 import tornado.locale
13 import tornado.web
14 import unicodedata
15
16 from tornado.database import Row
17
18 import backend
19 import backend.stasy
20
21 class UIModule(tornado.web.UIModule):
22 @property
23 def accounts(self):
24 return self.handler.accounts
25
26 @property
27 def advertisements(self):
28 return self.handler.advertisements
29
30 @property
31 def banners(self):
32 return self.handler.banners
33
34 @property
35 def memcached(self):
36 return self.handler.memcached
37
38 @property
39 def releases(self):
40 return self.handler.releases
41
42 @property
43 def geoip(self):
44 return self.handler.geoip
45
46 @property
47 def news(self):
48 return self.handler.news
49
50
51 class AdvertisementModule(UIModule):
52 def render(self, where):
53 assert where in ("download-splash",), where
54
55 ad = self.advertisements.get(where)
56 if not ad:
57 return ""
58
59 # Mark that advert has been shown.
60 ad.update_impressions()
61
62 return self.render_string("modules/ads/%s.html" % where, ad=ad)
63
64
65 class MapModule(UIModule):
66 def render(self, latitude, longitude):
67 return self.render_string("modules/map.html", latitude=latitude, longitude=longitude)
68
69
70 class MenuModule(UIModule):
71 def render(self):
72 return self.render_string("modules/menu.html")
73
74
75 class MirrorItemModule(UIModule):
76 def render(self, item):
77 return self.render_string("modules/mirror-item.html", item=item)
78
79
80 class MirrorsTableModule(UIModule):
81 def render(self, mirrors, preferred_mirrors=[]):
82 return self.render_string("modules/mirrors-table.html",
83 mirrors=mirrors, preferred_mirrors=preferred_mirrors)
84
85
86 class NetBootMenuConfigModule(UIModule):
87 def render(self, release):
88 return self.render_string("netboot/menu-config.cfg", release=release)
89
90
91 class NetBootMenuHeaderModule(UIModule):
92 def render(self, title, releases):
93 id = unicodedata.normalize("NFKD", unicode(title)).encode("ascii", "ignore")
94 id = re.sub(r"[^\w]+", " ", id)
95 id = "-".join(id.lower().strip().split())
96
97 return self.render_string("netboot/menu-header.cfg", id=id,
98 title=title, releases=releases)
99
100
101 class NetBootMenuSeparatorModule(UIModule):
102 def render(self):
103 return self.render_string("netboot/menu-separator.cfg")
104
105
106 class NewsItemModule(UIModule):
107 def get_author(self, author):
108 # Get name of author
109 author = self.accounts.find(author)
110 if author:
111 return author.cn
112 else:
113 _ = self.locale.translate
114 return _("Unknown author")
115
116 def render(self, item, uncut=True, announcement=False, show_heading=True):
117 # Get author
118 item.author = self.get_author(item.author_id)
119
120 if not uncut and len(item.text) >= 400:
121 item.text = item.text[:400] + "..."
122
123 # Render text
124 item.text = textile.textile(item.text)
125
126 return self.render_string("modules/news-item.html", item=item,
127 uncut=uncut, announcement=announcement, show_heading=show_heading)
128
129
130 class NewsLineModule(UIModule):
131 def render(self, item):
132 return self.render_string("modules/news-line.html", item=item)
133
134
135 class NewsTableModule(UIModule):
136 def render(self, news):
137 return self.render_string("modules/news-table.html", news=news)
138
139
140 class NewsYearNavigationModule(UIModule):
141 def render(self, active=None):
142 try:
143 active = int(active)
144 except:
145 active = None
146
147 return self.render_string("modules/news-year-nav.html",
148 active=active, years=self.news.years)
149
150
151 class SidebarItemModule(UIModule):
152 def render(self):
153 return self.render_string("modules/sidebar-item.html")
154
155
156 class SidebarReleaseModule(UIModule):
157 def render(self):
158 return self.render_string("modules/sidebar-release.html",
159 latest=self.releases.get_latest())
160
161
162 class ReleaseItemModule(UIModule):
163 def render(self, release, latest=False):
164 arches = ("i586", "arm")
165
166 downloads = []
167 for arch in ("i586", "arm"):
168 files = []
169
170 for file in release.files:
171 if not file.arch == arch:
172 continue
173
174 files.append(file)
175
176 if files:
177 downloads.append((arch, files))
178
179 return self.render_string("modules/release-item.html",
180 release=release, latest=latest, downloads=downloads)
181
182
183 class SidebarBannerModule(UIModule):
184 def render(self, item=None):
185 if not item:
186 item = self.banners.get_random()
187
188 return self.render_string("modules/sidebar-banner.html", item=item)
189
190
191 class DownloadButtonModule(UIModule):
192 def render(self, release, text="Download now!"):
193 best_image = None
194
195 for file in release.files:
196 if file.type == "iso":
197 best_image = file
198 break
199
200 # Show nothing when there was no image found.
201 if not best_image:
202 return ""
203
204 return self.render_string("modules/download-button.html",
205 release=release, image=best_image)
206
207
208 class PlanetEntryModule(UIModule):
209 def render(self, entry, show_avatar=True):
210 return self.render_string("modules/planet-entry.html",
211 entry=entry, show_avatar=show_avatar)
212
213
214 class TrackerPeerListModule(UIModule):
215 def render(self, peers, percentages=False):
216 # Guess country code and hostname of the host
217 for peer in peers:
218 country_code = backend.GeoIP().get_country(peer["ip"])
219 peer["country_code"] = country_code or "unknown"
220
221 try:
222 peer["hostname"] = socket.gethostbyaddr(peer["ip"])[0]
223 except:
224 peer["hostname"] = ""
225
226 return self.render_string("modules/tracker-peerlist.html",
227 peers=[Row(p) for p in peers], percentages=percentages)
228
229
230 class StasyTableModule(UIModule):
231 def _make_percentages(self, items):
232 total = sum(items.values())
233
234 for k in items.keys():
235 items[k] *= 100
236 items[k] /= total
237
238 return items
239
240 def render(self, items, sortby="key", reverse=False, percentage=False, flags=False, locale=False):
241 hundred_percent = 0
242 for v in items.values():
243 hundred_percent += v
244
245 keys = []
246 if sortby == "key":
247 keys = sorted(items.keys(), reverse=reverse)
248 elif sortby == "percentage":
249 keys = [k for k,v in sorted(items.items(), key=operator.itemgetter(1))]
250 if not reverse:
251 keys = reversed(keys)
252 else:
253 raise Exception, "Unknown sortby parameter was provided"
254
255 if hundred_percent:
256 _items = []
257 for k in keys:
258 if not percentage:
259 v = items[k] * 100 / hundred_percent
260 else:
261 v = items[k] * 100
262 _items.append((k, v))
263 items = _items
264
265 if items and type(items[0][0]) == type(()) :
266 _ = self.locale.translate
267 _items = []
268 for k, v in items:
269 k = _("%s to %s") % k
270 _items.append((k, v))
271 items = _items
272
273 if locale:
274 flags = False
275 locales = tornado.locale.LOCALE_NAMES
276 _items = []
277 for k, v in items:
278 if k:
279 for code, locale in locales.items():
280 if code.startswith(k):
281 k = locale["name"].split()[0]
282 _items.append((k, v))
283 items = _items
284
285 return self.render_string("modules/stasy-table.html", items=items, flags=flags)
286
287
288 class StasyCPUCoreTableModule(StasyTableModule):
289 def render(self, items):
290 items = self._make_percentages(items)
291
292 items = items.items()
293 items.sort()
294
295 return self.render_string("modules/stasy-table.html", items=items,
296 flags=None #XXX
297 )
298
299
300 class StasyDeviceTableModule(UIModule):
301 def render(self, devices):
302 groups = {}
303
304 for device in devices:
305 if not groups.has_key(device.cls):
306 groups[device.cls] = []
307
308 groups[device.cls].append(device)
309
310 return self.render_string("modules/stasy-table-devices.html",
311 groups=groups.items())
312
313
314 class StasyGeoTableModule(UIModule):
315 def render(self, items):
316 _ = self.locale.translate
317
318 # Sort all items by value
319 items = sorted(items.items(), key=operator.itemgetter(1), reverse=True)
320
321 countries = []
322 for code, value in items:
323 country = tornado.database.Row({
324 "code" : code.lower(),
325 "name" : _(self.geoip.get_country_name(code)),
326 "value" : value * 100,
327 })
328 countries.append(country)
329
330 return self.render_string("modules/stasy-table-geo.html", countries=countries)
331
332
333 class WishlistModule(UIModule):
334 def render(self, wishes, short=False):
335 return self.render_string("wishlist/modules/wishlist.html",
336 wishes=wishes, short=short)
337
338
339 class WishModule(UIModule):
340 def render(self, wish, short=False):
341 progress_bar = "progress-bar-warning"
342
343 if wish.percentage >= 100:
344 progress_bar = "progress-bar-success"
345
346 return self.render_string("wishlist/modules/wish.html",
347 wish=wish, short=short, progress_bar=progress_bar)
348
349
350 class DonationBoxModule(UIModule):
351 def render(self, reason_for_transfer=None):
352 if reason_for_transfer:
353 reason_for_transfer = "IPFire.org - %s" % reason_for_transfer
354
355 return self.render_string("modules/donation-box.html",
356 reason_for_transfer=reason_for_transfer)