Commit 6879d13e authored by jan.koester's avatar jan.koester
Browse files

test

parent b62c7848
Loading
Loading
Loading
Loading
+74 −0
Original line number Diff line number Diff line
<?xml version="1.0" encoding="utf-8"?>
<WebEditor Version="1.0" editor="blogi">
  <Container background_color="#f5f8f1">
    <Grid columns="1fr" rows="auto" gap="14px" padding="40px 24px">
      <TextBox font_family="Arial, sans-serif" font_size="34px" font_color="#111111" text_align="left" font_weight="bold" font_style="normal" custom_css="line-height:1.2;">Schimmel im Haus? Wir stoppen die Ursache - schnell, nachhaltig &amp; mit Garantie!</TextBox>
      <TextBox font_family="Arial, sans-serif" font_size="19px" font_color="#2f3a23" text_align="left" font_weight="bold" font_style="normal" custom_css="">Über 1.500 Schimmelfälle gelöst – weil wir die Ursache beseitigen, nicht nur die Oberfläche.</TextBox>
      <TextBox font_family="Arial, sans-serif" font_size="16px" font_color="#435236" text_align="left" font_weight="normal" font_style="normal" custom_css="">Seit 20+ Jahren Ihr Fachbetrieb für Schimmelsanierung in Gießen, Aßlar, Lahnau, Solms, Hüttenberg sowie im gesamten Lahn-Dill-Kreis, Wetteraukreis &amp; Umgebung.</TextBox>
    </Grid>
  </Container>
  <Container background_color="#ffffff">
    <Grid columns="1fr" rows="auto" gap="10px" padding="28px 24px">
      <TextBox font_family="Arial, sans-serif" font_size="24px" font_color="#111111" text_align="left" font_weight="bold" font_style="normal" custom_css="">Leistungen im Überblick</TextBox>
      <TextBox font_family="Arial, sans-serif" font_size="16px" font_color="#1f2a17" text_align="left" font_weight="normal" font_style="normal" custom_css="">• Schimmel entfernen</TextBox>
      <TextBox font_family="Arial, sans-serif" font_size="16px" font_color="#1f2a17" text_align="left" font_weight="normal" font_style="normal" custom_css="">• Schimmelsanierung</TextBox>
      <TextBox font_family="Arial, sans-serif" font_size="16px" font_color="#1f2a17" text_align="left" font_weight="normal" font_style="normal" custom_css="">• Feuchtigkeit stoppen</TextBox>
      <TextBox font_family="Arial, sans-serif" font_size="16px" font_color="#1f2a17" text_align="left" font_weight="normal" font_style="normal" custom_css="">• Keller entfeuchten</TextBox>
    </Grid>
  </Container>
  <Container background_color="#f8faf6">
    <Grid columns="1fr" rows="auto" gap="10px" padding="28px 24px">
      <TextBox font_family="Arial, sans-serif" font_size="24px" font_color="#111111" text_align="left" font_weight="bold" font_style="normal" custom_css="">So läuft Ihre Schimmel- oder Feuchtigkeitslösung ab - Schritt für Schritt</TextBox>
      <TextBox font_family="Arial, sans-serif" font_size="18px" font_color="#2d3b22" text_align="left" font_weight="bold" font_style="normal" custom_css="">Schritt 1 - Kontakt &amp; Erstberatung</TextBox>
      <TextBox font_family="Arial, sans-serif" font_size="18px" font_color="#2d3b22" text_align="left" font_weight="bold" font_style="normal" custom_css="">Schritt 2 - Vor-Ort-Analyse &amp; Angebot</TextBox>
      <TextBox font_family="Arial, sans-serif" font_size="18px" font_color="#2d3b22" text_align="left" font_weight="bold" font_style="normal" custom_css="">Schritt 3 - Umsetzung &amp; Garantie</TextBox>
      <TextBox font_family="Arial, sans-serif" font_size="15px" font_color="#223018" text_align="left" font_weight="normal" font_style="normal" custom_css="">• Wir melden uns kurzfristig bei Ihnen</TextBox>
      <TextBox font_family="Arial, sans-serif" font_size="15px" font_color="#223018" text_align="left" font_weight="normal" font_style="normal" custom_css="">• Sie erhalten direkt eine erste fachliche Einschätzung am Telefon</TextBox>
      <TextBox font_family="Arial, sans-serif" font_size="15px" font_color="#223018" text_align="left" font_weight="normal" font_style="normal" custom_css="">• Wir vereinbaren einen Vor-Ort-Termin in Wetzlar oder Umgebung</TextBox>
      <TextBox font_family="Arial, sans-serif" font_size="15px" font_color="#223018" text_align="left" font_weight="normal" font_style="normal" custom_css="">• Von Anfang an haben Sie einen festen Ansprechpartner</TextBox>
      <TextBox font_family="Arial, sans-serif" font_size="15px" font_color="#223018" text_align="left" font_weight="normal" font_style="normal" custom_css="">• Wir prüfen den Schaden transparent, fachlich und ohne Zeitdruck</TextBox>
      <TextBox font_family="Arial, sans-serif" font_size="15px" font_color="#223018" text_align="left" font_weight="normal" font_style="normal" custom_css="">• Wir begutachten Feuchtigkeit &amp; Schimmel im Objekt</TextBox>
      <TextBox font_family="Arial, sans-serif" font_size="15px" font_color="#223018" text_align="left" font_weight="normal" font_style="normal" custom_css="">• Sie erhalten eine klare Einschätzung der Ursache – verständlich erklärt</TextBox>
      <TextBox font_family="Arial, sans-serif" font_size="15px" font_color="#223018" text_align="left" font_weight="normal" font_style="normal" custom_css="">• Anschließend bekommen Sie ein faires, individuelles Angebot</TextBox>
      <TextBox font_family="Arial, sans-serif" font_size="15px" font_color="#223018" text_align="left" font_weight="normal" font_style="normal" custom_css="">• Fachgerechte, saubere und sichere Ausführung</TextBox>
    </Grid>
  </Container>
  <Container background_color="#ffffff">
    <Grid columns="1fr" rows="auto" gap="10px" padding="24px">
      <TextBox font_family="Arial, sans-serif" font_size="22px" font_color="#111111" text_align="left" font_weight="bold" font_style="normal" custom_css="">Aufsteigende Feuchte</TextBox>
      <TextBox font_family="Arial, sans-serif" font_size="15px" font_color="#222222" text_align="left" font_weight="normal" font_style="normal" custom_css="">Problem: Feuchtigkeit steigt vom Fundament aus ins Mauerwerk – oft sichtbar an nassen unteren Wandbereichen. Ursache: Kapillarer Wasseraufstieg durch kleine Poren im Baustoff. Unsere Lösung: Wir setzen eine horizontale Sperre per Injektion – ohne Stemmen oder Aufgraben. Ergebnis: Die Wand bleibt trocken. Der Feuchtigkeitsaufstieg ist dauerhaft gestoppt.</TextBox>
    </Grid>
  </Container>
  <Container background_color="#ffffff">
    <Grid columns="1fr" rows="auto" gap="10px" padding="24px">
      <TextBox font_family="Arial, sans-serif" font_size="22px" font_color="#111111" text_align="left" font_weight="bold" font_style="normal" custom_css="">Querdurchfeuchtung</TextBox>
      <TextBox font_family="Arial, sans-serif" font_size="15px" font_color="#222222" text_align="left" font_weight="normal" font_style="normal" custom_css="">Problem: Feuchte Wände, meist auf Bodenhöhe oder an erdberührten Außenwänden. Ursache: Seitlich eindringende Nässe aus dem Erdreich durch fehlende oder defekte Abdichtung. Unsere Lösung: Wir setzen eine Flächensperre, die das Eindringen von Wasser seitlich blockiert – ganz ohne Schachtarbeiten. Ergebnis: Die Wände bleiben trocken und vor neuer Feuchtigkeit geschützt.</TextBox>
    </Grid>
  </Container>
  <Container background_color="#ffffff">
    <Grid columns="1fr" rows="auto" gap="10px" padding="24px">
      <TextBox font_family="Arial, sans-serif" font_size="22px" font_color="#111111" text_align="left" font_weight="bold" font_style="normal" custom_css="">Kondensationsfeuchte</TextBox>
      <TextBox font_family="Arial, sans-serif" font_size="15px" font_color="#222222" text_align="left" font_weight="normal" font_style="normal" custom_css="">Problem: Schimmel an den Innenwänden – besonders in Zimmerecken oder hinter Möbeln. Ursache: Kondenswasserbildung durch kalte Wandbereiche (Wärmebrücken) und schlechte Luftzirkulation. Unsere Lösung: Wir beheben die Ursache mit Wärmedämmung von innen und optimierter Raumluftführung. Ergebnis: Kein Schimmel mehr – dauerhaft gesunde Luft und ein angenehmes Raumklima.</TextBox>
    </Grid>
  </Container>
  <Container background_color="#ffffff">
    <Grid columns="1fr" rows="auto" gap="10px" padding="24px">
      <TextBox font_family="Arial, sans-serif" font_size="22px" font_color="#111111" text_align="left" font_weight="bold" font_style="normal" custom_css="">Eine Pfütze im Keller</TextBox>
      <TextBox font_family="Arial, sans-serif" font_size="15px" font_color="#222222" text_align="left" font_weight="normal" font_style="normal" custom_css="">Problem: Sichtbares Wasser oder feuchte Stellen am Kellerboden. Ursache: Druckwasser, das über Fugen oder Risse von außen eindringt. Unsere Lösung: Wir verpressen die betroffenen Stellen mit einem speziellen Reaktionsharz – dauerhaft abdichtend von innen. Ergebnis: Der Keller bleibt trocken – keine Pfützenbildung mehr möglich.</TextBox>
    </Grid>
  </Container>
  <Container background_color="#ffffff">
    <Grid columns="1fr" rows="auto" gap="10px" padding="24px">
      <TextBox font_family="Arial, sans-serif" font_size="22px" font_color="#111111" text_align="left" font_weight="bold" font_style="normal" custom_css="">Ausblühung durch Feuchtigkeit</TextBox>
      <TextBox font_family="Arial, sans-serif" font_size="15px" font_color="#222222" text_align="left" font_weight="normal" font_style="normal" custom_css="">Problem: Weiße, pudrige Ablagerungen an den Wänden – typisch für Keller und Erdgeschoss. Ursache: Durchfeuchtung der Wand von außen. Beim Trocknen kristallisieren Salze aus. Unsere Lösung: Wir entfernen den belasteten Putz und erneuern diesen mit unserem Zertifizierten Sanierputz System dauerhaft. Ergebnis: Die Wand bleibt sauber – Ausblühungen entstehen nicht mehr.</TextBox>
    </Grid>
  </Container>
  <Container background_color="#eaf2df">
    <Grid columns="1fr 1fr" rows="auto" gap="14px" padding="28px 24px">
      <TextBox font_family="Arial, sans-serif" font_size="24px" font_color="#111111" text_align="left" font_weight="bold" font_style="normal" custom_css="">Jetzt kostenlose Vor-Ort-Schadensanalyse von unseren Experten sichern – für ein dauerhaft trockenes &amp; schimmelfreies Zuhause</TextBox>
      <TextBox font_family="Arial, sans-serif" font_size="15px" font_color="#24331a" text_align="left" font_weight="normal" font_style="normal" custom_css="">Keine Sorge – wir prüfen Ihren Fall ganz unverbindlich. Wenn Sie unsicher sind, ob wir Ihnen bei Ihrem Schimmel- oder Feuchtigkeitsproblem helfen können, verschaffen wir Ihnen gern absolute Klarheit. Füllen Sie einfach die kurze Anfrage aus – wir analysieren Ihre Angaben vertraulich und bieten Ihnen anschließend eine kostenlose Vor-Ort-Schadensanalyse an. So erfahren Sie schnell und ohne Verpflichtung, wie ernst der Schaden ist und welche Schritte wirklich sinnvoll sind. Nehmen Sie sich eine Minute Zeit – wir unterstützen Sie kompetent, transparent und zuverlässig. Ihre Daten werden ausschließlich für die Bearbeitung Ihrer Anfrage verwendet und nicht an Dritte weitergegeben.</TextBox>
      <Button label="Kostenlose Analyse anfragen" link_url="https://www.google.com/maps/search/?api=1&amp;query=Niedergirmeser%20Weg%2059%2C%2035576%20Wetzlar" background_color="#83BB32" text_color="#ffffff" font_size="16px" padding="12px 20px" border_radius="8px" border_style="none" text_align="center" open_new_tab="false"/>
      <Button label="Zur Startseite" link_url="https://bautenschutz-wetzlar.de/" background_color="#455b2b" text_color="#ffffff" font_size="16px" padding="12px 20px" border_radius="8px" border_style="none" text_align="center" open_new_tab="false"/>
    </Grid>
  </Container>
