]> git.ipfire.org Git - pbs.git/blob - web/ui_modules.py
Fix uninitialized variable and change "running" colour.
[pbs.git] / web / ui_modules.py
1 #!/usr/bin/python
2
3 from __future__ import division
4
5 import datetime
6 import math
7 import pytz
8 import re
9 import string
10 import textile
11 import tornado.escape
12 import tornado.web
13
14 import backend.users
15 from backend.constants import *
16
17 class UIModule(tornado.web.UIModule):
18 @property
19 def pakfire(self):
20 return self.handler.application.pakfire
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 render(self, text, pre=False, remove_linebreaks=True):
63 link = """<a href="%s" target="_blank">%s</a>"""
64
65 if remove_linebreaks:
66 text = text.replace("\n", " ")
67
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"')
72
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)
76
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)
80
81 if pre:
82 return "<pre>%s</pre>" % text
83
84 return textile.textile(text)
85
86
87 class ModalModule(UIModule):
88 def render(self, what, **kwargs):
89 what = "modules/modal-%s.html" % what
90
91 return self.render_string(what, **kwargs)
92
93
94 class 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
103 class JobsStatusModule(UIModule):
104 def render(self, build):
105 return self.render_string("modules/jobs/status.html",
106 build=build, jobs=build.jobs)
107
108
109 class 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
116 class BugsTableModule(UIModule):
117 def render(self, pkg, bugs):
118 return self.render_string("modules/bugs-table.html",
119 pkg=pkg, bugs=bugs)
120
121
122 class 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
130 class ChangelogEntryModule(UIModule):
131 def render(self, build):
132 return self.render_string("modules/changelog/entry.html", build=build)
133
134
135 class 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
142 class FooterModule(UIModule):
143 def render(self):
144 return self.render_string("modules/footer.html")
145
146
147 class 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
167 class PackagesTableModule(UIModule):
168 def render(self, job, packages):
169 return self.render_string("modules/packages-table.html", job=job,
170 packages=packages)
171
172
173 class 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
207 class PackageTable2Module(UIModule):
208 def render(self, packages):
209 return self.render_string("modules/package-table-detail.html",
210 packages=packages)
211
212
213 class FilesTableModule(UIModule):
214 def render(self, files):
215 return self.render_string("modules/files-table.html", files=files)
216
217
218 class 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
224 class PackageHeaderModule(UIModule):
225 def render(self, pkg):
226 return self.render_string("modules/package-header.html", pkg=pkg)
227
228
229 class PackageFilesTableModule(UIModule):
230 def render(self, pkg, filelist):
231 return self.render_string("modules/packages-files-table.html",
232 pkg=pkg, filelist=filelist)
233
234
235 class BuildTableModule(UIModule):
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
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)
257
258
259 class BuildStateWarningsModule(UIModule):
260 def render(self, build):
261 return self.render_string("modules/build-state-warnings.html", build=build)
262
263
264 class 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
273 class JobStateModule(UIModule):
274 def render(self, job, cls=None, show_arch=False, show_icon=False, plain=False):
275 state = job.state
276
277 _ = self.locale.translate
278 classes = []
279
280 icon = None
281 if state == "aborted":
282 text = _("Aborted")
283 classes.append("muted")
284 icon = "icon-warning-sign"
285
286 elif state == "dependency_error":
287 text = _("Dependency problem")
288 classes.append("text-warning")
289 icon = "icon-random"
290
291 elif state == "dispatching":
292 text = _("Dispatching")
293 classes.append("text-info")
294 icon = "icon-download-alt"
295
296 elif state == "failed":
297 text = _("Failed")
298 classes.append("text-error")
299 icon = "icon-remove"
300
301 elif state == "finished":
302 text = _("Finished")
303 classes.append("text-success")
304 icon = "icon-ok"
305
306 elif state == "new":
307 text = _("New")
308 classes.append("muted")
309 icon = "icon-asterisk"
310
311 elif state == "pending":
312 text = _("Pending")
313 classes.append("muted")
314 icon = "icon-time"
315
316 elif state == "running":
317 text = _("Running")
318 classes.append("text-info")
319 icon = "icon-cogs"
320
321 elif state == "uploading":
322 text = _("Uploading")
323 classes.append("text-info")
324 icon = "icon-upload-alt"
325
326 # Return just the string, is state is unknown.
327 else:
328 text = _("Unknown: %s") % state
329 classes.append("muted")
330
331 if plain:
332 return text
333
334 if cls:
335 classes.append(cls)
336
337 if show_arch:
338 text = job.arch.name
339
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)
344
345
346 class 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
355 class JobsListModule(UIModule):
356 def render(self, jobs):
357 return self.render_string("modules/jobs/list.html", jobs=jobs)
358
359
360 class RepositoryTableModule(UIModule):
361 def render(self, distro, repos):
362 return self.render_string("modules/repository-table.html",
363 distro=distro, repos=repos)
364
365
366 class SourceTableModule(UIModule):
367 def render(self, distro, sources):
368 return self.render_string("modules/source-table.html",
369 distro=distro, sources=sources)
370
371
372 class 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
398 class LogModule(UIModule):
399 def render(self, entries, **args):
400 return self.render_string("modules/log.html",
401 entries=entries, args=args)
402
403
404 class LogEntryModule(UIModule):
405 def render(self, entry, small=None, **args):
406 if small or not entry.user:
407 template = "modules/log-entry-small.html"
408 else:
409 template = "modules/log-entry.html"
410
411 return self.render_string(template, entry=entry, u=entry.user,
412 show_build=False, **args)
413
414
415 class LogEntryCommentModule(LogEntryModule):
416 def render(self, entry, show_build=False, **args):
417 return self.render_string("modules/log-entry-comment.html",
418 entry=entry, u=entry.user, show_build=show_build, **args)
419
420
421 class 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
432 class 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
447 class 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
465 class UsersTableModule(UIModule):
466 def render(self, users):
467 return self.render_string("modules/user-table.html", users=users)
468
469
470 class BuildOffsetModule(UIModule):
471 def render(self):
472 return self.render_string("modules/build-offset.html")
473
474
475 class 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)
481
482
483 class UpdatesTableModule(UIModule):
484 def render(self, updates):
485 return self.render_string("modules/updates-table.html", updates=updates)
486
487
488 class 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)
498
499
500 class SelectLocaleModule(UIModule):
501 LOCALE_NAMES = [
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"),
514 ("nl_NL", u"Dutch", u"Nederlands"),
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"),
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])
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
529 class 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)