feat(dokuwiki): Add wiki-based template system and image upload
- Implemented wiki-based template system using c_template - Template loaded from werkstatt:ausruestung:c_template - Placeholder replacement for all equipment and related fields - Fallback to hardcoded content if template unavailable - Only view pages use templates, central doku page unchanged - Added image upload capability for equipment - New field: image_1920 (Image field, max 1920x1920px) - View integration: Positioned as avatar before button_box - Upload to DokuWiki media directory during sync - Temporary file approach for dokuwiki.py library compatibility - Extended template placeholders: - status: Equipment status from status_id - odoo_link: Formatted link back to Odoo equipment form - odoo_url: Direct URL to equipment in Odoo - image: Equipment image (300px width) - image_large: Equipment image (original size) - image_id: Media path for custom DokuWiki syntax - Updated documentation with all available placeholders
This commit is contained in:
parent
e53c4028c9
commit
3fa050f153
35
open_workshop_dokuwiki/README.md
Normal file
35
open_workshop_dokuwiki/README.md
Normal file
|
|
@ -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
|
||||
|
|
@ -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}"
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -8,6 +8,11 @@
|
|||
<field name="inherit_id" ref="maintenance.hr_equipment_view_form"/>
|
||||
<field name="arch" type="xml">
|
||||
|
||||
<!-- Bild-Feld oben rechts neben den Smart Buttons -->
|
||||
<xpath expr="//sheet/div[@name='button_box']" position="before">
|
||||
<field name="image_1920" widget="image" class="oe_avatar" options="{'preview_image': 'image_1920'}"/>
|
||||
</xpath>
|
||||
|
||||
<!-- Smart Button im Header -->
|
||||
<xpath expr="//div[@name='button_box']" position="inside">
|
||||
<button name="action_open_wiki"
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user