</WebEditor>
+7 −0
Original line number Diff line number Diff line
@@ -7,6 +7,9 @@ include(GNUInstallDirs)
set(CMAKE_CXX_STANDARD 23)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

find_package(PkgConfig REQUIRED)
pkg_check_modules(LIBARCHIVE REQUIRED libarchive)

# When built standalone, add parent cmake module path
if(NOT TARGET blogidev)
    list(APPEND CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/../cmake)
@@ -43,11 +46,14 @@ add_executable(blogi-editor
    src/htmlimport.cpp
)

target_include_directories(blogi-editor PRIVATE ${LIBARCHIVE_INCLUDE_DIRS})

if(TARGET blogidev)
    target_link_libraries(blogi-editor PRIVATE
        blogidev
        json-c::json-c
        tinyxml2::tinyxml2
        ${LIBARCHIVE_LIBRARIES}
    )
    target_sources(blogi-editor PRIVATE
        ${CMAKE_CURRENT_SOURCE_DIR}/../src/plugin.cpp
@@ -66,6 +72,7 @@ else()
        uuidp::uuidp
        authdb::client
        Brotli::Brotli
        ${LIBARCHIVE_LIBRARIES}
        dl
    )
endif()
+1 −1
Original line number Diff line number Diff line
@@ -152,7 +152,7 @@

    <dialog id="import-dialog">
        <h3 data-i18n="I18N_IMPORT_XML">XML importieren</h3>
        <label class="file-upload-label">&#x1F4C2; <span data-i18n="I18N_UPLOAD_FILE">Datei w&auml;hlen</span><input type="file" id="import-xml-file" accept=".xml,.txt" style="display:none"></label>
        <label class="file-upload-label">&#x1F4C2; <span data-i18n="I18N_UPLOAD_FILE">Datei w&auml;hlen</span><input type="file" id="import-xml-file" accept=".xml,.txt,.zip" style="display:none"></label>
        <textarea id="import-xml-content" rows="15" placeholder="XML hier einf&uuml;gen..."></textarea>
        <div class="dialog-actions">
            <button id="btn-import-confirm" data-i18n="I18N_IMPORT_XML">Importieren</button>
