]> git.ipfire.org Git - people/shoehn/ipfire.org.git/blob - webapp/ui_modules.py
basic styling Support 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 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 = ("x86_64", "i586", "arm")
268
269 downloads = []
270 for arch in arches:
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 (release.sname < "ipfire-2.19-core100" or file.arch == "x86_64") \
300 and file.type == "iso":
301 best_image = file
302 break
303
304 # Show nothing when there was no image found.
305 if not best_image:
306 return ""
307
308 return self.render_string("modules/download-button.html",
309 release=release, image=best_image)
310
311
312 class PlanetAuthorBoxModule(UIModule):
313 def render(self, author):
314 return self.render_string("planet/modules/author-box.html", author=author)
315
316
317 class PlanetEntryModule(UIModule):
318 def render(self, entry, show_avatar=True):
319 return self.render_string("modules/planet-entry.html",
320 entry=entry, show_avatar=show_avatar)
321
322
323 class ProgressBarModule(UIModule):
324 def render(self, value, colour=None):
325 value *= 100
326
327 return self.render_string("modules/progress-bar.html",
328 colour=colour, value=value)
329
330
331 class TalkCallLogModule(UIModule):
332 def render(self, account=None, viewer=None):
333 if (account is None or not self.current_user == account) \
334 and not self.current_user.is_admin():
335 raise RuntimeException("Insufficient permissions")
336
337 if viewer is None:
338 viewer = self.current_user
339
340 calls = self.talk.get_call_log(account)
341
342 return self.render_string("talk/modules/call-log.html",
343 calls=calls, viewer=viewer)
344
345
346 class TalkLinesModule(UIModule):
347 def render(self, account=None, show_account=False):
348 if (account is None or not self.current_user == account) \
349 and not self.current_user.is_admin():
350 raise RuntimeException("Insufficient permissions")
351
352 lines = self.talk.get_lines(account)
353
354 return self.render_string("talk/modules/lines.html",
355 show_account=show_account, lines=lines)
356
357
358 class TalkOngoingCallsModule(UIModule):
359 def render(self, account=None):
360 if (account is None or not self.current_user == account) \
361 and not self.current_user.is_admin():
362 raise RuntimeException("Insufficient permissions")
363
364 calls = self.talk.get_ongoing_calls(account)
365
366 if calls:
367 return self.render_string("talk/modules/ongoing-calls.html",
368 calls=calls)
369
370 return ""
371
372
373 class TrackerPeerListModule(UIModule):
374 def render(self, peers):
375 # Guess country code and hostname of the host
376 for peer in peers:
377 country_code = self.geoip.get_country(peer["ip"])
378 if country_code:
379 peer["country_code"] = country_code.lower()
380 else:
381 peer["country_code"] = "unknown"
382
383 try:
384 peer["hostname"] = socket.gethostbyaddr(peer["ip"])[0]
385 except:
386 peer["hostname"] = ""
387
388 return self.render_string("modules/tracker-peerlist.html",
389 peers=[backend.database.Row(p) for p in peers])
390
391
392 class WishlistModule(UIModule):
393 def render(self, wishes, short=False):
394 return self.render_string("wishlist/modules/wishlist.html",
395 wishes=wishes, short=short)
396
397
398 class WishModule(UIModule):
399 def render(self, wish, short=False):
400 progress_bar = "progress-bar-warning"
401
402 if wish.percentage >= 100:
403 progress_bar = "progress-bar-success"
404
405 return self.render_string("wishlist/modules/wish.html",
406 wish=wish, short=short, progress_bar=progress_bar)
407
408
409 class WishlistItemsModule(UIModule):
410 def render(self, wishlist_items):
411 return self.render_string("modules/wishlist-items.html",
412 wishlist_items=wishlist_items)
413
414
415 class DonationBoxModule(UIModule):
416 def render(self, reason_for_transfer=None):
417 if reason_for_transfer:
418 reason_for_transfer = "IPFire.org - %s" % reason_for_transfer
419
420 return self.render_string("modules/donation-box.html",
421 reason_for_transfer=reason_for_transfer)
422
423
424 class DonationButtonModule(UIModule):
425 # https://developer.paypal.com/docs/classic/paypal-payments-standard/integration-guide/Appx_websitestandard_htmlvariables/
426 COUNTRIES = (
427 "AU",
428 "AT",
429 "BE",
430 "BR",
431 "CA",
432 "CH",
433 "CN",
434 "DE",
435 "ES",
436 "GB",
437 "FR",
438 "IT",
439 "NL",
440 "PL",
441 "PT",
442 "RU",
443 "US",
444 )
445
446 LOCALES = (
447 "da_DK",
448 "he_IL",
449 "id_ID",
450 "ja_JP",
451 "no_NO",
452 "pt_BR",
453 "ru_RU",
454 "sv_SE",
455 "th_TH",
456 "zh_CN",
457 "zh_HK",
458 "zh_TW",
459 )
460
461 def render(self, reason_for_transfer=None, currency="EUR"):
462 if not reason_for_transfer:
463 reason_for_transfer = "IPFire.org"
464
465 primary = (currency == "EUR")
466
467 return self.render_string("modules/donation-button.html", primary=primary,
468 reason_for_transfer=reason_for_transfer, currency=currency, lc=self.lc)
469
470 @property
471 def lc(self):
472 """
473 Returns the locale of the user
474 """
475 try:
476 locale, delimiter, encoding = self.locale.code.partition(".")
477
478 # Break for languages in specific countries
479 if locale in self.LOCALES:
480 return locale
481
482 lang, delimiter, country_code = locale.partition("_")
483
484 if country_code and country_code in self.COUNTRIES:
485 return country_code
486
487 lang = lang.upper()
488 if lang in self.COUNTRIES:
489 return lang
490 except:
491 pass
492
493 # If anything goes wrong, fall back to GB
494 return "GB"
495
496
497 class DonationInputBoxModule(DonationButtonModule):
498 def render(self):
499 currencies = ("EUR", "USD", "GBP", "CHF", "AUD", "NZD", "CAD")
500
501 return self.render_string("modules/donation-input-box.html",
502 currencies=currencies, lc=self.lc)