Commit 496b4c1f authored by jan.koester's avatar jan.koester
Browse files

test

parent f45e952d
Loading
Loading
Loading
Loading
+22 −6
Changes for editor/html/js/editor.js: 22 added lines, 6 removed lines.
Original line number Diff line number Diff line
@@ -9,6 +9,7 @@
    var currentDocId = '';
    var currentDocGroup = '';
    var currentDocSeo = {};
    var currentDocPublishParams = {}; // target name -> last-used publish params for this template
    var saveDialogMode = 'save';
    var projectsCache = [];
    var currentProjectId = '';
@@ -185,6 +186,7 @@
            document.getElementById('doc-name').textContent = resp.name || 'Untitled';
            currentDocId = resp.id || '';
            currentDocSeo = resp.seo || {};
            currentDocPublishParams = resp.publish_params || {};
            Canvas.setTreeData(resp.tree || []);
            DocumentTree.render(resp.tree || []);
            Preview.refresh();
@@ -1975,7 +1977,10 @@
    // the main publish dialog, and the SEO & Social sub-dialog). "image" opens
    // the existing MediaBrowser (same pattern as properties.js/survey-manager.js)
    // instead of taking free text.
    function createPublishFieldInput(field, styleCss) {
    function createPublishFieldInput(field, styleCss, savedParams) {
        var savedValue = savedParams ? savedParams[field.key] : undefined;
        var hasSaved = savedValue !== undefined && savedValue !== null;

        if (field.type === 'select' && field.options) {
            var sel = document.createElement('select');
            sel.setAttribute('data-publish-key', field.key);
@@ -1986,6 +1991,7 @@
                optEl.textContent = field.options[o].label;
                sel.appendChild(optEl);
            }
            if (hasSaved) sel.value = savedValue;
            return sel;
        }
        if (field.type === 'textarea') {
@@ -1993,6 +1999,7 @@
            ta.setAttribute('data-publish-key', field.key);
            if (styleCss) ta.style.cssText = styleCss;
            if (field.placeholder) ta.placeholder = field.placeholder;
            if (hasSaved) ta.value = savedValue;
            return ta;
        }
        if (field.type === 'image') {
@@ -2004,6 +2011,7 @@
            imgInp.setAttribute('data-publish-key', field.key);
            imgInp.style.cssText = (styleCss || '') + 'flex:1;';
            if (field.placeholder) imgInp.placeholder = field.placeholder;
            if (hasSaved) imgInp.value = savedValue;
            var chooseBtn = document.createElement('button');
            chooseBtn.type = 'button';
            chooseBtn.textContent = I18n.t('I18N_CHOOSE_IMAGE', 'Wählen…');
@@ -2023,6 +2031,10 @@
        if (styleCss) inp.style.cssText = styleCss;
        if (field.placeholder) inp.placeholder = field.placeholder;
        if (field.required) inp.required = true;
        if (hasSaved) {
            if (inp.type === 'checkbox') inp.checked = !!savedValue;
            else inp.value = savedValue;
        }
        return inp;
    }

@@ -2048,6 +2060,7 @@
        container.style.gap = '0.4em';
        container.style.alignItems = 'center';
        var fieldStyle = 'padding:0.15em 0.3em;border-radius:3px;border:1px solid var(--border);background:var(--bg-secondary);color:var(--text-primary);font-size:0.75rem;width:8em;';
        var savedParams = currentDocPublishParams[targetName] || {};
        for (var f = 0; f < fields.length; f++) {
            var field = fields[f];
            var label = document.createElement('label');
@@ -2055,7 +2068,7 @@
            label.style.fontSize = '0.75rem';
            label.style.color = 'var(--text-secondary)';
            container.appendChild(label);
            container.appendChild(createPublishFieldInput(field, fieldStyle));
            container.appendChild(createPublishFieldInput(field, fieldStyle, savedParams));
        }
    }

