def render_entities(self, style_node):
        # We have a complete (start, stop) entity to render.
        if self.completed_entity is not None:
            entity_details = self.get_entity_details(self.completed_entity)
            opts = Options.for_entity(self.entity_decorators, entity_details['type'])
            props = entity_details['data'].copy()
            props['entity'] = {
                'type': entity_details['type'],
            }

            if len(self.element_stack) == 1:
                children = self.element_stack[0]
            else:
                children = DOM.create_element()

                for n in self.element_stack:
                    DOM.append_child(children, n)

            self.completed_entity = None
            self.element_stack = []

            # Is there still another entity? (adjacent) if so add the current style_node for it.
            if self.has_entity():
                self.element_stack.append(style_node)

            return DOM.create_element(opts.element, props, children)

        if self.has_entity():
            self.element_stack.append(style_node)
            return None

        return style_node
def button(props):
    href = props.get('href', '#')
    icon_name = props.get('icon', None)
    text = props.get('text', '')

    return DOM.create_element(
        'a',
        {'class': 'icon-text' if icon_name else None, 'href': href},
        DOM.create_element(icon, {'name': icon_name}) if icon_name else None,
        DOM.create_element('span', {'class': 'icon-text__text'}, text) if icon_name else text
    )
def image(props):
    return DOM.create_element('img', {
        'src': props.get('src'),
        'width': props.get('width'),
        'height': props.get('height'),
        'alt': props.get('alt'),
    })
    def update_stack(self, options, depth):
        if depth >= self.stack.length():
            # If the depth is gte the stack length, we need more wrappers.
            depth_levels = range(self.stack.length(), depth + 1)

            for level in depth_levels:
                new_wrapper = Wrapper(level, options)

                # Determine where to append the new wrapper.
                if self.stack.head().last_child is None:
                    # If there is no content in the current wrapper, we need
                    # to add an intermediary node.
                    props = dict(options.props)
                    props['block'] = {
                        'type': options.type,
                        'depth': depth,
                        'data': {},
                    }
                    props['blocks'] = self.blocks

                    wrapper_parent = DOM.create_element(options.element, props)
                    DOM.append_child(self.stack.head().elt, wrapper_parent)
                else:
                    # Otherwise we can append at the end of the last child.
                    wrapper_parent = self.stack.head().last_child

                DOM.append_child(wrapper_parent, new_wrapper.elt)

                self.stack.append(new_wrapper)
        else:
            # Cut the stack to where it now stops, and add new wrapper.
            self.stack.slice(depth)
            self.stack.append(Wrapper(depth, options))
Exemple #5
0
    def render(self, content_state=None):
        """
        Starts the export process on a given piece of content state.
        """
        if content_state is None:
            content_state = {}

        blocks = content_state.get('blocks', [])
        wrapper_state = WrapperState(self.block_map, blocks)
        document = DOM.create_element()
        entity_map = content_state.get('entityMap', {})
        min_depth = 0

        for block in blocks:
            depth = block['depth']
            elt = self.render_block(block, entity_map, wrapper_state)

            if depth > min_depth:
                min_depth = depth

            # At level 0, append the element to the document.
            if depth == 0:
                DOM.append_child(document, elt)

        # If there is no block at depth 0, we need to add the wrapper that contains the whole tree to the document.
        if min_depth > 0 and wrapper_state.stack.length() != 0:
            DOM.append_child(document, wrapper_state.stack.tail().elt)

        return DOM.render(document)
def link(props):
    attributes = {}
    for key in props:
        attr = key if key != 'url' else 'href'
        attributes[attr] = props[key]

    return DOM.create_element('a', attributes, props['children'])
