Loading editor/html/css/editor.css +17 −0 Changes for editor/html/css/editor.css: 17 added lines, 0 removed lines. Original line number Diff line number Diff line Loading @@ -225,6 +225,10 @@ body { color: var(--text-muted); } .tree-node-toggle.has-children { cursor: pointer; } .tree-node-name { flex: 1; overflow: hidden; Loading @@ -236,6 +240,19 @@ body { padding-left: 16px; } .tree-node.collapsed > .tree-children { display: none; } .tree-root-header { font-weight: 600; border-bottom: 1px solid var(--border); margin-bottom: 4px; padding-bottom: 6px; cursor: pointer; user-select: none; } /* Preview / visual canvas */ #preview-pane { flex: 1; Loading editor/html/index.html +2 −0 Changes for editor/html/index.html: 2 added lines, 0 removed lines. Original line number Diff line number Diff line Loading @@ -74,6 +74,8 @@ <button id="btn-copy-el" data-i18n-title="I18N_COPY_ELEMENT">⎘</button> <button id="btn-paste-el" data-i18n-title="I18N_PASTE_ELEMENT">📋</button> <button id="btn-remove" data-i18n-title="I18N_REMOVE_ELEMENT">✖</button> <button id="btn-expand-all" data-i18n-title="I18N_TITLE_EXPAND_ALL">⊞</button> <button id="btn-collapse-all" data-i18n-title="I18N_TITLE_COLLAPSE_ALL">⊟</button> </div> <div id="document-tree"></div> </div> Loading editor/html/js/editor.js +39 −1 Changes for editor/html/js/editor.js: 39 added lines, 1 removed line. Original line number Diff line number Diff line Loading @@ -11,6 +11,7 @@ var currentDocSeo = {}; var currentDocGlobalCss = ''; // page-level CSS collected by HTML import, see doc-seo-dialog var currentDocPublishParams = {}; // target name -> last-used publish params for this template var currentDocLastPublishTarget = ''; // target name last successfully published to, see applyPreferredPublishTarget var importHtmlPendingSource = ''; // raw HTML staged in the import dialog, once past step 1 var importHtmlPendingCandidates = []; // last parse-html result for the block picker var saveDialogMode = 'save'; Loading Loading @@ -61,6 +62,10 @@ Preview.highlight(uuid); PropertiesPanel.showProperties(uuid); }); DocumentTree.setOnRootSelect(function() { populateDocSeoDialog(); document.getElementById('doc-seo-dialog').showModal(); }); Canvas.setOnChange(refreshDocument); }); } Loading Loading @@ -256,6 +261,8 @@ currentDocSeo = resp.seo || {}; currentDocGlobalCss = resp.global_css || ''; currentDocPublishParams = resp.publish_params || {}; currentDocLastPublishTarget = resp.last_publish_target || ''; applyPreferredPublishTarget(); Canvas.setTreeData(resp.tree || []); DocumentTree.render(resp.tree || []); Preview.refresh(); Loading Loading @@ -426,6 +433,14 @@ document.getElementById('btn-remove').addEventListener('click', function() { removeSelected(); }); document.getElementById('btn-expand-all').addEventListener('click', function() { DocumentTree.expandAll(); }); document.getElementById('btn-collapse-all').addEventListener('click', function() { DocumentTree.collapseAll(); }); } function removeSelected() { Loading Loading @@ -2311,7 +2326,7 @@ select.disabled = false; publishBtn.disabled = false; publishBtn.style.opacity = '1'; renderPreviewPublishFields(targets[0].name); applyPreferredPublishTarget(); // Bind change event (only once) if (!select._bound) { Loading @@ -2322,6 +2337,28 @@ } } // Defaults the toolbar publish-target dropdown to whichever target was // last successfully published to for this document (currentDocLastPublishTarget, // see DocumentState::lastPublishTarget), falling back to the first target // otherwise. Called both once targets are known (showPreviewPublishTargets) // and once the document's last-used target is known (refreshDocument) -- // whichever of the two async loads finishes last ends up applying it. function applyPreferredPublishTarget() { if (!currentPublishTargets.length) return; var select = document.getElementById('preview-publish-target'); var name = currentPublishTargets[0].name; if (currentDocLastPublishTarget) { for (var i = 0; i < currentPublishTargets.length; i++) { if (currentPublishTargets[i].name === currentDocLastPublishTarget) { name = currentDocLastPublishTarget; break; } } } select.value = name; renderPreviewPublishFields(name); } // Builds the actual <select>/<textarea>/<input>/image-picker element for one // publish-form field descriptor (shared by the toolbar quick-publish bar, // the main publish dialog, and the SEO & Social sub-dialog). "image" opens Loading Loading @@ -2465,6 +2502,7 @@ return; } currentDocPublishParams[target] = params; currentDocLastPublishTarget = target; publishBtn.innerHTML = '✅ OK'; setTimeout(function() { publishBtn.innerHTML = origText; Loading editor/html/js/tree.js +83 −6 Changes for editor/html/js/tree.js: 83 added lines, 6 removed lines. Original line number Diff line number Diff line Loading @@ -12,7 +12,12 @@ var DocumentTree = (function() { var selectedUuid = null; var onSelectCallback = null; var onContextMenuCallback = null; var onRootSelectCallback = null; var lastTreeData = []; // Collapsed state survives across render() calls (e.g. after every // document refresh), keyed by widget uuid, so toggling a node doesn't // get reset the next time something else in the document changes. var collapsedUuids = {}; function setOnSelect(cb) { onSelectCallback = cb; Loading @@ -22,13 +27,22 @@ var DocumentTree = (function() { onContextMenuCallback = cb; } function setOnRootSelect(cb) { onRootSelectCallback = cb; } function render(treeData) { lastTreeData = treeData || []; var container = document.getElementById('document-tree'); container.innerHTML = ''; container.appendChild(renderRootNode()); if (!lastTreeData.length) { container.innerHTML = '<p class="hint">Dokument ist leer. Widget hinzufügen.</p>'; var hint = document.createElement('p'); hint.className = 'hint'; hint.textContent = I18n.t('I18N_DOCUMENT_EMPTY', 'Dokument ist leer. Widget hinzufügen.'); container.appendChild(hint); return; } Loading @@ -37,9 +51,37 @@ var DocumentTree = (function() { } } // Synthetic entry above the widget tree, representing the document // itself rather than any widget -- there's no uuid to select on the // canvas, it just opens the document-level SEO/Social + Page CSS dialog. function renderRootNode() { var header = document.createElement('div'); header.className = 'tree-node-header tree-root-header'; var icon = document.createElement('span'); icon.className = 'tree-node-toggle'; icon.textContent = '⚙'; header.appendChild(icon); var name = document.createElement('span'); name.className = 'tree-node-name'; name.textContent = I18n.t('I18N_DOCUMENT_ROOT', 'Dokument'); header.appendChild(name); header.addEventListener('click', function(e) { e.stopPropagation(); if (onRootSelectCallback) onRootSelectCallback(); }); return header; } function renderNode(node, depth) { var hasChildren = !!(node.children && node.children.length > 0); var isCollapsed = hasChildren && !!collapsedUuids[node.uuid]; var div = document.createElement('div'); div.className = 'tree-node'; div.className = 'tree-node' + (isCollapsed ? ' collapsed' : ''); div.setAttribute('data-uuid', node.uuid); div.setAttribute('data-can-children', node.can_have_children ? '1' : '0'); Loading @@ -51,8 +93,18 @@ var DocumentTree = (function() { var toggle = document.createElement('span'); toggle.className = 'tree-node-toggle'; if (node.children && node.children.length > 0) { toggle.textContent = '▼'; if (hasChildren) { toggle.textContent = isCollapsed ? '▶' : '▼'; toggle.classList.add('has-children'); toggle.addEventListener('click', function(e) { e.stopPropagation(); if (collapsedUuids[node.uuid]) { delete collapsedUuids[node.uuid]; } else { collapsedUuids[node.uuid] = true; } render(lastTreeData); }); } else if (node.can_have_children) { toggle.textContent = '▷'; } else { Loading @@ -79,7 +131,7 @@ var DocumentTree = (function() { div.appendChild(header); if (node.children && node.children.length > 0) { if (hasChildren) { var childContainer = document.createElement('div'); childContainer.className = 'tree-children'; for (var i = 0; i < node.children.length; i++) { Loading Loading @@ -120,12 +172,37 @@ var DocumentTree = (function() { highlightSelected(null); } function forEachNodeWithChildren(nodes, fn) { for (var i = 0; i < nodes.length; i++) { if (nodes[i].children && nodes[i].children.length > 0) { fn(nodes[i]); forEachNodeWithChildren(nodes[i].children, fn); } } } function expandAll() { collapsedUuids = {}; render(lastTreeData); } function collapseAll() { collapsedUuids = {}; forEachNodeWithChildren(lastTreeData, function(node) { collapsedUuids[node.uuid] = true; }); render(lastTreeData); } return { render: render, selectNode: selectNode, highlightSelected: highlightSelected, clearSelection: clearSelection, setOnSelect: setOnSelect, setOnContextMenu: setOnContextMenu setOnContextMenu: setOnContextMenu, setOnRootSelect: setOnRootSelect, expandAll: expandAll, collapseAll: collapseAll }; })(); editor/src/webedit_api.cpp +19 −5 Changes for editor/src/webedit_api.cpp: 19 added lines, 5 removed lines. Original line number Diff line number Diff line Loading @@ -508,6 +508,7 @@ void webedit::Api::loadDocumentTreeFromXml(DocumentState &doc, const std::string doc.ownedElements.clear(); doc.seo = blogi::SeoMeta(); doc.globalCss.clear(); doc.lastPublishTarget.clear(); doc.publishParams.clear(); tinyxml2::XMLDocument xmlDoc; Loading @@ -523,6 +524,9 @@ void webedit::Api::loadDocumentTreeFromXml(DocumentState &doc, const std::string if (const char *globalCss = rootEl->Attribute("GlobalCss")) { doc.globalCss = globalCss; } if (const char *lastTarget = rootEl->Attribute("LastPublishTarget")) { doc.lastPublishTarget = lastTarget; } if (auto *seoEl = rootEl->FirstChildElement("SeoMeta")) { auto attr = [&](const char *name) { const char *v = seoEl->Attribute(name); return v ? v : ""; }; Loading Loading @@ -1050,6 +1054,7 @@ void webedit::Api::handleNewDocument(libhttppp::HttpRequest &curreq, doc.currentDocGroup.clear(); doc.seo = blogi::SeoMeta(); doc.globalCss.clear(); doc.lastPublishTarget.clear(); doc.publishParams.clear(); persistDocumentStateToSession(sessionid, doc); Loading @@ -1072,6 +1077,7 @@ void webedit::Api::handleGetDocument(libhttppp::HttpRequest &curreq, 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, "global_css", json_object_new_string(doc.globalCss.c_str())); json_object_object_add(resp, "last_publish_target", json_object_new_string(doc.lastPublishTarget.c_str())); json_object_object_add(resp, "publish_params", publishParamsMapToJson(doc.publishParams)); json_object *tree = json_object_new_array(); Loading Loading @@ -4892,14 +4898,19 @@ 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")) { // Remember these params (and which target was used) on the template // itself (not just this publish) so the next publish to the same // target starts pre-filled and the target dropdown defaults to it. // Only once the blog actually accepted the content — "duplicate" is // still an unresolved overwrite prompt at this point, not a // completed publish. if (pubStatus == "success" || pubStatus == "ok") { doc.lastPublishTarget = target; if (paramsObj) { doc.publishParams[target] = json_object_to_json_string_ext(paramsObj, JSON_C_TO_STRING_NOSLASHESCAPE); } } } else { // Auth might have expired if (pubStatus == "error") { Loading Loading @@ -5701,6 +5712,9 @@ std::string webedit::Api::exportTreeXml(DocumentState &doc) { if (!doc.globalCss.empty()) { root->SetAttribute("GlobalCss", doc.globalCss.c_str()); } if (!doc.lastPublishTarget.empty()) { root->SetAttribute("LastPublishTarget", doc.lastPublishTarget.c_str()); } const blogi::SeoMeta &seo = doc.seo; if (!seo.ogTitle.empty() || !seo.ogDescription.empty() || !seo.ogImage.empty() || Loading Loading
editor/html/css/editor.css +17 −0 Changes for editor/html/css/editor.css: 17 added lines, 0 removed lines. Original line number Diff line number Diff line Loading @@ -225,6 +225,10 @@ body { color: var(--text-muted); } .tree-node-toggle.has-children { cursor: pointer; } .tree-node-name { flex: 1; overflow: hidden; Loading @@ -236,6 +240,19 @@ body { padding-left: 16px; } .tree-node.collapsed > .tree-children { display: none; } .tree-root-header { font-weight: 600; border-bottom: 1px solid var(--border); margin-bottom: 4px; padding-bottom: 6px; cursor: pointer; user-select: none; } /* Preview / visual canvas */ #preview-pane { flex: 1; Loading
editor/html/index.html +2 −0 Changes for editor/html/index.html: 2 added lines, 0 removed lines. Original line number Diff line number Diff line Loading @@ -74,6 +74,8 @@ <button id="btn-copy-el" data-i18n-title="I18N_COPY_ELEMENT">⎘</button> <button id="btn-paste-el" data-i18n-title="I18N_PASTE_ELEMENT">📋</button> <button id="btn-remove" data-i18n-title="I18N_REMOVE_ELEMENT">✖</button> <button id="btn-expand-all" data-i18n-title="I18N_TITLE_EXPAND_ALL">⊞</button> <button id="btn-collapse-all" data-i18n-title="I18N_TITLE_COLLAPSE_ALL">⊟</button> </div> <div id="document-tree"></div> </div> Loading
editor/html/js/editor.js +39 −1 Changes for editor/html/js/editor.js: 39 added lines, 1 removed line. Original line number Diff line number Diff line Loading @@ -11,6 +11,7 @@ var currentDocSeo = {}; var currentDocGlobalCss = ''; // page-level CSS collected by HTML import, see doc-seo-dialog var currentDocPublishParams = {}; // target name -> last-used publish params for this template var currentDocLastPublishTarget = ''; // target name last successfully published to, see applyPreferredPublishTarget var importHtmlPendingSource = ''; // raw HTML staged in the import dialog, once past step 1 var importHtmlPendingCandidates = []; // last parse-html result for the block picker var saveDialogMode = 'save'; Loading Loading @@ -61,6 +62,10 @@ Preview.highlight(uuid); PropertiesPanel.showProperties(uuid); }); DocumentTree.setOnRootSelect(function() { populateDocSeoDialog(); document.getElementById('doc-seo-dialog').showModal(); }); Canvas.setOnChange(refreshDocument); }); } Loading Loading @@ -256,6 +261,8 @@ currentDocSeo = resp.seo || {}; currentDocGlobalCss = resp.global_css || ''; currentDocPublishParams = resp.publish_params || {}; currentDocLastPublishTarget = resp.last_publish_target || ''; applyPreferredPublishTarget(); Canvas.setTreeData(resp.tree || []); DocumentTree.render(resp.tree || []); Preview.refresh(); Loading Loading @@ -426,6 +433,14 @@ document.getElementById('btn-remove').addEventListener('click', function() { removeSelected(); }); document.getElementById('btn-expand-all').addEventListener('click', function() { DocumentTree.expandAll(); }); document.getElementById('btn-collapse-all').addEventListener('click', function() { DocumentTree.collapseAll(); }); } function removeSelected() { Loading Loading @@ -2311,7 +2326,7 @@ select.disabled = false; publishBtn.disabled = false; publishBtn.style.opacity = '1'; renderPreviewPublishFields(targets[0].name); applyPreferredPublishTarget(); // Bind change event (only once) if (!select._bound) { Loading @@ -2322,6 +2337,28 @@ } } // Defaults the toolbar publish-target dropdown to whichever target was // last successfully published to for this document (currentDocLastPublishTarget, // see DocumentState::lastPublishTarget), falling back to the first target // otherwise. Called both once targets are known (showPreviewPublishTargets) // and once the document's last-used target is known (refreshDocument) -- // whichever of the two async loads finishes last ends up applying it. function applyPreferredPublishTarget() { if (!currentPublishTargets.length) return; var select = document.getElementById('preview-publish-target'); var name = currentPublishTargets[0].name; if (currentDocLastPublishTarget) { for (var i = 0; i < currentPublishTargets.length; i++) { if (currentPublishTargets[i].name === currentDocLastPublishTarget) { name = currentDocLastPublishTarget; break; } } } select.value = name; renderPreviewPublishFields(name); } // Builds the actual <select>/<textarea>/<input>/image-picker element for one // publish-form field descriptor (shared by the toolbar quick-publish bar, // the main publish dialog, and the SEO & Social sub-dialog). "image" opens Loading Loading @@ -2465,6 +2502,7 @@ return; } currentDocPublishParams[target] = params; currentDocLastPublishTarget = target; publishBtn.innerHTML = '✅ OK'; setTimeout(function() { publishBtn.innerHTML = origText; Loading
editor/html/js/tree.js +83 −6 Changes for editor/html/js/tree.js: 83 added lines, 6 removed lines. Original line number Diff line number Diff line Loading @@ -12,7 +12,12 @@ var DocumentTree = (function() { var selectedUuid = null; var onSelectCallback = null; var onContextMenuCallback = null; var onRootSelectCallback = null; var lastTreeData = []; // Collapsed state survives across render() calls (e.g. after every // document refresh), keyed by widget uuid, so toggling a node doesn't // get reset the next time something else in the document changes. var collapsedUuids = {}; function setOnSelect(cb) { onSelectCallback = cb; Loading @@ -22,13 +27,22 @@ var DocumentTree = (function() { onContextMenuCallback = cb; } function setOnRootSelect(cb) { onRootSelectCallback = cb; } function render(treeData) { lastTreeData = treeData || []; var container = document.getElementById('document-tree'); container.innerHTML = ''; container.appendChild(renderRootNode()); if (!lastTreeData.length) { container.innerHTML = '<p class="hint">Dokument ist leer. Widget hinzufügen.</p>'; var hint = document.createElement('p'); hint.className = 'hint'; hint.textContent = I18n.t('I18N_DOCUMENT_EMPTY', 'Dokument ist leer. Widget hinzufügen.'); container.appendChild(hint); return; } Loading @@ -37,9 +51,37 @@ var DocumentTree = (function() { } } // Synthetic entry above the widget tree, representing the document // itself rather than any widget -- there's no uuid to select on the // canvas, it just opens the document-level SEO/Social + Page CSS dialog. function renderRootNode() { var header = document.createElement('div'); header.className = 'tree-node-header tree-root-header'; var icon = document.createElement('span'); icon.className = 'tree-node-toggle'; icon.textContent = '⚙'; header.appendChild(icon); var name = document.createElement('span'); name.className = 'tree-node-name'; name.textContent = I18n.t('I18N_DOCUMENT_ROOT', 'Dokument'); header.appendChild(name); header.addEventListener('click', function(e) { e.stopPropagation(); if (onRootSelectCallback) onRootSelectCallback(); }); return header; } function renderNode(node, depth) { var hasChildren = !!(node.children && node.children.length > 0); var isCollapsed = hasChildren && !!collapsedUuids[node.uuid]; var div = document.createElement('div'); div.className = 'tree-node'; div.className = 'tree-node' + (isCollapsed ? ' collapsed' : ''); div.setAttribute('data-uuid', node.uuid); div.setAttribute('data-can-children', node.can_have_children ? '1' : '0'); Loading @@ -51,8 +93,18 @@ var DocumentTree = (function() { var toggle = document.createElement('span'); toggle.className = 'tree-node-toggle'; if (node.children && node.children.length > 0) { toggle.textContent = '▼'; if (hasChildren) { toggle.textContent = isCollapsed ? '▶' : '▼'; toggle.classList.add('has-children'); toggle.addEventListener('click', function(e) { e.stopPropagation(); if (collapsedUuids[node.uuid]) { delete collapsedUuids[node.uuid]; } else { collapsedUuids[node.uuid] = true; } render(lastTreeData); }); } else if (node.can_have_children) { toggle.textContent = '▷'; } else { Loading @@ -79,7 +131,7 @@ var DocumentTree = (function() { div.appendChild(header); if (node.children && node.children.length > 0) { if (hasChildren) { var childContainer = document.createElement('div'); childContainer.className = 'tree-children'; for (var i = 0; i < node.children.length; i++) { Loading Loading @@ -120,12 +172,37 @@ var DocumentTree = (function() { highlightSelected(null); } function forEachNodeWithChildren(nodes, fn) { for (var i = 0; i < nodes.length; i++) { if (nodes[i].children && nodes[i].children.length > 0) { fn(nodes[i]); forEachNodeWithChildren(nodes[i].children, fn); } } } function expandAll() { collapsedUuids = {}; render(lastTreeData); } function collapseAll() { collapsedUuids = {}; forEachNodeWithChildren(lastTreeData, function(node) { collapsedUuids[node.uuid] = true; }); render(lastTreeData); } return { render: render, selectNode: selectNode, highlightSelected: highlightSelected, clearSelection: clearSelection, setOnSelect: setOnSelect, setOnContextMenu: setOnContextMenu setOnContextMenu: setOnContextMenu, setOnRootSelect: setOnRootSelect, expandAll: expandAll, collapseAll: collapseAll }; })();
editor/src/webedit_api.cpp +19 −5 Changes for editor/src/webedit_api.cpp: 19 added lines, 5 removed lines. Original line number Diff line number Diff line Loading @@ -508,6 +508,7 @@ void webedit::Api::loadDocumentTreeFromXml(DocumentState &doc, const std::string doc.ownedElements.clear(); doc.seo = blogi::SeoMeta(); doc.globalCss.clear(); doc.lastPublishTarget.clear(); doc.publishParams.clear(); tinyxml2::XMLDocument xmlDoc; Loading @@ -523,6 +524,9 @@ void webedit::Api::loadDocumentTreeFromXml(DocumentState &doc, const std::string if (const char *globalCss = rootEl->Attribute("GlobalCss")) { doc.globalCss = globalCss; } if (const char *lastTarget = rootEl->Attribute("LastPublishTarget")) { doc.lastPublishTarget = lastTarget; } if (auto *seoEl = rootEl->FirstChildElement("SeoMeta")) { auto attr = [&](const char *name) { const char *v = seoEl->Attribute(name); return v ? v : ""; }; Loading Loading @@ -1050,6 +1054,7 @@ void webedit::Api::handleNewDocument(libhttppp::HttpRequest &curreq, doc.currentDocGroup.clear(); doc.seo = blogi::SeoMeta(); doc.globalCss.clear(); doc.lastPublishTarget.clear(); doc.publishParams.clear(); persistDocumentStateToSession(sessionid, doc); Loading @@ -1072,6 +1077,7 @@ void webedit::Api::handleGetDocument(libhttppp::HttpRequest &curreq, 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, "global_css", json_object_new_string(doc.globalCss.c_str())); json_object_object_add(resp, "last_publish_target", json_object_new_string(doc.lastPublishTarget.c_str())); json_object_object_add(resp, "publish_params", publishParamsMapToJson(doc.publishParams)); json_object *tree = json_object_new_array(); Loading Loading @@ -4892,14 +4898,19 @@ 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")) { // Remember these params (and which target was used) on the template // itself (not just this publish) so the next publish to the same // target starts pre-filled and the target dropdown defaults to it. // Only once the blog actually accepted the content — "duplicate" is // still an unresolved overwrite prompt at this point, not a // completed publish. if (pubStatus == "success" || pubStatus == "ok") { doc.lastPublishTarget = target; if (paramsObj) { doc.publishParams[target] = json_object_to_json_string_ext(paramsObj, JSON_C_TO_STRING_NOSLASHESCAPE); } } } else { // Auth might have expired if (pubStatus == "error") { Loading Loading @@ -5701,6 +5712,9 @@ std::string webedit::Api::exportTreeXml(DocumentState &doc) { if (!doc.globalCss.empty()) { root->SetAttribute("GlobalCss", doc.globalCss.c_str()); } if (!doc.lastPublishTarget.empty()) { root->SetAttribute("LastPublishTarget", doc.lastPublishTarget.c_str()); } const blogi::SeoMeta &seo = doc.seo; if (!seo.ogTitle.empty() || !seo.ogDescription.empty() || !seo.ogImage.empty() || Loading