@@ -2101,6 +2114,7 @@
                    }
                    return;
                }
                currentDocPublishParams[target] = params;
                publishBtn.innerHTML = '&#x2705; OK';
                setTimeout(function() {
                    publishBtn.innerHTML = origText;
@@ -2273,13 +2287,13 @@
        renderPublishDialogFields(targets[0].name);
    }

    function appendFormFieldRow(container, field) {
    function appendFormFieldRow(container, field, savedParams) {
        var div = document.createElement('div');
        div.className = 'set-field';
        var label = document.createElement('label');
        label.textContent = field.label || field.key;
        div.appendChild(label);
        div.appendChild(createPublishFieldInput(field));
        div.appendChild(createPublishFieldInput(field, undefined, savedParams));
        container.appendChild(div);
    }

@@ -2302,13 +2316,14 @@
        }
        var fields = target.fields;
        var hasSeoFields = false;
        var savedParams = currentDocPublishParams[targetName] || {};
        for (var f = 0; f < fields.length; f++) {
            var field = fields[f];
            if (field.group === 'seo') {
                hasSeoFields = true;
                appendFormFieldRow(seoContainer, field);
                appendFormFieldRow(seoContainer, field, savedParams);
            } else {
                appendFormFieldRow(container, field);
                appendFormFieldRow(container, field, savedParams);
            }
        }
        seoBtn.style.display = hasSeoFields ? '' : 'none';
@@ -2377,6 +2392,7 @@
            statusEl.className = 'set-status';

            EditorApi.publishToConnection(connId, target, params).then(function() {
                currentDocPublishParams[target] = params;
                statusEl.textContent = I18n.t('I18N_PUBLISH_SUCCESS', 'Erfolgreich ver\u00f6ffentlicht');
                statusEl.className = 'set-status success';
            }).catch(function(err) {
+58 −1
Changes for editor/src/webedit_api.cpp: 58 added lines, 1 removed line.
Original line number Diff line number Diff line
@@ -98,6 +98,17 @@ json_object* seoMetaToJson(const blogi::SeoMeta &seo) {
    return obj;
}

// { "<target name>": {...last-used publish params...}, ... } for the editor to pre-fill
// the publish dialog's dynamic fields for whichever target the user picks.
json_object* publishParamsMapToJson(const std::map<std::string, std::string> &params) {
    json_object *obj = json_object_new_object();
    for (auto &kv : params) {
        json_object *parsed = json_tokener_parse(kv.second.c_str());
        json_object_object_add(obj, kv.first.c_str(), parsed ? parsed : json_object_new_object());
    }
    return obj;
}

std::string trimCopy(const std::string &value) {
    size_t start = 0;
    while (start < value.size() && std::isspace(static_cast<unsigned char>(value[start]))) {
@@ -494,6 +505,7 @@ void webedit::Api::loadDocumentTreeFromXml(DocumentState &doc, const std::string
    doc.root = nullptr;
    doc.ownedElements.clear();
    doc.seo = blogi::SeoMeta();
    doc.publishParams.clear();

    tinyxml2::XMLDocument xmlDoc;
    if (xmlDoc.Parse(xml.c_str()) != tinyxml2::XML_SUCCESS)
@@ -520,9 +532,19 @@ void webedit::Api::loadDocumentTreeFromXml(DocumentState &doc, const std::string
        }
    }

    if (auto *paramsEl = rootEl->FirstChildElement("PublishParams")) {
        for (auto *targetEl = paramsEl->FirstChildElement("Target"); targetEl;
             targetEl = targetEl->NextSiblingElement("Target")) {
            const char *name = targetEl->Attribute("name");
            const char *text = targetEl->GetText();
            if (name && text) doc.publishParams[name] = text;
        }
    }

    blogi::webedit::EditPlugin *lastTop = nullptr;
    for (auto *child = rootEl->FirstChildElement(); child; child = child->NextSiblingElement()) {
        if (strcmp(child->Name(), "property") == 0 || strcmp(child->Name(), "SeoMeta") == 0)
        if (strcmp(child->Name(), "property") == 0 || strcmp(child->Name(), "SeoMeta") == 0 ||
            strcmp(child->Name(), "PublishParams") == 0)
            continue;

        const char *typeName = child->Name();
@@ -945,6 +967,7 @@ void webedit::Api::handleNewDocument(libhttppp::HttpRequest &curreq,
    doc.currentDocName = "Untitled";
    doc.currentDocGroup.clear();
    doc.seo = blogi::SeoMeta();
    doc.publishParams.clear();
    persistDocumentStateToSession(sessionid, doc);

    json_object *resp = json_object_new_object();
@@ -965,6 +988,7 @@ void webedit::Api::handleGetDocument(libhttppp::HttpRequest &curreq,
    json_object_object_add(resp, "name", json_object_new_string(doc.currentDocName.c_str()));
    json_object_object_add(resp, "id", json_object_new_string(doc.currentDocId.c_str()));
    json_object_object_add(resp, "seo", seoMetaToJson(doc.seo));
    json_object_object_add(resp, "publish_params", publishParamsMapToJson(doc.publishParams));

    json_object *tree = json_object_new_array();
    auto *el = doc.root;
@@ -4024,6 +4048,20 @@ void webedit::Api::handlePublishToConnection(libhttppp::HttpRequest &curreq,
    json_object_put(req);

    if (result.ok) {
        // publishToConnectionCore() just updated doc.publishParams in memory on an
        // actual successful publish — write it back to the template itself (session
        // cookie for this session, DB row if the template has already been saved) so
        // it's there to pre-fill the next time this template is opened/published.
        if (result.status == "success" || result.status == "ok") {
            persistDocumentStateToSession(sessionid, doc);
            if (!doc.currentDocId.empty()) {
                std::string author;
                _session.getData(sessionid, "username", author);
                _db.updateDocument(domain, doc.currentDocId, doc.currentDocName,
                                   exportTreeXml(doc), author, doc.currentDocGroup);
            }
        }

        json_object *resp = json_object_new_object();
        json_object_object_add(resp, "status", json_object_new_string(result.status.c_str()));
        json_object_object_add(resp, "message", json_object_new_string(result.message.c_str()));
@@ -4114,6 +4152,14 @@ webedit::Api::PublishCoreResult webedit::Api::publishToConnectionCore(

        if (pubStatus == "success" || pubStatus == "ok" || pubStatus == "duplicate") {
            result.ok = true;
            // Remember these params on the template itself (not just this publish) so
            // the next publish to the same target starts pre-filled. Only once the blog
            // actually accepted the content — "duplicate" is still an unresolved
            // overwrite prompt at this point, not a completed publish.
            if (paramsObj && (pubStatus == "success" || pubStatus == "ok")) {
                doc.publishParams[target] =
                    json_object_to_json_string_ext(paramsObj, JSON_C_TO_STRING_NOSLASHESCAPE);
            }
        } else {
            // Auth might have expired
            if (pubStatus == "error") {
@@ -4860,6 +4906,17 @@ std::string webedit::Api::exportTreeXml(DocumentState &doc) {
        root->InsertEndChild(seoEl);
    }

    if (!doc.publishParams.empty()) {
        auto *paramsEl = xmlDoc.NewElement("PublishParams");
        for (auto &kv : doc.publishParams) {
            auto *targetEl = xmlDoc.NewElement("Target");
            targetEl->SetAttribute("name", kv.first.c_str());
            targetEl->SetText(kv.second.c_str());
            paramsEl->InsertEndChild(targetEl);
        }
        root->InsertEndChild(paramsEl);
    }

    auto *el = doc.root;
    while (el) {
        auto *xmlEl = el->toXmlObject(&xmlDoc);
+4 −0
Changes for editor/src/webedit_api.h: 4 added lines, 0 removed lines.
Original line number Diff line number Diff line
@@ -118,6 +118,10 @@ namespace webedit {
            std::string currentDocName;
            std::string currentDocGroup;
            blogi::SeoMeta seo; // round-tripped via the <WebEditor> root's seo_* attributes
            // Last-used publish params per target name (raw JSON text), round-tripped
            // via <WebEditor><PublishParams><Target name="...">...</Target></PublishParams>
            // so the publish dialog can pre-fill them next time this template is opened.
            std::map<std::string, std::string> publishParams;
            std::vector<std::unique_ptr<blogi::webedit::EditPlugin,
                                        blogi::webedit::EditPluginDeleter>> ownedElements;
            std::mutex mtx;