Exemple #7
0
    def render_block(self, block, entity_map, wrapper_state):
        if block['inlineStyleRanges'] or block['entityRanges']:
            content = DOM.create_element()
            entity_state = EntityState(self.entity_decorators, entity_map)
            style_state = StyleState(self.style_map)

            for (text, commands) in self.build_command_groups(block):
                for command in commands:
                    entity_state.apply(command)
                    style_state.apply(command)

                # Decorators are not rendered inside entities.
                if entity_state.has_no_entity() and self.has_decorators:
                    decorated_node = render_decorators(self.composite_decorators, text, block, wrapper_state.blocks)
                else:
                    decorated_node = text

                styled_node = style_state.render_styles(decorated_node, block, wrapper_state.blocks)
                entity_node = entity_state.render_entities(styled_node)

                if entity_node is not None:
                    DOM.append_child(content, entity_node)

                    # Check whether there actually are two different nodes, confirming we are not inserting an upcoming entity.
                    if styled_node != entity_node and entity_state.has_no_entity():
                        DOM.append_child(content, styled_node)
        # Fast track for blocks which do not contain styles nor entities, which is very common.
        elif self.has_decorators:
            content = render_decorators(self.composite_decorators, block['text'], block, wrapper_state.blocks)
        else:
            content = block['text']

        return wrapper_state.element_for(block, content)
    def test_render_www(self):
        match = next(LINKIFY_DECORATOR['strategy'].finditer('test www.example.com'))

        self.assertEqual(DOM.render(DOM.create_element(LINKIFY_DECORATOR['component'], {
            'block': {'type': BLOCK_TYPES.UNSTYLED},
            'match': match,
        }, match.group(0))), '<a href="http://www.example.com">www.example.com</a>')
    def test_render_code_block(self):
        match = next(LINKIFY_DECORATOR['strategy'].finditer('test https://www.example.com'))

        self.assertEqual(DOM.create_element(LINKIFY_DECORATOR['component'], {
            'block': {'type': BLOCK_TYPES.CODE},
            'match': match,
        }, match.group(0)), match.group(0))
def hashtag(props):
    """
    Wrap hashtags in spans with a specific class.
    """
    # Do not process matches inside code blocks.
    if props['block']['type'] == BLOCK_TYPES.CODE:
        return props['children']

    return DOM.create_element('span', {'class': 'hashtag'}, props['children'])
def br(props):
    """
    Replace line breaks (\n) with br tags.
    """
    # Do not process matches inside code blocks.
    if props['block']['type'] == BLOCK_TYPES.CODE:
        return props['children']

    return DOM.create_element('br')
    def __init__(self, depth, options=None):
        self.depth = depth
        self.last_child = None

        if options:
            self.type = options.wrapper
            self.props = options.wrapper_props

            wrapper_props = dict(self.props) if self.props else {}
            wrapper_props['block'] = {
                'type': options.type,
                'depth': depth,
            }

            self.elt = DOM.create_element(self.type, wrapper_props)
        else:
            self.type = None
            self.props = None
            self.elt = DOM.create_element()
Exemple #13
0
def media_embed_entity(props):
    """
    Helper to construct elements of the form
    <embed embedtype="media" url="https://www.youtube.com/watch?v=y8Kyi0WNg40"/>
    when converting from contentstate data
    """
    return DOM.create_element('embed', {
        'embedtype': 'media',
        'url': props.get('url'),
    })
Exemple #14
0
def document_link_entity(props):
    """
    Helper to construct elements of the form
    <a id="1" linktype="document">document link</a>
    when converting from contentstate data
    """

    return DOM.create_element('a', {
        'linktype': 'document',
        'id': props.get('id'),
    }, props['children'])
def render_decorators(decorators, text, block, blocks):
    decorated_children = list(apply_decorators(decorators, text, block, blocks))

    if len(decorated_children) == 1:
        decorated_node = decorated_children[0]
    else:
        decorated_node = DOM.create_element()
        for decorated_child in decorated_children:
            DOM.append_child(decorated_node, decorated_child)

    return decorated_node
Exemple #16
0
def image_entity(props):
    """
    Helper to construct elements of the form
    <embed alt="Right-aligned image" embedtype="image" format="right" id="1"/>
    when converting from contentstate data
    """
    return DOM.create_element('embed', {
        'embedtype': 'image',
        'format': props.get('format'),
        'id': props.get('id'),
        'alt': props.get('alt'),
    })
Exemple #17
0
 def test_element_for_dismiss_content(self):
     self.assertEqual(
         DOM.render_debug(
             self.wrapper_state.element_for(
                 {
                     'key': '5s7g9',
                     'text': 'Paragraph',
                     'type': 'ignore',
                     'depth': 0,
                     'inlineStyleRanges': [],
                     'entityRanges': []
                 }, DOM.create_element('img', {'src': '/example.png'}))),
         '<fragment></fragment>')
    def render_link(self, props):
        if "url" in props:
            attribs = {"href": props["url"]}
        else:
            link_data = props.get("link", {})
            if link_data.get("attachment"):
                attribs = {"data-attachment": link_data["attachment"]}
            elif link_data.get("target"):
                attribs = {"href": link_data["href"], "target": link_data["target"]}
            else:
                attribs = {"href": link_data["href"]}

        return DOM.create_element("a", attribs, props["children"])
