]> git.ipfire.org Git - ipfire.org.git/blame - webapp/ui_modules.py
Massive web site update
[ipfire.org.git] / webapp / ui_modules.py
CommitLineData
81675874 1#!/usr/bin/python
2
91a446f0
MT
3from __future__ import division
4
de683d7c 5import hashlib
940227cb 6import logging
91a446f0 7import operator
9068dba1 8import re
940227cb
MT
9import socket
10import textile
11import tornado.escape
6af40477 12import tornado.locale
feb02477 13import tornado.web
9068dba1 14import unicodedata
81675874 15
494d80e6
MT
16import backend
17
81675874 18class UIModule(tornado.web.UIModule):
940227cb
MT
19 @property
20 def accounts(self):
21 return self.handler.accounts
81675874 22
a0048e66
MT
23 @property
24 def advertisements(self):
25 return self.handler.advertisements
26
d0d074e0 27 @property
940227cb
MT
28 def banners(self):
29 return self.handler.banners
d0d074e0 30
de683d7c
MT
31 @property
32 def memcached(self):
33 return self.handler.memcached
34
940227cb
MT
35 @property
36 def releases(self):
37 return self.handler.releases
81675874 38
638e9782
MT
39 @property
40 def geoip(self):
41 return self.handler.geoip
42
7771acea
MT
43 @property
44 def news(self):
45 return self.handler.news
46
fa7e1a0a
MT
47 @property
48 def planet(self):
49 return self.handler.planet
50
77431b9c
MT
51 @property
52 def talk(self):
53 return self.handler.talk
54
e64ce07e
MT
55 @property
56 def wishlist(self):
57 return self.handler.wishlist
58
5a1018ab 59
a0048e66
MT
60class 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
66862195
MT
74class FireinfoDeviceTableModule(UIModule):
75 def render(self, devices):
76 return self.render_string("fireinfo/modules/table-devices.html",
77 devices=devices)
78
79
80class 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
104class 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
494d80e6 120 country = backend.database.Row({
66862195
MT
121 "code" : code,
122 "name" : name,
123 "value" : value,
124 })
125 countries.append(country)
126
66862195
MT
127 return self.render_string("fireinfo/modules/table-geo.html",
128 countries=countries, other_countries=other_countries)
129
130
131class 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
9068dba1
MT
159class MapModule(UIModule):
160 def render(self, latitude, longitude):
161 return self.render_string("modules/map.html", latitude=latitude, longitude=longitude)
162
163
940227cb
MT
164class MenuModule(UIModule):
165 def render(self):
60024cc8 166 return self.render_string("modules/menu.html")
81675874 167
168
9068dba1
MT
169class MirrorItemModule(UIModule):
170 def render(self, item):
171 return self.render_string("modules/mirror-item.html", item=item)
172
173
174class 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
180class NetBootMenuConfigModule(UIModule):
181 def render(self, release):
182 return self.render_string("netboot/menu-config.cfg", release=release)
183
184
185class 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
195class NetBootMenuSeparatorModule(UIModule):
196 def render(self):
197 return self.render_string("netboot/menu-separator.cfg")
198
199
81675874 200class NewsItemModule(UIModule):
940227cb
MT
201 def get_author(self, author):
202 # Get name of author
203 author = self.accounts.find(author)
204 if author:
2fed2438 205 return author.name
940227cb
MT
206 else:
207 _ = self.locale.translate
208 return _("Unknown author")
209
60024cc8 210 def render(self, item, uncut=True, announcement=False, show_heading=True):
940227cb
MT
211 # Get author
212 item.author = self.get_author(item.author_id)
81675874 213
940227cb
MT
214 if not uncut and len(item.text) >= 400:
215 item.text = item.text[:400] + "..."
81675874 216
940227cb 217 # Render text
e46e8df8 218 item.text = textile.textile(item.text.decode("utf8"))
81675874 219
60024cc8
MT
220 return self.render_string("modules/news-item.html", item=item,
221 uncut=uncut, announcement=announcement, show_heading=show_heading)
940227cb
MT
222
223
7771acea 224class NewsLineModule(UIModule):
940227cb
MT
225 def render(self, item):
226 return self.render_string("modules/news-line.html", item=item)
227
228
7771acea
MT
229class NewsTableModule(UIModule):
230 def render(self, news):
231 return self.render_string("modules/news-table.html", news=news)
232
233
234class 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
fa7e1a0a
MT
245class PlanetSearchBoxModule(UIModule):
246 def render(self, query=None):
60b0917c 247 return self.render_string("modules/planet/search-box.html", query=query)
fa7e1a0a
MT
248
249
81675874 250class SidebarItemModule(UIModule):
251 def render(self):
252 return self.render_string("modules/sidebar-item.html")
253
254
255class SidebarReleaseModule(UIModule):
81675874 256 def render(self):
257 return self.render_string("modules/sidebar-release.html",
940227cb 258 latest=self.releases.get_latest())
81675874 259
260
261class ReleaseItemModule(UIModule):
60024cc8 262 def render(self, release, latest=False):
110e8687 263 arches = ("i586", "arm")
60024cc8 264
110e8687
MT
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))
60024cc8
MT
277
278 return self.render_string("modules/release-item.html",
110e8687 279 release=release, latest=latest, downloads=downloads)
81675874 280
281
282class SidebarBannerModule(UIModule):
940227cb
MT
283 def render(self, item=None):
284 if not item:
285 item = self.banners.get_random()
286
81675874 287 return self.render_string("modules/sidebar-banner.html", item=item)
288
289
60024cc8
MT
290class 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
d88b8f41
MT
307class PlanetAuthorBoxModule(UIModule):
308 def render(self, author):
309 return self.render_string("planet/modules/author-box.html", author=author)
310
311
940227cb 312class PlanetEntryModule(UIModule):
60024cc8 313 def render(self, entry, show_avatar=True):
940227cb 314 return self.render_string("modules/planet-entry.html",
60024cc8 315 entry=entry, show_avatar=show_avatar)
d0d074e0
MT
316
317
66862195
MT
318class 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
326class TalkCallLogModule(UIModule):
77431b9c
MT
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
341class 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)
66862195
MT
351
352
353class TalkOngoingCallsModule(UIModule):
77431b9c
MT
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 ""
66862195
MT
366
367
940227cb 368class TrackerPeerListModule(UIModule):
6d524cba 369 def render(self, peers):
940227cb
MT
370 # Guess country code and hostname of the host
371 for peer in peers:
6d524cba
MT
372 country_code = self.geoip.get_country(peer["ip"])
373 if country_code:
66862195
MT
374 peer["country_code"] = country_code.lower()
375 else:
376 peer["country_code"] = "unknown"
d0d074e0 377
940227cb
MT
378 try:
379 peer["hostname"] = socket.gethostbyaddr(peer["ip"])[0]
380 except:
381 peer["hostname"] = ""
d0d074e0 382
940227cb 383 return self.render_string("modules/tracker-peerlist.html",
e46e8df8 384 peers=[backend.database.Row(p) for p in peers])
372efc19
MT
385
386
7771acea
MT
387class 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
393class WishModule(UIModule):
394 def render(self, wish, short=False):
3907b320 395 progress_bar = "progress-bar-warning"
7771acea 396
f60b79ce 397 if wish.percentage >= 100:
3907b320 398 progress_bar = "progress-bar-success"
7771acea
MT
399
400 return self.render_string("wishlist/modules/wish.html",
401 wish=wish, short=short, progress_bar=progress_bar)
402
403
e64ce07e
MT
404class WishlistItemsModule(UIModule):
405 def render(self, wishlist_items):
406 return self.render_string("modules/wishlist-items.html",
407 wishlist_items=wishlist_items)
408
409
7771acea 410class DonationBoxModule(UIModule):
e00c06b9 411 def render(self, reason_for_transfer=None):
353880e5
MT
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",
e00c06b9
MT
416 reason_for_transfer=reason_for_transfer)
417
418
419class DonationButtonModule(UIModule):
069e18f5
MT
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
e00c06b9
MT
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,
069e18f5
MT
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("_")
069e18f5
MT
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"
60b0917c
MT
490
491
492class 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)