mkdocs2confluence 0.8.4__tar.gz → 0.9.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {mkdocs2confluence-0.8.4 → mkdocs2confluence-0.9.0}/PKG-INFO +10 -2
- {mkdocs2confluence-0.8.4 → mkdocs2confluence-0.9.0}/README.md +9 -1
- {mkdocs2confluence-0.8.4 → mkdocs2confluence-0.9.0}/pyproject.toml +1 -1
- {mkdocs2confluence-0.8.4 → mkdocs2confluence-0.9.0}/src/mkdocs2confluence.egg-info/PKG-INFO +10 -2
- {mkdocs2confluence-0.8.4 → mkdocs2confluence-0.9.0}/src/mkdocs2confluence.egg-info/SOURCES.txt +2 -0
- {mkdocs2confluence-0.8.4 → mkdocs2confluence-0.9.0}/src/mkdocs_to_confluence/emitter/xhtml.py +25 -32
- {mkdocs2confluence-0.8.4 → mkdocs2confluence-0.9.0}/src/mkdocs_to_confluence/ir/nodes.py +20 -4
- {mkdocs2confluence-0.8.4 → mkdocs2confluence-0.9.0}/src/mkdocs_to_confluence/publisher/pipeline.py +7 -2
- {mkdocs2confluence-0.8.4 → mkdocs2confluence-0.9.0}/src/mkdocs_to_confluence/transforms/editlink.py +14 -15
- mkdocs2confluence-0.9.0/src/mkdocs_to_confluence/transforms/footer.py +69 -0
- {mkdocs2confluence-0.8.4 → mkdocs2confluence-0.9.0}/tests/test_editlink.py +19 -15
- mkdocs2confluence-0.9.0/tests/test_footer.py +119 -0
- {mkdocs2confluence-0.8.4 → mkdocs2confluence-0.9.0}/tests/test_frontmatter.py +1 -68
- {mkdocs2confluence-0.8.4 → mkdocs2confluence-0.9.0}/LICENSE +0 -0
- {mkdocs2confluence-0.8.4 → mkdocs2confluence-0.9.0}/setup.cfg +0 -0
- {mkdocs2confluence-0.8.4 → mkdocs2confluence-0.9.0}/src/mkdocs2confluence.egg-info/dependency_links.txt +0 -0
- {mkdocs2confluence-0.8.4 → mkdocs2confluence-0.9.0}/src/mkdocs2confluence.egg-info/entry_points.txt +0 -0
- {mkdocs2confluence-0.8.4 → mkdocs2confluence-0.9.0}/src/mkdocs2confluence.egg-info/requires.txt +0 -0
- {mkdocs2confluence-0.8.4 → mkdocs2confluence-0.9.0}/src/mkdocs2confluence.egg-info/top_level.txt +0 -0
- {mkdocs2confluence-0.8.4 → mkdocs2confluence-0.9.0}/src/mkdocs_to_confluence/__init__.py +0 -0
- {mkdocs2confluence-0.8.4 → mkdocs2confluence-0.9.0}/src/mkdocs_to_confluence/cli.py +0 -0
- {mkdocs2confluence-0.8.4 → mkdocs2confluence-0.9.0}/src/mkdocs_to_confluence/emitter/__init__.py +0 -0
- {mkdocs2confluence-0.8.4 → mkdocs2confluence-0.9.0}/src/mkdocs_to_confluence/ir/__init__.py +0 -0
- {mkdocs2confluence-0.8.4 → mkdocs2confluence-0.9.0}/src/mkdocs_to_confluence/ir/document.py +0 -0
- {mkdocs2confluence-0.8.4 → mkdocs2confluence-0.9.0}/src/mkdocs_to_confluence/ir/treeutil.py +0 -0
- {mkdocs2confluence-0.8.4 → mkdocs2confluence-0.9.0}/src/mkdocs_to_confluence/loader/__init__.py +0 -0
- {mkdocs2confluence-0.8.4 → mkdocs2confluence-0.9.0}/src/mkdocs_to_confluence/loader/config.py +0 -0
- {mkdocs2confluence-0.8.4 → mkdocs2confluence-0.9.0}/src/mkdocs_to_confluence/loader/extra_css.py +0 -0
- {mkdocs2confluence-0.8.4 → mkdocs2confluence-0.9.0}/src/mkdocs_to_confluence/loader/nav.py +0 -0
- {mkdocs2confluence-0.8.4 → mkdocs2confluence-0.9.0}/src/mkdocs_to_confluence/loader/page.py +0 -0
- {mkdocs2confluence-0.8.4 → mkdocs2confluence-0.9.0}/src/mkdocs_to_confluence/parser/__init__.py +0 -0
- {mkdocs2confluence-0.8.4 → mkdocs2confluence-0.9.0}/src/mkdocs_to_confluence/parser/markdown.py +0 -0
- {mkdocs2confluence-0.8.4 → mkdocs2confluence-0.9.0}/src/mkdocs_to_confluence/pdf/__init__.py +0 -0
- {mkdocs2confluence-0.8.4 → mkdocs2confluence-0.9.0}/src/mkdocs_to_confluence/pdf/generator.py +0 -0
- {mkdocs2confluence-0.8.4 → mkdocs2confluence-0.9.0}/src/mkdocs_to_confluence/pdf/render.py +0 -0
- {mkdocs2confluence-0.8.4 → mkdocs2confluence-0.9.0}/src/mkdocs_to_confluence/preprocess/__init__.py +0 -0
- {mkdocs2confluence-0.8.4 → mkdocs2confluence-0.9.0}/src/mkdocs_to_confluence/preprocess/abbrevs.py +0 -0
- {mkdocs2confluence-0.8.4 → mkdocs2confluence-0.9.0}/src/mkdocs_to_confluence/preprocess/fence.py +0 -0
- {mkdocs2confluence-0.8.4 → mkdocs2confluence-0.9.0}/src/mkdocs_to_confluence/preprocess/frontmatter.py +0 -0
- {mkdocs2confluence-0.8.4 → mkdocs2confluence-0.9.0}/src/mkdocs_to_confluence/preprocess/icons.py +0 -0
- {mkdocs2confluence-0.8.4 → mkdocs2confluence-0.9.0}/src/mkdocs_to_confluence/preprocess/includes.py +0 -0
- {mkdocs2confluence-0.8.4 → mkdocs2confluence-0.9.0}/src/mkdocs_to_confluence/preprocess/linkdefs.py +0 -0
- {mkdocs2confluence-0.8.4 → mkdocs2confluence-0.9.0}/src/mkdocs_to_confluence/preview/__init__.py +0 -0
- {mkdocs2confluence-0.8.4 → mkdocs2confluence-0.9.0}/src/mkdocs_to_confluence/preview/render.py +0 -0
- {mkdocs2confluence-0.8.4 → mkdocs2confluence-0.9.0}/src/mkdocs_to_confluence/preview/server.py +0 -0
- {mkdocs2confluence-0.8.4 → mkdocs2confluence-0.9.0}/src/mkdocs_to_confluence/publisher/__init__.py +0 -0
- {mkdocs2confluence-0.8.4 → mkdocs2confluence-0.9.0}/src/mkdocs_to_confluence/publisher/client.py +0 -0
- {mkdocs2confluence-0.8.4 → mkdocs2confluence-0.9.0}/src/mkdocs_to_confluence/sync/__init__.py +0 -0
- {mkdocs2confluence-0.8.4 → mkdocs2confluence-0.9.0}/src/mkdocs_to_confluence/sync/anchoring.py +0 -0
- {mkdocs2confluence-0.8.4 → mkdocs2confluence-0.9.0}/src/mkdocs_to_confluence/sync/command.py +0 -0
- {mkdocs2confluence-0.8.4 → mkdocs2confluence-0.9.0}/src/mkdocs_to_confluence/sync/comments.py +0 -0
- {mkdocs2confluence-0.8.4 → mkdocs2confluence-0.9.0}/src/mkdocs_to_confluence/sync/github.py +0 -0
- {mkdocs2confluence-0.8.4 → mkdocs2confluence-0.9.0}/src/mkdocs_to_confluence/sync/platform.py +0 -0
- {mkdocs2confluence-0.8.4 → mkdocs2confluence-0.9.0}/src/mkdocs_to_confluence/sync/state.py +0 -0
- {mkdocs2confluence-0.8.4 → mkdocs2confluence-0.9.0}/src/mkdocs_to_confluence/transforms/__init__.py +0 -0
- {mkdocs2confluence-0.8.4 → mkdocs2confluence-0.9.0}/src/mkdocs_to_confluence/transforms/abbrevs.py +0 -0
- {mkdocs2confluence-0.8.4 → mkdocs2confluence-0.9.0}/src/mkdocs_to_confluence/transforms/assets.py +0 -0
- {mkdocs2confluence-0.8.4 → mkdocs2confluence-0.9.0}/src/mkdocs_to_confluence/transforms/images.py +0 -0
- {mkdocs2confluence-0.8.4 → mkdocs2confluence-0.9.0}/src/mkdocs_to_confluence/transforms/internallinks.py +0 -0
- {mkdocs2confluence-0.8.4 → mkdocs2confluence-0.9.0}/src/mkdocs_to_confluence/transforms/mermaid.py +0 -0
- {mkdocs2confluence-0.8.4 → mkdocs2confluence-0.9.0}/tests/test_abbrevs.py +0 -0
- {mkdocs2confluence-0.8.4 → mkdocs2confluence-0.9.0}/tests/test_cli.py +0 -0
- {mkdocs2confluence-0.8.4 → mkdocs2confluence-0.9.0}/tests/test_emitter.py +0 -0
- {mkdocs2confluence-0.8.4 → mkdocs2confluence-0.9.0}/tests/test_extra_css.py +0 -0
- {mkdocs2confluence-0.8.4 → mkdocs2confluence-0.9.0}/tests/test_icons.py +0 -0
- {mkdocs2confluence-0.8.4 → mkdocs2confluence-0.9.0}/tests/test_images.py +0 -0
- {mkdocs2confluence-0.8.4 → mkdocs2confluence-0.9.0}/tests/test_internallinks.py +0 -0
- {mkdocs2confluence-0.8.4 → mkdocs2confluence-0.9.0}/tests/test_ir.py +0 -0
- {mkdocs2confluence-0.8.4 → mkdocs2confluence-0.9.0}/tests/test_linkdefs.py +0 -0
- {mkdocs2confluence-0.8.4 → mkdocs2confluence-0.9.0}/tests/test_loader.py +0 -0
- {mkdocs2confluence-0.8.4 → mkdocs2confluence-0.9.0}/tests/test_mermaid.py +0 -0
- {mkdocs2confluence-0.8.4 → mkdocs2confluence-0.9.0}/tests/test_page_loader.py +0 -0
- {mkdocs2confluence-0.8.4 → mkdocs2confluence-0.9.0}/tests/test_parser.py +0 -0
- {mkdocs2confluence-0.8.4 → mkdocs2confluence-0.9.0}/tests/test_pdf.py +0 -0
- {mkdocs2confluence-0.8.4 → mkdocs2confluence-0.9.0}/tests/test_preprocess.py +0 -0
- {mkdocs2confluence-0.8.4 → mkdocs2confluence-0.9.0}/tests/test_preview.py +0 -0
- {mkdocs2confluence-0.8.4 → mkdocs2confluence-0.9.0}/tests/test_publish_client.py +0 -0
- {mkdocs2confluence-0.8.4 → mkdocs2confluence-0.9.0}/tests/test_publish_config.py +0 -0
- {mkdocs2confluence-0.8.4 → mkdocs2confluence-0.9.0}/tests/test_publish_pipeline.py +0 -0
- {mkdocs2confluence-0.8.4 → mkdocs2confluence-0.9.0}/tests/test_server.py +0 -0
- {mkdocs2confluence-0.8.4 → mkdocs2confluence-0.9.0}/tests/test_sync_anchoring.py +0 -0
- {mkdocs2confluence-0.8.4 → mkdocs2confluence-0.9.0}/tests/test_sync_command.py +0 -0
- {mkdocs2confluence-0.8.4 → mkdocs2confluence-0.9.0}/tests/test_sync_comments.py +0 -0
- {mkdocs2confluence-0.8.4 → mkdocs2confluence-0.9.0}/tests/test_sync_github.py +0 -0
- {mkdocs2confluence-0.8.4 → mkdocs2confluence-0.9.0}/tests/test_sync_state.py +0 -0
- {mkdocs2confluence-0.8.4 → mkdocs2confluence-0.9.0}/tests/test_treeutil.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: mkdocs2confluence
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.9.0
|
|
4
4
|
Summary: Publish MkDocs Material pages to Confluence Cloud — admonitions, Mermaid diagrams, tabs, page properties and more
|
|
5
5
|
Author: Anders Hybertz
|
|
6
6
|
License: GPL-3.0-or-later
|
|
@@ -423,7 +423,15 @@ status: in-progress
|
|
|
423
423
|
| `status` | Sets the Confluence page status badge — common values: `rough-draft`, `in-progress`, `ready-for-review` (space-specific values are also supported). Not shown in the properties table. |
|
|
424
424
|
| *other fields* | Title-cased key, value stringified |
|
|
425
425
|
|
|
426
|
-
If `
|
|
426
|
+
If `site_url` is set in `mkdocs.yml`, a **Published Page** row links to the rendered MkDocs site.
|
|
427
|
+
|
|
428
|
+
### Source footer
|
|
429
|
+
|
|
430
|
+
When `repo_url` + `edit_uri` are set in `mkdocs.yml`, a **Page source** footer panel is appended to the bottom of each published page containing:
|
|
431
|
+
|
|
432
|
+
- **Edit this page** — links to the source file in your VCS (GitHub/GitLab/etc.)
|
|
433
|
+
- **View history** — links to the file's commit history (derived automatically for GitHub and GitLab URLs)
|
|
434
|
+
- **Last commit** — short commit SHA, message, author, and relative date from `git log` at publish time (omitted if git is unavailable or the file is untracked)
|
|
427
435
|
|
|
428
436
|
### Abbreviation expansion
|
|
429
437
|
|
|
@@ -383,7 +383,15 @@ status: in-progress
|
|
|
383
383
|
| `status` | Sets the Confluence page status badge — common values: `rough-draft`, `in-progress`, `ready-for-review` (space-specific values are also supported). Not shown in the properties table. |
|
|
384
384
|
| *other fields* | Title-cased key, value stringified |
|
|
385
385
|
|
|
386
|
-
If `
|
|
386
|
+
If `site_url` is set in `mkdocs.yml`, a **Published Page** row links to the rendered MkDocs site.
|
|
387
|
+
|
|
388
|
+
### Source footer
|
|
389
|
+
|
|
390
|
+
When `repo_url` + `edit_uri` are set in `mkdocs.yml`, a **Page source** footer panel is appended to the bottom of each published page containing:
|
|
391
|
+
|
|
392
|
+
- **Edit this page** — links to the source file in your VCS (GitHub/GitLab/etc.)
|
|
393
|
+
- **View history** — links to the file's commit history (derived automatically for GitHub and GitLab URLs)
|
|
394
|
+
- **Last commit** — short commit SHA, message, author, and relative date from `git log` at publish time (omitted if git is unavailable or the file is untracked)
|
|
387
395
|
|
|
388
396
|
### Abbreviation expansion
|
|
389
397
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "mkdocs2confluence"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.9.0"
|
|
4
4
|
description = "Publish MkDocs Material pages to Confluence Cloud — admonitions, Mermaid diagrams, tabs, page properties and more"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
license = { text = "GPL-3.0-or-later" }
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: mkdocs2confluence
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.9.0
|
|
4
4
|
Summary: Publish MkDocs Material pages to Confluence Cloud — admonitions, Mermaid diagrams, tabs, page properties and more
|
|
5
5
|
Author: Anders Hybertz
|
|
6
6
|
License: GPL-3.0-or-later
|
|
@@ -423,7 +423,15 @@ status: in-progress
|
|
|
423
423
|
| `status` | Sets the Confluence page status badge — common values: `rough-draft`, `in-progress`, `ready-for-review` (space-specific values are also supported). Not shown in the properties table. |
|
|
424
424
|
| *other fields* | Title-cased key, value stringified |
|
|
425
425
|
|
|
426
|
-
If `
|
|
426
|
+
If `site_url` is set in `mkdocs.yml`, a **Published Page** row links to the rendered MkDocs site.
|
|
427
|
+
|
|
428
|
+
### Source footer
|
|
429
|
+
|
|
430
|
+
When `repo_url` + `edit_uri` are set in `mkdocs.yml`, a **Page source** footer panel is appended to the bottom of each published page containing:
|
|
431
|
+
|
|
432
|
+
- **Edit this page** — links to the source file in your VCS (GitHub/GitLab/etc.)
|
|
433
|
+
- **View history** — links to the file's commit history (derived automatically for GitHub and GitLab URLs)
|
|
434
|
+
- **Last commit** — short commit SHA, message, author, and relative date from `git log` at publish time (omitted if git is unavailable or the file is untracked)
|
|
427
435
|
|
|
428
436
|
### Abbreviation expansion
|
|
429
437
|
|
{mkdocs2confluence-0.8.4 → mkdocs2confluence-0.9.0}/src/mkdocs2confluence.egg-info/SOURCES.txt
RENAMED
|
@@ -49,6 +49,7 @@ src/mkdocs_to_confluence/transforms/__init__.py
|
|
|
49
49
|
src/mkdocs_to_confluence/transforms/abbrevs.py
|
|
50
50
|
src/mkdocs_to_confluence/transforms/assets.py
|
|
51
51
|
src/mkdocs_to_confluence/transforms/editlink.py
|
|
52
|
+
src/mkdocs_to_confluence/transforms/footer.py
|
|
52
53
|
src/mkdocs_to_confluence/transforms/images.py
|
|
53
54
|
src/mkdocs_to_confluence/transforms/internallinks.py
|
|
54
55
|
src/mkdocs_to_confluence/transforms/mermaid.py
|
|
@@ -57,6 +58,7 @@ tests/test_cli.py
|
|
|
57
58
|
tests/test_editlink.py
|
|
58
59
|
tests/test_emitter.py
|
|
59
60
|
tests/test_extra_css.py
|
|
61
|
+
tests/test_footer.py
|
|
60
62
|
tests/test_frontmatter.py
|
|
61
63
|
tests/test_icons.py
|
|
62
64
|
tests/test_images.py
|
{mkdocs2confluence-0.8.4 → mkdocs2confluence-0.9.0}/src/mkdocs_to_confluence/emitter/xhtml.py
RENAMED
|
@@ -19,7 +19,6 @@ from __future__ import annotations
|
|
|
19
19
|
import html
|
|
20
20
|
from pathlib import Path
|
|
21
21
|
from typing import Sequence
|
|
22
|
-
from urllib.parse import urlparse
|
|
23
22
|
|
|
24
23
|
from mkdocs_to_confluence.ir.nodes import (
|
|
25
24
|
AbbrevFootnoteNode,
|
|
@@ -52,6 +51,7 @@ from mkdocs_to_confluence.ir.nodes import (
|
|
|
52
51
|
RawHTML,
|
|
53
52
|
RawInlineHtml,
|
|
54
53
|
Section,
|
|
54
|
+
SourceFooter,
|
|
55
55
|
StrikethroughNode,
|
|
56
56
|
SubscriptNode,
|
|
57
57
|
SuperscriptNode,
|
|
@@ -196,6 +196,8 @@ def _emit_node(node: IRNode) -> str:
|
|
|
196
196
|
return _emit_abbrev_glossary_block(node)
|
|
197
197
|
if isinstance(node, GridCards):
|
|
198
198
|
return _emit_grid_cards(node)
|
|
199
|
+
if isinstance(node, SourceFooter):
|
|
200
|
+
return _emit_source_footer(node)
|
|
199
201
|
if isinstance(node, UnsupportedBlock):
|
|
200
202
|
return _emit_unsupported(node)
|
|
201
203
|
# Inline nodes at block level (shouldn't normally appear, but be safe)
|
|
@@ -216,29 +218,6 @@ def _emit_section(node: Section) -> str:
|
|
|
216
218
|
return heading + body
|
|
217
219
|
|
|
218
220
|
|
|
219
|
-
def _source_link_label(url: str) -> str:
|
|
220
|
-
"""Return a platform-aware label for the source edit link.
|
|
221
|
-
|
|
222
|
-
Detects GitHub, GitLab and Bitbucket from the URL hostname and returns
|
|
223
|
-
"Edit in <Platform> ↗". Falls back to "Edit source ↗" for anything else.
|
|
224
|
-
Only the proven-safe ↗ arrow is used — no emoji that may render as ??? on
|
|
225
|
-
Confluence Cloud.
|
|
226
|
-
"""
|
|
227
|
-
try:
|
|
228
|
-
hostname = urlparse(url).hostname or ""
|
|
229
|
-
except ValueError:
|
|
230
|
-
hostname = ""
|
|
231
|
-
if hostname == "github.com" or hostname.endswith(".github.com"):
|
|
232
|
-
platform = "GitHub"
|
|
233
|
-
elif hostname == "gitlab.com" or hostname.endswith(".gitlab.com") or "gitlab" in hostname:
|
|
234
|
-
platform = "GitLab"
|
|
235
|
-
elif hostname == "bitbucket.org" or hostname.endswith(".bitbucket.org"):
|
|
236
|
-
platform = "Bitbucket"
|
|
237
|
-
else:
|
|
238
|
-
return "Edit source \u2197"
|
|
239
|
-
return f"Edit in {platform} \u2197"
|
|
240
|
-
|
|
241
|
-
|
|
242
221
|
def _emit_front_matter(node: FrontMatter) -> str:
|
|
243
222
|
"""Emit front matter as an optional subtitle paragraph + Page Properties macro."""
|
|
244
223
|
parts: list[str] = []
|
|
@@ -246,20 +225,13 @@ def _emit_front_matter(node: FrontMatter) -> str:
|
|
|
246
225
|
if node.subtitle:
|
|
247
226
|
parts.append(f"<p><em>{html.escape(node.subtitle)}</em></p>\n")
|
|
248
227
|
|
|
249
|
-
has_table = node.properties or node.
|
|
228
|
+
has_table = node.properties or node.site_url
|
|
250
229
|
if has_table:
|
|
251
230
|
rows = "".join(
|
|
252
231
|
f" <tr><th>{html.escape(display)}</th>"
|
|
253
232
|
f"<td>{html.escape(value)}</td></tr>\n"
|
|
254
233
|
for display, value in node.properties
|
|
255
234
|
)
|
|
256
|
-
if node.source_url:
|
|
257
|
-
label = html.escape(_source_link_label(node.source_url))
|
|
258
|
-
href = html.escape(node.source_url)
|
|
259
|
-
rows += (
|
|
260
|
-
f' <tr><th>Source</th>'
|
|
261
|
-
f'<td><a href="{href}">{label}</a></td></tr>\n'
|
|
262
|
-
)
|
|
263
235
|
if node.site_url:
|
|
264
236
|
href = html.escape(node.site_url)
|
|
265
237
|
rows += (
|
|
@@ -279,6 +251,27 @@ def _emit_front_matter(node: FrontMatter) -> str:
|
|
|
279
251
|
return "".join(parts)
|
|
280
252
|
|
|
281
253
|
|
|
254
|
+
def _emit_source_footer(node: SourceFooter) -> str:
|
|
255
|
+
"""Emit a 'Page source' panel macro with edit/history links and last-commit info."""
|
|
256
|
+
edit_href = html.escape(node.edit_url)
|
|
257
|
+
links = f'<a href="{edit_href}">Edit this page</a>'
|
|
258
|
+
if node.history_url:
|
|
259
|
+
hist_href = html.escape(node.history_url)
|
|
260
|
+
links += f' \u00b7 <a href="{hist_href}">View history</a>'
|
|
261
|
+
body = f"<p>{links}</p>\n"
|
|
262
|
+
if node.last_commit:
|
|
263
|
+
body += f"<p><em>Last commit: {html.escape(node.last_commit)}</em></p>\n"
|
|
264
|
+
return (
|
|
265
|
+
'<ac:structured-macro ac:name="panel">\n'
|
|
266
|
+
' <ac:parameter ac:name="title">Page source</ac:parameter>\n'
|
|
267
|
+
' <ac:parameter ac:name="borderStyle">solid</ac:parameter>\n'
|
|
268
|
+
" <ac:rich-text-body>\n"
|
|
269
|
+
f" {body}"
|
|
270
|
+
" </ac:rich-text-body>\n"
|
|
271
|
+
"</ac:structured-macro>\n"
|
|
272
|
+
)
|
|
273
|
+
|
|
274
|
+
|
|
282
275
|
def _emit_paragraph(node: Paragraph) -> str:
|
|
283
276
|
return f"<p>{_emit_inlines(node.children)}</p>\n"
|
|
284
277
|
|
|
@@ -413,9 +413,6 @@ class FrontMatter(IRNode):
|
|
|
413
413
|
front matter extractor.
|
|
414
414
|
labels: Confluence page labels derived from the ``tags:`` field.
|
|
415
415
|
Applied via the REST API at publish time (not in XHTML).
|
|
416
|
-
source_url: Optional URL to the source file in the version-control
|
|
417
|
-
repository. Rendered as a clickable link row ("Source")
|
|
418
|
-
at the bottom of the Page Properties table.
|
|
419
416
|
site_url: Optional URL to the rendered page on the published MkDocs
|
|
420
417
|
site. Rendered as a "Published Page" row in the table.
|
|
421
418
|
"""
|
|
@@ -424,7 +421,6 @@ class FrontMatter(IRNode):
|
|
|
424
421
|
subtitle: str | None
|
|
425
422
|
properties: tuple[tuple[str, str], ...]
|
|
426
423
|
labels: tuple[str, ...]
|
|
427
|
-
source_url: str | None = None
|
|
428
424
|
site_url: str | None = None
|
|
429
425
|
confluence_status: str | None = None
|
|
430
426
|
|
|
@@ -432,6 +428,26 @@ class FrontMatter(IRNode):
|
|
|
432
428
|
# ── Abbreviation footnotes ────────────────────────────────────────────────────
|
|
433
429
|
|
|
434
430
|
|
|
431
|
+
@dataclass(frozen=True)
|
|
432
|
+
class SourceFooter(IRNode):
|
|
433
|
+
"""Footer panel showing source-control links and last-commit info.
|
|
434
|
+
|
|
435
|
+
Emitted as a Confluence ``panel`` macro at the bottom of the page.
|
|
436
|
+
|
|
437
|
+
Attributes:
|
|
438
|
+
edit_url: URL to edit the source file (e.g. GitHub edit link).
|
|
439
|
+
history_url: URL to the file's commit history. ``None`` when it
|
|
440
|
+
cannot be derived from ``edit_url``.
|
|
441
|
+
last_commit: Human-readable last-commit summary from ``git log``,
|
|
442
|
+
e.g. ``"abc1234 · Fix typo · Jane · 2 days ago"``.
|
|
443
|
+
``None`` when git is unavailable or the file is untracked.
|
|
444
|
+
"""
|
|
445
|
+
|
|
446
|
+
edit_url: str
|
|
447
|
+
history_url: str | None = None
|
|
448
|
+
last_commit: str | None = None
|
|
449
|
+
|
|
450
|
+
|
|
435
451
|
@dataclass(frozen=True)
|
|
436
452
|
class AbbrevFootnoteNode(IRNode):
|
|
437
453
|
"""Inline: abbreviated term with a superscript anchor-link to the glossary.
|
{mkdocs2confluence-0.8.4 → mkdocs2confluence-0.9.0}/src/mkdocs_to_confluence/publisher/pipeline.py
RENAMED
|
@@ -49,6 +49,7 @@ from mkdocs_to_confluence.publisher.client import ConfluenceError
|
|
|
49
49
|
from mkdocs_to_confluence.transforms.abbrevs import apply_abbreviations
|
|
50
50
|
from mkdocs_to_confluence.transforms.assets import _make_attachment_name, resolve_local_assets
|
|
51
51
|
from mkdocs_to_confluence.transforms.editlink import attach_source_url
|
|
52
|
+
from mkdocs_to_confluence.transforms.footer import build_source_footer
|
|
52
53
|
from mkdocs_to_confluence.transforms.internallinks import build_link_map, resolve_internal_links
|
|
53
54
|
from mkdocs_to_confluence.transforms.mermaid import DEFAULT_KROKI_URL, render_mermaid_diagrams
|
|
54
55
|
|
|
@@ -187,8 +188,12 @@ def compile_page(
|
|
|
187
188
|
ir_nodes = (front_matter,) + ir_nodes
|
|
188
189
|
edit_url = config.page_edit_url(node.docs_path or "")
|
|
189
190
|
site_url = config.page_site_url(node.docs_path or "")
|
|
190
|
-
if
|
|
191
|
-
ir_nodes = attach_source_url(ir_nodes,
|
|
191
|
+
if site_url:
|
|
192
|
+
ir_nodes = attach_source_url(ir_nodes, "", site_url)
|
|
193
|
+
if edit_url:
|
|
194
|
+
abs_path = str(config.docs_dir / (node.docs_path or ""))
|
|
195
|
+
footer = build_source_footer(edit_url, abs_path)
|
|
196
|
+
ir_nodes = ir_nodes + (footer,)
|
|
192
197
|
|
|
193
198
|
# Extract labels and confluence_status from FrontMatter node.
|
|
194
199
|
labels: tuple[str, ...] = ()
|
{mkdocs2confluence-0.8.4 → mkdocs2confluence-0.9.0}/src/mkdocs_to_confluence/transforms/editlink.py
RENAMED
|
@@ -1,16 +1,13 @@
|
|
|
1
1
|
"""Edit-link injection transform.
|
|
2
2
|
|
|
3
|
-
Attaches the
|
|
4
|
-
:class:`~ir.nodes.FrontMatter` node so
|
|
5
|
-
Page Properties table.
|
|
6
|
-
|
|
7
|
-
- **Source** row: the VCS edit URL (from ``repo_url`` + ``edit_uri``)
|
|
8
|
-
- **Published Page** row: the rendered MkDocs URL (from ``site_url``)
|
|
3
|
+
Attaches the published-page URL (from ``site_url``) to the page's
|
|
4
|
+
:class:`~ir.nodes.FrontMatter` node so it appears as a row in the
|
|
5
|
+
Confluence Page Properties table.
|
|
9
6
|
|
|
10
7
|
If the page has no front matter a minimal :class:`~ir.nodes.FrontMatter`
|
|
11
|
-
node is created and prepended so the
|
|
8
|
+
node is created and prepended so the row always appears.
|
|
12
9
|
|
|
13
|
-
|
|
10
|
+
The row is only injected when the URL is non-empty.
|
|
14
11
|
"""
|
|
15
12
|
|
|
16
13
|
from __future__ import annotations
|
|
@@ -23,42 +20,44 @@ def attach_source_url(
|
|
|
23
20
|
edit_url: str,
|
|
24
21
|
site_url: str | None = None,
|
|
25
22
|
) -> tuple[IRNode, ...]:
|
|
26
|
-
"""Attach *
|
|
23
|
+
"""Attach *site_url* to the page's FrontMatter for the Page Properties table.
|
|
27
24
|
|
|
28
25
|
Parameters
|
|
29
26
|
----------
|
|
30
27
|
nodes:
|
|
31
28
|
Top-level IR nodes for the page.
|
|
32
29
|
edit_url:
|
|
33
|
-
|
|
30
|
+
Kept for backwards compatibility; no longer stored on FrontMatter.
|
|
31
|
+
Pass an empty string or ``None`` — it is ignored.
|
|
34
32
|
site_url:
|
|
35
33
|
Full URL to the rendered page on the MkDocs site. When provided,
|
|
36
|
-
a "Published Page" row is added
|
|
34
|
+
a "Published Page" row is added to the Page Properties table.
|
|
37
35
|
|
|
38
36
|
Returns
|
|
39
37
|
-------
|
|
40
38
|
tuple[IRNode, ...]
|
|
41
|
-
New nodes tuple with
|
|
39
|
+
New nodes tuple with ``site_url`` attached to the FrontMatter.
|
|
42
40
|
"""
|
|
41
|
+
if not site_url:
|
|
42
|
+
return nodes
|
|
43
|
+
|
|
43
44
|
if nodes and isinstance(nodes[0], FrontMatter):
|
|
44
45
|
updated = FrontMatter(
|
|
45
46
|
title=nodes[0].title,
|
|
46
47
|
subtitle=nodes[0].subtitle,
|
|
47
48
|
properties=nodes[0].properties,
|
|
48
49
|
labels=nodes[0].labels,
|
|
49
|
-
source_url=edit_url,
|
|
50
50
|
site_url=site_url,
|
|
51
51
|
confluence_status=nodes[0].confluence_status,
|
|
52
52
|
)
|
|
53
53
|
return (updated,) + nodes[1:]
|
|
54
54
|
|
|
55
|
-
# No existing front matter — create a minimal one just for the link
|
|
55
|
+
# No existing front matter — create a minimal one just for the link row.
|
|
56
56
|
minimal = FrontMatter(
|
|
57
57
|
title=None,
|
|
58
58
|
subtitle=None,
|
|
59
59
|
properties=(),
|
|
60
60
|
labels=(),
|
|
61
|
-
source_url=edit_url,
|
|
62
61
|
site_url=site_url,
|
|
63
62
|
)
|
|
64
63
|
return (minimal,) + nodes
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
"""Source-footer transform.
|
|
2
|
+
|
|
3
|
+
Builds a :class:`~ir.nodes.SourceFooter` node from a VCS edit URL and the
|
|
4
|
+
absolute path of the source file. The node is appended to the IR node list
|
|
5
|
+
by the publish pipeline so it renders as a footer panel on each Confluence page.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
import subprocess
|
|
11
|
+
|
|
12
|
+
from mkdocs_to_confluence.ir.nodes import SourceFooter
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def _derive_history_url(edit_url: str) -> str | None:
|
|
16
|
+
"""Derive a commit-history URL from a VCS edit URL.
|
|
17
|
+
|
|
18
|
+
Supports GitHub (``/edit/``) and GitLab (``/-/edit/``).
|
|
19
|
+
Returns ``None`` for any other URL shape.
|
|
20
|
+
"""
|
|
21
|
+
if "/-/edit/" in edit_url:
|
|
22
|
+
return edit_url.replace("/-/edit/", "/-/commits/", 1)
|
|
23
|
+
if "/edit/" in edit_url:
|
|
24
|
+
return edit_url.replace("/edit/", "/commits/", 1)
|
|
25
|
+
return None
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def _last_commit(abs_path: str) -> str | None:
|
|
29
|
+
"""Return a human-readable last-commit summary for *abs_path*.
|
|
30
|
+
|
|
31
|
+
Runs ``git log -1 --format=... --date=relative`` on the file.
|
|
32
|
+
Returns ``None`` when git is unavailable, the path is untracked, or the
|
|
33
|
+
command fails for any reason.
|
|
34
|
+
"""
|
|
35
|
+
try:
|
|
36
|
+
result = subprocess.run(
|
|
37
|
+
[
|
|
38
|
+
"git", "log", "-1",
|
|
39
|
+
"--format=%h \u00b7 %s \u00b7 %an \u00b7 %ad",
|
|
40
|
+
"--date=relative",
|
|
41
|
+
"--",
|
|
42
|
+
abs_path,
|
|
43
|
+
],
|
|
44
|
+
capture_output=True,
|
|
45
|
+
text=True,
|
|
46
|
+
timeout=5,
|
|
47
|
+
)
|
|
48
|
+
output = result.stdout.strip()
|
|
49
|
+
return output if output else None
|
|
50
|
+
except Exception: # noqa: BLE001
|
|
51
|
+
return None
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def build_source_footer(edit_url: str, abs_path: str) -> SourceFooter:
|
|
55
|
+
"""Build a :class:`SourceFooter` for the given *edit_url* and source file.
|
|
56
|
+
|
|
57
|
+
Parameters
|
|
58
|
+
----------
|
|
59
|
+
edit_url:
|
|
60
|
+
Full URL to edit the source file (e.g. GitHub edit link).
|
|
61
|
+
abs_path:
|
|
62
|
+
Absolute filesystem path to the source Markdown file. Used to
|
|
63
|
+
query ``git log`` for the last-commit summary.
|
|
64
|
+
"""
|
|
65
|
+
return SourceFooter(
|
|
66
|
+
edit_url=edit_url,
|
|
67
|
+
history_url=_derive_history_url(edit_url),
|
|
68
|
+
last_commit=_last_commit(abs_path),
|
|
69
|
+
)
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
"""Tests for the edit-link /
|
|
1
|
+
"""Tests for the edit-link / site-url attachment transform."""
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
@@ -8,31 +8,29 @@ from mkdocs_to_confluence.transforms.editlink import attach_source_url
|
|
|
8
8
|
# ── attach_source_url ─────────────────────────────────────────────────────────
|
|
9
9
|
|
|
10
10
|
|
|
11
|
-
def
|
|
12
|
-
"""
|
|
11
|
+
def test_source_url_ignored_when_only_edit_url_provided():
|
|
12
|
+
"""edit_url is now ignored; attach_source_url with no site_url returns nodes unchanged."""
|
|
13
13
|
fm = FrontMatter(title="My Page", subtitle=None, properties=(), labels=())
|
|
14
14
|
original = (fm, Paragraph(children=(TextNode(text="Hello"),)))
|
|
15
15
|
result = attach_source_url(original, "https://github.com/org/repo/edit/main/docs/index.md")
|
|
16
|
-
assert
|
|
17
|
-
assert result[0].source_url == "https://github.com/org/repo/edit/main/docs/index.md"
|
|
18
|
-
assert result[1] is original[1]
|
|
16
|
+
assert result is original
|
|
19
17
|
|
|
20
18
|
|
|
21
19
|
def test_existing_front_matter_fields_preserved():
|
|
22
|
-
"""Attaching
|
|
20
|
+
"""Attaching site_url does not lose other FrontMatter fields."""
|
|
23
21
|
fm = FrontMatter(
|
|
24
22
|
title="My Page",
|
|
25
23
|
subtitle="A subtitle",
|
|
26
24
|
properties=(("Version", "1.0"),),
|
|
27
25
|
labels=("arch",),
|
|
28
26
|
)
|
|
29
|
-
result = attach_source_url((fm,), "https://
|
|
27
|
+
result = attach_source_url((fm,), "", site_url="https://site.io/page/")
|
|
30
28
|
updated: FrontMatter = result[0] # type: ignore[assignment]
|
|
31
29
|
assert updated.title == "My Page"
|
|
32
30
|
assert updated.subtitle == "A subtitle"
|
|
33
31
|
assert updated.properties == (("Version", "1.0"),)
|
|
34
32
|
assert updated.labels == ("arch",)
|
|
35
|
-
assert updated.
|
|
33
|
+
assert updated.site_url == "https://site.io/page/"
|
|
36
34
|
|
|
37
35
|
|
|
38
36
|
def test_confluence_status_preserved_by_attach_source_url():
|
|
@@ -44,7 +42,7 @@ def test_confluence_status_preserved_by_attach_source_url():
|
|
|
44
42
|
labels=("arch",),
|
|
45
43
|
confluence_status="in-progress",
|
|
46
44
|
)
|
|
47
|
-
result = attach_source_url((fm,), "https://
|
|
45
|
+
result = attach_source_url((fm,), "", site_url="https://site.io/page/")
|
|
48
46
|
updated: FrontMatter = result[0] # type: ignore[assignment]
|
|
49
47
|
assert updated.confluence_status == "in-progress"
|
|
50
48
|
|
|
@@ -52,20 +50,27 @@ def test_confluence_status_preserved_by_attach_source_url():
|
|
|
52
50
|
def test_minimal_front_matter_created_when_none_present():
|
|
53
51
|
"""A minimal FrontMatter is prepended when the page has no front matter."""
|
|
54
52
|
body = (Paragraph(children=(TextNode(text="Content"),)),)
|
|
55
|
-
result = attach_source_url(body, "https://
|
|
53
|
+
result = attach_source_url(body, "", site_url="https://site.io/page/")
|
|
56
54
|
assert len(result) == 2
|
|
57
55
|
assert isinstance(result[0], FrontMatter)
|
|
58
|
-
assert result[0].
|
|
56
|
+
assert result[0].site_url == "https://site.io/page/"
|
|
59
57
|
assert result[0].title is None
|
|
60
58
|
assert result[0].properties == ()
|
|
61
59
|
assert result[1] is body[0]
|
|
62
60
|
|
|
63
61
|
|
|
64
62
|
def test_empty_nodes_gets_minimal_front_matter():
|
|
65
|
-
result = attach_source_url((), "https://
|
|
63
|
+
result = attach_source_url((), "", site_url="https://site.io/")
|
|
66
64
|
assert len(result) == 1
|
|
67
65
|
assert isinstance(result[0], FrontMatter)
|
|
68
|
-
assert result[0].
|
|
66
|
+
assert result[0].site_url == "https://site.io/"
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def test_no_site_url_returns_nodes_unchanged():
|
|
70
|
+
"""When site_url is absent, nodes are returned as-is (no FrontMatter injected)."""
|
|
71
|
+
body = (Paragraph(children=(TextNode(text="Content"),)),)
|
|
72
|
+
result = attach_source_url(body, "")
|
|
73
|
+
assert result is body
|
|
69
74
|
|
|
70
75
|
|
|
71
76
|
# ── page_edit_url ─────────────────────────────────────────────────────────────
|
|
@@ -258,5 +263,4 @@ def test_site_url_attached_by_transform():
|
|
|
258
263
|
(fm,), "https://github.com/edit", site_url="https://site.io/page/"
|
|
259
264
|
)
|
|
260
265
|
assert isinstance(result[0], FrontMatter)
|
|
261
|
-
assert result[0].source_url == "https://github.com/edit"
|
|
262
266
|
assert result[0].site_url == "https://site.io/page/"
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
"""Tests for transforms/footer.py and the SourceFooter emitter."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from unittest.mock import patch
|
|
6
|
+
|
|
7
|
+
from mkdocs_to_confluence.emitter.xhtml import emit
|
|
8
|
+
from mkdocs_to_confluence.ir.nodes import SourceFooter
|
|
9
|
+
from mkdocs_to_confluence.transforms.footer import _derive_history_url, _last_commit, build_source_footer
|
|
10
|
+
|
|
11
|
+
# ── _derive_history_url ───────────────────────────────────────────────────────
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def test_derive_history_url_github():
|
|
15
|
+
url = "https://github.com/org/repo/edit/main/docs/guide/setup.md"
|
|
16
|
+
assert _derive_history_url(url) == "https://github.com/org/repo/commits/main/docs/guide/setup.md"
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def test_derive_history_url_gitlab():
|
|
20
|
+
url = "https://gitlab.com/org/repo/-/edit/main/docs/guide/setup.md"
|
|
21
|
+
assert _derive_history_url(url) == "https://gitlab.com/org/repo/-/commits/main/docs/guide/setup.md"
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def test_derive_history_url_unknown_returns_none():
|
|
25
|
+
url = "https://bitbucket.org/org/repo/src/main/docs/guide/setup.md"
|
|
26
|
+
assert _derive_history_url(url) is None
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def test_derive_history_url_empty_string():
|
|
30
|
+
assert _derive_history_url("") is None
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
# ── _last_commit ──────────────────────────────────────────────────────────────
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def test_last_commit_returns_output(tmp_path):
|
|
37
|
+
fake_file = str(tmp_path / "docs" / "page.md")
|
|
38
|
+
with patch("mkdocs_to_confluence.transforms.footer.subprocess.run") as mock_run:
|
|
39
|
+
mock_run.return_value.stdout = "abc1234 · Fix typo · Jane · 2 days ago\n"
|
|
40
|
+
result = _last_commit(fake_file)
|
|
41
|
+
assert result == "abc1234 · Fix typo · Jane · 2 days ago"
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def test_last_commit_returns_none_when_empty(tmp_path):
|
|
45
|
+
fake_file = str(tmp_path / "docs" / "untracked.md")
|
|
46
|
+
with patch("mkdocs_to_confluence.transforms.footer.subprocess.run") as mock_run:
|
|
47
|
+
mock_run.return_value.stdout = ""
|
|
48
|
+
result = _last_commit(fake_file)
|
|
49
|
+
assert result is None
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def test_last_commit_returns_none_on_exception(tmp_path):
|
|
53
|
+
fake_file = str(tmp_path / "docs" / "page.md")
|
|
54
|
+
with patch("mkdocs_to_confluence.transforms.footer.subprocess.run", side_effect=FileNotFoundError):
|
|
55
|
+
result = _last_commit(fake_file)
|
|
56
|
+
assert result is None
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
# ── build_source_footer ───────────────────────────────────────────────────────
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def test_build_source_footer_full(tmp_path):
|
|
63
|
+
edit_url = "https://github.com/org/repo/edit/main/docs/guide.md"
|
|
64
|
+
abs_path = str(tmp_path / "docs" / "guide.md")
|
|
65
|
+
with patch("mkdocs_to_confluence.transforms.footer._last_commit", return_value="abc · msg · Jane · 1 day ago"):
|
|
66
|
+
footer = build_source_footer(edit_url, abs_path)
|
|
67
|
+
assert footer.edit_url == edit_url
|
|
68
|
+
assert footer.history_url == "https://github.com/org/repo/commits/main/docs/guide.md"
|
|
69
|
+
assert footer.last_commit == "abc · msg · Jane · 1 day ago"
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def test_build_source_footer_no_commit(tmp_path):
|
|
73
|
+
edit_url = "https://github.com/org/repo/edit/main/docs/guide.md"
|
|
74
|
+
abs_path = str(tmp_path / "docs" / "guide.md")
|
|
75
|
+
with patch("mkdocs_to_confluence.transforms.footer._last_commit", return_value=None):
|
|
76
|
+
footer = build_source_footer(edit_url, abs_path)
|
|
77
|
+
assert footer.last_commit is None
|
|
78
|
+
assert footer.history_url is not None
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
# ── _emit_source_footer ───────────────────────────────────────────────────────
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def test_emit_footer_contains_edit_link():
|
|
85
|
+
footer = SourceFooter(
|
|
86
|
+
edit_url="https://github.com/org/repo/edit/main/docs/page.md",
|
|
87
|
+
history_url="https://github.com/org/repo/commits/main/docs/page.md",
|
|
88
|
+
last_commit="abc1234 · Fix typo · Jane · 2 days ago",
|
|
89
|
+
)
|
|
90
|
+
out = emit((footer,))
|
|
91
|
+
assert "Edit this page" in out
|
|
92
|
+
assert "View history" in out
|
|
93
|
+
assert "abc1234" in out
|
|
94
|
+
assert "Fix typo" in out
|
|
95
|
+
assert 'ac:name="panel"' in out
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def test_emit_footer_no_history_url():
|
|
99
|
+
footer = SourceFooter(
|
|
100
|
+
edit_url="https://example.com/edit/docs/page.md",
|
|
101
|
+
history_url=None,
|
|
102
|
+
last_commit=None,
|
|
103
|
+
)
|
|
104
|
+
out = emit((footer,))
|
|
105
|
+
assert "Edit this page" in out
|
|
106
|
+
assert "View history" not in out
|
|
107
|
+
assert "Last commit" not in out
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
def test_emit_footer_escapes_html():
|
|
111
|
+
footer = SourceFooter(
|
|
112
|
+
edit_url='https://example.com/edit/path?a=1&b=2',
|
|
113
|
+
history_url=None,
|
|
114
|
+
last_commit='abc · <script>alert(1)</script> · Jane · today',
|
|
115
|
+
)
|
|
116
|
+
out = emit((footer,))
|
|
117
|
+
assert "<script>" not in out
|
|
118
|
+
assert "<script>" in out
|
|
119
|
+
assert "a=1&b=2" in out
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
|
-
from mkdocs_to_confluence.emitter.xhtml import
|
|
5
|
+
from mkdocs_to_confluence.emitter.xhtml import emit
|
|
6
6
|
from mkdocs_to_confluence.ir.nodes import FrontMatter
|
|
7
7
|
from mkdocs_to_confluence.preprocess.frontmatter import extract_front_matter
|
|
8
8
|
|
|
@@ -194,70 +194,3 @@ def test_special_chars_escaped_in_properties():
|
|
|
194
194
|
assert "&" in xhtml
|
|
195
195
|
assert "<" in xhtml
|
|
196
196
|
assert "O'Brien" in xhtml or "O'Brien" in xhtml or "O'" in xhtml or "O'" not in xhtml
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
def test_source_url_renders_as_link_row():
|
|
200
|
-
fm = FrontMatter(
|
|
201
|
-
title=None,
|
|
202
|
-
subtitle=None,
|
|
203
|
-
properties=(),
|
|
204
|
-
labels=(),
|
|
205
|
-
source_url="https://github.com/org/repo/edit/main/docs/index.md",
|
|
206
|
-
)
|
|
207
|
-
xhtml = emit((fm,))
|
|
208
|
-
assert 'ac:name="details"' in xhtml
|
|
209
|
-
assert '<th>Source</th>' in xhtml
|
|
210
|
-
assert 'href="https://github.com/org/repo/edit/main/docs/index.md"' in xhtml
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
def test_source_url_appears_after_other_properties():
|
|
214
|
-
fm = FrontMatter(
|
|
215
|
-
title=None,
|
|
216
|
-
subtitle=None,
|
|
217
|
-
properties=(("Version", "2.0"),),
|
|
218
|
-
labels=(),
|
|
219
|
-
source_url="https://example.com/edit",
|
|
220
|
-
)
|
|
221
|
-
xhtml = emit((fm,))
|
|
222
|
-
version_pos = xhtml.index("Version")
|
|
223
|
-
source_pos = xhtml.index("Source")
|
|
224
|
-
assert source_pos > version_pos, "Source row should appear after other properties"
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
def test_source_url_alone_still_emits_details_macro():
|
|
228
|
-
"""A page with no front matter but a source_url should show the table."""
|
|
229
|
-
fm = FrontMatter(title=None, subtitle=None, properties=(), labels=(), source_url="https://example.com/edit")
|
|
230
|
-
xhtml = emit((fm,))
|
|
231
|
-
assert 'ac:name="details"' in xhtml
|
|
232
|
-
assert '<th>Source</th>' in xhtml
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
# --- _source_link_label ---
|
|
236
|
-
|
|
237
|
-
def test_source_link_label_github():
|
|
238
|
-
assert _source_link_label("https://github.com/org/repo/edit/main/docs/page.md") == "Edit in GitHub ↗"
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
def test_source_link_label_gitlab_dot_com():
|
|
242
|
-
assert _source_link_label("https://gitlab.com/org/repo/-/edit/main/docs/page.md") == "Edit in GitLab ↗"
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
def test_source_link_label_self_hosted_gitlab():
|
|
246
|
-
assert _source_link_label("https://gitlab.mycompany.com/org/repo/-/edit/main/docs/page.md") == "Edit in GitLab ↗"
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
def test_source_link_label_bitbucket():
|
|
250
|
-
assert _source_link_label("https://bitbucket.org/org/repo/src/main/docs/page.md") == "Edit in Bitbucket ↗"
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
def test_source_link_label_unknown_host_fallback():
|
|
254
|
-
assert _source_link_label("https://example.com/edit") == "Edit source ↗"
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
def test_source_link_label_renders_in_xhtml():
|
|
258
|
-
fm = FrontMatter(
|
|
259
|
-
title=None, subtitle=None, properties=(), labels=(),
|
|
260
|
-
source_url="https://github.com/org/repo/edit/main/docs/index.md",
|
|
261
|
-
)
|
|
262
|
-
xhtml = emit((fm,))
|
|
263
|
-
assert "Edit in GitHub" in xhtml
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{mkdocs2confluence-0.8.4 → mkdocs2confluence-0.9.0}/src/mkdocs2confluence.egg-info/entry_points.txt
RENAMED
|
File without changes
|
{mkdocs2confluence-0.8.4 → mkdocs2confluence-0.9.0}/src/mkdocs2confluence.egg-info/requires.txt
RENAMED
|
File without changes
|
{mkdocs2confluence-0.8.4 → mkdocs2confluence-0.9.0}/src/mkdocs2confluence.egg-info/top_level.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{mkdocs2confluence-0.8.4 → mkdocs2confluence-0.9.0}/src/mkdocs_to_confluence/emitter/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{mkdocs2confluence-0.8.4 → mkdocs2confluence-0.9.0}/src/mkdocs_to_confluence/loader/__init__.py
RENAMED
|
File without changes
|
{mkdocs2confluence-0.8.4 → mkdocs2confluence-0.9.0}/src/mkdocs_to_confluence/loader/config.py
RENAMED
|
File without changes
|
{mkdocs2confluence-0.8.4 → mkdocs2confluence-0.9.0}/src/mkdocs_to_confluence/loader/extra_css.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{mkdocs2confluence-0.8.4 → mkdocs2confluence-0.9.0}/src/mkdocs_to_confluence/parser/__init__.py
RENAMED
|
File without changes
|
{mkdocs2confluence-0.8.4 → mkdocs2confluence-0.9.0}/src/mkdocs_to_confluence/parser/markdown.py
RENAMED
|
File without changes
|
{mkdocs2confluence-0.8.4 → mkdocs2confluence-0.9.0}/src/mkdocs_to_confluence/pdf/__init__.py
RENAMED
|
File without changes
|
{mkdocs2confluence-0.8.4 → mkdocs2confluence-0.9.0}/src/mkdocs_to_confluence/pdf/generator.py
RENAMED
|
File without changes
|
|
File without changes
|
{mkdocs2confluence-0.8.4 → mkdocs2confluence-0.9.0}/src/mkdocs_to_confluence/preprocess/__init__.py
RENAMED
|
File without changes
|
{mkdocs2confluence-0.8.4 → mkdocs2confluence-0.9.0}/src/mkdocs_to_confluence/preprocess/abbrevs.py
RENAMED
|
File without changes
|
{mkdocs2confluence-0.8.4 → mkdocs2confluence-0.9.0}/src/mkdocs_to_confluence/preprocess/fence.py
RENAMED
|
File without changes
|
|
File without changes
|
{mkdocs2confluence-0.8.4 → mkdocs2confluence-0.9.0}/src/mkdocs_to_confluence/preprocess/icons.py
RENAMED
|
File without changes
|
{mkdocs2confluence-0.8.4 → mkdocs2confluence-0.9.0}/src/mkdocs_to_confluence/preprocess/includes.py
RENAMED
|
File without changes
|
{mkdocs2confluence-0.8.4 → mkdocs2confluence-0.9.0}/src/mkdocs_to_confluence/preprocess/linkdefs.py
RENAMED
|
File without changes
|
{mkdocs2confluence-0.8.4 → mkdocs2confluence-0.9.0}/src/mkdocs_to_confluence/preview/__init__.py
RENAMED
|
File without changes
|
{mkdocs2confluence-0.8.4 → mkdocs2confluence-0.9.0}/src/mkdocs_to_confluence/preview/render.py
RENAMED
|
File without changes
|
{mkdocs2confluence-0.8.4 → mkdocs2confluence-0.9.0}/src/mkdocs_to_confluence/preview/server.py
RENAMED
|
File without changes
|
{mkdocs2confluence-0.8.4 → mkdocs2confluence-0.9.0}/src/mkdocs_to_confluence/publisher/__init__.py
RENAMED
|
File without changes
|
{mkdocs2confluence-0.8.4 → mkdocs2confluence-0.9.0}/src/mkdocs_to_confluence/publisher/client.py
RENAMED
|
File without changes
|
{mkdocs2confluence-0.8.4 → mkdocs2confluence-0.9.0}/src/mkdocs_to_confluence/sync/__init__.py
RENAMED
|
File without changes
|
{mkdocs2confluence-0.8.4 → mkdocs2confluence-0.9.0}/src/mkdocs_to_confluence/sync/anchoring.py
RENAMED
|
File without changes
|
{mkdocs2confluence-0.8.4 → mkdocs2confluence-0.9.0}/src/mkdocs_to_confluence/sync/command.py
RENAMED
|
File without changes
|
{mkdocs2confluence-0.8.4 → mkdocs2confluence-0.9.0}/src/mkdocs_to_confluence/sync/comments.py
RENAMED
|
File without changes
|
|
File without changes
|
{mkdocs2confluence-0.8.4 → mkdocs2confluence-0.9.0}/src/mkdocs_to_confluence/sync/platform.py
RENAMED
|
File without changes
|
|
File without changes
|
{mkdocs2confluence-0.8.4 → mkdocs2confluence-0.9.0}/src/mkdocs_to_confluence/transforms/__init__.py
RENAMED
|
File without changes
|
{mkdocs2confluence-0.8.4 → mkdocs2confluence-0.9.0}/src/mkdocs_to_confluence/transforms/abbrevs.py
RENAMED
|
File without changes
|
{mkdocs2confluence-0.8.4 → mkdocs2confluence-0.9.0}/src/mkdocs_to_confluence/transforms/assets.py
RENAMED
|
File without changes
|
{mkdocs2confluence-0.8.4 → mkdocs2confluence-0.9.0}/src/mkdocs_to_confluence/transforms/images.py
RENAMED
|
File without changes
|
|
File without changes
|
{mkdocs2confluence-0.8.4 → mkdocs2confluence-0.9.0}/src/mkdocs_to_confluence/transforms/mermaid.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|