Mercurial > libervia-backend
comparison sat/tools/common/template.py @ 2599:5b26033c49a8
tools (common): moved date_fmt function from template filters to new date_utils module, so it can be used everywhere.
author | Goffi <goffi@goffi.org> |
---|---|
date | Fri, 01 Jun 2018 12:04:06 +0200 |
parents | 26edcf3a30eb |
children | 56f94936df1e |
comparison
equal
deleted
inserted
replaced
2598:0b6adc2672d9 | 2599:5b26033c49a8 |
---|---|
20 """ template generation """ | 20 """ template generation """ |
21 | 21 |
22 from sat.core.constants import Const as C | 22 from sat.core.constants import Const as C |
23 from sat.core.i18n import _ | 23 from sat.core.i18n import _ |
24 from sat.core import exceptions | 24 from sat.core import exceptions |
25 from sat.tools.common import date_utils | |
25 from sat.core.log import getLogger | 26 from sat.core.log import getLogger |
26 log = getLogger(__name__) | 27 log = getLogger(__name__) |
27 import os.path | 28 import os.path |
28 from xml.sax.saxutils import quoteattr | 29 from xml.sax.saxutils import quoteattr |
29 import datetime | |
30 import time | 30 import time |
31 import re | 31 import re |
32 from babel import support | 32 from babel import support |
33 from babel import Locale | 33 from babel import Locale |
34 from babel.core import UnknownLocaleError | 34 from babel.core import UnknownLocaleError |
35 from babel import dates | |
36 import pygments | 35 import pygments |
37 from pygments import lexers | 36 from pygments import lexers |
38 from pygments import formatters | 37 from pygments import formatters |
39 try: | 38 try: |
40 import sat_templates | 39 import sat_templates |
51 from jinja2 import Markup as safe | 50 from jinja2 import Markup as safe |
52 from jinja2 import is_undefined | 51 from jinja2 import is_undefined |
53 from lxml import etree | 52 from lxml import etree |
54 | 53 |
55 HTML_EXT = ('html', 'xhtml') | 54 HTML_EXT = ('html', 'xhtml') |
56 DEFAULT_LOCALE = u'en_GB' | |
57 RE_ATTR_ESCAPE = re.compile(r'[^a-z_-]') | 55 RE_ATTR_ESCAPE = re.compile(r'[^a-z_-]') |
58 # TODO: handle external path (an additional search path for templates should be settable by user | 56 # TODO: handle external path (an additional search path for templates should be settable by user |
59 # TODO: handle absolute URL (should be used for trusted use cases) only (e.g. jp) for security reason | 57 # TODO: handle absolute URL (should be used for trusted use cases) only (e.g. jp) for security reason |
60 | 58 |
61 | 59 |
203 autoescape=jinja2.select_autoescape(['html', 'xhtml', 'xml']), | 201 autoescape=jinja2.select_autoescape(['html', 'xhtml', 'xml']), |
204 trim_blocks=True, | 202 trim_blocks=True, |
205 lstrip_blocks=True, | 203 lstrip_blocks=True, |
206 extensions=['jinja2.ext.i18n'], | 204 extensions=['jinja2.ext.i18n'], |
207 ) | 205 ) |
208 self._locale_str = DEFAULT_LOCALE | 206 self._locale_str = C.DEFAULT_LOCALE |
209 self._locale = Locale.parse(self._locale_str) | 207 self._locale = Locale.parse(self._locale_str) |
210 self.installTranslations() | 208 self.installTranslations() |
211 # we want to have access to SàT constants in templates | 209 # we want to have access to SàT constants in templates |
212 self.env.globals[u'C'] = C | 210 self.env.globals[u'C'] = C |
213 # custom filters | 211 # custom filters |
256 locale_str = 'en_GB' | 254 locale_str = 'en_GB' |
257 try: | 255 try: |
258 locale = Locale.parse(locale_str) | 256 locale = Locale.parse(locale_str) |
259 except ValueError as e: | 257 except ValueError as e: |
260 log.warning(_(u"invalid locale value: {msg}").format(msg=e)) | 258 log.warning(_(u"invalid locale value: {msg}").format(msg=e)) |
261 locale_str = self._locale_str = DEFAULT_LOCALE | 259 locale_str = self._locale_str = C.DEFAULT_LOCALE |
262 locale = Locale.parse(locale_str) | 260 locale = Locale.parse(locale_str) |
263 | 261 |
264 locale_str = unicode(locale) | 262 locale_str = unicode(locale) |
265 if locale_str != DEFAULT_LOCALE: | 263 if locale_str != C.DEFAULT_LOCALE: |
266 try: | 264 try: |
267 translations = self.translations[locale] | 265 translations = self.translations[locale] |
268 except KeyError: | 266 except KeyError: |
269 log.warning(_(u"Can't find locale {locale}".format(locale=locale))) | 267 log.warning(_(u"Can't find locale {locale}".format(locale=locale))) |
270 locale_str = DEFAULT_LOCALE | 268 locale_str = C.DEFAULT_LOCALE |
271 locale = Locale.parse(self._locale_str) | 269 locale = Locale.parse(self._locale_str) |
272 else: | 270 else: |
273 self.env.install_gettext_translations(translations, True) | 271 self.env.install_gettext_translations(translations, True) |
274 log.debug(_(u'Switched to {lang}').format(lang=locale.english_name)) | 272 log.debug(_(u'Switched to {lang}').format(lang=locale.english_name)) |
275 | 273 |
276 if locale_str == DEFAULT_LOCALE: | 274 if locale_str == C.DEFAULT_LOCALE: |
277 self.env.install_null_translations(True) | 275 self.env.install_null_translations(True) |
278 | 276 |
279 self._locale = locale | 277 self._locale = locale |
280 self._locale_str = locale_str | 278 self._locale_str = locale_str |
281 | 279 |
364 """Use current current global index as suffix""" | 362 """Use current current global index as suffix""" |
365 current = ctx['gidx'].current(value) | 363 current = ctx['gidx'].current(value) |
366 return value if not current else u"{}_{}".format(value, current) | 364 return value if not current else u"{}_{}".format(value, current) |
367 | 365 |
368 def _date_fmt(self, timestamp, fmt='short', date_only=False, auto_limit=None, auto_old_fmt=None): | 366 def _date_fmt(self, timestamp, fmt='short', date_only=False, auto_limit=None, auto_old_fmt=None): |
367 if is_undefined(fmt): | |
368 fmt = u'short' | |
369 | |
369 try: | 370 try: |
370 return self.date_fmt(timestamp, fmt, date_only, auto_limit, auto_old_fmt) | 371 return date_utils.date_fmt(timestamp, fmt, date_only, auto_limit, auto_old_fmt) |
371 except Exception as e: | 372 except Exception as e: |
372 log.warning(_(u"Can't parse date: {msg}").format(msg=e)) | 373 log.warning(_(u"Can't parse date: {msg}").format(msg=e)) |
373 return timestamp | 374 return timestamp |
374 | |
375 def date_fmt(self, timestamp, fmt='short', date_only=False, auto_limit=7, auto_old_fmt='short', auto_new_fmt='relative'): | |
376 """format date according to locale | |
377 | |
378 @param timestamp(basestring, int): unix time | |
379 @param fmt(str): one of: | |
380 - short: e.g. u'31/12/17' | |
381 - medium: e.g. u'Apr 1, 2007' | |
382 - long: e.g. u'April 1, 2007' | |
383 - full: e.g. u'Sunday, April 1, 2007' | |
384 - relative: format in relative time | |
385 e.g.: 3 hours | |
386 note that this format is not precise | |
387 - iso: ISO 8601 format | |
388 e.g.: u'2007-04-01T19:53:23Z' | |
389 - auto: use auto_old_fmt if date is older than auto_limit | |
390 else use auto_new_fmt | |
391 - auto_day: shorcut to set auto format with change on day | |
392 old format will be short, and new format will be time only | |
393 or a free value which is passed to babel.dates.format_datetime | |
394 @param date_only(bool): if True, only display date (not datetime) | |
395 @param auto_limit (int): limit in days before using auto_old_fmt | |
396 use 0 to have a limit at last midnight (day change) | |
397 @param auto_old_fmt(unicode): format to use when date is older than limit | |
398 @param auto_new_fmt(unicode): format to use when date is equal to or more recent | |
399 than limit | |
400 | |
401 """ | |
402 if is_undefined(fmt): | |
403 fmt = u'short' | |
404 | |
405 if (auto_limit is not None or auto_old_fmt is not None) and fmt != 'auto': | |
406 raise ValueError(u'auto argument can only be used with auto fmt') | |
407 if fmt == 'auto_day': | |
408 fmt, auto_limit, auto_old_fmt, auto_new_fmt = 'auto', 0, 'short', 'HH:mm' | |
409 if fmt == 'auto': | |
410 if auto_limit == 0: | |
411 today = time.mktime(datetime.date.today().timetuple()) | |
412 if int(timestamp) < today: | |
413 fmt = auto_old_fmt | |
414 else: | |
415 fmt = auto_new_fmt | |
416 else: | |
417 days_delta = (time.time() - int(timestamp)) / 3600 | |
418 if days_delta > (auto_limit or 7): | |
419 fmt = auto_old_fmt | |
420 else: | |
421 fmt = auto_new_fmt | |
422 | |
423 if fmt == 'relative': | |
424 delta = int(timestamp) - time.time() | |
425 return dates.format_timedelta(delta, granularity="minute", add_direction=True, locale=self._locale_str) | |
426 elif fmt in ('short', 'long'): | |
427 formatter = dates.format_date if date_only else dates.format_datetime | |
428 return formatter(int(timestamp), format=fmt, locale=self._locale_str) | |
429 elif fmt == 'iso': | |
430 if date_only: | |
431 fmt = 'yyyy-MM-dd' | |
432 else: | |
433 fmt = "yyyy-MM-ddTHH:mm:ss'Z'" | |
434 return dates.format_datetime(int(timestamp), format=fmt) | |
435 else: | |
436 return dates.format_datetime(int(timestamp), format=fmt, locale=self._locale_str) | |
437 | 375 |
438 def attr_escape(self, text): | 376 def attr_escape(self, text): |
439 """escape a text to a value usable as an attribute | 377 """escape a text to a value usable as an attribute |
440 | 378 |
441 remove spaces, and put in lower case | 379 remove spaces, and put in lower case |
593 </svg> | 531 </svg> |
594 """.format( | 532 """.format( |
595 name=name, | 533 name=name, |
596 cls=(' ' + cls) if cls else '')) | 534 cls=(' ' + cls) if cls else '')) |
597 | 535 |
598 def render(self, template, theme=None, locale=DEFAULT_LOCALE, root_path=u'', media_path=u'', css_files=None, css_inline=False, **kwargs): | 536 def render(self, template, theme=None, locale=C.DEFAULT_LOCALE, root_path=u'', media_path=u'', css_files=None, css_inline=False, **kwargs): |
599 """render a template | 537 """render a template |
600 . | 538 . |
601 @param template(unicode): template to render (e.g. blog/articles.html) | 539 @param template(unicode): template to render (e.g. blog/articles.html) |
602 @param theme(unicode): template theme | 540 @param theme(unicode): template theme |
603 @param root_path(unicode): prefix of the path/URL to use for template root | 541 @param root_path(unicode): prefix of the path/URL to use for template root |