Exemple #19
0
 def test_element_for_element_content(self):
     self.assertEqual(
         DOM.render_debug(
             self.wrapper_state.element_for(
                 {
                     'key': '5s7g9',
                     'text': 'Paragraph',
                     'type': 'unstyled',
                     'depth': 0,
                     'inlineStyleRanges': [],
                     'entityRanges': []
                 }, DOM.create_element('strong', {}, 'Paragraph'))),
         '<div><strong>Paragraph</strong></div>')
Exemple #20
0
 def test_element_for_no_block(self):
     self.assertEqual(
         DOM.render_debug(
             self.wrapper_state.element_for(
                 {
                     'key': '5s7g9',
                     'text': 'Paragraph',
                     'type': 'atomic',
                     'depth': 0,
                     'inlineStyleRanges': [],
                     'entityRanges': []
                 }, DOM.create_element('img', {'src': '/example.png'}))),
         '<img src="/example.png"/>')
def titled_link_entity(props):
    """
    <a linktype="page" id="1">internal page link</a>
    """
    id_ = props.get('id')
    link_props = {'title': props.get('link_title', '')}
    if id_ is not None:
        link_props['linktype'] = 'page'
        link_props['id'] = id_
    else:
        link_props['href'] = props.get('url')

    return DOM.create_element('a', link_props, props['children'])
    def test_render_www(self):
        match = next(
            LINKIFY_DECORATOR['strategy'].finditer('test www.example.com'))

        self.assertEqual(
            DOM.render(
                DOM.create_element(LINKIFY_DECORATOR['component'], {
                    'block': {
                        'type': BLOCK_TYPES.UNSTYLED
                    },
                    'match': match,
                }, match.group(0))),
            '<a href="http://www.example.com">www.example.com</a>')
Exemple #23
0
def anchor_entity_decorator(props):
    """
    Draft.js ContentState to database HTML.
    Converts the ANCHOR entities into <a> tags.
    """
    return DOM.create_element(
        'a',
        {
            'data-anchor': True,
            # 'href': props['fragment'],
            'href': '#{}'.format(props['fragment'].lstrip('#')),
        },
        props['children'])
Exemple #24
0
 def element_with_uuid(props):
     added_props = {BLOCK_KEY_NAME: props['block'].get('key')}
     try:
         # See if the element is a function - if so, we can only run it and modify its return value to include the data attribute
         elt = element(props)
         if elt is not None:
             elt.attr.update(added_props)
         return elt
     except TypeError:
         # Otherwise we can do the normal process of creating a DOM element with the right element type
         # and simply adding the data attribute to its props
         added_props.update(element_props)
         return DOM.create_element(element, added_props, props['children'])
 def test_render_without_icon(self):
     button = DOM.create_element(
         Button,
         {'data': {
             'href': 'http://example.com',
             'text': 'Launch',
         }})
     self.assertEqual(DOM.get_tag_name(button), 'a')
     self.assertEqual(DOM.get_text_content(button), 'Launch')
     self.assertEqual(button.get('href'), 'http://example.com')
     self.assertEqual(DOM.get_class_list(button), [])
     self.assertEqual(DOM.render(button),
                      '<a href="http://example.com">Launch</a>')
Exemple #26
0
def mention(props):
    """
    Decorator for the `mention` entity in Draft.js ContentState.

    This entity is added by the `draft-js-mention-plugin` plugin.
    (https://www.draft-js-plugins.com/plugin/mention)
    """

    user_id = props.get("mention").get("user")
    name = props.get("mention").get("name")

    if name and user_id:
        return DOM.create_element(
            "a",
            {
                "class": "mention",
                "href": f"/forum/member/profile/{user_id}/",
            },
            DOM.create_element("span", {"class": "mention"}, f"@{name}"),
        )

    return None
Exemple #27
0
 def test_render_with_icon(self):
     button = DOM.create_element(Button, {
         'data': {
             'href': 'http://example.com',
             'icon': 'rocket',
             'text': 'Launch',
         }
     })
     self.assertEqual(DOM.get_tag_name(button), 'a')
     self.assertEqual(DOM.get_text_content(button), None)
     self.assertEqual(button.get('href'), 'http://example.com')
     self.assertEqual(DOM.get_class_list(button), ['icon-text'])
     self.assertEqual(DOM.render(button), '<a class="icon-text" href="http://example.com"><svg class="icon"><use xlink:href="icon-rocket"></use></svg><span class="icon-text__text">Launch</span></a>')
