From 85f58e252829f0996228e06cc5d7ab256d1d4258 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Lohrer?= Date: Wed, 5 Nov 2025 05:32:20 +0100 Subject: [PATCH 1/7] Feature: Map authors as tags in Vorname_Nachname format MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Added format_author_as_tag() function to convert author names to tag format - Author names automatically added to tags with spaces replaced by underscores - Example: 'Jörg Lohrer' → 'Jörg_Lohrer' tag - Enables author filtering via WordPress tag taxonomy - Updated documentation to reflect new author handling approach - Version bump to 0.3.0 --- CHANGELOG.md | 31 +++++++++++++++++++++++++++++++ README.md | 8 +++++--- markdown_parser.py | 36 ++++++++++++++++++++++++++++++++++++ 3 files changed, 72 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ab250b0..4f10847 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,36 @@ # Changelog +## Version 0.3.0 (2025-10-01) + +### Feature - Autor-zu-Tag-Mapping 🏷️ + +**Neues Feature:** Autoren werden automatisch als Tags hinzugefügt! + +**Funktion:** +- Autor aus Frontmatter wird extrahiert (`author`, `#staticSiteGenerator.author`, `#commonMetadata.creator`) +- Autor-Name wird automatisch als Tag im Format `Vorname_Nachname` hinzugefügt +- Leerzeichen werden durch Unterstriche ersetzt +- Mehrfache Unterstriche werden konsolidiert + +**Beispiel:** +```yaml +author: Jörg Lohrer +tags: + - OER + - Community +``` + +**Ergebnis:** +- Tags in WordPress: `OER`, `Community`, `Jörg_Lohrer` +- Filterung nach Autoren über WordPress-Tag-Taxonomie möglich + +**Technische Details:** +- Neue Funktion `format_author_as_tag()` in `markdown_parser.py` +- Autor-Tag wird nur hinzugefügt, wenn Autor vorhanden und noch nicht in Tags +- Integration in `extract_wordpress_metadata()` nach Autor-Extraktion + +--- + ## Version 0.2.2 (2025-10-01) ### Bugfix - Kritisch! 🐛 diff --git a/README.md b/README.md index 2176a92..02e3f19 100644 --- a/README.md +++ b/README.md @@ -7,9 +7,10 @@ Automatisierter Workflow zum Erstellen von WordPress-Beiträgen aus Markdown-Dat ## ⚠️ Bekannte Einschränkungen **Autor-Zuordnung:** -- Beiträge werden aktuell immer dem importierenden WordPress-Benutzer zugeordnet -- Der `author` aus dem Frontmatter wird extrahiert, aber nicht mit WordPress-Benutzern abgeglichen -- **Zu entwickeln:** Automatisches Mapping von Frontmatter-Autoren zu WordPress-User-IDs oder manuelle Zuordnungs-Konfiguration +- Beiträge werden immer dem importierenden WordPress-Benutzer zugeordnet (WordPress REST-API Limitation) +- Der `author` aus dem Frontmatter wird automatisch als **Tag** im Format `Vorname_Nachname` hinzugefügt +- Beispiel: `author: Jörg Lohrer` → Tag: `Jörg_Lohrer` +- Dies ermöglicht die Filterung nach Autoren über die WordPress-Tag-Taxonomie **Forgejo-Repository-Import:** - Die Batch-Verarbeitung ganzer Repositories wurde noch nicht ausreichend getestet @@ -21,6 +22,7 @@ Automatisierter Workflow zum Erstellen von WordPress-Beiträgen aus Markdown-Dat ## Features - ✅ **Automatische Metadaten-Extraktion**: name, description, tags, image, author aus YAML-Frontmatter +- ✅ **Autor-zu-Tag-Mapping**: Autoren werden automatisch als Tags im Format `Vorname_Nachname` hinzugefügt - ✅ **Drei Verwendungsmodi**: Einzelne URL, YAML-Batch, Forgejo-Repository - ✅ **Duplikatsprüfung**: Verhindert das doppelte Erstellen von Beiträgen und Medien - ✅ **Markdown zu HTML**: Automatische Konvertierung von Markdown-Inhalten diff --git a/markdown_parser.py b/markdown_parser.py index 03df5a1..18a15be 100644 --- a/markdown_parser.py +++ b/markdown_parser.py @@ -39,6 +39,29 @@ def extract_frontmatter(markdown_content: str) -> tuple[Optional[Dict[str, Any]] return None, markdown_content +def format_author_as_tag(author_name: str) -> str: + """ + Formatiert einen Autornamen als Tag im Format Vorname_Nachname + + Args: + author_name: Autorenname (z.B. "Max Mustermann" oder "Max") + + Returns: + Tag-formatierter Name (z.B. "Max_Mustermann") + """ + # Entferne führende/nachfolgende Leerzeichen + author_name = author_name.strip() + + # Ersetze Leerzeichen durch Unterstriche + tag_name = author_name.replace(' ', '_') + + # Entferne mehrfache Unterstriche + while '__' in tag_name: + tag_name = tag_name.replace('__', '_') + + return tag_name + + def extract_wordpress_metadata(frontmatter: Dict[str, Any], default_author: str = "admin") -> Dict[str, Any]: """ @@ -94,6 +117,10 @@ def extract_wordpress_metadata(frontmatter: Dict[str, Any], elif isinstance(tags, str): metadata['tags'] = [t.strip() for t in tags.split(',')] + # Initialisiere tags falls nicht vorhanden + if 'tags' not in metadata: + metadata['tags'] = [] + # Kategorien extrahieren (falls vorhanden) if 'categories' in frontmatter: categories = frontmatter['categories'] @@ -134,6 +161,15 @@ def extract_wordpress_metadata(frontmatter: Dict[str, Any], if 'author' not in metadata: metadata['author'] = default_author + # Autor als Tag hinzufügen (Format: Vorname_Nachname) + if metadata.get('author'): + author_tag = format_author_as_tag(metadata['author']) + # Füge Autor-Tag zu den Tags hinzu, falls noch nicht vorhanden + if author_tag and author_tag not in metadata.get('tags', []): + if 'tags' not in metadata: + metadata['tags'] = [] + metadata['tags'].append(author_tag) + # Status extrahieren (falls vorhanden) if 'status' in frontmatter: metadata['status'] = frontmatter['status'] From aa50145633ffa4617a576203a2569ef44f8113c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Lohrer?= Date: Wed, 5 Nov 2025 05:40:46 +0100 Subject: [PATCH 2/7] Fix: Support multiple authors as tags MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Previously only first author from list was converted to tag - Now collects all authors from all sources (author, #staticSiteGenerator.author, #commonMetadata.creator) - Handles both single authors and author lists - Removes duplicates if same author appears in multiple sources - All authors are added as individual tags in Vorname_Nachname format - Example: ['Florian Mayrhofer', 'Gina Buchwald-Chassée'] → tags 'Florian_Mayrhofer', 'Gina_Buchwald-Chassée' --- markdown_parser.py | 60 +++++++++++++++++++++++++++++++++++++++------- 1 file changed, 52 insertions(+), 8 deletions(-) diff --git a/markdown_parser.py b/markdown_parser.py index 18a15be..6bf6421 100644 --- a/markdown_parser.py +++ b/markdown_parser.py @@ -161,14 +161,58 @@ def extract_wordpress_metadata(frontmatter: Dict[str, Any], if 'author' not in metadata: metadata['author'] = default_author - # Autor als Tag hinzufügen (Format: Vorname_Nachname) - if metadata.get('author'): - author_tag = format_author_as_tag(metadata['author']) - # Füge Autor-Tag zu den Tags hinzu, falls noch nicht vorhanden - if author_tag and author_tag not in metadata.get('tags', []): - if 'tags' not in metadata: - metadata['tags'] = [] - metadata['tags'].append(author_tag) + # Alle Autoren als Tags hinzufügen (Format: Vorname_Nachname) + # Sammle alle Autoren aus verschiedenen Quellen + all_authors = [] + + # Aus direktem author-Feld + if 'author' in frontmatter: + author = frontmatter['author'] + if isinstance(author, list): + all_authors.extend(author) + elif isinstance(author, str): + all_authors.append(author) + + # Aus #staticSiteGenerator + if isinstance(frontmatter.get('#staticSiteGenerator'), dict): + static_gen = frontmatter['#staticSiteGenerator'] + if 'author' in static_gen: + author = static_gen['author'] + if isinstance(author, list): + all_authors.extend(author) + elif isinstance(author, str): + all_authors.append(author) + + # Aus #commonMetadata.creator + if isinstance(frontmatter.get('#commonMetadata'), dict): + common = frontmatter['#commonMetadata'] + if 'creator' in common: + creator = common['creator'] + if isinstance(creator, list): + for c in creator: + if isinstance(c, dict): + given = c.get('givenName', '') + family = c.get('familyName', '') + full_name = f"{given} {family}".strip() + if full_name: + all_authors.append(full_name) + elif isinstance(creator, dict): + given = creator.get('givenName', '') + family = creator.get('familyName', '') + full_name = f"{given} {family}".strip() + if full_name: + all_authors.append(full_name) + + # Duplikate entfernen und als Tags hinzufügen + seen_authors = set() + for author_name in all_authors: + if author_name and author_name not in seen_authors: + seen_authors.add(author_name) + author_tag = format_author_as_tag(author_name) + if author_tag and author_tag not in metadata.get('tags', []): + if 'tags' not in metadata: + metadata['tags'] = [] + metadata['tags'].append(author_tag) # Status extrahieren (falls vorhanden) if 'status' in frontmatter: From fb9720fb2ac8af416ba4b490ed5cd78b2c5ae597 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Lohrer?= Date: Wed, 5 Nov 2025 05:45:24 +0100 Subject: [PATCH 3/7] Feature: Update existing posts instead of skipping - Added update_post() method to WordPress API client - Added _put() method for HTTP PUT requests - Modified create_post() to call update_post() when duplicate is found - Existing posts now get updated with latest content, tags, categories, dates, etc. - Prevents manual deletion and re-creation workflow - Added excerpt as explicit parameter to create_post() - Debug output shows 'Aktualisiere...' message when updating Example: Re-running import on existing post now updates all fields including newly added author tags --- wordpress_api.py | 103 +++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 100 insertions(+), 3 deletions(-) diff --git a/wordpress_api.py b/wordpress_api.py index 59f7773..adeefd5 100644 --- a/wordpress_api.py +++ b/wordpress_api.py @@ -46,6 +46,13 @@ class WordPressAPI: response.raise_for_status() return response + def _put(self, endpoint: str, data: Optional[Dict] = None) -> requests.Response: + """PUT-Request an WordPress API (für Updates)""" + url = urljoin(self.api_base, endpoint) + response = self.session.put(url, json=data) + response.raise_for_status() + return response + def check_post_exists(self, title: str) -> Optional[int]: """ Prüft, ob ein Beitrag mit dem Titel bereits existiert @@ -184,6 +191,7 @@ class WordPressAPI: featured_media: Optional[int] = None, categories: Optional[List[int]] = None, tags: Optional[List[int]] = None, + excerpt: Optional[str] = None, check_duplicate: bool = True, **kwargs) -> Optional[int]: """ @@ -202,12 +210,22 @@ class WordPressAPI: Returns: Post-ID des erstellten Beitrags, oder None bei Fehler """ - # Duplikatsprüfung + # Duplikatsprüfung - bei Duplikat Update durchführen if check_duplicate: existing_id = self.check_post_exists(title) if existing_id: - print(f"Beitrag '{title}' existiert bereits (ID: {existing_id})") - return existing_id + print(f"Beitrag '{title}' existiert bereits (ID: {existing_id}) - Aktualisiere...") + return self.update_post( + post_id=existing_id, + title=title, + content=content, + status=status, + featured_media=featured_media, + categories=categories, + tags=tags, + excerpt=excerpt, + **kwargs + ) # Post-Daten zusammenstellen post_data = { @@ -217,6 +235,8 @@ class WordPressAPI: **kwargs } + if excerpt: + post_data['excerpt'] = excerpt if featured_media: post_data['featured_media'] = featured_media if categories: @@ -257,6 +277,83 @@ class WordPressAPI: print(f"Details: {e.response.text}") return None + def update_post(self, post_id: int, title: Optional[str] = None, + content: Optional[str] = None, status: Optional[str] = None, + featured_media: Optional[int] = None, + categories: Optional[List[int]] = None, + tags: Optional[List[int]] = None, + excerpt: Optional[str] = None, + **kwargs) -> Optional[int]: + """ + Aktualisiert einen existierenden WordPress-Beitrag + + Args: + post_id: ID des zu aktualisierenden Beitrags + title: Neuer Titel (optional) + content: Neuer Inhalt (optional) + status: Neuer Status (optional) + featured_media: ID des Beitragsbilds (optional) + categories: Liste der Kategorie-IDs (optional) + tags: Liste der Tag-IDs (optional) + excerpt: Auszug (optional) + **kwargs: Weitere WordPress-Post-Felder + + Returns: + Post-ID des aktualisierten Beitrags, oder None bei Fehler + """ + # Post-Daten zusammenstellen (nur Felder die gesetzt sind) + post_data = {**kwargs} + + if title is not None: + post_data['title'] = title + if content is not None: + post_data['content'] = content + if status is not None: + post_data['status'] = status + if excerpt is not None: + post_data['excerpt'] = excerpt + if featured_media is not None: + post_data['featured_media'] = featured_media + if categories is not None: + post_data['categories'] = categories + if tags is not None: + post_data['tags'] = tags + + # Debug: Zeige was aktualisiert wird + print(f"Aktualisiere Beitrag (ID: {post_id}):") + if title: + print(f" - Titel: {title}") + if status: + print(f" - Status: {status}") + if tags: + print(f" - Tags: {tags}") + if categories: + print(f" - Kategorien: {categories}") + if 'date' in post_data: + print(f" - Datum: {post_data['date']}") + if 'date_gmt' in post_data: + print(f" - Datum GMT: {post_data['date_gmt']}") + + # Beitrag aktualisieren + try: + response = self._put(f'posts/{post_id}', data=post_data) + post = response.json() + print(f"✅ Beitrag aktualisiert (ID: {post_id}, Status: {post.get('status')})") + + # Debug: Zeige was WordPress zurückgibt + if 'tags' in post and post['tags']: + print(f" WordPress-Tags: {post['tags']}") + if 'date' in post: + print(f" WordPress-Datum: {post['date']}") + + return post_id + + except requests.exceptions.RequestException as e: + print(f"Fehler beim Aktualisieren des Beitrags: {e}") + if hasattr(e.response, 'text'): + print(f"Details: {e.response.text}") + return None + def get_categories(self, search: Optional[str] = None) -> List[Dict[str, Any]]: """ Holt alle verfügbaren Kategorien oder sucht nach einer bestimmten Kategorie From 98d7244e9d21d79c1ab93876f59c2c0aef97ed47 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Lohrer?= Date: Wed, 5 Nov 2025 06:02:31 +0100 Subject: [PATCH 4/7] Change default post status from 'draft' to 'publish' - Changed default_status in direct URL mode from 'draft' to 'publish' - Changed default_status in Forgejo repository mode from 'draft' to 'publish' - Changed fallback in status determination from 'draft' to 'publish' - Posts without creativeWorkStatus in frontmatter will now be published immediately - Can still be overridden by setting creativeWorkStatus in frontmatter or status in YAML config --- workflow.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/workflow.py b/workflow.py index 76b8a17..8dc7fa4 100644 --- a/workflow.py +++ b/workflow.py @@ -231,7 +231,7 @@ def process_post(wp_api: WordPressAPI, post_config: Dict[str, Any], ) # Status - status = metadata.get('status') or post_config.get('status') or global_settings.get('default_status', 'draft') + status = metadata.get('status') or post_config.get('status') or global_settings.get('default_status', 'publish') # Excerpt excerpt = metadata.get('excerpt') or post_config.get('excerpt', '') @@ -361,7 +361,7 @@ def main(): # Erstelle minimale Konfiguration post_config = {'url': arg} global_settings = { - 'default_status': 'draft', + 'default_status': 'publish', 'default_author': 'admin', 'skip_duplicates': True, 'skip_duplicate_media': True @@ -410,7 +410,7 @@ def main(): wp_api = WordPressAPI(wp_url, wp_username, wp_password) global_settings = { - 'default_status': 'draft', + 'default_status': 'publish', 'default_author': 'admin', 'skip_duplicates': True, 'skip_duplicate_media': True From 86717185c410ea1876486eeeb6afddb24ecc180f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Lohrer?= Date: Wed, 5 Nov 2025 06:11:15 +0100 Subject: [PATCH 5/7] Feature: Add path filter for Forgejo repository imports - Added path_filter parameter to fetch_forgejo_repo_markdown_files() - Filter supports wildcard patterns (e.g., 'posts/*/index.md') - Fixed hardcoded base URL - now detects git.rpi-virtuell.de and other instances - Added --filter command line option for repo mode - Updated help text with filter examples - Enables selective import of specific markdown files from repository Usage: python workflow.py --repo [branch] --filter 'Website/content/posts/*/index.md' Example: Imports only index.md files from posts subdirectories (59 files found) --- workflow.py | 50 ++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 40 insertions(+), 10 deletions(-) diff --git a/workflow.py b/workflow.py index 8dc7fa4..2735734 100644 --- a/workflow.py +++ b/workflow.py @@ -279,13 +279,15 @@ def process_post(wp_api: WordPressAPI, post_config: Dict[str, Any], return post_id -def fetch_forgejo_repo_markdown_files(repo_url: str, branch: str = 'main') -> List[str]: +def fetch_forgejo_repo_markdown_files(repo_url: str, branch: str = 'main', + path_filter: str = None) -> List[str]: """ Holt alle Markdown-URLs aus einem Forgejo-Repository Args: repo_url: URL zum Repository (z.B. https://codeberg.org/user/repo) branch: Branch-Name (Standard: main) + path_filter: Optionaler Filter für Pfade (z.B. 'posts/*/index.md') Returns: Liste von URLs zu Markdown-Dateien @@ -302,17 +304,23 @@ def fetch_forgejo_repo_markdown_files(repo_url: str, branch: str = 'main') -> Li owner = parts[-2] repo = parts[-1] - # API-URL ermitteln + # Basis-URL ermitteln (für Raw-URLs) if 'codeberg.org' in repo_url: + base_url = 'https://codeberg.org' api_base = 'https://codeberg.org/api/v1' + elif 'git.rpi-virtuell.de' in repo_url: + base_url = 'https://git.rpi-virtuell.de' + api_base = 'https://git.rpi-virtuell.de/api/v1' elif 'gitea' in repo_url or 'forgejo' in repo_url: # Generischer Ansatz für selbst-gehostete Instanzen base_parts = repo_url.split('/')[:3] - api_base = '/'.join(base_parts) + '/api/v1' + base_url = '/'.join(base_parts) + api_base = base_url + '/api/v1' else: print(f"Warnung: Unbekannte Forgejo-Instanz, versuche generischen API-Pfad") base_parts = repo_url.split('/')[:3] - api_base = '/'.join(base_parts) + '/api/v1' + base_url = '/'.join(base_parts) + api_base = base_url + '/api/v1' api_url = f"{api_base}/repos/{owner}/{repo}/git/trees/{branch}?recursive=true" @@ -324,8 +332,17 @@ def fetch_forgejo_repo_markdown_files(repo_url: str, branch: str = 'main') -> Li markdown_files = [] for item in data.get('tree', []): if item['type'] == 'blob' and item['path'].endswith('.md'): + # Filter anwenden falls vorhanden + if path_filter: + # Konvertiere Wildcard-Filter zu regex-Pattern + # z.B. 'posts/*/index.md' -> '^posts/[^/]+/index\.md$' + import re + pattern = path_filter.replace('*', '[^/]+').replace('.', r'\.') + if not re.match(f'^{pattern}$', item['path']): + continue + # Raw-URL konstruieren - raw_url = f"https://codeberg.org/{owner}/{repo}/raw/branch/{branch}/{item['path']}" + raw_url = f"{base_url}/{owner}/{repo}/raw/branch/{branch}/{item['path']}" markdown_files.append(raw_url) return markdown_files @@ -385,12 +402,21 @@ def main(): repo_index = sys.argv.index('--forgejo-repo') if '--forgejo-repo' in sys.argv else sys.argv.index('--repo') if len(sys.argv) > repo_index + 1: repo_url = sys.argv[repo_index + 1] - branch = sys.argv[repo_index + 2] if len(sys.argv) > repo_index + 2 else 'main' + branch = sys.argv[repo_index + 2] if len(sys.argv) > repo_index + 2 and not sys.argv[repo_index + 2].startswith('--') else 'main' + + # Pfad-Filter für spezifische Dateien (z.B. nur index.md in posts/) + path_filter = None + if '--filter' in sys.argv: + filter_index = sys.argv.index('--filter') + if len(sys.argv) > filter_index + 1: + path_filter = sys.argv[filter_index + 1] print(f"Forgejo-Modus: Verarbeite Repository: {repo_url}") print(f"Branch: {branch}") + if path_filter: + print(f"Filter: {path_filter}") - markdown_urls = fetch_forgejo_repo_markdown_files(repo_url, branch) + markdown_urls = fetch_forgejo_repo_markdown_files(repo_url, branch, path_filter) if not markdown_urls: print("Keine Markdown-Dateien im Repository gefunden") @@ -450,9 +476,13 @@ def main(): if not os.path.exists(config_file): print(f"Fehler: Konfigurationsdatei '{config_file}' nicht gefunden") print("\nVerwendung:") - print(" python workflow.py [config.yaml] # YAML-Konfiguration") - print(" python workflow.py # Einzelne Markdown-URL") - print(" python workflow.py --repo [branch] # Forgejo-Repository") + print(" python workflow.py [config.yaml] # YAML-Konfiguration") + print(" python workflow.py # Einzelne Markdown-URL") + print(" python workflow.py --repo [branch] # Forgejo-Repository (alle .md)") + print(" python workflow.py --repo [branch] --filter # Mit Filter") + print("\nFilter-Beispiele:") + print(" --filter 'posts/*/index.md' # Nur index.md in posts/-Unterverzeichnissen") + print(" --filter 'Website/content/posts/*/index.md' # Mit vollständigem Pfad") sys.exit(1) print(f"Lade Konfiguration aus: {config_file}") From a931ff9ace5844f2736f50ba8a15c3591e8eb5d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Lohrer?= Date: Wed, 5 Nov 2025 06:12:34 +0100 Subject: [PATCH 6/7] Docs: Add filter examples to QUICKSTART.md - Added --filter option examples for repository imports - Included practical filter patterns (posts/*/index.md, etc.) - Removed experimental warning as feature is now tested - Added real-world example with FOERBICO repository --- QUICKSTART.md | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/QUICKSTART.md b/QUICKSTART.md index 0ee30cd..80e13c5 100644 --- a/QUICKSTART.md +++ b/QUICKSTART.md @@ -43,14 +43,26 @@ source .venv/bin/activate python workflow.py posts.yaml ``` -### 3. Ganzes Repository (Forgejo/Gitea) ⚠️ Experimentell +### 3. Ganzes Repository (Forgejo/Gitea) +**Alle Markdown-Dateien importieren:** ```bash source .venv/bin/activate python workflow.py --repo "https://codeberg.org/user/repo" main ``` -**Hinweis:** Diese Funktion wurde noch nicht ausgiebig getestet. Nutzen Sie zunächst Modus 1 oder 2. +**Nur bestimmte Dateien importieren (mit Filter):** +```bash +source .venv/bin/activate +python workflow.py --repo "https://git.rpi-virtuell.de/Comenius-Institut/FOERBICO_und_rpi-virtuell" main --filter 'Website/content/posts/*/index.md' +``` + +**Filter-Beispiele:** +- `'posts/*/index.md'` - Nur index.md in posts/-Unterverzeichnissen +- `'Website/content/posts/*/index.md'` - Mit vollständigem Pfad +- `'content/*.md'` - Alle .md-Dateien direkt in content/ + +**Hinweis:** Der Filter unterstützt Wildcards (`*`) für Verzeichnisnamen. ## Schnellstart-Schritte From fbd4745afb90dc9bf841ca8d5d69263c6880afad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Lohrer?= Date: Wed, 5 Nov 2025 06:25:26 +0100 Subject: [PATCH 7/7] a --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 02e3f19..25e74f7 100644 --- a/README.md +++ b/README.md @@ -64,7 +64,7 @@ Automatisierter Workflow zum Erstellen von WordPress-Beiträgen aus Markdown-Dat ```env WORDPRESS_URL=https://news.rpi-virtuell.de WORDPRESS_USERNAME=ihr_benutzername - WORDPRESS_APP_PASSWORD=UIVI 4Tdy oojL 9iZG g3X2 iAn5 + WORDPRESS_APP_PASSWORD=UIVI 23H6 oojL 9iZG g3X2 Aon5 ``` ## WordPress Anwendungspasswort erstellen