"""Template tags provided by projectroles for use in other apps"""
from importlib import import_module
import mistune
from django import template
from django.conf import settings
from django.contrib.auth import get_user_model
from django.contrib.staticfiles import finders
from django.core.exceptions import ObjectDoesNotExist
from django.template.loader import get_template
from django.templatetags.static import static
from django.urls import reverse
import projectroles
from projectroles.app_settings import AppSettingAPI
from projectroles.models import Project, RemoteProject, SODAR_CONSTANTS
from projectroles.plugins import get_backend_api, BackendPluginPoint
from projectroles.utils import get_display_name as _get_display_name
# SODAR constants
SITE_MODE_SOURCE = SODAR_CONSTANTS['SITE_MODE_SOURCE']
SITE_MODE_TARGET = SODAR_CONSTANTS['SITE_MODE_TARGET']
SITE_MODE_PEER = SODAR_CONSTANTS['SITE_MODE_PEER']
site = import_module(settings.SITE_PACKAGE)
User = get_user_model()
# App settings API
app_settings = AppSettingAPI()
register = template.Library()
# SODAR and site operations ----------------------------------------------------
[docs]@register.simple_tag
def site_version():
"""Return the site version"""
return site.__version__ if hasattr(site, '__version__') else '[UNKNOWN]'
[docs]@register.simple_tag
def core_version():
"""Return the SODAR Core version"""
return projectroles.__version__
[docs]@register.simple_tag
def check_backend(name):
"""Return True if backend app is available, else False"""
return True if get_backend_api(name) else False
[docs]@register.simple_tag
def get_project_by_uuid(sodar_uuid):
"""Return Project by sodar_uuid"""
try:
return Project.objects.get(sodar_uuid=sodar_uuid)
except Project.DoesNotExist:
return None
[docs]@register.simple_tag
def get_user_by_username(username):
"""Return User by username"""
try:
return User.objects.get(username=username)
except User.DoesNotExist:
return None
# Django helpers ---------------------------------------------------------------
[docs]@register.simple_tag
def get_django_setting(name, default=None, js=False):
"""
Return value of Django setting by name or the default value if the setting
is not found. Return a Javascript-safe value if js=True.
"""
val = getattr(settings, name, default)
if js and isinstance(val, bool):
val = int(val)
return val
[docs]@register.simple_tag
def get_app_setting(app_name, setting_name, project=None, user=None):
"""
Get a project/user specific app setting from AppSettingAPI
"""
return app_settings.get_app_setting(app_name, setting_name, project, user)
[docs]@register.simple_tag
def static_file_exists(path):
"""Return True/False based on whether a static file exists"""
return True if finders.find(path) else False
[docs]@register.simple_tag
def template_exists(path):
"""Return True/False based on whether a template exists"""
try:
get_template(path)
return True
except template.TemplateDoesNotExist:
return False
[docs]@register.simple_tag
def get_full_url(request, url):
"""Get full URL based on a local URL"""
return request.scheme + '://' + request.get_host() + url
# Template rendering -----------------------------------------------------------
[docs]@register.simple_tag
def get_display_name(key, title=False, count=1, plural=False):
"""Return display name from SODAR_CONSTANTS"""
return _get_display_name(key, title, count, plural)
[docs]@register.simple_tag
def get_project_title_html(project):
"""Return HTML version of the full project title including parents"""
ret = ''
if project.get_parents():
ret += ' / '.join(project.get_full_title().split(' / ')[:-1]) + ' / '
ret += project.title
return ret
[docs]@register.simple_tag
def get_project_link(project, full_title=False, request=None):
"""Return link to project with a simple or full title"""
remote_icon = ''
if request:
remote_icon = get_remote_icon(project, request)
return (
'<a href="{}" title="{}" data-toggle="tooltip" '
'data-placement="top">{}</a>{}'.format(
reverse(
'projectroles:detail', kwargs={'project': project.sodar_uuid}
),
project.description if project.description else '',
project.get_full_title() if full_title else project.title,
' ' + remote_icon if remote_icon else '',
)
)
[docs]@register.simple_tag
def get_user_html(user):
"""Return standard HTML representation for a User object"""
return (
'<a title="{}" href="mailto:{}" data-toggle="tooltip" '
'data-placement="top">{}'
'</a>'.format(user.get_full_name(), user.email, user.username)
)
[docs]@register.simple_tag
def get_backend_include(backend_name, include_type='js'):
"""Returns import string for backend app Javascript or CSS.
Returns empty string if not found."""
# TODO: Replace with get_app_plugin() and if None check
# TODO: once get_app_plugin() can be used for backend plugins
# TODO: Don't forget to remove ObjectDoesNotExist import
try:
plugin = BackendPluginPoint.get_plugin(backend_name)
except ObjectDoesNotExist:
return ''
include = ''
include_string = ''
try:
if include_type == 'js':
include = plugin.javascript_url
include_string = '<script type="text/javascript" src="{}"></script>'
elif include_type == 'css':
include = plugin.css_url
include_string = (
'<link rel="stylesheet" type="text/css" href="{}"/>'
)
except AttributeError:
return ''
if include and finders.find(include):
return include_string.format(static(include))
return ''
[docs]@register.simple_tag
def get_history_dropdown(project, obj):
"""Return link to object timeline events within project"""
timeline = get_backend_api('timeline_backend')
if not timeline:
return ''
url = timeline.get_object_url(project.sodar_uuid, obj)
return (
'<a class="dropdown-item" href="{}">\n<i class="fa fa-fw '
'fa-clock-o"></i> History</a>\n'.format(url)
)
[docs]@register.simple_tag
def highlight_search_term(item, term):
"""Return string with search term highlighted"""
def get_highlights(item):
pos = item.lower().find(term.lower())
tl = len(term)
if pos == -1:
return item # Nothing to highlight
ret = item[:pos]
ret += (
'<span class="sodar-search-highlight">'
+ item[pos : pos + tl]
+ '</span>'
)
if len(item[pos + tl :]) > 0:
ret += get_highlights(item[pos + tl :])
return ret
return get_highlights(item)
[docs]@register.simple_tag
def get_info_link(content, html=False):
"""Return info popover link icon"""
return (
'<a class="sodar-info-link" tabindex="0" data-toggle="popover" '
'data-trigger="focus" data-placement="top" data-content="{}" '
'{}><i class="fa fa-info-circle text-info"></i></a>'.format(
content, 'data-html="true"' if html else ''
)
)
[docs]@register.simple_tag
def get_remote_icon(project, request):
"""Get remote project icon HTML"""
if project.is_remote() and request.user.is_superuser:
try:
remote_project = RemoteProject.objects.get(
project=project, site__mode=SITE_MODE_SOURCE
)
return (
'<i class="fa fa-globe {} mx-1 '
'sodar-pr-remote-project-icon" title="{} project from '
'{}" data-toggle="tooltip" data-placement="top">'
'</i>'.format(
'text-danger' if project.is_revoked() else 'text-info',
'REVOKED remote' if project.is_revoked() else 'Remote',
remote_project.site.name,
)
)
except RemoteProject.DoesNotExist:
pass
return ''
[docs]@register.simple_tag
def get_visible_projects(projects, can_view_hidden_projects=False):
"""Return all projects that are either visible by user display or by view hidden permission"""
return [
p for p in projects if p.site.user_display or can_view_hidden_projects
]
[docs]@register.simple_tag
def render_markdown(raw_markdown):
"""Markdown field rendering helper"""
return mistune.markdown(raw_markdown)
[docs]@register.filter
def force_wrap(s, length):
"""Force wrapping of string"""
# If string contains spaces or hyphens, leave wrapping to browser
if not {' ', '-'}.intersection(s) and len(s) > length:
return '<wbr />'.join(
[s[i : i + length] for i in range(0, len(s), length)]
)
return s
# General helpers -------------------------------------------------------------
[docs]@register.simple_tag
def get_class(obj, lower=False):
"""Return object class as string"""
c = obj.__class__.__name__
return c.lower() if lower else c