"""
Kolibri Content hooks
---------------------
Hooks for managing the display and rendering of content.
"""
import json
from abc import abstractmethod
from abc import abstractproperty
from django.core.serializers.json import DjangoJSONEncoder
from django.utils.safestring import mark_safe
from kolibri.core.webpack.hooks import WebpackBundleHook
from kolibri.core.webpack.hooks import WebpackInclusionMixin
from kolibri.plugins.hooks import define_hook
from kolibri.plugins.hooks import KolibriHook
[docs]@define_hook
class ContentRendererHook(WebpackBundleHook, WebpackInclusionMixin):
"""
An inheritable hook that allows special behaviour for a frontend module that defines
a content renderer.
"""
#: Set tuple of format presets that this content renderer can handle
@abstractproperty
def presets(self):
pass
@classmethod
def html(cls):
tags = []
for hook in cls.registered_hooks:
tags.append(hook.template_html())
return mark_safe("\n".join(tags))
[docs] def template_html(self):
"""
Generates template tags containing data to register a content renderer.
:returns: HTML of a template tags to insert into a page.
"""
# Note, while most plugins use sorted chunks to filter by text direction
# content renderers do not, as they may need to have styling for a different
# text direction than the interface due to the text direction of content
urls = [chunk["url"] for chunk in self.bundle]
tags = (
self.frontend_message_tag()
+ self.plugin_data_tag()
+ [
'<template data-viewer="{bundle}">{data}</template>'.format(
bundle=self.unique_id,
data=json.dumps(
{"urls": urls, "presets": self.presets},
separators=(",", ":"),
ensure_ascii=False,
cls=DjangoJSONEncoder,
),
)
]
)
return mark_safe("\n".join(tags))
[docs]@define_hook
class ContentNodeDisplayHook(KolibriHook):
"""
A hook that registers a capability of a plugin to provide a user interface
for a content node. When subclassed, this hook should expose a method that
accepts a ContentNode instance as an argument, and returns a URL where the
interface to interacting with that node for the user is exposed.
If this plugin cannot produce an interface for this particular content node
then it may return None.
"""
@abstractmethod
def node_url(self, content_node):
pass