from django.conf import settings
from django.template import Library, TemplateSyntaxError, loader
from ..models import Album, Article, Category
from ..utils.language import get_language_code
register = Library()
[docs]
@register.simple_tag(takes_context=True)
def article_state_list(context, article, **kwargs):
"""
Return all state names for given article object.
This is a shortcut around ``Article.get_states`` to be able to use the ``lotus_now``
context variable or force another value.
Example:
At least you must give an Article object: ::
{% load lotus %}
{% article_state_list article [now=custom_now] [prefix="foo_"] %}
* Optional ``now`` argument expect a datetime to use instead of current date;
* Optional ``prefix`` argument expect a string to prepend all returned state
names.
Arguments:
context (object): Either a ``django.template.Context`` or a dictionnary for
context variable for template where the tag is included.
article (lotus.models.article.Article): Article object to compute states.
Keyword Arguments:
now (datetime.datetime): A datetime to use to compare against publish
start and end dates to check for some publication criterias. See
``Article.get_states`` docstring for more details.
prefix (string): A string to prefix every names. Empty by default.
Returns:
list: A list of every state names.
"""
now = kwargs.get("now") or context.get("lotus_now")
prefix = kwargs.get("prefix", "")
states = [
prefix + item
for item in article.get_states(now=now)
]
return states
[docs]
@register.simple_tag(takes_context=True)
def article_states(context, article, **kwargs):
"""
Return a string of state names for given article object.
Identical to ``article_state_list`` but return a string instead of list.
Example:
At least you must give an Article object: ::
{% load lotus %}
{% article_states article [now=custom_now] [prefix="foo_"] %}
* Optional ``now`` argument expect a datetime to use instead of current date;
* Optional ``prefix`` argument expect a string to prepend all returned state
names.
Arguments:
context (object): Either a ``django.template.Context`` or a dictionnary for
context variable for template where the tag is included.
article (lotus.models.article.Article): Article object to compute states.
Keyword Arguments:
now (datetime.datetime): A datetime to use to compare against publish
start and end dates to check for some publication criterias. See
``Article.get_states`` docstring for more details.
prefix (string): A string to prefix every names. Empty by default.
Returns:
string: Every state names divided by a white space.
"""
now = kwargs.get("now") or context.get("lotus_now")
prefix = kwargs.get("prefix", "")
states = [
prefix + item
for item in article.get_states(now=now)
]
return " ".join(states)
[docs]
@register.simple_tag(takes_context=True)
def translation_siblings(context, source, tag_name=None, **kwargs):
"""
A tag to get translation siblings for given source object.
This tag can work for an Article or a Category object to retrieve every translation
siblings, like all translation children for an original source or translation
children and original for a translation.
Note than for Article object the tag will require a datetime it may refer to for
filtering results with publication criterias. Either the datetime will be set as a
template context variable ``lotus_now`` (as implemented in ``ArticleFilterMixin``)
or it can be given through the tag argument ``now``.
Example:
At least you must give an Article object: ::
{% load lotus %}
{% translation_siblings_html article [now=custom_now] [preview=True] %}
* Optional ``now`` argument expect a datetime to use instead of current date;
* Optional ``preview`` argument expect a boolean to explicitely disable or
enable preview mode, on default it is ``None`` to determine it automatically.
Arguments:
context (object): Either a ``django.template.Context`` or a dictionnary for
context variable for template where the tag is included. This is only used
with an Article object, so it should be safe to be empty for a Category.
source (object): Either a ``lotus.models.Article`` or ``lotus.models.Category``
to retrieve its translation siblings.
Keyword Arguments:
now (datetime.datetime): A datetime to use to compare against publish
start and end dates to check for some publication criterias. Only used
for Article object.
preview (boolean): Option to bypass checking preview mode from context and
force it to a value, either True to enable it, False to disable it or None
to let the basic behavior to determine it from its template context
variable. Default to None.
tag_name (string): Template tag name to display in error messages. This is
something used in template tags which inherit from ``translation_siblings``.
You don't have to care about it in your common template tag usage.
Returns:
dict: A dictionnary with item ``source`` for the given source object and item
``siblings`` for retrieved translation sibling objects.
"""
model = type(source)
tag_name = tag_name or "translation_siblings"
preview = context.get(settings.LOTUS_PREVIEW_VARNAME, False)
if kwargs.get("preview", None) is not None:
preview = kwargs.get("preview")
# If unsupported model has been given
if not isinstance(source, Article) and not isinstance(source, Category):
source_name = type(source).__name__
raise TemplateSyntaxError(
(
"'{tag_name}' only accepts a Category or Article object "
"for 'source' argument. Object type '{source_name}' was given."
).format(tag_name=tag_name, source_name=source_name)
)
# Get the base queryset for siblings
siblings = model.objects.get_siblings(source=source)
# Article model make additional filtering on publication criteria if not in admin
# mode
if isinstance(source, Article) and not preview:
lotus_now = kwargs.get("now") or context.get("lotus_now")
if lotus_now is None:
raise TemplateSyntaxError(
(
"'{tag_name}' require either a context variable 'lotus_now' to be "
"set or a tag argument named 'now'."
).format(tag_name=tag_name)
)
siblings = siblings.get_published(target_date=lotus_now)
# Enforce order on language code
siblings = siblings.order_by("language")
# All available language names and codes
existing_languages = [item.language for item in [source] + list(siblings)]
available_languages = [
code
for code, name in settings.LANGUAGES
if (code not in existing_languages)
]
return {
"source": source,
"siblings": siblings,
"existing_languages": existing_languages,
"available_languages": available_languages,
}
[docs]
@register.simple_tag(takes_context=True)
def translation_siblings_html(context, source, **kwargs):
"""
Work like ``translation_siblings`` but render HTML from a template instead.
Example:
At least you must give an Article object: ::
{% load lotus %}
{% translation_siblings_html article [now=custom_now] [template="foo/bar.html"] [preview=True] %}
* Optional ``now`` argument expect a datetime to use instead of current date;
* Optional ``template`` argument expect a string for a template path to use
instead of default one;
* Optional ``preview`` argument expect a boolean to explicitely disable or
enable preview mode, on default it is ``None`` to determine it automatically;
Arguments:
context (object): Either a ``django.template.Context`` or a dictionnary for
context variable for template where the tag is included. This is only used
with an Article object, so it should be safe to be empty for a Category.
source (object): Either a ``lotus.models.Article`` or ``lotus.models.Category``
to retrieve its translation siblings.
Keyword Arguments:
now (datetime.datetime): A datetime to use to compare against publish
start and end dates to check for some publication criterias. Only used
for Article object.
template (string): A path to a custom template to use instead of the default
one. If not given, the default one will be used, each model have its own
default template, see settings.
preview (boolean): Option to bypass checking preview mode from context and
force it to a value, either True to enable it, False to disable it or None
to let the basic behavior to determine it from its template context
variable. Default to None.
Returns:
string: Rendered template tag fragment.
""" # noqa: E501
# Use the right template depending model
if isinstance(source, Article):
template_path = (
kwargs.get("template") or settings.LOTUS_ARTICLE_SIBLING_TEMPLATE
)
elif isinstance(source, Category):
template_path = (
kwargs.get("template") or settings.LOTUS_CATEGORY_SIBLING_TEMPLATE
)
render_context = translation_siblings(
context,
source,
tag_name="translation_siblings_html",
**kwargs
)
return loader.get_template(template_path).render(render_context)
[docs]
@register.inclusion_tag(settings.LOTUS_PREVIEW_SWITCH_TEMPLATE, takes_context=True)
def preview_switch(context):
"""
Display a button to enable or disable preview mode.
Example:
This tag does not expect any argument: ::
{% load lotus %}
{% preview_switch %}
Arguments:
context (object): Either a ``django.template.Context`` or a dictionnary for
context variable for template where the tag is included. Note that context
requires the session and user objects to be present (when available for
authenticated users) so you need to enable the respective middleware/context
processors in your project settings.
Returns:
dict: A dictionnary of payload to build preview button template.
* user: User object (or Anonymous)
* allowed: Boolean to determine if user is allowed for preview mode or not;
* current_mode: Either ``enabled`` or ``disabled`` string to indicate current
preview mode state;
* redirection: A string for the path to give to button URL that will be used
to redirect to once preview toggle has been requested;
"""
allowed = False
current_mode = None
redirection = None
request = context.get("request")
session = getattr(request, "session", None)
user = getattr(request, "user", None)
if user and user.is_staff:
allowed = True
redirection = request.get_full_path()
current_mode = "disabled"
if session.get(settings.LOTUS_PREVIEW_KEYWORD, None) is True:
current_mode = "enabled"
return {
"user": user,
"allowed": allowed,
"current_mode": current_mode,
"redirection": redirection,
}
[docs]
@register.simple_tag(takes_context=True)
def check_object_lang_availability(context, source, **kwargs):
"""
Determine if an object has a language that are not available from ``LANGUAGES``
setting.
Example:
This tag does not expect any other argument than ``source``: ::
{% load lotus %}
{% check_object_lang_availability article_object as object_lang_availability %}
Arguments:
context (object): Either a ``django.template.Context`` or a dictionnary for
context variables. It is not used from this tag.
source (object): Any object with an attribute ``language`` that will be checked
against ``settings.LANGUAGES`` however the tag will fails silently for
given source that does not have this attribute.
Returns:
dict: A dictionnary with summary informations of object language and its
availability.
""" # noqa: E501
is_available = False
if getattr(source, "language", None):
is_available = source.language in [k for k, v in settings.LANGUAGES]
return {
"is_available": is_available,
"languages": settings.LANGUAGES,
"language_keys": [k for k, v in settings.LANGUAGES],
"language_labels": [v for k, v in settings.LANGUAGES],
}
[docs]
@register.simple_tag(takes_context=True)
def article_get_related(context, article, **kwargs):
"""
Returns the related articles for a given article object.
It rely on an optional filtering function used to filter article objects to apply
publication and language lookups. This function is searched in template context
as an item named ``apply_article_lookups``, if not found no filtering is applied.
Commonly the filtering function would be
``ArticleFilterMixin.apply_article_lookups`` that is already supplied in Article
detail view and viewset.
Example:
You must give an Article object: ::
{% load lotus %}
{% article_get_related myarticle %}
Or:
{% load lotus %}
{% article_get_related myarticle as relateds %}
No other arguments are expected.
Arguments:
context (object): Either a ``django.template.Context`` or a dictionnary for
context variable for template where the tag is included.
article (lotus.models.article.Article): Article object to compute states.
Returns:
queyrset: Queryset for retrieved related articles.
"""
filter_func = context.get("article_filter_func", None)
return article.get_related(filter_func=filter_func)
[docs]
@register.simple_tag(takes_context=True)
def get_categories(context, current=None):
"""
Generates and returns a list of all categories for current language.
Example:
This tag does not require any argument to work: ::
{% load lotus %}
{% get_categories_html [current=mycategory] %}
And accept optional arguments:
* ``current`` argument expect a category object to check against each item,
the matching item will be marked as active.
Arguments:
context (object): Either a ``django.template.Context`` or a dictionnary for
context variable for template where the tag is included.
Keyword Arguments:
current (lotus.models.article.Category): A category object to check against
each item, the matching item will be marked as active.
Returns:
dict: A dictionary containing a list of dictionaries, each of which has the
``title`` and ``url`` of a category.
"""
request = context.get("request", None)
queryset = Category.objects.get_for_lang(get_language_code(request=request))
if current and not isinstance(current, Category):
current_type = type(current).__name__
raise TemplateSyntaxError(
(
"'get_categories' tag only accepts a Category object as 'current' "
"argument. Object type '{current_type}' was given."
).format(current_type=current_type)
)
return {
"categories": [
{
"title": category.title,
"url": category.get_absolute_url(),
"is_active": (category.id == current.id) if current else False
}
for category in queryset
]
}
[docs]
@register.simple_tag(takes_context=True)
def get_categories_html(context, current=None, template=None):
"""
Work like ``get_categories`` but render HTML from a template instead.
Example:
This tag does not require any argument to work: ::
{% load lotus %}
{% get_categories_html [current=mycategory] [template="foo/bar.html"] %}
And accept optional arguments:
* ``current``: expect a category object to check against each item,
the matching item will be marked as active;
* ``template``: a string for a template path to use;
Arguments:
context (object): Either a ``django.template.Context`` or a dictionnary for
context variable for template where the tag is included. This is only used
with an Article object, so it should be safe to be empty for a Category.
Keyword Arguments:
current (lotus.models.article.Category): A category object to check against
each item, the matching item will be marked as active.
template (string): A path for custom template to use. If not given a default
one will be used from setting ``LOTUS_CATEGORIES_TAG_TEMPLATE``.
Returns:
string: Rendered template tag fragment.
""" # noqa: E501
# Use given template if any else the default one from settings
template_path = template or settings.LOTUS_CATEGORIES_TAG_TEMPLATE
render_context = get_categories(
context,
current=current,
)
return loader.get_template(template_path).render(render_context)
[docs]
@register.simple_tag(takes_context=True)
def get_album_html(context, album, template=None):
"""
Render an Album object with a template.
Example:
This tag requires ``album`` argument to work: ::
{% load lotus %}
{% get_album_html album %}
Arguments:
context (object): Either a ``django.template.Context`` or a dictionnary for
context variable for template where the tag is included.
album (lotus.models.article.Album): An album object.
Keyword Arguments:
template (string): A path for custom template to use. If not given a default
one will be used from setting ``LOTUS_ALBUM_TAG_TEMPLATE``.
Returns:
string: Rendered template tag fragment.
"""
if album and not isinstance(album, Album):
current_type = type(album).__name__
raise TemplateSyntaxError(
(
"'get_album_html' tag only accepts an Album object as 'album' "
"argument. Object type '{current_type}' was given."
).format(current_type=current_type)
)
# Use given template if any else the default one from settings
template_path = template or settings.LOTUS_ALBUM_TAG_TEMPLATE
return loader.get_template(template_path).render({
"album_object": album,
})