]> git.ipfire.org Git - people/shoehn/ipfire.org.git/blob - webapp/ui_modules.py
netboot: Allow booting multiple architectures
[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 import backend
17
18 class UIModule(tornado.web.UIModule):
19 @property
20 def accounts(self):
21 return self.handler.accounts
22
23 @property
24 def advertisements(self):
25 return self.handler.advertisements
26
27 @property
28 def banners(self):
29 return self.handler.banners
30
31 @property
32 def memcached(self):
33 return self.handler.memcached
34
35 @property
36 def releases(self):
37 return self.handler.releases
38
39 @property
40 def geoip(self):
41 return self.handler.geoip
42
43 @property
44 def news(self):
45 return self.handler.news
46
47 @property
48 def planet(self):
49 return self.handler.planet
50
51 @property
52 def talk(self):
53 return self.handler.talk
54
55 @property
56 def wishlist(self):
57 return self.handler.wishlist
58
59
60 class AdvertisementModule(UIModule):
61 def render(self, where):
62 assert where in ("download-splash",), where
63
64 ad = self.advertisements.get(where)
65 if not ad:
66 return ""
67
68 # Mark that advert has been shown.
69 ad.update_impressions()
70
71 return self.render_string("modules/ads/%s.html" % where, ad=ad)
72
73
74 class FireinfoDeviceTableModule(UIModule):
75 def render(self, devices):
76 return self.render_string("fireinfo/modules/table-devices.html",
77 devices=devices)
78
79
80 class FireinfoDeviceAndGroupsTableModule(UIModule):
81 def render(self, devices):
82 _ = self.locale.translate
83
84 groups = {}
85
86 for device in devices:
87 if not groups.has_key(device.cls):
88 groups[device.cls] = []
89
90 groups[device.cls].append(device)
91
92 # Sort all devices
93 for key in groups.keys():
94 groups[key].sort()
95
96 # Order the groups by their name
97 groups = groups.items()
98 groups.sort()
99
100 return self.render_string("fireinfo/modules/table-devices-and-groups.html",
101 groups=groups)
102
103
104 class FireinfoGeoTableModule(UIModule):
105 def render(self, items):
106 countries = []
107 other_countries = []
108 for code, value in items:
109 # Skip the satellite providers in this ranking
110 if code in (None, "A1", "A2"):
111 continue
112
113 name = self.geoip.get_country_name(code)
114
115 # Don't add countries with a small share on the list
116 if value < 0.01:
117 other_countries.append(name)
118 continue
119
120 country = backend.database.Row({
121 "code" : code,
122 "name" : name,
123 "value" : value,
124 })
125 countries.append(country)
126
127 return self.render_string("fireinfo/modules/table-geo.html",
128 countries=countries, other_countries=other_countries)
129
130
131 class LanguageNameModule(UIModule):
132 def render(self, language):
133 _ = self.locale.translate
134
135 if language == "de":
136 return _("German")
137 elif language == "en":
138 return _("English")
139 elif language == "es":
140 return _("Spanish")
141 elif language == "fr":
142 return _("French")
143 elif language == "it":
144 return _("Italian")
145 elif language == "nl":
146 return _("Dutch")
147 elif language == "pl":
148 return _("Polish")
149 elif language == "pt":
150 return _("Portuguese")
151 elif language == "ru":
152 return _("Russian")
153 elif language == "tr":
154 return _("Turkish")
155
156 return language
157
158
159 class MapModule(UIModule):
160 def render(self, latitude, longitude):
161 return self.render_string("modules/map.html", latitude=latitude, longitude=longitude)
162
163
164 class MenuModule(UIModule):
165 def render(self):
166 return self.render_string("modules/menu.html")
167
168
169 class MirrorItemModule(UIModule):
170 def render(self, item):
171 return self.render_string("modules/mirror-item.html", item=item)
172
173
174 class MirrorsTableModule(UIModule):
175 def render(self, mirrors, preferred_mirrors=[]):
176 return self.render_string("modules/mirrors-table.html",
177 mirrors=mirrors, preferred_mirrors=preferred_mirrors)
178
179
180 class NetBootMenuConfigModule(UIModule):
181 def render(self, release, arch=None, platform=None):
182 return self.render_string("netboot/menu-config.cfg", release=release,
183 arch=arch, platform=platform)
184
185
186 class NetBootMenuHeaderModule(UIModule):
187 def render(self, title, releases, arch=None, platform=None):
188 id = unicodedata.normalize("NFKD", unicode(title)).encode("ascii", "ignore")
189 id = re.sub(r"[^\w]+", " ", id)
190 id = "-".join(id.lower().strip().split())
191
192 return self.render_string("netboot/menu-header.cfg", id=id,
193 title=title, releases=releases, arch=arch, platform=platform)
194
195
196 class NetBootMenuSeparatorModule(UIModule):
197 def render(self):
198 return self.render_string("netboot/menu-separator.cfg")
199
200
201 class NewsItemModule(UIModule):
202 def get_author(self, author):
203 # Get name of author
204 author = self.accounts.find(author)
205 if author:
206 return author.name
207 else:
208 _ = self.locale.translate
209 return _("Unknown author")
210
211 def render(self, item, uncut=True, announcement=False, show_heading=True):
212 # Get author
213 item.author = self.get_author(item.author_id)
214
215 if not uncut and len(item.text) >= 400:
216 item.text = item.text[:400] + "..."
217
218 # Render text
219 item.text = textile.textile(item.text.decode("utf8"))
220
221 # Find a release if one exists
222 release = self.releases.get_by_news_id(item.uuid)
223
224 return self.render_string("modules/news-item.html", item=item, release=release,
225 uncut=uncut, announcement=announcement, show_heading=show_heading)
226
227
228 class NewsLineModule(UIModule):
229 def render(self, item):
230 return self.render_string("modules/news-line.html", item=item)
231
232
233 class NewsTableModule(UIModule):
234 def render(self, news):
235 return self.render_string("modules/news-table.html", news=news)
236
237
238 class NewsYearNavigationModule(UIModule):
239 def render(self, active=None):
240 try:
241 active = int(active)
242 except:
243 active = None
244
245 return self.render_string("modules/news-year-nav.html",
246 active=active, years=self.news.years)
247
248
249 class PlanetSearchBoxModule(UIModule):
250 def render(self, query=None):
251 return self.render_string("modules/planet/search-box.html", query=query)
252
253
254 class SidebarItemModule(UIModule):
255 def render(self):
256 return self.render_string("modules/sidebar-item.html")
257
258
259 class SidebarReleaseModule(UIModule):
260 def render(self):
261 return self.render_string("modules/sidebar-release.html",
262 latest=self.releases.get_latest())
263
264
265 class ReleaseItemModule(UIModule):
266 def render(self, release, latest=False):
267 arches = ("i586", "arm")
268
269 downloads = []
270 for arch in ("i586", "arm"):
271 files = []
272
273 for file in release.files:
274 if not file.arch == arch:
275 continue
276
277 files.append(file)
278
279 if files:
280 downloads.append((arch, files))
281
282 return self.render_string("modules/release-item.html",
283 release=release, latest=latest, downloads=downloads)
284
285
286 class SidebarBannerModule(UIModule):
287 def render(self, item=None):
288 if not item:
289 item = self.banners.get_random()
290
291 return self.render_string("modules/sidebar-banner.html", item=item)
292
293
294 class DownloadButtonModule(UIModule):
295 def render(self, release, text="Download now!"):
296 best_image = None
297
298 for file in release.files:
299 if file.type == "iso":
300 best_image = file
301 break
302
303 # Show nothing when there was no image found.
304 if not best_image:
305 return ""
306
307 return self.render_string("modules/download-button.html",
308 release=release, image=best_image)
309
310
311 class PlanetAuthorBoxModule(UIModule):
312 def render(self, author):
313 return self.render_string("planet/modules/author-box.html", author=author)
314
315
316 class PlanetEntryModule(UIModule):
317 def render(self, entry, show_avatar=True):
318 return self.render_string("modules/planet-entry.html",
319 entry=entry, show_avatar=show_avatar)
320
321
322 class ProgressBarModule(UIModule):
323 def render(self, value, colour=None):
324 value *= 100
325
326 return self.render_string("modules/progress-bar.html",
327 colour=colour, value=value)
328
329
330 class TalkCallLogModule(UIModule):
331 def render(self, account=None, viewer=None):
332 if (account is None or not self.current_user == account) \
333 and not self.current_user.is_admin():
334 raise RuntimeException("Insufficient permissions")
335
336 if viewer is None:
337 viewer = self.current_user
338
339 calls = self.talk.get_call_log(account)
340
341 return self.render_string("talk/modules/call-log.html",
342 calls=calls, viewer=viewer)
343
344
345 class TalkLinesModule(UIModule):
346 def render(self, account=None, show_account=False):
347 if (account is None or not self.current_user == account) \
348 and not self.current_user.is_admin():
349 raise RuntimeException("Insufficient permissions")
350
351 lines = self.talk.get_lines(account)
352
353 return self.render_string("talk/modules/lines.html",
354 show_account=show_account, lines=lines)
355
356
357 class TalkOngoingCallsModule(UIModule):
358 def render(self, account=None):
359 if (account is None or not self.current_user == account) \
360 and not self.current_user.is_admin():
361 raise RuntimeException("Insufficient permissions")
362
363 calls = self.talk.get_ongoing_calls(account)
364
365 if calls:
366 return self.render_string("talk/modules/ongoing-calls.html",
367 calls=calls)
368
369 return ""
370
371
372 class TrackerPeerListModule(UIModule):
373 def render(self, peers):
374 # Guess country code and hostname of the host
375 for peer in peers:
376 country_code = self.geoip.get_country(peer["ip"])
377 if country_code:
378 peer["country_code"] = country_code.lower()
379 else:
380 peer["country_code"] = "unknown"
381
382 try:
383 peer["hostname"] = socket.gethostbyaddr(peer["ip"])[0]
384 except:
385 peer["hostname"] = ""
386
387 return self.render_string("modules/tracker-peerlist.html",
388 peers=[backend.database.Row(p) for p in peers])
389
390
391 class WishlistModule(UIModule):
392 def render(self, wishes, short=False):
393 return self.render_string("wishlist/modules/wishlist.html",
394 wishes=wishes, short=short)
395
396
397 class WishModule(UIModule):
398 def render(self, wish, short=False):
399 progress_bar = "progress-bar-warning"
400
401 if wish.percentage >= 100:
402 progress_bar = "progress-bar-success"
403
404 return self.render_string("wishlist/modules/wish.html",
405 wish=wish, short=short, progress_bar=progress_bar)
406
407
408 class WishlistItemsModule(UIModule):
409 def render(self, wishlist_items):
410 return self.render_string("modules/wishlist-items.html",
411 wishlist_items=wishlist_items)
412
413
414 class DonationBoxModule(UIModule):
415 def render(self, reason_for_transfer=None):
416 if reason_for_transfer:
417 reason_for_transfer = "IPFire.org - %s" % reason_for_transfer
418
419 return self.render_string("modules/donation-box.html",
420 reason_for_transfer=reason_for_transfer)
421
422
423 class DonationButtonModule(UIModule):
424 # https://developer.paypal.com/docs/classic/paypal-payments-standard/integration-guide/Appx_websitestandard_htmlvariables/
425 COUNTRIES = (
426 "AU",
427 "AT",
428 "BE",
429 "BR",
430 "CA",
431 "CH",
432 "CN",
433 "DE",
434 "ES",
435 "GB",
436 "FR",
437 "IT",
438 "NL",
439 "PL",
440 "PT",
441 "RU",
442 "US",
443 )
444
445 LOCALES = (
446 "da_DK",
447 "he_IL",
448 "id_ID",
449 "ja_JP",
450 "no_NO",
451 "pt_BR",
452 "ru_RU",
453 "sv_SE",
454 "th_TH",
455 "zh_CN",
456 "zh_HK",
457 "zh_TW",
458 )
459
460 def render(self, reason_for_transfer=None, currency="EUR"):
461 if not reason_for_transfer:
462 reason_for_transfer = "IPFire.org"
463
464 primary = (currency == "EUR")
465
466 return self.render_string("modules/donation-button.html", primary=primary,
467 reason_for_transfer=reason_for_transfer, currency=currency, lc=self.lc)
468
469 @property
470 def lc(self):
471 """
472 Returns the locale of the user
473 """
474 try:
475 locale, delimiter, encoding = self.locale.code.partition(".")
476
477 # Break for languages in specific countries
478 if locale in self.LOCALES:
479 return locale
480
481 lang, delimiter, country_code = locale.partition("_")
482
483 if country_code and country_code in self.COUNTRIES:
484 return country_code
485
486 lang = lang.upper()
487 if lang in self.COUNTRIES:
488 return lang
489 except:
490 pass
491
492 # If anything goes wrong, fall back to GB
493 return "GB"
494
495
496 class DonationInputBoxModule(DonationButtonModule):
497 def render(self):
498 currencies = ("EUR", "USD", "GBP", "CHF", "AUD", "NZD", "CAD")
499
500 return self.render_string("modules/donation-input-box.html",
501 currencies=currencies, lc=self.lc)