kopia lustrzana https://github.com/wagtail/wagtail
Use feature registry to manage contentstate conversion rules
rodzic
6ceee1a6ce
commit
1ceff85f12
|
@ -2,12 +2,13 @@ import json
|
|||
import logging
|
||||
import re
|
||||
|
||||
from draftjs_exporter.constants import BLOCK_TYPES, ENTITY_TYPES, INLINE_STYLES
|
||||
from draftjs_exporter.constants import BLOCK_TYPES, ENTITY_TYPES
|
||||
from draftjs_exporter.defaults import render_children
|
||||
from draftjs_exporter.dom import DOM
|
||||
from draftjs_exporter.html import HTML as HTMLExporter
|
||||
|
||||
from wagtail.admin.rich_text.converters.html_to_contentstate import HtmlToContentStateHandler
|
||||
from wagtail.core.rich_text import features as feature_registry
|
||||
|
||||
|
||||
def Image(props):
|
||||
|
@ -85,55 +86,6 @@ def EntityFallback(props):
|
|||
return None
|
||||
|
||||
|
||||
EXPORTER_CONFIG_BY_FEATURE = {
|
||||
'h1': {
|
||||
'block_map': {BLOCK_TYPES.HEADER_ONE: 'h1'}
|
||||
},
|
||||
'h2': {
|
||||
'block_map': {BLOCK_TYPES.HEADER_TWO: 'h2'}
|
||||
},
|
||||
'h3': {
|
||||
'block_map': {BLOCK_TYPES.HEADER_THREE: 'h3'}
|
||||
},
|
||||
'h4': {
|
||||
'block_map': {BLOCK_TYPES.HEADER_FOUR: 'h4'}
|
||||
},
|
||||
'h5': {
|
||||
'block_map': {BLOCK_TYPES.HEADER_FIVE: 'h5'}
|
||||
},
|
||||
'h6': {
|
||||
'block_map': {BLOCK_TYPES.HEADER_SIX: 'h6'}
|
||||
},
|
||||
'bold': {
|
||||
'style_map': {INLINE_STYLES.BOLD: 'b'}
|
||||
},
|
||||
'italic': {
|
||||
'style_map': {INLINE_STYLES.ITALIC: 'i'}
|
||||
},
|
||||
'ol': {
|
||||
'block_map': {BLOCK_TYPES.ORDERED_LIST_ITEM: {'element': 'li', 'wrapper': 'ol'}}
|
||||
},
|
||||
'ul': {
|
||||
'block_map': {BLOCK_TYPES.UNORDERED_LIST_ITEM: {'element': 'li', 'wrapper': 'ul'}}
|
||||
},
|
||||
'hr': {
|
||||
'entity_decorators': {ENTITY_TYPES.HORIZONTAL_RULE: lambda props: DOM.create_element('hr')}
|
||||
},
|
||||
'link': {
|
||||
'entity_decorators': {ENTITY_TYPES.LINK: Link}
|
||||
},
|
||||
'document-link': {
|
||||
'entity_decorators': {ENTITY_TYPES.DOCUMENT: Document}
|
||||
},
|
||||
'image': {
|
||||
'entity_decorators': {ENTITY_TYPES.IMAGE: Image}
|
||||
},
|
||||
'embed': {
|
||||
'entity_decorators': {ENTITY_TYPES.EMBED: Embed}
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
class ContentstateConverter():
|
||||
def __init__(self, features=None):
|
||||
self.features = features
|
||||
|
@ -156,10 +108,12 @@ class ContentstateConverter():
|
|||
}
|
||||
|
||||
for feature in self.features:
|
||||
feature_config = EXPORTER_CONFIG_BY_FEATURE.get(feature, {})
|
||||
exporter_config['block_map'].update(feature_config.get('block_map', {}))
|
||||
exporter_config['style_map'].update(feature_config.get('style_map', {}))
|
||||
exporter_config['entity_decorators'].update(feature_config.get('entity_decorators', {}))
|
||||
rule = feature_registry.get_converter_rule('contentstate', feature)
|
||||
if rule is not None:
|
||||
feature_config = rule['to_database_format']
|
||||
exporter_config['block_map'].update(feature_config.get('block_map', {}))
|
||||
exporter_config['style_map'].update(feature_config.get('style_map', {}))
|
||||
exporter_config['entity_decorators'].update(feature_config.get('entity_decorators', {}))
|
||||
|
||||
self.exporter = HTMLExporter(exporter_config)
|
||||
|
||||
|
|
|
@ -5,6 +5,7 @@ from wagtail.admin.rich_text.converters.contentstate_models import (
|
|||
Block, ContentState, Entity, EntityRange, InlineStyleRange
|
||||
)
|
||||
from wagtail.admin.rich_text.converters.html_ruleset import HTMLRuleset
|
||||
from wagtail.core.rich_text import features as feature_registry
|
||||
from wagtail.core.models import Page
|
||||
from wagtail.documents.models import get_document_model
|
||||
|
||||
|
@ -226,60 +227,6 @@ class HorizontalRuleHandler(AtomicBlockEntityElementHandler):
|
|||
return Entity('HORIZONTAL_RULE', 'IMMUTABLE', {})
|
||||
|
||||
|
||||
ELEMENT_HANDLERS_BY_FEATURE = {
|
||||
'ol': {
|
||||
'ol': ListElementHandler('ordered-list-item'),
|
||||
'li': ListItemElementHandler(),
|
||||
},
|
||||
'ul': {
|
||||
'ul': ListElementHandler('unordered-list-item'),
|
||||
'li': ListItemElementHandler(),
|
||||
},
|
||||
'h1': {
|
||||
'h1': BlockElementHandler('header-one'),
|
||||
},
|
||||
'h2': {
|
||||
'h2': BlockElementHandler('header-two'),
|
||||
},
|
||||
'h3': {
|
||||
'h3': BlockElementHandler('header-three'),
|
||||
},
|
||||
'h4': {
|
||||
'h4': BlockElementHandler('header-four'),
|
||||
},
|
||||
'h5': {
|
||||
'h5': BlockElementHandler('header-five'),
|
||||
},
|
||||
'h6': {
|
||||
'h6': BlockElementHandler('header-six'),
|
||||
},
|
||||
'italic': {
|
||||
'i': InlineStyleElementHandler('ITALIC'),
|
||||
'em': InlineStyleElementHandler('ITALIC'),
|
||||
},
|
||||
'bold': {
|
||||
'b': InlineStyleElementHandler('BOLD'),
|
||||
'strong': InlineStyleElementHandler('BOLD'),
|
||||
},
|
||||
'link': {
|
||||
'a[href]': ExternalLinkElementHandler('LINK'),
|
||||
'a[linktype="page"]': PageLinkElementHandler('LINK'),
|
||||
},
|
||||
'document-link': {
|
||||
'a[linktype="document"]': DocumentLinkElementHandler('DOCUMENT'),
|
||||
},
|
||||
'image': {
|
||||
'embed[embedtype="image"]': ImageElementHandler(),
|
||||
},
|
||||
'embed': {
|
||||
'embed[embedtype="media"]': MediaEmbedElementHandler(),
|
||||
},
|
||||
'hr': {
|
||||
'hr': HorizontalRuleHandler(),
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
class HtmlToContentStateHandler(HTMLParser):
|
||||
def __init__(self, features=None):
|
||||
self.paragraph_handler = BlockElementHandler('unstyled')
|
||||
|
@ -288,11 +235,9 @@ class HtmlToContentStateHandler(HTMLParser):
|
|||
})
|
||||
if features is not None:
|
||||
for feature in features:
|
||||
try:
|
||||
feature_element_handlers = ELEMENT_HANDLERS_BY_FEATURE[feature]
|
||||
except KeyError:
|
||||
continue
|
||||
self.element_handlers.add_rules(feature_element_handlers)
|
||||
rule = feature_registry.get_converter_rule('contentstate', feature)
|
||||
if rule is not None:
|
||||
self.element_handlers.add_rules(rule['from_database_format'])
|
||||
|
||||
super().__init__()
|
||||
|
||||
|
|
|
@ -3,12 +3,18 @@ from django.urls import reverse
|
|||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from draftjs_exporter.constants import BLOCK_TYPES, ENTITY_TYPES, INLINE_STYLES
|
||||
from draftjs_exporter.dom import DOM
|
||||
|
||||
from wagtail.admin.menu import MenuItem, SubmenuMenuItem, settings_menu
|
||||
from wagtail.admin.navigation import get_explorable_root_page
|
||||
from wagtail.admin.rich_text import (
|
||||
HalloFormatPlugin, HalloHeadingPlugin, HalloListPlugin, HalloPlugin)
|
||||
from wagtail.admin.rich_text.converters.contentstate import Link
|
||||
from wagtail.admin.rich_text.converters.editor_html import LinkTypeRule, WhitelistRule
|
||||
from wagtail.admin.rich_text.converters.html_to_contentstate import (
|
||||
BlockElementHandler, ExternalLinkElementHandler, HorizontalRuleHandler, InlineStyleElementHandler,
|
||||
ListElementHandler, ListItemElementHandler, PageLinkElementHandler
|
||||
)
|
||||
import wagtail.admin.rich_text.editors.draftail.features as draftail_features
|
||||
from wagtail.admin.search import SearchArea
|
||||
from wagtail.admin.utils import user_has_any_page_permission
|
||||
|
@ -256,50 +262,138 @@ def register_core_features(features):
|
|||
features.register_editor_plugin(
|
||||
'draftail', 'hr', draftail_features.BooleanFeature('enableHorizontalRule')
|
||||
)
|
||||
|
||||
features.register_editor_plugin(
|
||||
'draftail', 'br', draftail_features.BooleanFeature('enableLineBreak')
|
||||
)
|
||||
features.register_converter_rule('contentstate', 'hr', {
|
||||
'from_database_format': {
|
||||
'hr': HorizontalRuleHandler(),
|
||||
},
|
||||
'to_database_format': {
|
||||
'entity_decorators': {ENTITY_TYPES.HORIZONTAL_RULE: lambda props: DOM.create_element('hr')}
|
||||
}
|
||||
})
|
||||
|
||||
features.register_editor_plugin(
|
||||
'draftail', 'h1', draftail_features.BlockFeature({'label': 'H1', 'type': BLOCK_TYPES.HEADER_ONE})
|
||||
)
|
||||
features.register_converter_rule('contentstate', 'h1', {
|
||||
'from_database_format': {
|
||||
'h1': BlockElementHandler('header-one'),
|
||||
},
|
||||
'to_database_format': {
|
||||
'block_map': {BLOCK_TYPES.HEADER_ONE: 'h1'}
|
||||
}
|
||||
})
|
||||
features.register_editor_plugin(
|
||||
'draftail', 'h2', draftail_features.BlockFeature({'label': 'H2', 'type': BLOCK_TYPES.HEADER_TWO})
|
||||
)
|
||||
features.register_converter_rule('contentstate', 'h2', {
|
||||
'from_database_format': {
|
||||
'h2': BlockElementHandler('header-two'),
|
||||
},
|
||||
'to_database_format': {
|
||||
'block_map': {BLOCK_TYPES.HEADER_TWO: 'h2'}
|
||||
}
|
||||
})
|
||||
features.register_editor_plugin(
|
||||
'draftail', 'h3', draftail_features.BlockFeature({'label': 'H3', 'type': BLOCK_TYPES.HEADER_THREE})
|
||||
)
|
||||
features.register_converter_rule('contentstate', 'h3', {
|
||||
'from_database_format': {
|
||||
'h3': BlockElementHandler('header-three'),
|
||||
},
|
||||
'to_database_format': {
|
||||
'block_map': {BLOCK_TYPES.HEADER_THREE: 'h3'}
|
||||
}
|
||||
})
|
||||
features.register_editor_plugin(
|
||||
'draftail', 'h4', draftail_features.BlockFeature({'label': 'H4', 'type': BLOCK_TYPES.HEADER_FOUR})
|
||||
)
|
||||
features.register_converter_rule('contentstate', 'h4', {
|
||||
'from_database_format': {
|
||||
'h4': BlockElementHandler('header-four'),
|
||||
},
|
||||
'to_database_format': {
|
||||
'block_map': {BLOCK_TYPES.HEADER_FOUR: 'h4'}
|
||||
}
|
||||
})
|
||||
features.register_editor_plugin(
|
||||
'draftail', 'h5', draftail_features.BlockFeature({'label': 'H5', 'type': BLOCK_TYPES.HEADER_FIVE})
|
||||
)
|
||||
features.register_converter_rule('contentstate', 'h5', {
|
||||
'from_database_format': {
|
||||
'h5': BlockElementHandler('header-five'),
|
||||
},
|
||||
'to_database_format': {
|
||||
'block_map': {BLOCK_TYPES.HEADER_FIVE: 'h5'}
|
||||
}
|
||||
})
|
||||
features.register_editor_plugin(
|
||||
'draftail', 'h6', draftail_features.BlockFeature({'label': 'H6', 'type': BLOCK_TYPES.HEADER_SIX})
|
||||
)
|
||||
features.register_converter_rule('contentstate', 'h6', {
|
||||
'from_database_format': {
|
||||
'h6': BlockElementHandler('header-six'),
|
||||
},
|
||||
'to_database_format': {
|
||||
'block_map': {BLOCK_TYPES.HEADER_SIX: 'h6'}
|
||||
}
|
||||
})
|
||||
features.register_editor_plugin(
|
||||
'draftail', 'ul', draftail_features.BlockFeature({
|
||||
'label': 'UL', 'type': BLOCK_TYPES.UNORDERED_LIST_ITEM, 'icon': 'icon-list-ul'
|
||||
})
|
||||
)
|
||||
features.register_converter_rule('contentstate', 'ul', {
|
||||
'from_database_format': {
|
||||
'ul': ListElementHandler('unordered-list-item'),
|
||||
'li': ListItemElementHandler(),
|
||||
},
|
||||
'to_database_format': {
|
||||
'block_map': {BLOCK_TYPES.UNORDERED_LIST_ITEM: {'element': 'li', 'wrapper': 'ul'}}
|
||||
}
|
||||
})
|
||||
features.register_editor_plugin(
|
||||
'draftail', 'ol', draftail_features.BlockFeature({
|
||||
'label': 'OL', 'type': BLOCK_TYPES.ORDERED_LIST_ITEM, 'icon': 'icon-list-ol'
|
||||
})
|
||||
)
|
||||
features.register_converter_rule('contentstate', 'ol', {
|
||||
'from_database_format': {
|
||||
'ol': ListElementHandler('ordered-list-item'),
|
||||
'li': ListItemElementHandler(),
|
||||
},
|
||||
'to_database_format': {
|
||||
'block_map': {BLOCK_TYPES.ORDERED_LIST_ITEM: {'element': 'li', 'wrapper': 'ol'}}
|
||||
}
|
||||
})
|
||||
|
||||
features.register_editor_plugin(
|
||||
'draftail', 'bold', draftail_features.InlineStyleFeature({
|
||||
'label': 'Bold', 'type': INLINE_STYLES.BOLD, 'icon': 'icon-bold'
|
||||
})
|
||||
)
|
||||
features.register_converter_rule('contentstate', 'bold', {
|
||||
'from_database_format': {
|
||||
'b': InlineStyleElementHandler('BOLD'),
|
||||
'strong': InlineStyleElementHandler('BOLD'),
|
||||
},
|
||||
'to_database_format': {
|
||||
'style_map': {INLINE_STYLES.BOLD: 'b'}
|
||||
}
|
||||
})
|
||||
features.register_editor_plugin(
|
||||
'draftail', 'italic', draftail_features.InlineStyleFeature({
|
||||
'label': 'Italic', 'type': INLINE_STYLES.ITALIC, 'icon': 'icon-italic'
|
||||
})
|
||||
)
|
||||
features.register_converter_rule('contentstate', 'italic', {
|
||||
'from_database_format': {
|
||||
'i': InlineStyleElementHandler('ITALIC'),
|
||||
'em': InlineStyleElementHandler('ITALIC'),
|
||||
},
|
||||
'to_database_format': {
|
||||
'style_map': {INLINE_STYLES.ITALIC: 'i'}
|
||||
}
|
||||
})
|
||||
|
||||
features.register_editor_plugin(
|
||||
'draftail', 'link', draftail_features.EntityFeature({
|
||||
|
@ -310,27 +404,12 @@ def register_core_features(features):
|
|||
'decorator': 'Link',
|
||||
})
|
||||
)
|
||||
|
||||
features.register_editor_plugin(
|
||||
'draftail', 'document-link', draftail_features.EntityFeature({
|
||||
'label': 'Document',
|
||||
'type': ENTITY_TYPES.DOCUMENT,
|
||||
'icon': 'icon-doc-full',
|
||||
'source': 'DocumentSource',
|
||||
'decorator': 'Document',
|
||||
})
|
||||
)
|
||||
|
||||
features.register_editor_plugin(
|
||||
'draftail', 'image', draftail_features.ImageFeature()
|
||||
)
|
||||
|
||||
features.register_editor_plugin(
|
||||
'draftail', 'embed', draftail_features.EntityFeature({
|
||||
'label': 'Embed',
|
||||
'type': ENTITY_TYPES.EMBED,
|
||||
'icon': 'icon-media',
|
||||
'source': 'EmbedSource',
|
||||
'decorator': 'Embed',
|
||||
})
|
||||
)
|
||||
features.register_converter_rule('contentstate', 'link', {
|
||||
'from_database_format': {
|
||||
'a[href]': ExternalLinkElementHandler('LINK'),
|
||||
'a[linktype="page"]': PageLinkElementHandler('LINK'),
|
||||
},
|
||||
'to_database_format': {
|
||||
'entity_decorators': {ENTITY_TYPES.LINK: Link}
|
||||
}
|
||||
})
|
||||
|
|
|
@ -7,9 +7,14 @@ from django.utils.html import format_html, format_html_join
|
|||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.utils.translation import ungettext
|
||||
|
||||
from draftjs_exporter.constants import ENTITY_TYPES
|
||||
|
||||
from wagtail.admin.menu import MenuItem
|
||||
from wagtail.admin.rich_text import HalloPlugin
|
||||
from wagtail.admin.rich_text.converters.contentstate import Document as DocumentEntity
|
||||
from wagtail.admin.rich_text.converters.editor_html import LinkTypeRule
|
||||
from wagtail.admin.rich_text.converters.html_to_contentstate import DocumentLinkElementHandler
|
||||
import wagtail.admin.rich_text.editors.draftail.features as draftail_features
|
||||
from wagtail.admin.search import SearchArea
|
||||
from wagtail.admin.site_summary import SummaryItem
|
||||
from wagtail.core import hooks
|
||||
|
@ -75,6 +80,7 @@ def editor_js():
|
|||
@hooks.register('register_rich_text_features')
|
||||
def register_document_feature(features):
|
||||
features.register_link_type('document', document_linktype_handler)
|
||||
|
||||
features.register_editor_plugin(
|
||||
'hallo', 'document-link',
|
||||
HalloPlugin(
|
||||
|
@ -82,9 +88,28 @@ def register_document_feature(features):
|
|||
js=['wagtaildocs/js/hallo-plugins/hallo-wagtaildoclink.js'],
|
||||
)
|
||||
)
|
||||
features.register_editor_plugin(
|
||||
'draftail', 'document-link', draftail_features.EntityFeature({
|
||||
'label': 'Document',
|
||||
'type': ENTITY_TYPES.DOCUMENT,
|
||||
'icon': 'icon-doc-full',
|
||||
'source': 'DocumentSource',
|
||||
'decorator': 'Document',
|
||||
})
|
||||
)
|
||||
|
||||
features.register_converter_rule('editorhtml', 'document-link', [
|
||||
LinkTypeRule('document', DocumentLinkHandler),
|
||||
])
|
||||
features.register_converter_rule('contentstate', 'document-link', {
|
||||
'from_database_format': {
|
||||
'a[linktype="document"]': DocumentLinkElementHandler('DOCUMENT'),
|
||||
},
|
||||
'to_database_format': {
|
||||
'entity_decorators': {ENTITY_TYPES.DOCUMENT: DocumentEntity}
|
||||
}
|
||||
})
|
||||
|
||||
features.default_features.append('document-link')
|
||||
|
||||
|
||||
|
|
|
@ -2,8 +2,13 @@ from django.conf.urls import include, url
|
|||
from django.urls import reverse
|
||||
from django.utils.html import format_html
|
||||
|
||||
from draftjs_exporter.constants import ENTITY_TYPES
|
||||
|
||||
from wagtail.admin.rich_text import HalloPlugin
|
||||
from wagtail.admin.rich_text.converters.contentstate import Embed as EmbedEntity
|
||||
from wagtail.admin.rich_text.converters.editor_html import EmbedTypeRule
|
||||
from wagtail.admin.rich_text.converters.html_to_contentstate import MediaEmbedElementHandler
|
||||
import wagtail.admin.rich_text.editors.draftail.features as draftail_features
|
||||
from wagtail.core import hooks
|
||||
from wagtail.embeds import urls
|
||||
from wagtail.embeds.rich_text import MediaEmbedHandler, media_embedtype_handler
|
||||
|
@ -48,5 +53,27 @@ def register_embed_feature(features):
|
|||
EmbedTypeRule('media', MediaEmbedHandler)
|
||||
])
|
||||
|
||||
# define a draftail plugin to use when the 'embed' feature is active
|
||||
features.register_editor_plugin(
|
||||
'draftail', 'embed', draftail_features.EntityFeature({
|
||||
'label': 'Embed',
|
||||
'type': ENTITY_TYPES.EMBED,
|
||||
'icon': 'icon-media',
|
||||
'source': 'EmbedSource',
|
||||
'decorator': 'Embed',
|
||||
})
|
||||
)
|
||||
|
||||
# define how to convert between contentstate's representation of embeds and
|
||||
# the database representation
|
||||
features.register_converter_rule('contentstate', 'embed', {
|
||||
'from_database_format': {
|
||||
'embed[embedtype="media"]': MediaEmbedElementHandler(),
|
||||
},
|
||||
'to_database_format': {
|
||||
'entity_decorators': {ENTITY_TYPES.EMBED: EmbedEntity}
|
||||
}
|
||||
})
|
||||
|
||||
# add 'embed' to the set of on-by-default rich text features
|
||||
features.default_features.append('embed')
|
||||
|
|
|
@ -5,9 +5,14 @@ from django.utils.html import format_html, format_html_join
|
|||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.utils.translation import ungettext
|
||||
|
||||
from draftjs_exporter.constants import ENTITY_TYPES
|
||||
|
||||
from wagtail.admin.menu import MenuItem
|
||||
from wagtail.admin.rich_text import HalloPlugin
|
||||
from wagtail.admin.rich_text.converters.contentstate import Image as ImageEntity
|
||||
from wagtail.admin.rich_text.converters.editor_html import EmbedTypeRule
|
||||
from wagtail.admin.rich_text.converters.html_to_contentstate import ImageElementHandler
|
||||
import wagtail.admin.rich_text.editors.draftail.features as draftail_features
|
||||
from wagtail.admin.search import SearchArea
|
||||
from wagtail.admin.site_summary import SummaryItem
|
||||
from wagtail.core import hooks
|
||||
|
@ -84,6 +89,22 @@ def register_image_feature(features):
|
|||
EmbedTypeRule('image', ImageEmbedHandler)
|
||||
])
|
||||
|
||||
# define a draftail plugin to use when the 'image' feature is active
|
||||
features.register_editor_plugin(
|
||||
'draftail', 'image', draftail_features.ImageFeature()
|
||||
)
|
||||
|
||||
# define how to convert between contentstate's representation of images and
|
||||
# the database representation
|
||||
features.register_converter_rule('contentstate', 'image', {
|
||||
'from_database_format': {
|
||||
'embed[embedtype="image"]': ImageElementHandler(),
|
||||
},
|
||||
'to_database_format': {
|
||||
'entity_decorators': {ENTITY_TYPES.IMAGE: ImageEntity}
|
||||
}
|
||||
})
|
||||
|
||||
# add 'image' to the set of on-by-default rich text features
|
||||
features.default_features.append('image')
|
||||
|
||||
|
|
Ładowanie…
Reference in New Issue