]> git.ipfire.org Git - people/shoehn/ipfire.org.git/blob - webapp/ui_modules.py
Massive web site update
[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):
182 return self.render_string("netboot/menu-config.cfg", release=release)
183
184
185 class NetBootMenuHeaderModule(UIModule):
186 def render(self, title, releases):
187 id = unicodedata.normalize("NFKD", unicode(title)).encode("ascii", "ignore")
188 id = re.sub(r"[^\w]+", " ", id)
189 id = "-".join(id.lower().strip().split())
190
191 return self.render_string("netboot/menu-header.cfg", id=id,
192 title=title, releases=releases)
193
194
195 class NetBootMenuSeparatorModule(UIModule):
196 def render(self):
197 return self.render_string("netboot/menu-separator.cfg")
198
199
200 class NewsItemModule(UIModule):
201 def get_author(self, author):
202 # Get name of author
203 author = self.accounts.find(author)
204 if author:
205 return author.name
206 else:
207 _ = self.locale.translate
208 return _("Unknown author")
209
210 def render(self, item, uncut=True, announcement=False, show_heading=True):
211 # Get author
212 item.author = self.get_author(item.author_id)
213
214 if not uncut and len(item.text) >= 400:
215 item.text = item.text[:400] + "..."
216
217 # Render text
218 item.text = textile.textile(item.text.decode("utf8"))
219
220 return self.render_string("modules/news-item.html", item=item,
221 uncut=uncut, announcement=announcement, show_heading=show_heading)
222
223
224 class NewsLineModule(UIModule):
225 def render(self, item):
226 return self.render_string("modules/news-line.html", item=item)
227
228
229 class NewsTableModule(UIModule):
230 def render(self, news):
231 return self.render_string("modules/news-table.html", news=news)
232
233
234 class NewsYearNavigationModule(UIModule):
235 def render(self, active=None):
236 try:
237 active = int(active)
238 except:
239 active = None
240
241 return self.render_string("modules/news-year-nav.html",
242 active=active, years=self.news.years)
243
244
245 class PlanetSearchBoxModule(UIModule):
246 def render(self, query=None):
247 return self.render_string("modules/planet/search-box.html", query=query)
248
249
250 class SidebarItemModule(UIModule):
251 def render(self):
252 return self.render_string("modules/sidebar-item.html")
253
254
255 class SidebarReleaseModule(UIModule):
256 def render(self):
257 return self.render_string("modules/sidebar-release.html",
258 latest=self.releases.get_latest())
259
260
261 class ReleaseItemModule(UIModule):
262 def render(self, release, latest=False):
263 arches = ("i586", "arm")
264
265 downloads = []
266 for arch in ("i586", "arm"):
267 files = []
268
269 for file in release.files:
270 if not file.arch == arch:
271 continue
272
273 files.append(file)
274
275 if files:
276 downloads.append((arch, files))
277
278 return self.render_string("modules/release-item.html",
279 release=release, latest=latest, downloads=downloads)
280
281
282 class SidebarBannerModule(UIModule):
283 def render(self, item=None):
284 if not item:
285 item = self.banners.get_random()
286
287 return self.render_string("modules/sidebar-banner.html", item=item)
288
289
290 class DownloadButtonModule(UIModule):
291 def render(self, release, text="Download now!"):
292 best_image = None
293
294 for file in release.files:
295 if file.type == "iso":
296 best_image = file
297 break
298
299 # Show nothing when there was no image found.
300 if not best_image:
301 return ""
302
303 return self.render_string("modules/download-button.html",
304 release=release, image=best_image)
305
306
307 class PlanetAuthorBoxModule(UIModule):
308 def render(self, author):
309 return self.render_string("planet/modules/author-box.html", author=author)
310
311
312 class PlanetEntryModule(UIModule):
313 def render(self, entry, show_avatar=True):
314 return self.render_string("modules/planet-entry.html",
315 entry=entry, show_avatar=show_avatar)
316
317
318 class ProgressBarModule(UIModule):
319 def render(self, value, colour=None):
320 value *= 100
321
322 return self.render_string("modules/progress-bar.html",
323 colour=colour, value=value)
324
325
326 class TalkCallLogModule(UIModule):
327 def render(self, account=None, viewer=None):
328 if (account is None or not self.current_user == account) \
329 and not self.current_user.is_admin():
330 raise RuntimeException("Insufficient permissions")
331
332 if viewer is None:
333 viewer = self.current_user
334
335 calls = self.talk.get_call_log(account)
336
337 return self.render_string("talk/modules/call-log.html",
338 calls=calls, viewer=viewer)
339
340
341 class TalkLinesModule(UIModule):
342 def render(self, account=None, show_account=False):
343 if (account is None or not self.current_user == account) \
344 and not self.current_user.is_admin():
345 raise RuntimeException("Insufficient permissions")
346
347 lines = self.talk.get_lines(account)
348
349 return self.render_string("talk/modules/lines.html",
350 show_account=show_account, lines=lines)
351
352
353 class TalkOngoingCallsModule(UIModule):
354 def render(self, account=None):
355 if (account is None or not self.current_user == account) \
356 and not self.current_user.is_admin():
357 raise RuntimeException("Insufficient permissions")
358
359 calls = self.talk.get_ongoing_calls(account)
360
361 if calls:
362 return self.render_string("talk/modules/ongoing-calls.html",
363 calls=calls)
364
365 return ""
366
367
368 class TrackerPeerListModule(UIModule):
369 def render(self, peers):
370 # Guess country code and hostname of the host
371 for peer in peers:
372 country_code = self.geoip.get_country(peer["ip"])
373 if country_code:
374 peer["country_code"] = country_code.lower()
375 else:
376 peer["country_code"] = "unknown"
377
378 try:
379 peer["hostname"] = socket.gethostbyaddr(peer["ip"])[0]
380 except:
381 peer["hostname"] = ""
382
383 return self.render_string("modules/tracker-peerlist.html",
384 peers=[backend.database.Row(p) for p in peers])
385
386
387 class WishlistModule(UIModule):
388 def render(self, wishes, short=False):
389 return self.render_string("wishlist/modules/wishlist.html",
390 wishes=wishes, short=short)
391
392
393 class WishModule(UIModule):
394 def render(self, wish, short=False):
395 progress_bar = "progress-bar-warning"
396
397 if wish.percentage >= 100:
398 progress_bar = "progress-bar-success"
399
400 return self.render_string("wishlist/modules/wish.html",
401 wish=wish, short=short, progress_bar=progress_bar)
402
403
404 class WishlistItemsModule(UIModule):
405 def render(self, wishlist_items):
406 return self.render_string("modules/wishlist-items.html",
407 wishlist_items=wishlist_items)
408
409
410 class DonationBoxModule(UIModule):
411 def render(self, reason_for_transfer=None):
412 if reason_for_transfer:
413 reason_for_transfer = "IPFire.org - %s" % reason_for_transfer
414
415 return self.render_string("modules/donation-box.html",
416 reason_for_transfer=reason_for_transfer)
417
418
419 class DonationButtonModule(UIModule):
420 # https://developer.paypal.com/docs/classic/paypal-payments-standard/integration-guide/Appx_websitestandard_htmlvariables/
421 COUNTRIES = (
422 "AU",
423 "AT",
424 "BE",
425 "BR",
426 "CA",
427 "CH",
428 "CN",
429 "DE",
430 "ES",
431 "GB",
432 "FR",
433 "IT",
434 "NL",
435 "PL",
436 "PT",
437 "RU",
438 "US",
439 )
440
441 LOCALES = (
442 "da_DK",
443 "he_IL",
444 "id_ID",
445 "ja_JP",
446 "no_NO",
447 "pt_BR",
448 "ru_RU",
449 "sv_SE",
450 "th_TH",
451 "zh_CN",
452 "zh_HK",
453 "zh_TW",
454 )
455
456 def render(self, reason_for_transfer=None, currency="EUR"):
457 if not reason_for_transfer:
458 reason_for_transfer = "IPFire.org"
459
460 primary = (currency == "EUR")
461
462 return self.render_string("modules/donation-button.html", primary=primary,
463 reason_for_transfer=reason_for_transfer, currency=currency, lc=self.lc)
464
465 @property
466 def lc(self):
467 """
468 Returns the locale of the user
469 """
470 try:
471 locale, delimiter, encoding = self.locale.code.partition(".")
472
473 # Break for languages in specific countries
474 if locale in self.LOCALES:
475 return locale
476
477 lang, delimiter, country_code = locale.partition("_")
478
479 if country_code and country_code in self.COUNTRIES:
480 return country_code
481
482 lang = lang.upper()
483 if lang in self.COUNTRIES:
484 return lang
485 except:
486 pass
487
488 # If anything goes wrong, fall back to GB
489 return "GB"
490
491
492 class DonationInputBoxModule(DonationButtonModule):
493 def render(self):
494 currencies = ("EUR", "USD", "GBP", "CHF", "AUD", "NZD", "CAD")
495
496 return self.render_string("modules/donation-input-box.html",
497 currencies=currencies, lc=self.lc)