Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion md2cf/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ def __init__(
def _request(self, method, path, **kwargs):
r = self.api.request(method, urljoin(self.host, path), **kwargs)
r.raise_for_status()

return bunchify(r.json())

def _get(self, path, **kwargs):
Expand Down Expand Up @@ -186,13 +187,14 @@ def update_page(
update_message=None,
labels=None,
minor_edit=False,
title=None,
):
update_structure = {
"version": {
"number": page.version.number + 1,
"minorEdit": minor_edit,
},
"title": page.title,
"title": title if title is not None else page.title,
"type": content_type,
"body": {"storage": {"value": body, "representation": "storage"}},
}
Expand Down
117 changes: 96 additions & 21 deletions md2cf/confluence_renderer.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import uuid
import re
from pathlib import Path
from typing import List, NamedTuple
from typing import List, NamedTuple, Optional, Any
from urllib.parse import unquote, urlparse

import mistune
Expand Down Expand Up @@ -60,7 +61,7 @@ def append(self, child):
self.children.append(child)


class ConfluenceRenderer(mistune.Renderer):
class ConfluenceRenderer(mistune.HTMLRenderer):
def __init__(
self,
strip_header=False,
Expand Down Expand Up @@ -90,8 +91,8 @@ def header(self, text, level, raw=None):

return super(ConfluenceRenderer, self).header(text, level, raw=raw)

def structured_macro(self, name):
return ConfluenceTag("structured-macro", attrib={"name": name})
def structured_macro(self, name, text=""):
return ConfluenceTag("structured-macro", attrib={"name": name}, text=text)

def parameter(self, name, value):
parameter_tag = ConfluenceTag("parameter", attrib={"name": name})
Expand All @@ -103,8 +104,13 @@ def plain_text_body(self, text):
body_tag.text = text
return body_tag

def link(self, link, title, text):
parsed_link = urlparse(link)
def rich_text_body(self, text):
body_tag = ConfluenceTag("rich-text-body", cdata=False)
body_tag.text = text
return body_tag

def link(self, text, url, title=None):
parsed_link = urlparse(url)
if (
self.enable_relative_links
and (not parsed_link.scheme and not parsed_link.netloc)
Expand All @@ -119,44 +125,113 @@ def link(self, link, title, text):
path=unquote(parsed_link.path),
replacement=replacement_link,
fragment=parsed_link.fragment,
original=link,
escaped_original=mistune.escape_link(link),
original=url,
escaped_original=mistune.escape_link(url),
)
)
link = replacement_link
return super(ConfluenceRenderer, self).link(link, title, text)
url = replacement_link
return super(ConfluenceRenderer, self).link(text, url, title)

def text(self, text):
if self.remove_text_newlines:
text = text.replace("\n", " ")

return super().text(text)

def block_code(self, code, lang=None):
def block_code(self, code, info=None):
root_element = self.structured_macro("code")
if lang is not None:
lang_parameter = self.parameter(name="language", value=lang)
if info is not None:
lang_parameter = self.parameter(name="language", value=info)
root_element.append(lang_parameter)
root_element.append(self.parameter(name="linenumbers", value="true"))
root_element.append(self.plain_text_body(code))
return root_element.render()

def image(self, src, title, text):
attributes = {"alt": text}
if title:
attributes["title"] = title
def image(self, alt, url, title=None, width=None, height=None):
attributes = {"alt": alt,
"title": title if title is not None else alt,
}
if width:
attributes["width"] = width
if height:
attributes["height"] = height

root_element = ConfluenceTag(name="image", attrib=attributes)
parsed_source = urlparse(src)
parsed_source = urlparse(url)
if not parsed_source.netloc:
# Local file, requires upload
basename = Path(src).name
basename = Path(url).name
url_tag = ConfluenceTag(
"attachment", attrib={"filename": basename}, namespace="ri"
)
self.attachments.append(src)
self.attachments.append(url)
else:
url_tag = ConfluenceTag("url", attrib={"value": src}, namespace="ri")
url_tag = ConfluenceTag("url", attrib={"value": url}, namespace="ri")
root_element.append(url_tag)

return root_element.render()

def strikethrough(self, text):
return f"""<span style="text-decoration: line-through;">{text}</span>"""

def task_list_item(self, text, checked=False, **attrs):
return f"""
<ac:task-list>
<ac:task>
<ac:task-status>{"in" if not checked else ""}complete</ac:task-status>
<ac:task-body>{text}</ac:task-body>
</ac:task>
</ac:task-list>
"""

def block_spoiler(self, text):
lines = text.splitlines(keepends=True)
firstline = re.sub('<.*?>', '', lines[0])

root_element = self.structured_macro("expand")
title_param = self.parameter(name="title", value=firstline)
root_element.append(title_param)

root_element.append(self.rich_text_body(''.join(lines[1:])))
return root_element.render()


def mark(self, text):
return f"""<span style="background: yellow;">{text}</span>"""

def insert(self, text):
return f"""<span style="color: red;">{text}</span>"""

def admonition(self, text: str, name: str, **attrs) -> str:
confluence_mapping = {"tip" : "tip",
"attention": "warning",
"caution": "warning",
"danger": "warning",
"error": "warning",
"hint" : "tip",
"important": "note",
"note": "info",
"warning": "warning"}

adm_class = confluence_mapping.get(name, "info")
root_element = self.structured_macro(name=adm_class, text=text)
return root_element.render()

def admonition_title(self, text: str) -> str:
param = self.parameter(name="title", value=text)
return param.render()


def admonition_content(self, text: str) -> str:
body = self.rich_text_body(text)
return body.render()

def block_image(
self,
src: str,
alt: Optional[str] = None,
width: Optional[str] = None,
height: Optional[str] = None,
**attrs: Any,
) -> str:
return self.image(alt, src, alt, width, height)
1 change: 1 addition & 0 deletions md2cf/upsert.py
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@ def upsert_page(
update_message=page_message,
labels=page.labels if replace_all_labels else None,
minor_edit=minor_edit,
title=page.title
)
action = UpsertAction.UPDATED
else:
Expand Down
4 changes: 2 additions & 2 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,9 @@
install_requires=[
"rich-argparse==1.0.0",
"rich==13.0.1",
"mistune==0.8.4",
"mistune==3.1.2",
"chardet==5.1.0",
"requests==2.31.0",
"requests==2.32.3",
"PyYAML==6.0.1",
"gitignorefile==1.1.2",
],
Expand Down