diff --git a/open_workshop_dokuwiki/README.md b/open_workshop_dokuwiki/README.md
new file mode 100644
index 0000000..ac351cf
--- /dev/null
+++ b/open_workshop_dokuwiki/README.md
@@ -0,0 +1,35 @@
+Verfügbare Platzhalter:
+
+Basis-Felder (maintenance.equipment):
+
+{name} - Equipment-Name
+{serial_no} - Seriennummer
+{model} - Modell
+{category} - Kategoriename
+{status} - Status (aus status_id)
+{location} - Standort
+{ows_area} - Bereichsname
+{assign_date} - Zuweisungsdatum (formatiert)
+{cost} - Kosten
+{warranty_date} - Garantiedatum (formatiert)
+{note} - Notizen
+Spezial-Felder:
+
+{view_type} - "Bereich" oder "Einsatzzweck"
+{view_name} - Name des Bereichs/Einsatzzwecks
+{wiki_doku_page} - ID der zentralen Doku-Seite
+{wiki_doku_link} - Fertiger Link zur zentralen Doku-Seite
+{odoo_link} - Fertiger Link zur Odoo Equipment-Seite
+{odoo_url} - URL zur Odoo Equipment-Seite (ohne Link-Markup)
+{sync_datetime} - Aktuelles Datum/Zeit
+{image} - Equipment-Bild (300px Breite) - Format: {{:media_id?300}}
+{image_large} - Equipment-Bild (Originalgröße) - Format: {{:media_id}}
+{image_id} - Media-ID des Bildes (z.B. werkstatt:ausruestung:media:sabako-laser.jpg)
+ows.machine Felder (falls verknüpft):
+
+{ows_machine_id.name} - Name
+{ows_machine_id.model} - Modell
+{ows_machine_id.serial_no} - Seriennummer
+{ows_machine_id.location} - Standort
+{ows_machine_id.note} - Notizen
+{ows_machine_id.category} - Kategorie
\ No newline at end of file
diff --git a/open_workshop_dokuwiki/models/dokuwiki_client.py b/open_workshop_dokuwiki/models/dokuwiki_client.py
index b5b4954..097f717 100644
--- a/open_workshop_dokuwiki/models/dokuwiki_client.py
+++ b/open_workshop_dokuwiki/models/dokuwiki_client.py
@@ -175,3 +175,67 @@ class DokuWikiClient(models.AbstractModel):
wiki_url = wiki_url.rstrip('/')
return f"{wiki_url}/doku.php?id={page_id}"
+
+ @api.model
+ def upload_media(self, media_id, file_content, overwrite=True):
+ """
+ Lädt eine Mediendatei (Bild) ins DokuWiki hoch.
+
+ Args:
+ media_id (str): DokuWiki Media-ID (z.B. "werkstatt:ausruestung:equipment_3.jpg")
+ file_content (bytes): Binärer Dateiinhalt
+ overwrite (bool): Existierende Datei überschreiben
+
+ Returns:
+ bool: True bei Erfolg
+
+ Raises:
+ UserError: Bei Fehler beim Upload
+ """
+ wiki = self._get_wiki_connection()
+
+ import tempfile
+ import os
+
+ # Temporäre Datei erstellen (dokuwiki.py erwartet Datei-Pfad)
+ with tempfile.NamedTemporaryFile(delete=False, suffix='.jpg') as tmp_file:
+ tmp_file.write(file_content)
+ tmp_path = tmp_file.name
+
+ try:
+ wiki.medias.add(media_id, tmp_path, overwrite=overwrite)
+ _logger.info(f"Mediendatei hochgeladen: {media_id}")
+ return True
+ except DokuWikiError as e:
+ error_msg = f"Fehler beim Hochladen der Mediendatei {media_id}: {e}"
+ _logger.error(error_msg)
+ raise UserError(error_msg)
+ finally:
+ # Temporäre Datei löschen
+ if os.path.exists(tmp_path):
+ os.unlink(tmp_path)
+
+ @api.model
+ def get_media_url(self, media_id):
+ """
+ Generiert die vollständige URL zu einer DokuWiki-Mediendatei.
+
+ Args:
+ media_id (str): DokuWiki Media-ID
+
+ Returns:
+ str: Vollständige URL zur Mediendatei
+ """
+ IrConfigParameter = self.env['ir.config_parameter'].sudo()
+ wiki_url = IrConfigParameter.get_param('dokuwiki.url', '')
+
+ if not wiki_url:
+ return ""
+
+ # Entferne trailing slash falls vorhanden
+ wiki_url = wiki_url.rstrip('/')
+
+ # Ersetze : durch / für Media-Pfad
+ media_path = media_id.replace(':', '/')
+
+ return f"{wiki_url}/lib/exe/fetch.php?media={media_id}"
diff --git a/open_workshop_dokuwiki/models/maintenance_equipment.py b/open_workshop_dokuwiki/models/maintenance_equipment.py
index 370a334..4154a92 100644
--- a/open_workshop_dokuwiki/models/maintenance_equipment.py
+++ b/open_workshop_dokuwiki/models/maintenance_equipment.py
@@ -11,6 +11,14 @@ _logger = logging.getLogger(__name__)
class MaintenanceEquipment(models.Model):
_inherit = 'maintenance.equipment'
+ # Bild-Feld
+ image_1920 = fields.Image(
+ string='Bild',
+ max_width=1920,
+ max_height=1920,
+ help='Equipment-Bild (wird beim Wiki-Sync hochgeladen)'
+ )
+
# Wiki-Felder
wiki_doku_id = fields.Char(
string='Wiki Dokumentations-ID',
@@ -143,9 +151,163 @@ class MaintenanceEquipment(models.Model):
return name or "unnamed"
- def _generate_wiki_main_page_content(self, view_type='area'):
+ def _render_template_from_wiki(self, view_type='area'):
"""
- Generiert den Wiki-Markup-Inhalt für eine Haupt-Ansichtsseite.
+ Lädt c_template aus DokuWiki und ersetzt Platzhalter mit Odoo-Feldwerten.
+ Template-Pfad: werkstatt:ausruestung:c_template
+
+ Platzhalter-Format:
+ - {feldname} für maintenance.equipment Felder, z.B. {name}, {serial_no}
+ - {ows_machine_id.feldname} für ows.machine Felder, z.B. {ows_machine_id.power}
+ - {wiki_doku_page} für die zentrale Doku-Seite ID
+ - {wiki_doku_link} für Link zur zentralen Doku-Seite
+ - {ows_area} für Bereichsname
+ - {category} für Kategoriename
+ - {sync_datetime} für aktuelles Datum/Zeit
+
+ Args:
+ view_type (str): 'area' oder 'purpose'
+
+ Returns:
+ str: Gerenderter Wiki-Markup-Inhalt
+ """
+ self.ensure_one()
+ dokuwiki_client = self.env['dokuwiki.client']
+ template_page_id = 'werkstatt:ausruestung:c_template'
+
+ try:
+ # Template aus Wiki laden
+ template_content = dokuwiki_client.get_page(template_page_id)
+ if not template_content:
+ _logger.warning(f"Template {template_page_id} nicht gefunden, verwende Fallback")
+ return self._generate_wiki_main_page_content_fallback(view_type)
+
+ _logger.info(f"Template geladen: {template_page_id} ({len(template_content)} Zeichen)")
+
+ # Werte-Dictionary vorbereiten
+ values = self._prepare_template_values(view_type)
+
+ # Platzhalter ersetzen
+ rendered_content = template_content
+ for key, value in values.items():
+ placeholder = '{' + key + '}'
+ rendered_content = rendered_content.replace(placeholder, str(value or ''))
+
+ return rendered_content
+
+ except Exception as e:
+ _logger.warning(f"Fehler beim Laden des Templates {template_page_id}: {e}")
+ return self._generate_wiki_main_page_content_fallback(view_type)
+
+ def _prepare_template_values(self, view_type='area'):
+ """
+ Bereitet Dictionary mit allen verfügbaren Feldwerten für Template-Rendering vor.
+
+ Args:
+ view_type (str): 'area' oder 'purpose'
+
+ Returns:
+ dict: Dictionary mit Feldnamen als Keys und Werten
+ """
+ self.ensure_one()
+
+ # Zentrale Doku-Seite
+ doku_page_id = self._get_wiki_doku_page_id()
+
+ # Odoo Base URL
+ base_url = self.env['ir.config_parameter'].sudo().get_param('web.base.url', 'http://localhost:8069')
+ odoo_equipment_url = f"{base_url}/web#id={self.id}&model=maintenance.equipment&view_type=form"
+
+ # Basis-Werte für maintenance.equipment
+ values = {
+ # Spezielle Werte
+ 'wiki_doku_page': doku_page_id,
+ 'wiki_doku_link': f"[[{doku_page_id}|✏️ Zentrale Dokumentation bearbeiten]]",
+ 'odoo_link': f"[[{odoo_equipment_url}|🔗 In Odoo öffnen]]",
+ 'odoo_url': odoo_equipment_url,
+ 'sync_datetime': datetime.now().strftime('%d.%m.%Y %H:%M'),
+
+ # Standard Equipment-Felder
+ 'name': self.name or '',
+ 'serial_no': self.serial_no or '',
+ 'model': self.model or '',
+ 'ows_area': self.ows_area_id.name if self.ows_area_id else '',
+ 'category': self.category_id.name if self.category_id else '',
+ 'status': self.status_id.name if self.status_id else '',
+ 'location': self.location or '',
+ 'assign_date': self.assign_date.strftime('%d.%m.%Y') if self.assign_date else '',
+ 'cost': str(self.cost) if self.cost else '',
+ 'warranty_date': self.warranty_date.strftime('%d.%m.%Y') if self.warranty_date else '',
+ 'color': str(self.color) if self.color else '',
+ 'note': self.note or '',
+ }
+
+ # ows.machine Felder hinzufügen (falls verknüpft)
+ if self.ows_machine_id:
+ machine = self.ows_machine_id
+ # Nur existierende Felder hinzufügen
+ ows_machine_fields = {}
+
+ # Standard-Felder von ows.machine
+ if hasattr(machine, 'name') and machine.name:
+ ows_machine_fields['ows_machine_id.name'] = machine.name
+ if hasattr(machine, 'model') and machine.model:
+ ows_machine_fields['ows_machine_id.model'] = machine.model
+ if hasattr(machine, 'serial_no') and machine.serial_no:
+ ows_machine_fields['ows_machine_id.serial_no'] = machine.serial_no
+ if hasattr(machine, 'location') and machine.location:
+ ows_machine_fields['ows_machine_id.location'] = machine.location
+ if hasattr(machine, 'note'):
+ ows_machine_fields['ows_machine_id.note'] = machine.note or ''
+ if hasattr(machine, 'category_id') and machine.category_id:
+ ows_machine_fields['ows_machine_id.category'] = machine.category_id.name
+
+ values.update(ows_machine_fields)
+
+ # Bild-Upload und Referenz (falls vorhanden)
+ if self.image_1920:
+ wiki_doku_id = self._get_wiki_doku_id()
+ # Media-ID: werkstatt:ausruestung:media:equipment_name.jpg
+ media_id = f"werkstatt:ausruestung:media:{wiki_doku_id}.jpg"
+
+ # Bild ins Wiki hochladen
+ try:
+ import base64
+ # image_1920 ist base64-kodiert, in bytes umwandeln für XML-RPC
+ image_bytes = base64.b64decode(self.image_1920)
+
+ dokuwiki_client = self.env['dokuwiki.client']
+ dokuwiki_client.upload_media(media_id, image_bytes, overwrite=True)
+
+ # DokuWiki Image-Syntax: {{namespace:file.jpg?300}}
+ values['image'] = f"{{{{:{media_id}?300}}}}"
+ values['image_large'] = f"{{{{:{media_id}}}}}"
+ values['image_id'] = media_id
+ _logger.info(f"Bild hochgeladen: {media_id}")
+ except Exception as e:
+ _logger.error(f"Fehler beim Bild-Upload: {e}", exc_info=True)
+ values['image'] = ''
+ values['image_large'] = ''
+ values['image_id'] = ''
+ else:
+ values['image'] = ''
+ values['image_large'] = ''
+ values['image_id'] = ''
+
+ # View-Typ spezifische Werte
+ if view_type == 'area':
+ values['view_type'] = 'Bereich'
+ values['view_name'] = self.ows_area_id.name if self.ows_area_id else 'Unbekannt'
+ else:
+ values['view_type'] = 'Einsatzzweck'
+ values['view_name'] = 'TODO: Einsatzzweck'
+
+ return values
+
+ def _generate_wiki_main_page_content_fallback(self, view_type='area'):
+ """
+ Fallback-Methode: Generiert hart-codierten Wiki-Markup-Inhalt,
+ wenn c_template.txt nicht verfügbar ist.
Args:
view_type (str): 'area' oder 'purpose'
@@ -183,6 +345,19 @@ class MaintenanceEquipment(models.Model):
"""
return content
+ def _generate_wiki_main_page_content(self, view_type='area'):
+ """
+ Generiert den Wiki-Markup-Inhalt für die Haupt-Ansichtsseite.
+ Verwendet c_template.txt aus DokuWiki falls verfügbar, sonst Fallback.
+
+ Args:
+ view_type (str): 'area' oder 'purpose'
+
+ Returns:
+ str: Wiki-Markup-Inhalt
+ """
+ return self._render_template_from_wiki(view_type)
+
def _generate_wiki_doku_page_content(self):
"""
Generiert den initialen Wiki-Markup-Inhalt für die zentrale Dokumentationsseite.
@@ -270,7 +445,7 @@ Hier kann die Dokumentation für {self.name} geschrieben werden.
else:
_logger.info(f"Zentrale Doku-Seite für {self.name} existiert bereits: {doku_page_id}")
- # 2. Haupt-Ansichtsseite nach Bereich erstellen/aktualisieren
+ # 2. Haupt-Ansichtsseite nach Bereich erstellen/aktualisieren (verwendet c_template.txt)
area_page_id = self._get_wiki_page_id_by_area()
area_content = self._generate_wiki_main_page_content(view_type='area')
dokuwiki_client.create_page(
diff --git a/open_workshop_dokuwiki/views/maintenance_equipment_views.xml b/open_workshop_dokuwiki/views/maintenance_equipment_views.xml
index 49f4e5c..59826ff 100644
--- a/open_workshop_dokuwiki/views/maintenance_equipment_views.xml
+++ b/open_workshop_dokuwiki/views/maintenance_equipment_views.xml
@@ -8,6 +8,11 @@
+
+
+
+
+