# -*- coding: utf-8 -*-
# This file is part of Shuup.
#
# Copyright (c) 2012-2021, Shuup Commerce Inc. All rights reserved.
#
# This source code is licensed under the OSL-3.0 license found in the
# LICENSE file in the root directory of this source tree.
"""
Common helpers for Shuup templates.
.. note::
In addition to these, also the price rendering tags from
`shuup.core.templatetags.prices` module are available.
"""
from __future__ import unicode_literals
import bleach
from babel.dates import format_date, format_datetime, format_time
from babel.numbers import format_decimal
from datetime import date
from django.conf import settings
from django.template.defaultfilters import linebreaks
from django.utils import translation
from django.utils.safestring import mark_safe
from django.utils.timezone import localtime
from django_jinja import library
from jinja2 import Undefined
from jinja2.utils import contextfunction
from json import dumps as json_dump
from shuup.utils.i18n import format_money, format_percent, get_current_babel_locale
from shuup.utils.serialization import ExtendedJSONEncoder
@library.global_function
[docs]def get_language_choices():
"""
Get language choices as code and text in two languages.
:return:
Available language codes as tuples (`code`, `name`, `local_name`)
where `name` is in the currently active language, and `local_name`
is in the language of the item.
:rtype: Iterable[tuple[str,str,str]]
"""
for (code, name) in settings.LANGUAGES:
lang_info = translation.get_language_info(code)
name_in_current_lang = translation.ugettext(name)
local_name = lang_info["name_local"]
yield (code, name_in_current_lang, local_name)
@library.filter
[docs]def money(amount, digits=None, widen=0):
"""
Format money amount according to the current locale settings.
:param amount: Money or Price object to format.
:type amount: shuup.utils.money.Money
:param digits: Number of digits to use, by default use locale's default.
:type digits: int|None
:param widen:
Number of extra digits to add; for formatting with additional
precision, e.g. ``widen=3`` will use 5 digits instead of the default 2.
:type widen: int
:return: Formatted string representing the given amount
:rtype: str
"""
return format_money(amount, digits, widen)
@library.filter
[docs]def percent(value, ndigits=0):
return format_percent(value, ndigits)
@library.filter
[docs]def number(value):
return format_decimal(value, locale=get_current_babel_locale())
@library.filter
[docs]def datetime(value, kind="datetime", format="medium", tz=True):
"""
Format a `datetime` for human consumption.
The currently active locale's formatting rules are used. The output
of this function is probably not machine-parseable.
:param value: datetime object to format
:type value: datetime.datetime
:param kind: Format as 'datetime', 'date' or 'time'.
:type kind: str
:param format:
Format specifier or one of 'full', 'long', 'medium' or 'short'.
:type format: str
:param tz:
Convert to current or given timezone. Accepted values are:
True (default)
convert to the currently active timezone (as reported by
:func:`django.utils.timezone.get_current_timezone`)
False (or other false value like empty string)
do no convert to any timezone (use UTC)
Other values (as str)
convert to a given timezone (e.g. ``"US/Hawaii"``)
:type tz: bool|str
"""
locale = get_current_babel_locale()
if type(value) is date: # Not using isinstance, since `datetime`s are `date` too.
# We can't do any TZ manipulation for dates, so just always use `format_date`
return format_date(value, format=format, locale=locale)
if tz:
value = localtime(value, (None if tz is True else tz))
if kind == "datetime":
return format_datetime(value, format=format, locale=locale)
elif kind == "date":
return format_date(value, format=format, locale=locale)
elif kind == "time":
return format_time(value, format=format, locale=locale)
else:
raise ValueError("Error! Unknown `datetime` kind: %r." % kind)
@library.filter(name="json")
[docs]def json(value):
if isinstance(value, Undefined):
value = None
return mark_safe(json_dump(value, cls=ExtendedJSONEncoder))
@library.filter
[docs]def safe_product_description(value):
if isinstance(value, Undefined):
return value
if not settings.SHUUP_ADMIN_ALLOW_HTML_IN_PRODUCT_DESCRIPTION:
value = linebreaks(bleach.clean(value, tags=[]))
return mark_safe(value)
@library.filter
[docs]def safe_vendor_description(value):
if isinstance(value, Undefined):
return value
if not settings.SHUUP_ADMIN_ALLOW_HTML_IN_VENDOR_DESCRIPTION:
value = linebreaks(bleach.clean(value, tags=[]))
return mark_safe(value)
@library.global_function
@contextfunction
[docs]def get_shop_configuration(context, name, default=None):
"""
Get configuration variable value for the current shop.
:type context: jinja2.runtime.Context
:type name: str
:type default: Any
"""
from shuup import configuration
return configuration.get(context.get("request").shop, name, default)
@library.global_function
[docs]def get_global_configuration(name, default=None):
"""
Get global configuration variable value.
:type name: str
:type default: Any
"""
from shuup import configuration
return configuration.get(None, name, default)
@library.global_function
[docs]def get_shuup_version():
from shuup import __version__
return __version__
@library.global_function
[docs]def shuup_static(path, package=None):
"""
`path` is the static source path, e.g. myapp/styles.css
`package` is the package name to get the version from.
If not set, Shuup version is used. You can pass
the name if any installed pacakge and use that
version as a base.
"""
from shuup.core.utils.static import get_shuup_static_url
return get_shuup_static_url(path, package)