]> git.ipfire.org Git - people/jschlag/pbs.git/blame - web/ui_modules.py
Move builders templates to own directory.
[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):
d3a9ee4e 95 def render(self, build, short=False, shorter=False):
f6e6ff79
MT
96 if shorter:
97 short = True
98
99 return self.render_string("modules/build-headline.html",
d3a9ee4e 100 build=build, pkg=build.pkg, short=short, shorter=shorter)
f6e6ff79
MT
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
c62cd4db 280 icon = None
3c7e0537
MT
281 if state == "aborted":
282 text = _("Aborted")
283 classes.append("muted")
20e70907 284 icon = "icon-warning-sign"
3c7e0537
MT
285
286 elif state == "dependency_error":
287 text = _("Dependency problem")
288 classes.append("text-warning")
20e70907 289 icon = "icon-random"
3c7e0537
MT
290
291 elif state == "dispatching":
292 text = _("Dispatching")
293 classes.append("text-info")
20e70907 294 icon = "icon-download-alt"
3c7e0537
MT
295
296 elif state == "failed":
297 text = _("Failed")
298 classes.append("text-error")
20e70907 299 icon = "icon-remove"
3c7e0537
MT
300
301 elif state == "finished":
302 text = _("Finished")
303 classes.append("text-success")
20e70907 304 icon = "icon-ok"
3c7e0537
MT
305
306 elif state == "new":
307 text = _("New")
308 classes.append("muted")
20e70907 309 icon = "icon-asterisk"
3c7e0537
MT
310
311 elif state == "pending":
312 text = _("Pending")
313 classes.append("muted")
20e70907 314 icon = "icon-time"
3c7e0537
MT
315
316 elif state == "running":
317 text = _("Running")
c62cd4db 318 classes.append("text-info")
20e70907
MT
319 icon = "icon-cogs"
320
321 elif state == "uploading":
322 text = _("Uploading")
3c7e0537 323 classes.append("text-info")
20e70907 324 icon = "icon-upload-alt"
3c7e0537
MT
325
326 # Return just the string, is state is unknown.
327 else:
328 text = _("Unknown: %s") % state
329 classes.append("muted")
330
eedc6432
MT
331 if plain:
332 return text
333
3c7e0537
MT
334 if cls:
335 classes.append(cls)
336
eedc6432
MT
337 if show_arch:
338 text = job.arch.name
339
20e70907
MT
340 if show_icon and icon:
341 text = """<i class="%s"></i> %s""" % (icon, text)
342
343 return """<span class="%s">%s</span>""" % (" ".join(classes), text)
3c7e0537
MT
344
345
f6e6ff79
MT
346class JobsTableModule(UIModule):
347 def render(self, build, jobs=None, type="release"):
348 if jobs is None:
349 jobs = build.jobs
350
351 return self.render_string("modules/jobs-table.html", build=build,
352 jobs=jobs, type=type)
353
354
355class JobsListModule(UIModule):
6e63ed49
MT
356 def render(self, jobs):
357 return self.render_string("modules/jobs/list.html", jobs=jobs)
9137135a
MT
358
359
360class RepositoryTableModule(UIModule):
361 def render(self, distro, repos):
362 return self.render_string("modules/repository-table.html",
363 distro=distro, repos=repos)
364
365
366class SourceTableModule(UIModule):
367 def render(self, distro, sources):
368 return self.render_string("modules/source-table.html",
369 distro=distro, sources=sources)
370
371
372class CommentsTableModule(UIModule):
373 def render(self, comments, show_package=False, show_user=True):
374 pkgs, users = {}, {}
375 for comment in comments:
376 if show_package:
377 try:
378 pkg = pkgs[comment.pkg_id]
379 except KeyError:
380 pkg = pkgs[comment.pkg_id] = \
381 self.pakfire.packages.get_by_id(comment.pkg_id)
382
383 comment["pkg"] = pkg
384
385 if show_user:
386 try:
387 user = users[comment.user_id]
388 except KeyError:
389 user = users[comment.user_id] = \
390 self.pakfire.users.get_by_id(comment.user_id)
391
392 comment["user"] = user
393
394 return self.render_string("modules/comments-table.html",
395 comments=comments, show_package=show_package, show_user=show_user)
396
397
f6e6ff79
MT
398class LogModule(UIModule):
399 def render(self, entries, **args):
400 return self.render_string("modules/log.html",
401 entries=entries, args=args)
402
403
404class LogEntryModule(UIModule):
e434b018 405 def render(self, entry, small=None, **args):
7d0635e2 406 if small or not entry.user:
e434b018
MT
407 template = "modules/log-entry-small.html"
408 else:
409 template = "modules/log-entry.html"
410
62c7e7cd
MT
411 return self.render_string(template, entry=entry, u=entry.user,
412 show_build=False, **args)
f6e6ff79
MT
413
414
415class LogEntryCommentModule(LogEntryModule):
62c7e7cd 416 def render(self, entry, show_build=False, **args):
f6e6ff79 417 return self.render_string("modules/log-entry-comment.html",
62c7e7cd 418 entry=entry, u=entry.user, show_build=show_build, **args)
f6e6ff79
MT
419
420
421class MaintainerModule(UIModule):
422 def render(self, maintainer):
423 if isinstance(maintainer, backend.users.User):
424 type = "user"
425 else:
426 type = "string"
427
428 return self.render_string("modules/maintainer.html",
429 type=type, maintainer=maintainer)
430
431
9137135a
MT
432class BuildLogModule(UIModule):
433 # XXX deprecated
434 def render(self, messages):
435 _ = self.locale.translate
436
437 for message in messages:
438 try:
439 msg = LOG2MSG[message.message]
440 message["message"] = _(msg)
441 except KeyError:
442 pass
443
444 return self.render_string("modules/build-log.html", messages=messages)
445
446
447class LogTableModule(UIModule):
448 def render(self, messages, links=["pkg",]):
449 for message in messages:
450 try:
451 message["message"] = LOG2MSG[message.message]
452 except KeyError:
453 pass
454
455 if message.build_id:
456 message["build"] = self.pakfire.builds.get_by_id(message.build_id)
457
458 elif message.pkg_id:
459 message["pkg"] = self.pakfire.packages.get_by_id(message.pkg_id)
460
461 return self.render_string("modules/log-table.html",
462 messages=messages, links=links)
463
464
465class UsersTableModule(UIModule):
466 def render(self, users):
467 return self.render_string("modules/user-table.html", users=users)
468
469
470class BuildOffsetModule(UIModule):
471 def render(self):
472 return self.render_string("modules/build-offset.html")
473
474
475class RepoActionsTableModule(UIModule):
476 def render(self, repo):
477 actions = repo.get_actions()
478
479 return self.render_string("modules/repo-actions-table.html",
480 repo=repo, actions=actions)
f6e6ff79
MT
481
482
483class UpdatesTableModule(UIModule):
484 def render(self, updates):
485 return self.render_string("modules/updates-table.html", updates=updates)
486
487
488class WatchersSidebarTableModule(UIModule):
489 def css_files(self):
490 return "css/watchers-sidebar-table.css"
491
492 def render(self, build, watchers, limit=5):
493 # Sort the watchers by their realname.
494 watchers.sort(key=lambda watcher: watcher.realname)
495
496 return self.render_string("modules/watchers-sidebar-table.html",
497 build=build, watchers=watchers, limit=limit)
cd870d0a
MT
498
499
500class SelectLocaleModule(UIModule):
b1eb6312 501 LOCALE_NAMES = [
cd870d0a
MT
502 # local code, English name, name
503 ("ca_ES", u"Catalan", "Catal\xc3\xa0"),
504 ("da_DK", u"Danish", u"Dansk"),
505 ("de_DE", u"German", u"Deutsch"),
506 ("en_GB", u"English (UK)", u"English (UK)"),
507 ("en_US", u"English (US)", u"English (US)"),
508 ("es_ES", u"Spanish (Spain)", u"Espa\xf1ol (Espa\xf1a)"),
509 ("es_LA", u"Spanish", u"Espa\xf1ol"),
510 ("fr_CA", u"French (Canada)", u"Fran\xe7ais (Canada)"),
511 ("fr_FR", u"French", u"Fran\xe7ais"),
512 ("it_IT", u"Italian", u"Italiano"),
513 ("km_KH", u"Khmer", u"\u1797\u17b6\u179f\u17b6\u1781\u17d2\u1798\u17c2\u179a"),
d3bda196 514 ("nl_NL", u"Dutch", u"Nederlands"),
cd870d0a
MT
515 ("pt_BR", u"Portuguese (Brazil)", u"Portugu\xeas (Brasil)"),
516 ("pt_PT", u"Portuguese (Portugal)", u"Portugu\xeas (Portugal)"),
517 ("ru_RU", u"Russian", u"\u0440\u0443\u0441\u0441\u043a\u0438\u0439"),
b1eb6312
MT
518 ("uk_UA", u"Ukrainian", u"\u0423\u043a\u0440\u0430\u0457\u043d\u0441\u044c\u043a\u0430"),
519 ]
520
521 # Sort the list of locales by their English name.
522 LOCALE_NAMES.sort(key=lambda x: x[1])
cd870d0a
MT
523
524 def render(self, name=None, id=None, preselect=None):
525 return self.render_string("modules/select/locale.html",
526 name=name, id=id, preselect=preselect, supported_locales=self.LOCALE_NAMES)
527
528
529class SelectTimezoneModule(UIModule):
530 def render(self, name=None, id=None, preselect=None):
531 return self.render_string("modules/select/timezone.html",
532 name=name, id=id, preselect=preselect,
533 supported_timezones=pytz.common_timezones)