Ссылка на идентификатор фрагмента в текстовом формате трясогузки

У меня есть куча контента в текстовом поле Wagtail 2.0, которое выглядит как

Page heading
(intro blurb)

heading 1
(heading-1-relevant text)

heading 2
(heading-2-relevant text)

...

и я хотел бы дать каждому заголовку id, чтобы любой текст можно было сделать ссылкой для перехода к соответствующему контенту. Кажется, я не могу найти возможность присвоить заголовкам явный id, а кнопка «ссылка» в редакторе форматированного текста, похоже, не позволяет мне выбирать идентификаторы активных фрагментов в содержимом.

Есть ли способ добавить навигацию на основе идентификатора фрагмента на той же странице, работающую с текстовым редактором Wagtail?


person Mike 'Pomax' Kamermans    schedule 21.03.2018    source источник
comment
Здесь есть два отдельных вопроса: как добавить идентификаторы в заголовки в расширенном тексте и как добавить ссылки с идентификаторами фрагментов. Ни у одного из них нет простых ответов в трясогузке прямо сейчас (всхлип), поэтому они, вероятно, требуют отдельных вопросов. Ссылки с идентификаторами фрагментов см. на github.com/wagtail/wagtail/issues/1049. Я оставлю комментарий, чтобы объяснить, как это может работать.   -  person Thibaud Colas    schedule 24.03.2018
comment
Если вам нужны идентификаторы, контролируемые пользователем, то нет возможности настроить рендеринг блоков заголовков редактора форматированного текста, чтобы добавить туда поле. Лучше всего создать собственный объект (например, github.com/wagtail). /wagtail/issues/1049#issuecomment-375815036 и официальная документация Stock), которые позволят авторам размещать тег с идентификатором в произвольном тексте или без текста в произвольной точке содержимого — например. <h2><a id="my-anchor"></a> My heading</h2>.   -  person Thibaud Colas    schedule 24.03.2018


Ответы (2)


Возвращаясь к моему собственному вопросу год спустя, потому что это все еще то, что нам нужно, решение, которое мы придумали, состоит в том, чтобы просто обернуть сериализацию RichText html и поместить инъекцию идентификатора фрагмента сверху:

import re
from django import template
from django.utils.text import slugify
from wagtail.core.rich_text import RichText

# We'll be wrapping the original RichText.__html__(), so make
# sure we have a reference to it that we can call.
__original__html__ = RichText.__html__

# This matches an h1/.../h6, using a regexp that is only
# guaranteed to work because we know that the source of
# the HTML code we'll be working with generates nice
# and predictable HTML code (and note the non-greedy
# "one or more" for the heading content).
heading_re = r"<h([1-6])([^>]*)>(.+?)</h\1>"


def add_id_attribute(match):
    """
    This is a regexp replacement function that takes
    in the above regex match results, and then turns:
        <h1>some text</h1>
    Into:
        <h1><a id="some-text"></a><a href="#some-text">some text</a></h1>
    where the id attribute value is generated by running
    the heading text through Django's slugify() function.
    """
    n = match.group(1)
    attributes= match.group(2)
    text_content = match.group(3)
    id = slugify(text_content)
    return f'<h{n}{attributes}><a id="{id}"></a><a href="#{id}">{text_content}</a></h{n}>'


def with_heading_ids(self):
    """
    We don't actually change how RichText.__html__ works, we just replace
    it with a function that does "whatever it already did", plus a
    substitution pass that adds fragment ids and their associated link
    elements to any headings that might be in the rich text content.
    """
    html = __original__html__(self)
    return re.sub(heading_re, add_id_attribute, html)


# Rebind the RichText's html serialization function such that
# the output is still entirely functional as far as wagtail
# can tell, except with headings enriched with fragment ids.
RichText.__html__ = with_heading_ids

Это работает довольно хорошо, не требует никакого взлома в draftail или wagtail, и его очень легко включить/отключить, просто загрузив этот код как часть процесса запуска сервера (мы живем в нашем файле wagtailcustom_tags.py, поэтому, когда Django загружает все наборы тегов шаблона, обогащение RichText включается автоматически).

Сначала мы пытались расширить фильтр шаблонов ... | richtext, но, хотя это вполне возможно, это работает только для пользовательских блоков, которые мы сами написали, с нашими собственными пользовательскими шаблонами, и поэтому оказалось, что это не решение, учитывая идею, что это должно просто работать .

person Mike 'Pomax' Kamermans    schedule 12.03.2019
comment
Это круто. Испытанная трясогузка 2.13 - person Nandesh; 24.05.2021

Чтобы иметь контроль над структурой тела вашей страницы, желательно поощрять пользователей использовать блоки заголовков, а не заголовки внутри блока форматированного текста. Затем у вас может быть тип блока заголовка с двумя полями: «текст» и «идентификатор», и вы можете укажите шаблон, который выводит элемент h с атрибутом id.

class Heading2Block(blocks.StructBlock):
    heading = blocks.CharBlock(classname='full title')
    link_id = blocks.CharBlock(help_text='For making hyperlinks to this heading')

    class Meta:
        template = 'blocks/h2.html'

Поместите следующее в blocks/h2.html:

<h1{% if value.link_id %} id="{{ value.link_id|slugify }}"{% endif %}>{{ value.heading }}</h1>

В более ранних версиях трясогузки можно было удалить виджет h из текстового редактора Hallo.js, и это был хороший способ поощрения пользователей к принятию блока заголовка. Подобного ограничения в настоящее время нет в Draftail, но есть запрос на извлечение, который переопределяет его. .

person nimasmi    schedule 23.03.2018
comment
Как отмечает @Alexey, настройка Draftail для этого, вероятно, является лучшей перспективой, чем когда-либо с hallo.js, но вышеизложенное требует меньше работы. - person nimasmi; 23.03.2018
comment
Я также понимаю, что просто предположил, что вы используете Stream Field и блок Rich Text, а не RichTextField непосредственно в модели страницы. - person nimasmi; 23.03.2018
comment
Кроме того, несмотря на дзен трясогузки, принуждение создателей контента к разделению своего контента — это мышление с надетой шляпой разработчика. Было бы хорошо, если бы аудиторией были другие разработчики, но это не так. Редактирование черновика, чтобы заставить работать связывание фрагментов, кажется, путь вперед. - person Mike 'Pomax' Kamermans; 23.03.2018
comment
Я не согласен с такой интерпретацией. Редактор не должен использовать поле форматированного текста для создания структуры страницы в режиме WYSIWYG, а только отформатированный текст. Преобразование заголовков (или изображений, или цитат) в их собственные типы блоков убирает структуру из поля форматированного текста и, в качестве бонуса, отдает разметку в руки разработчика. Этот конкретный случай представляет собой немного более серую область. Я бы сказал, что если бы атрибут id был сгенерирован автоматически, то функция Draftail покрывала бы его, но для чего-то, с чем взаимодействует редактор, лучше использовать отдельный блок. - person nimasmi; 23.03.2018