diff --git a/.gitignore b/.gitignore
index fe498e7..0c731df 100644
--- a/.gitignore
+++ b/.gitignore
@@ -128,5 +128,9 @@ dmypy.json
# Pyre type checker
.pyre/
+# branch feature_ha_append_page_metadata
+tmp
+appTest.py
+
# End of https://www.gitignore.io/api/code,python
diff --git a/confluence_markdown_exporter/confluence.py b/confluence_markdown_exporter/confluence.py
index 0993815..9df9a97 100644
--- a/confluence_markdown_exporter/confluence.py
+++ b/confluence_markdown_exporter/confluence.py
@@ -16,6 +16,9 @@
from typing import TypeAlias
from typing import cast
+import json
+from datetime import datetime, timedelta
+
import jmespath
import yaml
from atlassian.errors import ApiError
@@ -324,6 +327,30 @@ class Page(Document):
editor2: str
labels: list["Label"]
attachments: list["Attachment"]
+ page_metadata: str
+
+ @staticmethod
+ def get_custom_metadata(response: dict | None) -> dict:
+ if response is None:
+ return {}
+ custom_metadata = {
+ 'id' : response.get('id',''),
+ 'title' : response.get('title',''),
+ 'type' : response.get('type',''),
+ 'created' : response.get('history', {}).get('createdDate',''),
+ 'author' : response.get('history', {}).get('createdBy', {}).get('displayName',''),
+ 'version' : response.get('version', {}).get('number','1'),
+ 'lastModified' : response.get('version', {}).get('when',''),
+ 'lastModifiedBy' : response.get('version', {}).get('by', {}).get('displayName',''),
+ 'lastModifiedWithin365Days': response.get('version', {}).get('when','')[10] >= (datetime.now()-timedelta(days=365)).strftime('%Y-%m-%d') if response.get('version', {}).get('when','') else False,
+ 'pageURL' : f"""{response.get('_links', {}).get('base','')}/{response.get('_links', {}).get('webui','')}""",
+ # 'pageURL' : f"""{response.get('_links', {}).get('base','')}/pages/viewpage.action?pageId={response.get('id','')}""",
+ }
+ custom_metadata_html = "
\n"
+ custom_metadata_html += "".join(f"| {key} | {value} |
\n" for key, value in custom_metadata.items())
+ custom_metadata_html += "
\n"
+ response['page_metadata'] = custom_metadata_html
+ return response
@property
def descendants(self) -> list[int]:
@@ -382,9 +409,12 @@ def export_path(self) -> Path:
@property
def html(self) -> str:
+ body = self.body
if settings.export.include_document_title:
- return f"{self.title}
{self.body}"
- return self.body
+ body = f"{self.title}
{self.body}"
+ if settings.export.include_document_metadata:
+ body = f"{body}
Metadata:
{self.page_metadata}"
+ return body
@property
def markdown(self) -> str:
@@ -486,20 +516,23 @@ def from_json(cls, data: JsonResponse) -> "Page":
],
attachments=Attachment.from_page_id(data.get("id", 0)),
ancestors=[ancestor.get("id") for ancestor in data.get("ancestors", [])][1:],
+ page_metadata=data.get("page_metadata", ""),
)
@classmethod
@functools.lru_cache(maxsize=1000)
def from_id(cls, page_id: int) -> "Page":
try:
+ response = confluence.get_page_by_id(
+ page_id,
+ expand="body.view,body.export_view,body.editor2,metadata.labels,"
+ "metadata.properties,ancestors,history,version",
+ )
+ response = cls.get_custom_metadata(response)
return cls.from_json(
cast(
JsonResponse,
- confluence.get_page_by_id(
- page_id,
- expand="body.view,body.export_view,body.editor2,metadata.labels,"
- "metadata.properties,ancestors",
- ),
+ response,
)
)
except (ApiError, HTTPError) as e:
@@ -515,6 +548,7 @@ def from_id(cls, page_id: int) -> "Page":
labels=[],
attachments=[],
ancestors=[],
+ page_metadata="",
)
@classmethod
diff --git a/confluence_markdown_exporter/utils/app_data_store.py b/confluence_markdown_exporter/utils/app_data_store.py
index bd2ec4f..ebc6e2b 100644
--- a/confluence_markdown_exporter/utils/app_data_store.py
+++ b/confluence_markdown_exporter/utils/app_data_store.py
@@ -221,6 +221,14 @@ class ExportConfig(BaseModel):
"If enabled, the title will be added as a top-level heading."
),
)
+ include_document_metadata: bool = Field(
+ default=False,
+ title="Include Document Metadata",
+ description=(
+ "Whether to include document metadata (like author, last modified date) "
+ "at the bottom of the exported markdown file."
+ ),
+ )
class ConfigModel(BaseModel):