2017-04-24 15:51:12 +00:00
|
|
|
#
|
2023-04-04 16:39:45 +00:00
|
|
|
# Copyright 2023 © AE UTBM
|
|
|
|
# ae@utbm.fr / ae.info@utbm.fr
|
2017-04-24 15:51:12 +00:00
|
|
|
#
|
2023-04-04 16:39:45 +00:00
|
|
|
# This file is part of the website of the UTBM Student Association (AE UTBM),
|
|
|
|
# https://ae.utbm.fr.
|
2017-04-24 15:51:12 +00:00
|
|
|
#
|
2023-04-04 16:39:45 +00:00
|
|
|
# You can find the source code of the website at https://github.com/ae-utbm/sith3
|
2017-04-24 15:51:12 +00:00
|
|
|
#
|
2023-04-04 16:39:45 +00:00
|
|
|
# LICENSED UNDER THE GNU GENERAL PUBLIC LICENSE VERSION 3 (GPLv3)
|
|
|
|
# SEE : https://raw.githubusercontent.com/ae-utbm/sith3/master/LICENSE
|
|
|
|
# OR WITHIN THE LOCAL FILE "LICENSE"
|
2017-04-24 15:51:12 +00:00
|
|
|
#
|
|
|
|
#
|
2024-07-01 15:33:05 +00:00
|
|
|
from __future__ import annotations
|
2017-04-24 15:51:12 +00:00
|
|
|
|
2016-12-24 00:41:49 +00:00
|
|
|
import re
|
2024-07-01 15:33:05 +00:00
|
|
|
from typing import TYPE_CHECKING
|
2024-06-24 11:07:36 +00:00
|
|
|
|
2024-07-01 15:33:05 +00:00
|
|
|
import mistune
|
2019-10-06 11:28:56 +00:00
|
|
|
from django.urls import reverse
|
2024-07-01 15:33:05 +00:00
|
|
|
from mistune import HTMLRenderer, Markdown
|
|
|
|
|
|
|
|
if TYPE_CHECKING:
|
|
|
|
from mistune import InlineParser, InlineState
|
|
|
|
|
|
|
|
# match __text__, without linebreak in the text, nor backslash prepending an underscore
|
|
|
|
# Examples :
|
|
|
|
# - "__text__" : OK
|
|
|
|
# - "__te xt__" : OK
|
|
|
|
# - "__te_xt__" : nope (underscore in the middle)
|
|
|
|
# - "__te\_xt__" : Ok (the middle underscore is escaped)
|
|
|
|
# - "__te\nxt__" : nope (there is a linebreak in the text)
|
|
|
|
# - "\__text__" : nope (one of the underscores have a backslash prepended)
|
|
|
|
# - "\\__text__" : Ok (the backslash is ignored, because there is another backslash before)
|
|
|
|
UNDERLINED_RE = (
|
|
|
|
r"(?<!\\)(?:\\{2})*" # ignore if there is an odd number of backslashes before
|
|
|
|
r"_{2}" # two underscores
|
|
|
|
r"(?P<underlined>([^\\_]|\\.)+)" # the actual text
|
|
|
|
r"_{2}" # closing underscores
|
|
|
|
)
|
|
|
|
|
|
|
|
SITH_LINK_RE = (
|
|
|
|
r"\[(?P<page_name>[\w\s]+)\]" # [nom du lien]
|
|
|
|
r"\(page:\/\/" # (page://
|
|
|
|
r"(?P<page_slug>[a-zA-Z0-9][a-zA-Z0-9.-]*[a-zA-Z0-9])" # actual page name
|
|
|
|
r"\)" # )
|
|
|
|
)
|
|
|
|
|
|
|
|
CUSTOM_DIMENSIONS_IMAGE_RE = (
|
|
|
|
r"\[(?P<img_name>[\w\s]+)\]" # [nom du lien]
|
|
|
|
r"\(img:\/\/" # (img://
|
|
|
|
r"(?P<img_slug>[a-zA-Z0-9][a-zA-Z0-9.-]*[a-zA-Z0-9])" # actual page name
|
|
|
|
r"\)" # )
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
def parse_underline(_inline: InlineParser, m: re.Match, state: InlineState):
|
|
|
|
state.append_token({"type": "underline", "raw": m.group("underlined")})
|
|
|
|
return m.end()
|
|
|
|
|
|
|
|
|
|
|
|
def underline(md_instance: Markdown):
|
|
|
|
md_instance.inline.register(
|
2018-10-04 19:29:19 +00:00
|
|
|
"underline",
|
2024-07-01 15:33:05 +00:00
|
|
|
UNDERLINED_RE,
|
|
|
|
parse_underline,
|
|
|
|
before="emphasis",
|
|
|
|
)
|
|
|
|
md_instance.renderer.register("underline", lambda _, text: f"<u>{text}</u>")
|
|
|
|
|
|
|
|
|
|
|
|
def parse_sith_link(_inline: InlineParser, m: re.Match, state: InlineState):
|
|
|
|
page_name = m.group("page_name")
|
|
|
|
page_slug = m.group("page_slug")
|
|
|
|
state.append_token(
|
|
|
|
{
|
|
|
|
"type": "link",
|
|
|
|
"children": [{"type": "text", "raw": page_name}],
|
|
|
|
"attrs": {"url": reverse("core:page", kwargs={"page_name": page_slug})},
|
|
|
|
}
|
|
|
|
)
|
|
|
|
return m.end()
|
|
|
|
|
|
|
|
|
|
|
|
def sith_link(md_instance: Markdown):
|
|
|
|
md_instance.inline.register(
|
|
|
|
"sith_link",
|
|
|
|
SITH_LINK_RE,
|
|
|
|
parse_sith_link,
|
|
|
|
before="emphasis",
|
|
|
|
)
|
|
|
|
# no custom renderer here.
|
|
|
|
# we just add another parsing rule, but render it as if it was
|
|
|
|
# a regular markdown link
|
|
|
|
|
|
|
|
|
|
|
|
class SithRenderer(HTMLRenderer):
|
|
|
|
def image(self, text: str, url: str, title=None) -> str:
|
|
|
|
if "?" not in url:
|
|
|
|
return super().image(text, url, title)
|
|
|
|
|
|
|
|
new_url, params = url.rsplit("?", maxsplit=1)
|
|
|
|
m = re.match(r"^(?P<width>\d+(%|px)?)(x(?P<height>\d+(%|px)?))?$", params)
|
|
|
|
if not m:
|
|
|
|
return super().image(text, url, title)
|
|
|
|
|
|
|
|
width, height = m.group("width"), m.group("height")
|
|
|
|
if not width.endswith(("%", "px")):
|
|
|
|
width += "px"
|
|
|
|
style = f"width:{width};"
|
|
|
|
if height is not None:
|
|
|
|
if not height.endswith(("%", "px")):
|
|
|
|
height += "px"
|
|
|
|
style += f"height:{height};"
|
|
|
|
return super().image(text, new_url, title).replace("/>", f'style="{style}" />')
|
|
|
|
|
|
|
|
|
|
|
|
markdown = mistune.create_markdown(
|
|
|
|
renderer=SithRenderer(escape=True),
|
|
|
|
plugins=[
|
|
|
|
underline,
|
|
|
|
sith_link,
|
2018-10-04 19:29:19 +00:00
|
|
|
"strikethrough",
|
2024-07-01 15:33:05 +00:00
|
|
|
"footnotes",
|
|
|
|
"table",
|
|
|
|
"spoiler",
|
|
|
|
"subscript",
|
|
|
|
"superscript",
|
2018-10-04 19:29:19 +00:00
|
|
|
"url",
|
2024-07-01 15:33:05 +00:00
|
|
|
],
|
|
|
|
)
|