From a7fd2af552b44fe19a6108e789cd001df13fb250 Mon Sep 17 00:00:00 2001 From: Udo Waechter Date: Thu, 8 Jan 2026 13:19:02 +0100 Subject: [PATCH] removed recursion --- .settings/org.eclipse.core.resources.prefs | 3 - redmine2gitea-wiki/redmine_to_gitea_wiki.py | 157 ++++++++++++++++---- 2 files changed, 124 insertions(+), 36 deletions(-) delete mode 100644 .settings/org.eclipse.core.resources.prefs diff --git a/.settings/org.eclipse.core.resources.prefs b/.settings/org.eclipse.core.resources.prefs deleted file mode 100644 index c53d143..0000000 --- a/.settings/org.eclipse.core.resources.prefs +++ /dev/null @@ -1,3 +0,0 @@ -eclipse.preferences.version=1 -encoding//kodi/context.kino.control/main.py=utf-8 -encoding//kodi/context.kino.control/resources/lib/context.py=utf-8 diff --git a/redmine2gitea-wiki/redmine_to_gitea_wiki.py b/redmine2gitea-wiki/redmine_to_gitea_wiki.py index afe0114..0c4244d 100644 --- a/redmine2gitea-wiki/redmine_to_gitea_wiki.py +++ b/redmine2gitea-wiki/redmine_to_gitea_wiki.py @@ -15,9 +15,10 @@ REDMINE_URL = "https://redmine.maketank.net" # e.g., https://redmine.example.co REDMINE_API_KEY = "62120b7ed6bf84023b2a1d7d5a265cf172f8d607" PROJECT_ID = "bastelstube" # e.g., "my-project" -GITEA_WIKI_URL = "https://git.maketank.net/chaos/spielplatz.wiki.git" # Gitea wiki Git URL -GITEA_USERNAME = "do" -GITEA_TOKEN = "2d2be00637182c6d5411512f456218ee59fa1f81" # Use a personal access token +#GITEA_WIKI_URL = "https://git.maketank.net/chaos/spielplatz.wiki.git" # Gitea wiki Git URL +GITEA_WIKI_URL = "ssh://git@git.maketank.net:2222/chaos/spielplatz.wiki.git" +#GITEA_USERNAME = "do" +#GITEA_TOKEN = "2d2be00637182c6d5411512f456218ee59fa1f81" # Use a personal access token # Optional: Gitea API for creating pages (if you want to use it instead of Git) # GITEA_API_URL = "https://gitea.example.com/api/v1" @@ -26,7 +27,7 @@ GITEA_TOKEN = "2d2be00637182c6d5411512f456218ee59fa1f81" # Use a personal acces def redmine_get_wiki_pages(): """Get all wiki pages from Redmine project""" - # First get the list of pages + # First get the list of pages and their parent information index_url = f"{REDMINE_URL}/projects/{PROJECT_ID}/wiki/index.xml" headers = {"X-Redmine-API-Key": REDMINE_API_KEY} response = requests.get(index_url, headers=headers) @@ -35,11 +36,16 @@ def redmine_get_wiki_pages(): soup = BeautifulSoup(response.content, "xml") pages = [] - # Get all page titles from the index for page in soup.find_all("wiki_page"): title = page.find("title").text - logger.info(f"Found page: {title}") - + parent = page.find('parent') + if parent: + parent = parent['title'] + else: + parent = None # Root level pages have no parent + + logger.info(f"Found page: {title} (Parent: {parent})") + # Fetch individual page content page_url = f"{REDMINE_URL}/projects/{PROJECT_ID}/wiki/{title}.xml" try: @@ -49,23 +55,36 @@ def redmine_get_wiki_pages(): page_soup = BeautifulSoup(page_response.content, "xml") content = page_soup.find("text").text if page_soup.find("text") else "" - pages.append({"title": title, "content": content}) + pages.append({"title": title, "parent": parent, "content": content}) logger.info(f"Fetched content for: {title}") except Exception as e: logger.warning(f"Failed to fetch content for page '{title}': {e}") # Add empty content if fetch fails - pages.append({"title": title, "content": ""}) + pages.append({"title": title, "parent": parent, "content": ""}) return pages +def redmine_markup_to_markdown(content, pages): + """Convert Redmine wiki markup to Markdown""" -def redmine_markup_to_markdown(content): - """Convert Redmine wiki markup (textile) to Markdown""" - # Replace Redmine-specific syntax to Markdown - - # Handle child_pages macro - remove it since we're importing all pages - content = re.sub(r'\{\{child_pages(?:\([^)]*\))?\}\}', '', content) + # Convert links - handle both [[page]] and [page] syntax + def replace_link(match): + link = match.group(1) + + if link in pages: # Ensure the linked page exists + # Create a relative path based on parent-child hierarchy + href = f"{pages[link]['title'].replace(' ', '_')}.md" + + return f"[{link}]({href})" + + else: + logger.warning(f"Linked page not found: {link}") + # Return original link if linked page not found + return match.group() + # Replace [[page]] with [page](page_url) + content = re.sub(r'\[\[(.*?)\]\]', replace_link, content) + # Convert bold (strong in textile) content = re.sub(r'\*\*(.*?)\*\*', r'**\1**', content) content = re.sub(r'\*(.*?)\*', r'*\1*', content) @@ -82,10 +101,6 @@ def redmine_markup_to_markdown(content): content = re.sub(r'^\s*\* (.*?)$', r'- \1', content, flags=re.MULTILINE) content = re.sub(r'^\s*\# (.*?)$', r'1. \1', content, flags=re.MULTILINE) - # Convert links - handle both [[page]] and [page] syntax - content = re.sub(r'\[\[(.*?)\]\]', r'[\1](\1)', content) # Simple link - content = re.sub(r'\[(.*?)\](.*?)\]', r'[\1](\2)', content) # [text](url) - # Convert images - handle !image! and ![alt](image) content = re.sub(r'!\[(.*?)\]\((.*?)\)', r'![](\2)', content) content = re.sub(r'!(.*?)!', r'![](\\1)', content) @@ -104,6 +119,28 @@ def redmine_markup_to_markdown(content): return content +def download_image(image_url, local_dir): + """Download an image from Redmine and save it locally""" + try: + headers = {"X-Redmine-API-Key": REDMINE_API_KEY} + response = requests.get(f"{REDMINE_URL}{image_url}", headers=headers) + response.raise_for_status() + + # Extract filename from URL + filename = os.path.basename(image_url) + filepath = os.path.join(local_dir, "images", filename) + + # Create images directory if it doesn't exist + os.makedirs(os.path.dirname(filepath), exist_ok=True) + + with open(filepath, 'wb') as f: + f.write(response.content) + + logger.info(f"Downloaded image: {filename}") + return f"./images/{filename}" + except Exception as e: + logger.warning(f"Failed to download image from {image_url}: {e}") + return None def clone_or_init_wiki_repo(repo_url, local_dir): """Clone or initialize the Gitea wiki repo""" @@ -114,11 +151,14 @@ def clone_or_init_wiki_repo(repo_url, local_dir): logger.info(f"Using existing local wiki repo: {local_dir}") os.chdir(local_dir) subprocess.run(["git", "pull"], check=True) + os.chdir('..') return local_dir - def push_to_gitea_wiki(local_dir, pages): """Add pages as Markdown files and push to Gitea""" + page_map = {page['title']: page for page in pages} # Map titles to their data + curr_dir = os.getcwd() + logger.info(f"We are in: {curr_dir}") # Remove all existing files in the repo (except .git folder) for item in os.listdir(local_dir): if item != '.git': @@ -128,43 +168,94 @@ def push_to_gitea_wiki(local_dir, pages): elif os.path.isdir(item_path): import shutil shutil.rmtree(item_path) - + # Create pages as .md files for page in pages: - filename = f"{page['title'].replace(' ', '_')}.md" + title = page['title'] + parent = page.get('parent') + + if title == 'Wiki': + filename = "Home.md" + else: + filename = f"{title.replace(' ', '_')}.md" + filepath = os.path.join(local_dir, filename) - # Convert content to Markdown - markdown_content = redmine_markup_to_markdown(page['content']) + if parent and parent in page_map: + # If this page has a parent, create relative path from the parent's directory + parent_filename = f"{parent.replace(' ', '_')}.md" + parent_filepath = os.path.join(local_dir, parent_filename) + if os.path.exists(parent_filepath): + # Parent directory exists, create subdirectory for this page + page_subdir = os.path.dirname(filepath) + os.makedirs(page_subdir, exist_ok=True) + + else: + # This is a root level page, no need to create subdirectories + pass + + # Convert content to Markdown and handle links + markdown_content = redmine_markup_to_markdown(page['content'], page_map) + + # Find all images in the content to log them + image_pattern = r'!(.*?)!' + found_images = re.findall(image_pattern, markdown_content) + if found_images: + logger.info(f"Found images on page '{title}': {found_images}") + + # Download images referenced in the content + # First, we need to find all image URLs and download them + # Look for Redmine-style image tags like !image.png! + def replace_image(match): + image_name = match.group(1) + + # Try to find the actual image URL by searching for it in the page content + # This is a simplified approach - we'll assume the image name matches + # what's found in Redmine's image system + if image_name: + # In Redmine, images are usually at /attachments/... path + image_url = f"/attachments/download/{image_name}" + logger.info(f"Attempting to download image: {image_name}") + + downloaded_path = download_image(image_url, local_dir) + if downloaded_path: + return f"![]({downloaded_path})" + else: + # If download fails, keep the original format + return f"!{image_name}!" + return match.group(0) + + # We need to handle both !image.png! and ![alt](image) formats + # For now, we'll process !image.png! patterns specifically + markdown_content = re.sub(image_pattern, replace_image, markdown_content) + # Write to file with open(filepath, 'w', encoding='utf-8') as f: - f.write(f"# {page['title']}\n\n") + f.write(f"# {title}\n\n") f.write(markdown_content) - + logger.info(f"Saved page: {filename}") - + # Add, commit, and push os.chdir(local_dir) subprocess.run(["git", "add", "."], check=True) - subprocess.run(["git", "commit", "-m", "ImportRedminewikipages"], check=True) + subprocess.run(["git", "commit", "-m", "'Import Redmine wiki pages'"], check=False) subprocess.run(["git", "push", "origin", "main"], check=True) - + logger.info("✅ Wiki pages pushed to Gitea!") - def main(): # Step 1: Get wiki pages from Redmine logger.info("Fetching Redmine wiki pages...") pages = redmine_get_wiki_pages() - + # Step 2: Prepare local directory for Gitea wiki local_wiki_dir = "gitea_wiki_clone" clone_or_init_wiki_repo(GITEA_WIKI_URL, local_wiki_dir) - + # Step 3: Push pages to Gitea push_to_gitea_wiki(local_wiki_dir, pages) - if __name__ == "__main__": main() \ No newline at end of file