112 lines
4.6 KiB
Python
112 lines
4.6 KiB
Python
from wagtail import hooks
|
|
|
|
|
|
class FeatureRegistry:
|
|
"""
|
|
A central store of information about optional features that can be enabled in rich text
|
|
editors by passing a ``features`` list to the RichTextField, such as how to
|
|
whitelist / convert HTML tags, and how to enable the feature on various editors.
|
|
|
|
This information may come from diverse sources - for example, wagtailimages might define
|
|
an 'images' feature and a Draftail plugin for it, while a third-party module might
|
|
define a TinyMCE plugin for the same feature. The information is therefore collected into
|
|
this registry via the 'register_rich_text_features' hook.
|
|
"""
|
|
|
|
def __init__(self):
|
|
# Has the register_rich_text_features hook been run for this registry?
|
|
self.has_scanned_for_features = False
|
|
|
|
# a dict of dicts, one for each editor (draftail.js, TinyMCE etc); each dict is a mapping
|
|
# of feature names to 'plugin' objects that define how to implement that feature
|
|
# (e.g. paths to JS files to import). The API of that plugin object is not defined
|
|
# here, and is specific to each editor.
|
|
self.plugins_by_editor = {}
|
|
|
|
# a list of feature names that will be applied on rich text areas that do not specify
|
|
# an explicit `feature` list.
|
|
self.default_features = []
|
|
|
|
# a mapping of linktype names to rewriter functions for converting database representations
|
|
# of links (e.g. <a linktype="page" id="123">) into front-end HTML. Each rewriter function
|
|
# takes a dict of attributes, and returns the rewritten opening tag as a string
|
|
self.link_types = {}
|
|
|
|
# a mapping of embedtype names to rewriter functions for converting database representations
|
|
# of embedded content (e.g. <embed embedtype="image" id="123" format="left" alt="foo">)
|
|
# into front-end HTML. Each rewriter function takes a dict of attributes, and returns an
|
|
# HTML fragment to replace it with
|
|
self.embed_types = {}
|
|
|
|
# a dict of dicts, one for each converter backend (editorhtml, contentstate etc);
|
|
# each dict is a mapping of feature names to 'rule' objects that define how to convert
|
|
# that feature's elements between editor representation and database representation
|
|
# (e.g. elements to whitelist, functions for transferring attributes).
|
|
# The API of that rule object is not defined here, and is specific to each converter backend.
|
|
self.converter_rules_by_converter = {}
|
|
|
|
def get_default_features(self):
|
|
if not self.has_scanned_for_features:
|
|
self._scan_for_features()
|
|
|
|
return self.default_features
|
|
|
|
def _scan_for_features(self):
|
|
for fn in hooks.get_hooks("register_rich_text_features"):
|
|
fn(self)
|
|
self.has_scanned_for_features = True
|
|
|
|
def register_editor_plugin(self, editor_name, feature_name, plugin):
|
|
plugins = self.plugins_by_editor.setdefault(editor_name, {})
|
|
plugins[feature_name] = plugin
|
|
|
|
def get_editor_plugin(self, editor_name, feature_name):
|
|
if not self.has_scanned_for_features:
|
|
self._scan_for_features()
|
|
|
|
try:
|
|
return self.plugins_by_editor[editor_name][feature_name]
|
|
except KeyError:
|
|
return None
|
|
|
|
def register_link_type(self, handler):
|
|
self.link_types[handler.identifier] = handler
|
|
|
|
def get_link_types(self):
|
|
if not self.has_scanned_for_features:
|
|
self._scan_for_features()
|
|
return self.link_types
|
|
|
|
def register_embed_type(self, handler):
|
|
self.embed_types[handler.identifier] = handler
|
|
|
|
def get_embed_types(self):
|
|
if not self.has_scanned_for_features:
|
|
self._scan_for_features()
|
|
return self.embed_types
|
|
|
|
def register_converter_rule(self, converter_name, feature_name, rule):
|
|
rules = self.converter_rules_by_converter.setdefault(converter_name, {})
|
|
rules[feature_name] = rule
|
|
|
|
def get_converter_rule(self, converter_name, feature_name):
|
|
if not self.has_scanned_for_features:
|
|
self._scan_for_features()
|
|
|
|
try:
|
|
return self.converter_rules_by_converter[converter_name][feature_name]
|
|
except KeyError:
|
|
return None
|
|
|
|
@staticmethod
|
|
def function_as_entity_handler(identifier, fn):
|
|
"""Supports legacy registering of entity handlers as functions."""
|
|
return type(
|
|
"EntityHandlerRegisteredAsFunction",
|
|
(object,),
|
|
{
|
|
"identifier": identifier,
|
|
"expand_db_attributes": staticmethod(fn),
|
|
},
|
|
)
|