def news_image_entity_decorator(props):
    """
    Convert our custom image embed from the Draft.js ContentState data into database HTML.
    """
    return DOM.create_element(
        'embed', {
            'embedtype': 'news-image',
            'id': props.get('id'),
            'alt': props.get('alt'),
            'title': props.get('title'),
            'href': props.get('href'),
            'width': props.get('width'),
        })
Exemple #29
0
def media_embed_entity(props):
    """
    Helper to construct elements of the form
    <embed embedtype="media" url="https://www.youtube.com/watch?v=y8Kyi0WNg40"/>
    when converting from contentstate data
    """
    return DOM.create_element(
        "embed",
        {
            "embedtype": "media",
            "url": props.get("url"),
        },
    )
Exemple #30
0
def image_entity(props):
    """
    Helper to construct elements of the form
    <embed alt="Right-aligned image" embedtype="image" format="right" id="1"/>
    when converting from contentstate data
    """
    return DOM.create_element(
        'embed', {
            'embedtype': 'image',
            'format': props.get('format'),
            'id': props.get('id'),
            'alt': props.get('alt'),
        })
Exemple #31
0
    def render_entities(self, style_node: Element, block: Block, blocks: Sequence[Block]) -> Element:
        # We have a complete (start, stop) entity to render.
        if self.completed_entity is not None:
            entity_details = self.get_entity_details(self.completed_entity)
            options = Options.get(self.entity_options, entity_details['type'], ENTITY_TYPES.FALLBACK)
            props = entity_details['data'].copy()
            props['entity'] = {
                'type': entity_details['type'],
                'mutability': entity_details['mutability'] if 'mutability' in entity_details else None,
                'block': block,
                'blocks': blocks,
                'entity_range': {
                    'key': self.completed_entity,
                },
            }

            if len(self.element_stack) == 1:
                children = self.element_stack[0]
            else:
                children = DOM.create_element()

                for n in self.element_stack:
                    DOM.append_child(children, n)

            self.completed_entity = None
            self.element_stack = []

            # Is there still another entity? (adjacent) if so add the current style_node for it.
            if self.has_entity():
                self.element_stack.append(style_node)

            return DOM.create_element(options.element, props, children)

        if self.has_entity():
            self.element_stack.append(style_node)
            return None

        return style_node
def link(props):
    """
    <a linktype="page" id="1">internal page link</a>
    """
    link_type = props.get('linkType', '')
    link_props = {}

    if link_type == 'page':
        link_props['linktype'] = link_type
        link_props['id'] = props.get('id')
    else:
        link_props['href'] = props.get('url')

    return DOM.create_element('a', link_props, props['children'])
Exemple #33
0
def anchor_identifier_entity_decorator(props):
    """
    Draft.js ContentState to database HTML.
    Converts the ANCHOR entities into <a> tags.
    """
    return DOM.create_element(
        "a",
        {
            "data-id": props["anchor"].lstrip("#"),
            "id": props["anchor"].lstrip("#"),
            "href": "#{}".format(props["anchor"].lstrip("#")),
        },
        props["children"],
    )
Exemple #34
0
def link_entity(props):
    """
    <a linktype="page" id="1">internal page link</a>
    """
    id_ = props.get('id')
    link_props = {}

    if id_ is not None:
        link_props['linktype'] = 'page'
        link_props['id'] = id_
    else:
        link_props['href'] = props.get('url')

    return DOM.create_element('a', link_props, props['children'])
Exemple #35
0
    def element_for(self, block, block_content):
        type_ = block['type'] if 'type' in block else 'unstyled'
        depth = block['depth'] if 'depth' in block else 0
        options = Options.get(self.block_options, type_, BLOCK_TYPES.FALLBACK)
        props = dict(options.props)
        props['block'] = block
        props['blocks'] = self.blocks

        # Make an element from the options specified in the block map.
        elt = DOM.create_element(options.element, props, block_content)

        parent = self.parent_for(options, depth, elt)

        return parent
    def element_for(self, block, block_content):
        type_ = block['type']
        depth = block['depth']
        options = Options.for_block(self.block_map, type_)
        props = dict(options.props)
        props['block'] = block
        props['blocks'] = self.blocks

        # Make an element from the options specified in the block map.
        elt = DOM.create_element(options.element, props, block_content)

        parent = self.parent_for(options, depth, elt)

        return parent
