]> git.ipfire.org Git - people/jschlag/pbs.git/blob - src/web/ui_modules.py
Fix type error in Python
[people/jschlag/pbs.git] / src / web / ui_modules.py
1 #!/usr/bin/python
2
3 from __future__ import division
4
5 import datetime
6 import itertools
7 import math
8 import pytz
9 import re
10 import string
11 import tornado.escape
12 import tornado.web
13
14 from .. import users
15 from ..constants import *
16
17 class UIModule(tornado.web.UIModule):
18 @property
19 def pakfire(self):
20 return self.handler.application.backend
21
22 @property
23 def settings(self):
24 return self.pakfire.settings
25
26
27 class TextModule(UIModule):
28 __cache = {}
29
30 LINK = """<a href="%s" target="_blank">%s</a>"""
31
32 @property
33 def bugzilla_url(self):
34 return self.settings.get("bugzilla_url", "")
35
36 @property
37 def bugzilla_pattern(self):
38 if not self.__cache.has_key("bugzilla_pattern"):
39 self.__cache["bugzilla_pattern"] = re.compile(BUGZILLA_PATTERN)
40
41 return self.__cache["bugzilla_pattern"]
42
43 @property
44 def bugzilla_repl(self):
45 return self.LINK % (self.bugzilla_url % { "bugid" : r"\2" }, r"\1\2")
46
47 @property
48 def cve_url(self):
49 return self.settings.get("cve_url", "")
50
51 @property
52 def cve_pattern(self):
53 if not self.__cache.has_key("cve_pattern"):
54 self.__cache["cve_pattern"] = re.compile(CVE_PATTERN)
55
56 return self.__cache["cve_pattern"]
57
58 @property
59 def cve_repl(self):
60 return self.LINK % (self.cve_url % r"\3", r"\1\2\3")
61
62 def split_paragraphs(self, s):
63 for group_seperator, line_iteration in itertools.groupby(s.splitlines(True), key=str.isspace):
64 if group_seperator:
65 continue
66
67 paragraph = "".join(line_iteration)
68 yield paragraph.replace("\n", " ")
69
70 def render(self, text, pre=False, remove_linebreaks=True):
71 link = """<a href="%s" target="_blank">%s</a>"""
72
73 if remove_linebreaks:
74 text = text.replace("\n", " ")
75
76 # Escape the text and create make urls clickable.
77 text = tornado.escape.xhtml_escape(text)
78 text = tornado.escape.linkify(text, shorten=True,
79 extra_params='target="_blank"')
80
81 # Search for bug ids that need to be linked to bugzilla.
82 if self.bugzilla_url:
83 text = re.sub(self.bugzilla_pattern, self.bugzilla_repl, text, re.I|re.U)
84
85 # Search for CVE numbers and create hyperlinks.
86 if self.cve_url:
87 text = re.sub(self.cve_pattern, self.cve_repl, text, re.I|re.U)
88
89 if pre:
90 return "<pre>%s</pre>" % text
91
92 return text
93
94
95 class CommitMessageModule(TextModule):
96 def render(self, commit):
97 s = "h5. %s\n\n" % commit.subject
98
99 paragraphs = self.split_paragraphs(commit.message)
100 s += "\n\n".join(paragraphs)
101
102 return TextModule.render(self, s, remove_linebreaks=False)
103
104
105 class ModalModule(UIModule):
106 def render(self, what, **kwargs):
107 what = "modules/modal-%s.html" % what
108
109 return self.render_string(what, **kwargs)
110
111
112 class BuildHeadlineModule(UIModule):
113 def render(self, build, short=False, shorter=False):
114 if shorter:
115 short = True
116
117 return self.render_string("modules/build-headline.html",
118 build=build, pkg=build.pkg, short=short, shorter=shorter)
119
120
121 class JobsStatusModule(UIModule):
122 def render(self, build):
123 return self.render_string("modules/jobs/status.html",
124 build=build, jobs=build.jobs)
125
126
127 class BuildersLoadModule(UIModule):
128 def render(self):
129 load = self.pakfire.builders.get_load()
130
131 return self.render_string("modules/builders/load.html", load=load)
132
133
134 class BugsTableModule(UIModule):
135 def render(self, pkg, bugs):
136 return self.render_string("modules/bugs-table.html",
137 pkg=pkg, bugs=bugs)
138
139
140 class ChangelogModule(UIModule):
141 def render(self, name=None, builds=None, *args, **kwargs):
142 if not builds:
143 builds = self.pakfire.builds.get_changelog(name, *args, **kwargs)
144
145 return self.render_string("modules/changelog/index.html", builds=builds)
146
147
148 class ChangelogEntryModule(UIModule):
149 def render(self, build):
150 return self.render_string("modules/changelog/entry.html", build=build)
151
152
153 class CommitsTableModule(UIModule):
154 def render(self, distro, source, commits, full_format=True):
155 return self.render_string("modules/commits-table.html",
156 distro=distro, source=source, commits=commits,
157 full_format=full_format)
158
159
160 class FooterModule(UIModule):
161 def render(self):
162 return self.render_string("modules/footer.html")
163
164
165 class HeadingDateModule(UIModule):
166 def render(self, date):
167 _ = self.locale.translate
168
169 # Check if this is today.
170 today = datetime.date.today()
171 if date == today:
172 return _("Today")
173
174 # Check if this was yesterday.
175 yesterday = today - datetime.timedelta(days=1)
176 if date == yesterday:
177 return _("Yesterday")
178
179 # Convert date to datetime.
180 date = datetime.datetime(date.year, date.month, date.day)
181
182 return self.locale.format_date(date, shorter=True, relative=False)
183
184
185 class PackagesTableModule(UIModule):
186 def render(self, job, packages):
187 return self.render_string("modules/packages-table.html", job=job,
188 packages=packages)
189
190
191 class PackagesDependencyTableModule(UIModule):
192 def render(self, pkg):
193 if pkg.type == "source":
194 all_deps = [
195 (None, pkg.requires),
196 ]
197 else:
198 all_deps = [
199 ("provides", pkg.provides),
200 ("requires", pkg.requires),
201 ("prerequires", pkg.prerequires),
202 ("conflicts", pkg.conflicts),
203 ("obsoletes", pkg.obsoletes),
204 ("recommends", pkg.recommends),
205 ("suggests", pkg.suggests),
206 ]
207
208 has_deps = []
209 for name, deps in all_deps:
210 if deps:
211 has_deps.append((name, deps))
212
213 if len(has_deps):
214 span = math.floor(12 / len(has_deps))
215
216 if span > 3:
217 span = 3
218 else:
219 span = 12
220
221 return self.render_string("modules/packages/dependency-table.html",
222 pkg=pkg, dependencies=has_deps, span=span)
223
224
225 class PackageTable2Module(UIModule):
226 def render(self, packages):
227 return self.render_string("modules/package-table-detail.html",
228 packages=packages)
229
230
231 class FilesTableModule(UIModule):
232 def render(self, files):
233 return self.render_string("modules/files-table.html", files=files)
234
235
236 class LogFilesTableModule(UIModule):
237 def render(self, job, files):
238 return self.render_string("modules/log-files-table.html", job=job,
239 files=files)
240
241
242 class PackageHeaderModule(UIModule):
243 def render(self, pkg):
244 return self.render_string("modules/package-header.html", pkg=pkg)
245
246
247 class PackageFilesTableModule(UIModule):
248 def render(self, pkg, filelist):
249 return self.render_string("modules/packages-files-table.html",
250 pkg=pkg, filelist=filelist)
251
252
253 class BuildTableModule(UIModule):
254 def render(self, builds, **kwargs):
255 settings = dict(
256 show_user = False,
257 show_repo = False,
258 show_repo_time = False,
259 show_can_move_forward = False,
260 show_when = True,
261 )
262 settings.update(kwargs)
263
264 dates = {}
265
266 for b in builds:
267 try:
268 dates[b.date].append(b)
269 except KeyError:
270 dates[b.date] = [b,]
271
272 dates = sorted(dates.items(), reverse=True)
273
274 return self.render_string("modules/build-table.html", dates=dates, **settings)
275
276
277 class BuildStateWarningsModule(UIModule):
278 def render(self, build):
279 return self.render_string("modules/build-state-warnings.html", build=build)
280
281
282 class JobsBoxesModule(UIModule):
283 def render(self, build, jobs=None):
284 if jobs is None:
285 jobs = build.jobs
286
287 return self.render_string("modules/jobs/boxes.html",
288 build=build, jobs=jobs)
289
290
291 class JobStateModule(UIModule):
292 def render(self, job, cls=None, show_arch=False, show_icon=False, plain=False):
293 state = job.state
294
295 _ = self.locale.translate
296 classes = []
297
298 icon = None
299 if state == "aborted":
300 text = _("Aborted")
301 classes.append("muted")
302 icon = "icon-warning-sign"
303
304 elif state == "dependency_error":
305 text = _("Dependency problem")
306 classes.append("text-warning")
307 icon = "icon-random"
308
309 elif state == "dispatching":
310 text = _("Dispatching")
311 classes.append("text-info")
312 icon = "icon-download-alt"
313
314 elif state == "failed":
315 text = _("Failed")
316 classes.append("text-error")
317 icon = "icon-remove"
318
319 elif state == "finished":
320 text = _("Finished")
321 classes.append("text-success")
322 icon = "icon-ok"
323
324 elif state == "new":
325 text = _("New")
326 classes.append("muted")
327 icon = "icon-asterisk"
328
329 elif state == "pending":
330 text = _("Pending")
331 classes.append("muted")
332 icon = "icon-time"
333
334 elif state == "running":
335 text = _("Running")
336 classes.append("text-info")
337 icon = "icon-cogs"
338
339 elif state == "uploading":
340 text = _("Uploading")
341 classes.append("text-info")
342 icon = "icon-upload-alt"
343
344 # Return just the string, is state is unknown.
345 else:
346 text = _("Unknown: %s") % state
347 classes.append("muted")
348
349 if plain:
350 return text
351
352 if cls:
353 classes.append(cls)
354
355 if show_arch:
356 text = job.arch.name
357
358 if show_icon and icon:
359 text = """<i class="%s"></i> %s""" % (icon, text)
360
361 return """<span class="%s">%s</span>""" % (" ".join(classes), text)
362
363
364 class JobsTableModule(UIModule):
365 def render(self, build, jobs=None, type="release"):
366 if jobs is None:
367 jobs = build.jobs
368
369 return self.render_string("modules/jobs-table.html", build=build,
370 jobs=jobs, type=type)
371
372
373 class JobsListModule(UIModule):
374 def render(self, jobs):
375 return self.render_string("modules/jobs/list.html", jobs=jobs)
376
377
378 class RepositoryTableModule(UIModule):
379 def render(self, distro, repos):
380 return self.render_string("modules/repository-table.html",
381 distro=distro, repos=repos)
382
383
384 class SourceTableModule(UIModule):
385 def render(self, distro, sources):
386 return self.render_string("modules/source-table.html",
387 distro=distro, sources=sources)
388
389
390 class CommentsTableModule(UIModule):
391 def render(self, comments, show_package=False, show_user=True):
392 pkgs, users = {}, {}
393 for comment in comments:
394 if show_package:
395 try:
396 pkg = pkgs[comment.pkg_id]
397 except KeyError:
398 pkg = pkgs[comment.pkg_id] = \
399 self.pakfire.packages.get_by_id(comment.pkg_id)
400
401 comment["pkg"] = pkg
402
403 if show_user:
404 try:
405 user = users[comment.user_id]
406 except KeyError:
407 user = users[comment.user_id] = \
408 self.pakfire.users.get_by_id(comment.user_id)
409
410 comment["user"] = user
411
412 return self.render_string("modules/comments-table.html",
413 comments=comments, show_package=show_package, show_user=show_user)
414
415
416 class LogModule(UIModule):
417 def render(self, entries, **args):
418 return self.render_string("modules/log.html",
419 entries=entries, args=args)
420
421
422 class LogEntryModule(UIModule):
423 def render(self, entry, small=None, **args):
424 if small or not entry.user:
425 template = "modules/log-entry-small.html"
426 else:
427 template = "modules/log-entry.html"
428
429 return self.render_string(template, entry=entry, u=entry.user,
430 show_build=False, **args)
431
432
433 class LogEntryCommentModule(LogEntryModule):
434 def render(self, entry, show_build=False, **args):
435 return self.render_string("modules/log-entry-comment.html",
436 entry=entry, u=entry.user, show_build=show_build, **args)
437
438
439 class MaintainerModule(UIModule):
440 def render(self, maintainer):
441 if isinstance(maintainer, users.User):
442 type = "user"
443 else:
444 type = "string"
445
446 return self.render_string("modules/maintainer.html",
447 type=type, maintainer=maintainer)
448
449
450 class BuildLogModule(UIModule):
451 # XXX deprecated
452 def render(self, messages):
453 _ = self.locale.translate
454
455 for message in messages:
456 try:
457 msg = LOG2MSG[message.message]
458 message["message"] = _(msg)
459 except KeyError:
460 pass
461
462 return self.render_string("modules/build-log.html", messages=messages)
463
464
465 class LogTableModule(UIModule):
466 def render(self, messages, links=["pkg",]):
467 for message in messages:
468 try:
469 message["message"] = LOG2MSG[message.message]
470 except KeyError:
471 pass
472
473 if message.build_id:
474 message["build"] = self.pakfire.builds.get_by_id(message.build_id)
475
476 elif message.pkg_id:
477 message["pkg"] = self.pakfire.packages.get_by_id(message.pkg_id)
478
479 return self.render_string("modules/log-table.html",
480 messages=messages, links=links)
481
482
483 class UsersTableModule(UIModule):
484 def render(self, users):
485 return self.render_string("modules/user-table.html", users=users)
486
487
488 class BuildOffsetModule(UIModule):
489 def render(self):
490 return self.render_string("modules/build-offset.html")
491
492
493 class RepoActionsTableModule(UIModule):
494 def render(self, repo):
495 actions = repo.get_actions()
496
497 return self.render_string("modules/repo-actions-table.html",
498 repo=repo, actions=actions)
499
500
501 class UpdatesTableModule(UIModule):
502 def render(self, updates):
503 return self.render_string("modules/updates-table.html", updates=updates)
504
505
506 class WatchersSidebarTableModule(UIModule):
507 def css_files(self):
508 return "css/watchers-sidebar-table.css"
509
510 def render(self, build, watchers, limit=5):
511 # Sort the watchers by their realname.
512 watchers.sort(key=lambda watcher: watcher.realname)
513
514 return self.render_string("modules/watchers-sidebar-table.html",
515 build=build, watchers=watchers, limit=limit)
516
517
518 class SelectLocaleModule(UIModule):
519 LOCALE_NAMES = [
520 # local code, English name, name
521 ("ca_ES", u"Catalan", "Catal\xc3\xa0"),
522 ("da_DK", u"Danish", u"Dansk"),
523 ("de_DE", u"German", u"Deutsch"),
524 ("en_GB", u"English (UK)", u"English (UK)"),
525 ("en_US", u"English (US)", u"English (US)"),
526 ("es_ES", u"Spanish (Spain)", u"Espa\xf1ol (Espa\xf1a)"),
527 ("es_LA", u"Spanish", u"Espa\xf1ol"),
528 ("fr_CA", u"French (Canada)", u"Fran\xe7ais (Canada)"),
529 ("fr_FR", u"French", u"Fran\xe7ais"),
530 ("it_IT", u"Italian", u"Italiano"),
531 ("km_KH", u"Khmer", u"\u1797\u17b6\u179f\u17b6\u1781\u17d2\u1798\u17c2\u179a"),
532 ("nl_NL", u"Dutch", u"Nederlands"),
533 ("pt_BR", u"Portuguese (Brazil)", u"Portugu\xeas (Brasil)"),
534 ("pt_PT", u"Portuguese (Portugal)", u"Portugu\xeas (Portugal)"),
535 ("ro_RO", u"Romanian", u"Rom\xe2n\u0103"),
536 ("ru_RU", u"Russian", u"\u0440\u0443\u0441\u0441\u043a\u0438\u0439"),
537 ("uk_UA", u"Ukrainian", u"\u0423\u043a\u0440\u0430\u0457\u043d\u0441\u044c\u043a\u0430"),
538 ]
539
540 # Sort the list of locales by their English name.
541 LOCALE_NAMES.sort(key=lambda x: x[1])
542
543 def render(self, name=None, id=None, preselect=None):
544 return self.render_string("modules/select/locale.html",
545 name=name, id=id, preselect=preselect, supported_locales=self.LOCALE_NAMES)
546
547
548 class SelectTimezoneModule(UIModule):
549 def render(self, name=None, id=None, preselect=None):
550 return self.render_string("modules/select/timezone.html",
551 name=name, id=id, preselect=preselect,
552 supported_timezones=pytz.common_timezones)