# -*- coding: utf-8 -*-
"""
Tags and filters available in aristotle templates
=================================================
A number of convenience tags are available for performing common actions in custom
templates.
Include the aristotle template tags in every template that uses them, like so::
{% load aristotle_tags %}
Available tags and filters
--------------------------
"""
from django import template
from django.core.urlresolvers import reverse, resolve
from django.template.defaultfilters import slugify
from django.utils.translation import ugettext_lazy as _
from aristotle_mdr import perms
import aristotle_mdr.models as MDR
register = template.Library()
@register.filter
def can_alter_comment(user, comment):
try:
return perms.user_can_alter_comment(user, comment)
except:
return False
@register.filter
def can_alter_post(user, post):
try:
return perms.user_can_alter_post(user, post)
except:
return False
@register.filter
def is_in(item, iterable):
return item in iterable
@register.filter
[docs]def in_workgroup(user, workgroup):
"""
A filter that acts as a wrapper around ``aristotle_mdr.perms.user_in_workgroup``.
Returns true if the user has permission to administer the workgroup, otherwise it returns False.
If calling ``user_in_workgroup`` throws an exception it safely returns False.
For example::
{% if request.user|in_workgroup:workgroup %}
{{ something }}
{% endif %}
"""
try:
return perms.user_in_workgroup(user, workgroup)
except:
return False
@register.filter
[docs]def can_edit(item, user):
"""
A filter that acts as a wrapper around ``aristotle_mdr.perms.user_can_edit``.
Returns true if the user has permission to edit the item, otherwise it returns False.
If calling ``user_can_edit`` throws an exception it safely returns False.
For example::
{% if myItem|can_edit:request.user %}
{{ item }}
{% endif %}
"""
# return perms.user_can_edit(user, item)
try:
return perms.user_can_edit(user, item)
except: # pragma: no cover -- passing a bad item or user is the template authors fault
return None
@register.filter
[docs]def can_view(item, user):
"""
A filter that acts as a wrapper around ``aristotle_mdr.perms.user_can_view``.
Returns true if the user has permission to view the item, otherwise it returns False.
If calling ``user_can_view`` throws an exception it safely returns False.
For example::
{% if myItem|can_view:request.user %}
{{ item }}
{% endif %}
"""
return perms.user_can_view(user, item)
@register.filter
[docs]def can_view_iter(qs, user):
"""
A filter that is a simple wrapper that applies the ``aristotle_mdr.models.ConceptManager.visible(user)``
for use in templates. Filtering on a Django ``Queryset`` and passing in the current
user as the argument returns a list (not a ``Queryset`` at this stage) of only
the items from the ``Queryset`` the user can view.
If calling ``can_view_iter`` throws an exception it safely returns an empty list.
For example::
{% for item in myItems|can_view_iter:request.user %}
{{ item }}
{% endfor %}
"""
try:
return qs.visible(user)
except: # pragma: no cover -- passing a bad queryset is the template authors fault
return []
@register.filter
[docs]def public_standards(regAuth, itemType="aristotle_mdr._concept"):
"""
This is a filter that accepts a registration Authority and an item type and returns
a list of tuples that contain all *public* items with a status of "Standard" or
"Preferred Standard" *in that Registration Authority only*, as well as a the
status object for that Authority.
The item type should consist of the name of the app the item is from and the
name of the item itself separated by a period (``.``).
This requires the django ``django.contrib.contenttypes`` app is installed.
If calling ``public_standards`` throws an exception or the item type requested
is not found it safely returns an empty list.
For example::
{% for item, status in registrationAuthority|public_standards:'aristotle_mdr.DataElement' %}
{{ item }} - made standard on {{ status.registrationDate }}.
{% endfor %}
"""
try:
from django.contrib.contenttypes.models import ContentType
app_label, model_name=itemType.lower().split('.', 1)[0:2]
standard_states = [MDR.STATES.standard, MDR.STATES.preferred]
return [
(i, i.statuses.filter(registrationAuthority=regAuth, state__in=standard_states).first())
for i in ContentType.objects.get(app_label=app_label, model=model_name).model_class().objects.filter(statuses__registrationAuthority=regAuth, statuses__state__in=standard_states).public()
]
except:
return []
@register.simple_tag
def paginator_get(request, pageNumber, pop=''):
# http://stackoverflow.com/questions/2047622/how-to-paginate-django-with-other-get-variables
dict_ = request.GET.copy()
for p in pop.split(','):
dict_.pop(p, None)
dict_['page'] = pageNumber
return dict_.urlencode()
@register.simple_tag
def ifeq(a, b, val):
return val if a == b else ""
@register.simple_tag
[docs]def ternary(condition, a, b):
"""
A simple ternary tag - it beats verbose if/else tags in templates for simple strings
If the ``condition`` is 'truthy' return ``a`` otherwise return ``b``. For example::
<a class="{% ternary item.is_public 'public' 'private' %}">{{item.name}}</a>
"""
if condition:
return a
else:
return b
@register.filter
def paginator_range(page, mode):
if mode=="start":
if page.number <= 5:
# show 4,5,6 if page is 4, 5,6,7 if page is 5...
return page.paginator.page_range[:max(5, page.number + 2)]
else:
return page.paginator.page_range[:3]
if mode=="middle":
if page.number > 5 and page.number < page.paginator.num_pages - 5:
return page.paginator.page_range[page.number - 3:page.number + 2]
if mode=="end":
if page.number > page.paginator.num_pages - 5:
return page.paginator.page_range[-5:]
else:
return page.paginator.page_range[-1:]
@register.filter
[docs]def stateToText(state):
# @register.simple_tag
"""
This tag takes the integer value of a state for a registration status and
converts it to its text equivilent.
"""
return MDR.STATES[int(state)]
@register.filter
def unique_recent(recent):
seen = []
out = []
for item in recent:
if item._model and item.object and item.object.id not in seen:
seen.append(item.object.id)
out.append(item)
return out
@register.simple_tag
[docs]def zws(string):
# Adds a zerowidth space before an em-dash
"""
``zws`` or "zero width space" is used to insert a soft break near em-dashed.
Since em-dashs are commonly used in Data Element Concept names, this helps them wrap
in the right places.
For example::
<h1>{% zws item.name %}</h1>
"""
string = string.encode('utf-8', 'xmlcharrefreplace')
return string.replace("—", "­—")
@register.simple_tag
[docs]def adminEdit(item):
"""
A tag for easily generating the link to an admin page for editing an item. For example::
<a href="{% adminEdit item %}">Advanced editor for {{item.name}}</a>
"""
app_name = item._meta.app_label
return reverse("admin:%s_%s_change" % (app_name, item._meta.model_name), args=[item.id])
@register.simple_tag
[docs]def clone(item):
"""
A tag for easily generating the link to an admin page for "cloning" an item. For example::
<a href="{% clone item %}">Clone {{item.name}}</a>
"""
app_name = item._meta.app_label
return reverse("admin:%s_%s_add" % (app_name, item._meta.model_name)) + "?clone=%s" % item.id
@register.simple_tag
[docs]def historyLink(item):
"""
A tag for easily generating the link to an admin page for "cloning" an item. For example::
<a href="{% clone item %}">Clone {{item.name}}</a>
"""
app_name = item._meta.app_label
return reverse("admin:%s_%s_history" % (app_name, item._meta.model_name), args=[item.id])
@register.simple_tag
@register.simple_tag
def extra_content(extension, item, user):
try:
from django.template.loader import get_template
from django.template import Context
s = item._meta.object_name
s = s[0].lower() + s[1:]
return get_template(extension + "/extra_content/" + s + ".html").render(
Context({'item': item, 'user': user})
)
except template.TemplateDoesNotExist:
# there is no extra content for this item, and thats ok.
return ""
@register.simple_tag
def bootstrap_modal(_id, size=None):
size_class = ""
if size == 'lg':
size_class = "modal-lg"
elif size == 'sm':
size_class = "modal-sm"
modal = '<div id="%s" class="modal fade"><div class="modal-dialog %s"><div class="modal-content"></div></div></div>'
return modal % (_id, size_class)
@register.simple_tag
[docs]def doc(item, field=None):
"""Gets the appropriate help text or docstring for a model or field.
Accepts 1 or 2 string arguments:
If 1, returns the docstring for the given model in the specified app.
If 2, returns the help_text for the field on the given model in the specified app.
"""
from django.contrib.contenttypes.models import ContentType
from aristotle_mdr.utils.doc_parse import parse_rst, parse_docstring
ct = item
if field is None:
model_name = ct._meta.model_name
title, body, metadata = parse_docstring(ct.__doc__)
if title:
title = parse_rst(title, 'model', _('model:') + model_name)
if body:
body = parse_rst(body, 'model', _('model:') + model_name)
return title
else:
if ct._meta.get_field(field).help_text:
return _(ct._meta.get_field(field).help_text)
else:
# return _("No help text for the field '%(field)s' found on the model '%(model)s' in the app '%(app)s'") % {'app':app_label,'model':model_name,'field':field}
return _("No help text for the field '%(field)s' found for the model '%(model)s'") % {'model': item.get_verbose_name(), 'field': field}
@register.filter
def can_use_action(user, bulk_action, *args):
from aristotle_mdr.views.bulk_actions import get_bulk_actions
bulk_action = get_bulk_actions().get(bulk_action)
return bulk_action['can_use'](user)
@register.filter
def template_path(item, _type):
from aristotle_mdr.utils import get_download_template_path_for_item
_type, subpath=_type.split(',')
return get_download_template_path_for_item(item, _type, subpath)
@register.filter
def visibility_text(item):
visibility = _("hidden")
if item._is_locked:
visibility = _("locked")
if item._is_public:
visibility = _("public")
return visibility