+41 −2
Original line number Diff line number Diff line
@@ -165,8 +165,23 @@ var EditorApi = (function() {
            return request('GET', '/api/document/export-xml');
        },

        importXml: function(xml) {
            return request('POST', '/api/document/import-xml', { xml: xml });
        importXml: function(xml, options) {
            var payload = { xml: xml };
            if (options && typeof options === 'object') {
                for (var k in options) {
                    if (Object.prototype.hasOwnProperty.call(options, k)) payload[k] = options[k];
                }
            }
            return request('POST', '/api/document/import-xml', payload);
        },

        importTemplateZip: function(zipBase64, fileName, mediaImport) {
            var payload = {
                zip_base64: zipBase64,
                file_name: fileName || 'template.zip'
            };
            if (mediaImport) payload.media_import = mediaImport;
            return request('POST', '/api/document/import-xml', payload);
        },

        saveDocument: function(name, group) {
@@ -311,6 +326,30 @@ var EditorApi = (function() {
            });
        },

        mediaListStores: function(connId) {
            return request('POST', '/api/connection/media/' + connId, {
                command: 'media_list_stores'
            });
        },

        mediaCreateAlbum: function(connId, storeName, albumName, isPublic) {
            return request('POST', '/api/connection/media/' + connId, {
                command: 'media_create_album',
                store_name: storeName,
                album_name: albumName,
                is_public: !!isPublic
            });
        },

        mediaUpload: function(connId, albumId, filename, dataBase64) {
            return request('POST', '/api/connection/media/' + connId, {
                command: 'media_upload',
                album_id: albumId,
                filename: filename,
                data: dataBase64
            });
        },

        mediaListMedia: function(connId, albumId) {
            return request('POST', '/api/connection/media/' + connId, {
                command: 'media_list_media',
+154 −0
Original line number Diff line number Diff line
@@ -351,6 +351,20 @@
        document.getElementById('import-xml-file').addEventListener('change', function() {
            var file = this.files[0];
            if (!file) return;

            var name = (file.name || '').toLowerCase();
            if (name.endsWith('.zip')) {
                importZipTemplateFile(file).then(function() {
                    DocumentTree.clearSelection();
                    PropertiesPanel.clear();
                    refreshDocument();
                }).catch(function(err) {
                    alert(I18n.t('I18N_IMPORT_FAILED') + ': ' + ((err && err.error) || err || ''));
                });
                this.value = '';
                return;
            }

            var reader = new FileReader();
            reader.onload = function(e) {
                var xml = e.target.result;
@@ -449,6 +463,146 @@
        });
    }

    function arrayBufferToBase64(buffer) {
        var bytes = new Uint8Array(buffer);
        var chunkSize = 0x8000;
        var binary = '';
        for (var i = 0; i < bytes.length; i += chunkSize) {
            var chunk = bytes.subarray(i, i + chunkSize);
            binary += String.fromCharCode.apply(null, chunk);
        }
        return btoa(binary);
    }

    function chooseIndexByPrompt(title, items, toLabel) {
        if (!items || items.length === 0) return -1;
        var lines = [title, ''];
        for (var i = 0; i < items.length; i++) {
            lines.push((i + 1) + '. ' + toLabel(items[i], i));
        }
        var input = prompt(lines.join('\n'), '1');
        if (input === null) return -1;
        var idx = parseInt(input, 10) - 1;
        if (isNaN(idx) || idx < 0 || idx >= items.length) return -1;
        return idx;
    }

    function collectZipMediaImportOptions() {
        return new Promise(function(resolve, reject) {
            if (!confirm('Bilder aus dem ZIP in die MediaDB importieren?')) {
                resolve(null);
                return;
            }

            EditorApi.listConnections().then(function(connResp) {
                var conns = connResp.connections || [];
                if (!conns.length) {
                    resolve(null);
                    return;
                }

                var connIdx = chooseIndexByPrompt(
                    'Verbindung wählen (muss bereits verbunden sein):',
                    conns,
                    function(c) { return (c.name || c.id) + ' [' + c.id + ']'; }
                );
                if (connIdx < 0) {
                    resolve(null);
                    return;
                }

                var connId = conns[connIdx].id;
                EditorApi.mediaListStores(connId).then(function(storeResp) {
                    var stores = storeResp.stores || [];
                    if (!stores.length) {
                        reject({error: 'Keine Stores gefunden oder Verbindung nicht aktiv.'});
                        return;
                    }

                    var storeIdx = chooseIndexByPrompt(
                        'Store wählen:',
                        stores,
                        function(s) { return s; }
                    );
                    if (storeIdx < 0) {
                        resolve(null);
                        return;
                    }

                    var storeName = stores[storeIdx];
                    var createAlbum = confirm('Neues Album anlegen? (OK = neu, Abbrechen = bestehendes Album wählen)');

                    if (createAlbum) {
                        var albumName = prompt('Neuer Albumname:', 'template-import-' + Date.now());
                        if (!albumName || !albumName.trim()) {
                            reject({error: 'Albumname fehlt.'});
                            return;
                        }
                        resolve({
                            conn_id: connId,
                            store_name: storeName,
                            create_album: true,
                            album_name: albumName.trim()
                        });
                        return;
                    }

                    EditorApi.mediaListAlbums(connId).then(function(albumResp) {
                        var albums = (albumResp.albums || []).filter(function(a) {
                            return (a.store || '') === storeName;
                        });
                        if (!albums.length) {
                            reject({error: 'Keine Alben im gewählten Store gefunden. Bitte neues Album anlegen.'});
                            return;
                        }

                        var albumIdx = chooseIndexByPrompt(
                            'Album wählen:',
                            albums,
                            function(a) {
                                var cnt = (a.media_count !== undefined) ? (' [' + a.media_count + ']') : '';
                                return a.name + cnt + ' (' + a.id + ')';
                            }
                        );
                        if (albumIdx < 0) {
                            resolve(null);
                            return;
                        }

                        resolve({
                            conn_id: connId,
                            store_name: storeName,
                            create_album: false,
                            album_id: albums[albumIdx].id
                        });
                    }).catch(reject);
                }).catch(reject);
            }).catch(reject);
        });
    }

    function importZipTemplateFile(file) {
        return new Promise(function(resolve, reject) {
            collectZipMediaImportOptions().then(function(mediaImport) {
                var reader = new FileReader();
                reader.onload = function(e) {
                    try {
                        var zipBase64 = arrayBufferToBase64(e.target.result);
                        EditorApi.importTemplateZip(zipBase64, file.name, mediaImport)
                            .then(resolve)
                            .catch(reject);
                    } catch (err) {
                        reject(err);
                    }
                };
                reader.onerror = function() {
                    reject({error: 'ZIP konnte nicht gelesen werden.'});
                };
                reader.readAsArrayBuffer(file);
            }).catch(reject);
        });
    }

    function openTemplateWizard() {
        var status = document.getElementById('template-wizard-status');
        status.textContent = '';
Loading