Exemple #37
0
def link_entity(props):
    """
    <a linktype="page" id="1">internal page link</a>
    """
    id_ = props.get("id")
    link_props = {}

    if id_ is not None:
        link_props["linktype"] = "page"
        link_props["id"] = id_
    else:
        link_props["href"] = check_url(props.get("url"))

    return DOM.create_element("a", link_props, props["children"])
Exemple #38
0
    def element_for(self, block, block_content):
        type_ = block['type']
        depth = block['depth']
        options = Options.for_block(self.block_map, type_)
        props = dict(options.props)
        props['block'] = block
        props['blocks'] = self.blocks

        # Make an element from the options specified in the block map.
        elt = DOM.create_element(options.element, props, block_content)

        parent = self.parent_for(options, depth, elt)

        return parent
Exemple #39
0
    def render_table(self, props):
        num_cols = props["data"]["numCols"]
        num_rows = props["data"]["numRows"]
        with_header = props["data"].get("withHeader", False)
        cells = props["data"]["cells"]
        table = DOM.create_element("table")
        if with_header:
            start_row = 1
            thead = DOM.create_element("thead")
            DOM.append_child(table, thead)
            tr = DOM.create_element("tr")
            DOM.append_child(thead, tr)
            for col_idx in range(num_cols):
                th = DOM.create_element("th")
                DOM.append_child(tr, th)
                try:
                    content_state = cells[0][col_idx]
                except IndexError:
                    continue
                try:
                    content = DOM.parse_html(
                        self.exporter.render(content_state))
                except etree.ParserError:
                    continue
                if content.text or len(content):
                    DOM.append_child(th, content)
        else:
            start_row = 0

        if not with_header or num_rows > 1:
            tbody = DOM.create_element("tbody")
            DOM.append_child(table, tbody)

        for row_idx in range(start_row, num_rows):
            tr = DOM.create_element("tr")
            DOM.append_child(tbody, tr)
            for col_idx in range(num_cols):
                td = DOM.create_element("td")
                DOM.append_child(tr, td)
                try:
                    content_state = cells[row_idx][col_idx]
                except IndexError:
                    continue
                try:
                    content = DOM.parse_html(
                        self.exporter.render(content_state))
                except etree.ParserError:
                    continue
                if content.text or len(content):
                    DOM.append_child(td, content)

        return table
def block_fallback(props):
    type_ = props['block']['type']

    if type_ == 'example-discard':
        logging.warn('Missing config for "%s". Discarding block, keeping content.' % type_)
        # Directly return the block's children to keep its content.
        return props['children']
    elif type_ == 'example-delete':
        logging.error('Missing config for "%s". Deleting block.' % type_)
        # Return None to not render anything, removing the whole block.
        return None
    else:
        logging.warn('Missing config for "%s". Using div instead.' % type_)
        # Provide a fallback.
        return DOM.create_element('div', {}, props['children'])
def image(props):
    """
    Components are simple functions that take `props` as parameter and return DOM elements.
    This component creates an image element, with the relevant attributes.

    :param props:
    :return:
    """
    return DOM.create_element(
        'img', {
            'src': props.get('src'),
            'width': props.get('width'),
            'height': props.get('height'),
            'alt': props.get('alt'),
        })
Exemple #42
0
 def test_create_element_nested(self):
     self.assertEqual(
         DOM.render_debug(
             DOM.create_element(
                 "a",
                 {},
                 DOM.create_element(
                     "span",
                     {"class": "file-info icon-text"},
                     DOM.create_element(
                         "span", {"class": "icon-text__text"}, "Test test"
                     ),
                     DOM.create_element(
                         "svg",
                         {"class": "icon"},
                         DOM.create_element(
                             "use", {"xlink:href": "#icon-test"}
                         ),
                     ),
                 ),
             )
         ),
         '<a><span class="file-info icon-text"><span class="icon-text__text">Test test</span><svg class="icon"><use xlink:href="#icon-test"></use></svg></span></a>',
     )
    def replace(self, match, block_type):
        protocol = match.group(1)
        url = match.group(2)
        href = protocol + url
        if block_type == BLOCK_TYPES.CODE:
            return DOM.create_text_node(href)

        text = cgi.escape(href)
        if href.startswith("www"):
            href = "http://" + href
        props = {'href': href}
        if self.new_window:
            props.update(target="_blank")

        return DOM.create_element('a', props, text)
