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