]> git.ipfire.org Git - people/jschlag/pbs.git/blame - src/web/ui_modules.py
Move main.py to __init__.py
[people/jschlag/pbs.git] / src / web / ui_modules.py
CommitLineData
f6e6ff79 1#!/usr/bin/python
9137135a 2
5669a87f
MT
3from __future__ import division
4
eedc6432 5import datetime
b9d096e0 6import itertools
5669a87f 7import math
cd870d0a 8import pytz
f6e6ff79
MT
9import re
10import string
11import tornado.escape
9137135a
MT
12import tornado.web
13
f6e6ff79 14import backend.users
9137135a
MT
15from backend.constants import *
16
17class UIModule(tornado.web.UIModule):
18 @property
19 def pakfire(self):
20 return self.handler.application.pakfire
21
f6e6ff79
MT
22 @property
23 def settings(self):
24 return self.pakfire.settings
25
26
27class TextModule(UIModule):
c764ce43 28 __cache = {}
f6e6ff79 29
c764ce43 30 LINK = """<a href="%s" target="_blank">%s</a>"""
f6e6ff79 31
c764ce43
MT
32 @property
33 def bugzilla_url(self):
34 return self.settings.get("bugzilla_url", "")
f6e6ff79 35
c764ce43
MT
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")
c21cb4ec 61
b9d096e0
MT
62 def split_paragraphs(self, s):
63 for group_seperator, line_iteration in itertools.groupby(s.splitlines(True), key=unicode.isspace):
64 if group_seperator:
65 continue
66
67 paragraph = "".join(line_iteration)
68 yield paragraph.replace("\n", " ")
69
c764ce43
MT
70 def render(self, text, pre=False, remove_linebreaks=True):
71 link = """<a href="%s" target="_blank">%s</a>"""
f6e6ff79 72
c764ce43
MT
73 if remove_linebreaks:
74 text = text.replace("\n", " ")
f6e6ff79 75
c764ce43
MT
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"')
f6e6ff79 80
c764ce43
MT
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)
f6e6ff79 84
c764ce43
MT
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)
f6e6ff79
MT
88
89 if pre:
c764ce43 90 return "<pre>%s</pre>" % text
f6e6ff79 91
9d0c3e75 92 return text
f6e6ff79
MT
93
94
b9d096e0
MT
95class 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
f6e6ff79
MT
105class ModalModule(UIModule):
106 def render(self, what, **kwargs):
107 what = "modules/modal-%s.html" % what
9137135a 108
f6e6ff79
MT
109 return self.render_string(what, **kwargs)
110
111
112class BuildHeadlineModule(UIModule):
d3a9ee4e 113 def render(self, build, short=False, shorter=False):
f6e6ff79
MT
114 if shorter:
115 short = True
116
117 return self.render_string("modules/build-headline.html",
d3a9ee4e 118 build=build, pkg=build.pkg, short=short, shorter=shorter)
f6e6ff79
MT
119
120
eedc6432
MT
121class JobsStatusModule(UIModule):
122 def render(self, build):
123 return self.render_string("modules/jobs/status.html",
124 build=build, jobs=build.jobs)
125
126
f96eb5ed
MT
127class 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
f6e6ff79
MT
134class BugsTableModule(UIModule):
135 def render(self, pkg, bugs):
136 return self.render_string("modules/bugs-table.html",
137 pkg=pkg, bugs=bugs)
138
139
4b1e87c4
MT
140class 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
148class ChangelogEntryModule(UIModule):
149 def render(self, build):
150 return self.render_string("modules/changelog/entry.html", build=build)
151
152
f6e6ff79
MT
153class 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
160class FooterModule(UIModule):
161 def render(self):
162 return self.render_string("modules/footer.html")
163
164
eedc6432
MT
165class 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
f6e6ff79
MT
185class PackagesTableModule(UIModule):
186 def render(self, job, packages):
187 return self.render_string("modules/packages-table.html", job=job,
188 packages=packages)
9137135a
MT
189
190
5669a87f
MT
191class 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
9137135a
MT
225class PackageTable2Module(UIModule):
226 def render(self, packages):
227 return self.render_string("modules/package-table-detail.html",
228 packages=packages)
229
230
231class FilesTableModule(UIModule):
232 def render(self, files):
233 return self.render_string("modules/files-table.html", files=files)
234
235
f6e6ff79
MT
236class 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
242class PackageHeaderModule(UIModule):
243 def render(self, pkg):
244 return self.render_string("modules/package-header.html", pkg=pkg)
245
246
9137135a 247class PackageFilesTableModule(UIModule):
f6e6ff79
MT
248 def render(self, pkg, filelist):
249 return self.render_string("modules/packages-files-table.html",
250 pkg=pkg, filelist=filelist)
9137135a
MT
251
252
253class BuildTableModule(UIModule):
f6e6ff79
MT
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
eedc6432
MT
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)
f6e6ff79
MT
275
276
277class BuildStateWarningsModule(UIModule):
278 def render(self, build):
279 return self.render_string("modules/build-state-warnings.html", build=build)
280
281
3c7e0537
MT
282class 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
291class JobStateModule(UIModule):
eedc6432 292 def render(self, job, cls=None, show_arch=False, show_icon=False, plain=False):
3c7e0537
MT
293 state = job.state
294
295 _ = self.locale.translate
296 classes = []
297
c62cd4db 298 icon = None
3c7e0537
MT
299 if state == "aborted":
300 text = _("Aborted")
301 classes.append("muted")
20e70907 302 icon = "icon-warning-sign"
3c7e0537
MT
303
304 elif state == "dependency_error":
305 text = _("Dependency problem")
306 classes.append("text-warning")
20e70907 307 icon = "icon-random"
3c7e0537
MT
308
309 elif state == "dispatching":
310 text = _("Dispatching")
311 classes.append("text-info")
20e70907 312 icon = "icon-download-alt"
3c7e0537
MT
313
314 elif state == "failed":
315 text = _("Failed")
316 classes.append("text-error")
20e70907 317 icon = "icon-remove"
3c7e0537
MT
318
319 elif state == "finished":
320 text = _("Finished")
321 classes.append("text-success")
20e70907 322 icon = "icon-ok"
3c7e0537
MT
323
324 elif state == "new":
325 text = _("New")
326 classes.append("muted")
20e70907 327 icon = "icon-asterisk"
3c7e0537
MT
328
329 elif state == "pending":
330 text = _("Pending")
331 classes.append("muted")
20e70907 332 icon = "icon-time"
3c7e0537
MT
333
334 elif state == "running":
335 text = _("Running")
c62cd4db 336 classes.append("text-info")
20e70907
MT
337 icon = "icon-cogs"
338
339 elif state == "uploading":
340 text = _("Uploading")
3c7e0537 341 classes.append("text-info")
20e70907 342 icon = "icon-upload-alt"
3c7e0537
MT
343
344 # Return just the string, is state is unknown.
345 else:
346 text = _("Unknown: %s") % state
347 classes.append("muted")
348
eedc6432
MT
349 if plain:
350 return text
351
3c7e0537
MT
352 if cls:
353 classes.append(cls)
354
eedc6432
MT
355 if show_arch:
356 text = job.arch.name
357
20e70907
MT
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)
3c7e0537
MT
362
363
f6e6ff79
MT
364class 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
373class JobsListModule(UIModule):
6e63ed49
MT
374 def render(self, jobs):
375 return self.render_string("modules/jobs/list.html", jobs=jobs)
9137135a
MT
376
377
378class RepositoryTableModule(UIModule):
379 def render(self, distro, repos):
380 return self.render_string("modules/repository-table.html",
381 distro=distro, repos=repos)
382
383
384class SourceTableModule(UIModule):
385 def render(self, distro, sources):
386 return self.render_string("modules/source-table.html",
387 distro=distro, sources=sources)
388
389
390class 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
f6e6ff79
MT
416class LogModule(UIModule):
417 def render(self, entries, **args):
418 return self.render_string("modules/log.html",
419 entries=entries, args=args)
420
421
422class LogEntryModule(UIModule):
e434b018 423 def render(self, entry, small=None, **args):
7d0635e2 424 if small or not entry.user:
e434b018
MT
425 template = "modules/log-entry-small.html"
426 else:
427 template = "modules/log-entry.html"
428
62c7e7cd
MT
429 return self.render_string(template, entry=entry, u=entry.user,
430 show_build=False, **args)
f6e6ff79
MT
431
432
433class LogEntryCommentModule(LogEntryModule):
62c7e7cd 434 def render(self, entry, show_build=False, **args):
f6e6ff79 435 return self.render_string("modules/log-entry-comment.html",
62c7e7cd 436 entry=entry, u=entry.user, show_build=show_build, **args)
f6e6ff79
MT
437
438
439class MaintainerModule(UIModule):
440 def render(self, maintainer):
441 if isinstance(maintainer, backend.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
9137135a
MT
450class 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
465class 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
483class UsersTableModule(UIModule):
484 def render(self, users):
485 return self.render_string("modules/user-table.html", users=users)
486
487
488class BuildOffsetModule(UIModule):
489 def render(self):
490 return self.render_string("modules/build-offset.html")
491
492
493class 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)
f6e6ff79
MT
499
500
501class UpdatesTableModule(UIModule):
502 def render(self, updates):
503 return self.render_string("modules/updates-table.html", updates=updates)
504
505
506class 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)
cd870d0a
MT
516
517
518class SelectLocaleModule(UIModule):
b1eb6312 519 LOCALE_NAMES = [
cd870d0a
MT
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"),
d3bda196 532 ("nl_NL", u"Dutch", u"Nederlands"),
cd870d0a
MT
533 ("pt_BR", u"Portuguese (Brazil)", u"Portugu\xeas (Brasil)"),
534 ("pt_PT", u"Portuguese (Portugal)", u"Portugu\xeas (Portugal)"),
b5321ec9 535 ("ro_RO", u"Romanian", u"Rom\xe2n\u0103"),
cd870d0a 536 ("ru_RU", u"Russian", u"\u0440\u0443\u0441\u0441\u043a\u0438\u0439"),
b1eb6312
MT
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])
cd870d0a
MT
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
548class 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)