Exemple #44
0
    def render_styles(self, decorated_node, block, blocks):
        node = decorated_node
        if not self.is_empty():
            # Nest the tags.
            for style in sorted(self.styles, reverse=True):
                opt = Options.for_style(self.style_map, style)
                props = dict(opt.props)
                props['block'] = block
                props['blocks'] = blocks
                props['inline_style_range'] = {
                    'style': style,
                }
                node = DOM.create_element(opt.element, props, node)

        return node
Exemple #45
0
def document_link_entity(props):
    """
    Helper to construct elements of the form
    <a id="1" linktype="document">document link</a>
    when converting from contentstate data
    """

    return DOM.create_element(
        "a",
        {
            "linktype": "document",
            "id": props.get("id"),
        },
        props["children"],
    )
Exemple #46
0
 def test_create_element_style_dict(self):
     self.assertEqual(
         DOM.render_debug(
             DOM.create_element(
                 "p",
                 {
                     "style": {
                         "borderColor": "red",
                         "textDecoration": "underline",
                     }
                 },
                 "Test test",
             )),
         '<p style="border-color: red;text-decoration: underline;">Test test</p>',
     )
Exemple #47
0
    def create_node(self, text):
        text_lines = self.replace_linebreaks(text)

        if self.is_unstyled():
            node = text_lines
        else:
            tags = self.get_style_tags()
            node = DOM.create_element(tags[0])
            child = node

            # Nest the tags.
            # Set the text and style attribute (if any) on the deepest node.
            for tag in tags[1:]:
                new_child = DOM.create_element(tag)
                DOM.append_child(child, new_child)
                child = new_child

            style_value = self.get_style_value()
            if style_value:
                DOM.set_attribute(child, 'style', style_value)

            DOM.append_child(child, text_lines)

        return node
Exemple #48
0
    def set_wrapper(self, options=[], depth=0):
        if len(options) == 0:
            element = DOM.create_document_fragment()
        else:
            element = DOM.create_element(options[0], options[1])

        new_wrapper = [element, depth, options]

        if depth >= len(self.wrapper_stack):
            DOM.append_child(DOM.get_children(self.get_wrapper_elt())[-1], element)

            self.wrapper_stack.append(new_wrapper)
        else:
            # Cut the stack to where it now stops, and add new wrapper.
            self.wrapper_stack = self.wrapper_stack[:depth] + [new_wrapper]
Exemple #49
0
    def replace_linebreaks(self, text):
        lines = text.split('\n')

        if len(lines) > 1:
            wrapper = DOM.create_document_fragment()

            DOM.append_child(wrapper, DOM.create_text_node(lines[0]))

            for l in lines[1:]:
                DOM.append_child(wrapper, DOM.create_element('br'))
                DOM.append_child(wrapper, DOM.create_text_node(l))
        else:
            wrapper = DOM.create_text_node(text)

        return wrapper
def apply_decorators(decorators, text, block, blocks):
    decorations = get_decorations(decorators, text)

    pointer = 0
    for begin, end, match, decorator in decorations:
        if pointer < begin:
            yield text[pointer:begin]

        yield DOM.create_element(decorator['component'], {
            'match': match,
            'block': block,
            'blocks': blocks,
        }, match.group(0))
        pointer = end

    if pointer < len(text):
        yield text[pointer:]
def linkify(props):
    """
    Wrap plain URLs with link tags.
    """
    match = props['match']
    protocol = match.group(1)
    url = match.group(2)
    href = protocol + url

    if props['block']['type'] == BLOCK_TYPES.CODE:
        return href

    link_props = {
        'href': href,
    }

    if href.startswith('www'):
        link_props['href'] = 'http://' + href

    return DOM.create_element('a', link_props, href)
Exemple #52
0
def register_core_features(features):
    # Hallo.js
    features.register_editor_plugin(
        'hallo', 'hr',
        HalloPlugin(
            name='hallohr',
            js=['wagtailadmin/js/hallo-plugins/hallo-hr.js'],
            order=45,
        )
    )
    features.register_converter_rule('editorhtml', 'hr', [
        WhitelistRule('hr', allow_without_attributes)
    ])

    features.register_editor_plugin(
        'hallo', 'link',
        HalloPlugin(
            name='hallowagtaillink',
            js=['wagtailadmin/js/hallo-plugins/hallo-wagtaillink.js'],
        )
    )
    features.register_converter_rule('editorhtml', 'link', [
        WhitelistRule('a', attribute_rule({'href': check_url})),
        LinkTypeRule('page', PageLinkHandler),
    ])

    features.register_editor_plugin(
        'hallo', 'bold', HalloFormatPlugin(format_name='bold')
    )
    features.register_converter_rule('editorhtml', 'bold', [
        WhitelistRule('b', allow_without_attributes),
        WhitelistRule('strong', allow_without_attributes),
    ])

    features.register_editor_plugin(
        'hallo', 'italic', HalloFormatPlugin(format_name='italic')
    )
    features.register_converter_rule('editorhtml', 'italic', [
        WhitelistRule('i', allow_without_attributes),
        WhitelistRule('em', allow_without_attributes),
    ])

    headings_elements = ['h1', 'h2', 'h3', 'h4', 'h5', 'h6']
    headings_order_start = HalloHeadingPlugin.default_order + 1
    for order, element in enumerate(headings_elements, start=headings_order_start):
        features.register_editor_plugin(
            'hallo', element, HalloHeadingPlugin(element=element, order=order)
        )
        features.register_converter_rule('editorhtml', element, [
            WhitelistRule(element, allow_without_attributes)
        ])

    features.register_editor_plugin(
        'hallo', 'ol', HalloListPlugin(list_type='ordered')
    )
    features.register_converter_rule('editorhtml', 'ol', [
        WhitelistRule('ol', allow_without_attributes),
        WhitelistRule('li', allow_without_attributes),
    ])

    features.register_editor_plugin(
        'hallo', 'ul', HalloListPlugin(list_type='unordered')
    )
    features.register_converter_rule('editorhtml', 'ul', [
        WhitelistRule('ul', allow_without_attributes),
        WhitelistRule('li', allow_without_attributes),
    ])

    # Draftail
    features.register_editor_plugin(
        'draftail', 'hr', draftail_features.BooleanFeature('enableHorizontalRule')
    )
    features.register_converter_rule('contentstate', 'hr', {
        'from_database_format': {
            'hr': HorizontalRuleHandler(),
        },
        'to_database_format': {
            'entity_decorators': {'HORIZONTAL_RULE': lambda props: DOM.create_element('hr')}
        }
    })

    features.register_editor_plugin(
        'draftail', 'h1', draftail_features.BlockFeature({
            'label': 'H1',
            'type': 'header-one',
            'description': ugettext('Heading {level}').format(level=1),
        })
    )
    features.register_converter_rule('contentstate', 'h1', {
        'from_database_format': {
            'h1': BlockElementHandler('header-one'),
        },
        'to_database_format': {
            'block_map': {'header-one': 'h1'}
        }
    })
    features.register_editor_plugin(
        'draftail', 'h2', draftail_features.BlockFeature({
            'label': 'H2',
            'type': 'header-two',
            'description': ugettext('Heading {level}').format(level=2),
        })
    )
    features.register_converter_rule('contentstate', 'h2', {
        'from_database_format': {
            'h2': BlockElementHandler('header-two'),
        },
        'to_database_format': {
            'block_map': {'header-two': 'h2'}
        }
    })
    features.register_editor_plugin(
        'draftail', 'h3', draftail_features.BlockFeature({
            'label': 'H3',
            'type': 'header-three',
            'description': ugettext('Heading {level}').format(level=3),
        })
    )
    features.register_converter_rule('contentstate', 'h3', {
        'from_database_format': {
            'h3': BlockElementHandler('header-three'),
        },
        'to_database_format': {
            'block_map': {'header-three': 'h3'}
        }
    })
    features.register_editor_plugin(
        'draftail', 'h4', draftail_features.BlockFeature({
            'label': 'H4',
            'type': 'header-four',
            'description': ugettext('Heading {level}').format(level=4),
        })
    )
    features.register_converter_rule('contentstate', 'h4', {
        'from_database_format': {
            'h4': BlockElementHandler('header-four'),
        },
        'to_database_format': {
            'block_map': {'header-four': 'h4'}
        }
    })
    features.register_editor_plugin(
        'draftail', 'h5', draftail_features.BlockFeature({
            'label': 'H5',
            'type': 'header-five',
            'description': ugettext('Heading {level}').format(level=5),
        })
    )
    features.register_converter_rule('contentstate', 'h5', {
        'from_database_format': {
            'h5': BlockElementHandler('header-five'),
        },
        'to_database_format': {
            'block_map': {'header-five': 'h5'}
        }
    })
    features.register_editor_plugin(
        'draftail', 'h6', draftail_features.BlockFeature({
            'label': 'H6',
            'type': 'header-six',
            'description': ugettext('Heading {level}').format(level=6),
        })
    )
    features.register_converter_rule('contentstate', 'h6', {
        'from_database_format': {
            'h6': BlockElementHandler('header-six'),
        },
        'to_database_format': {
            'block_map': {'header-six': 'h6'}
        }
    })
    features.register_editor_plugin(
        'draftail', 'ul', draftail_features.BlockFeature({
            'type': 'unordered-list-item',
            'icon': 'list-ul',
            'description': ugettext('Bulleted list'),
        })
    )
    features.register_converter_rule('contentstate', 'ul', {
        'from_database_format': {
            'ul': ListElementHandler('unordered-list-item'),
            'li': ListItemElementHandler(),
        },
        'to_database_format': {
            'block_map': {'unordered-list-item': {'element': 'li', 'wrapper': 'ul'}}
        }
    })
    features.register_editor_plugin(
        'draftail', 'ol', draftail_features.BlockFeature({
            'type': 'ordered-list-item',
            'icon': 'list-ol',
            'description': ugettext('Numbered list'),
        })
    )
    features.register_converter_rule('contentstate', 'ol', {
        'from_database_format': {
            'ol': ListElementHandler('ordered-list-item'),
            'li': ListItemElementHandler(),
        },
        'to_database_format': {
            'block_map': {'ordered-list-item': {'element': 'li', 'wrapper': 'ol'}}
        }
    })

    features.register_editor_plugin(
        'draftail', 'bold', draftail_features.InlineStyleFeature({
            'type': 'BOLD',
            'icon': 'bold',
            'description': ugettext('Bold'),
        })
    )
    features.register_converter_rule('contentstate', 'bold', {
        'from_database_format': {
            'b': InlineStyleElementHandler('BOLD'),
            'strong': InlineStyleElementHandler('BOLD'),
        },
        'to_database_format': {
            'style_map': {'BOLD': 'b'}
        }
    })
    features.register_editor_plugin(
        'draftail', 'italic', draftail_features.InlineStyleFeature({
            'type': 'ITALIC',
            'icon': 'italic',
            'description': ugettext('Italic'),
        })
    )
    features.register_converter_rule('contentstate', 'italic', {
        'from_database_format': {
            'i': InlineStyleElementHandler('ITALIC'),
            'em': InlineStyleElementHandler('ITALIC'),
        },
        'to_database_format': {
            'style_map': {'ITALIC': 'i'}
        }
    })

    features.register_editor_plugin(
        'draftail', 'link', draftail_features.EntityFeature({
            'type': 'LINK',
            'icon': 'link',
            'description': ugettext('Link'),
            # We want to enforce constraints on which links can be pasted into rich text.
            # Keep only the attributes Wagtail needs.
            'attributes': ['url', 'id', 'parentId'],
            'whitelist': {
                # Keep pasted links with http/https protocol, and not-pasted links (href = undefined).
                'href': "^(http:|https:|undefined$)",
            }
        })
    )
    features.register_converter_rule('contentstate', 'link', {
        'from_database_format': {
            'a[href]': ExternalLinkElementHandler('LINK'),
            'a[linktype="page"]': PageLinkElementHandler('LINK'),
        },
        'to_database_format': {
            'entity_decorators': {'LINK': link_entity}
        }
    })
def entity_fallback(props):
    type_ = props['entity']['type']
    logging.warn('Missing config for "%s".' % type_)
    return DOM.create_element('span', {'class': 'missing-entity'}, props['children'])
def code_block(props):
    return DOM.create_element('pre', {}, DOM.create_element('code', {}, props['children']))
Exemple #55
0
def br(props):
    if props['block']['type'] == 'code-block':
        return props['children']

    return DOM.create_element('br')
def blockquote(props):
    block_data = props['block']['data']

    return DOM.create_element('blockquote', {
        'cite': block_data.get('cite')
    }, props['children'])
def list_item(props):
    depth = props['block']['depth']

    return DOM.create_element('li', {
        'class': 'list-item--depth-{0}'.format(depth)
    }, props['children'])
def ordered_list(props):
    depth = props['block']['depth']

    return DOM.create_element('ol', {
        'class': 'list--depth-{0}'.format(depth)
    }, props['children'])
def link(props):
    return DOM.create_element('a', {
        'href': props['url']
    }, props['children'])