Loading editor/html/js/preview.js +39 −5 Original line number Diff line number Diff line Loading @@ -19,11 +19,45 @@ var Preview = (function() { } function refresh() { // Fetch render data (blogUrl, authid, html) then POST directly // to the blog's /render endpoint inside the iframe. fetch('/api/preview/page') .then(function(r) { return r.json(); }) .then(function(data) { if (data.error) { var f = getIframe(); f.srcdoc = '<html><body style="display:flex;align-items:center;justify-content:center;height:100vh;margin:0;font-family:sans-serif;color:#999;background:#1e1e2e;"><p>' + data.error + '</p></body></html>'; return; } // Create a hidden form targeting the iframe and POST to blog/render var f = getIframe(); // Clear srcdoc so src takes effect f.removeAttribute('srcdoc'); // Reload full page preview with cache-busting timestamp f.src = '/api/preview/page?t=' + Date.now(); f.name = 'preview-iframe'; var form = document.createElement('form'); form.method = 'POST'; form.action = data.blogUrl + '/render'; form.target = 'preview-iframe'; form.style.display = 'none'; var addField = function(name, value) { var input = document.createElement('input'); input.type = 'hidden'; input.name = name; input.value = value; form.appendChild(input); }; addField('authid', data.authid); addField('html', data.html); document.body.appendChild(form); form.submit(); document.body.removeChild(form); }) .catch(function(err) { var f = getIframe(); f.srcdoc = '<html><body style="display:flex;align-items:center;justify-content:center;height:100vh;margin:0;font-family:sans-serif;color:#c00;background:#1e1e2e;"><p>Preview error: ' + err + '</p></body></html>'; }); } function clear() { Loading editor/src/webedit_api.cpp +10 −68 Original line number Diff line number Diff line Loading @@ -757,7 +757,8 @@ void webedit::Api::handlePreview(libhttppp::HttpRequest &curreq, void webedit::Api::handlePreviewPage(libhttppp::HttpRequest &curreq, const std::string &sessionid) { // Find an active blog connection for rendering // Return JSON with blogUrl, authid, and rendered html so JS can POST // directly to the blog's /render endpoint. std::string authid, blogUrl; { std::lock_guard<std::mutex> lk(_connSessionMtx); Loading @@ -769,18 +770,11 @@ void webedit::Api::handlePreviewPage(libhttppp::HttpRequest &curreq, } if (authid.empty() || blogUrl.empty()) { std::string page = "<!DOCTYPE html><html><body style=\"display:flex;" "align-items:center;justify-content:center;height:100vh;margin:0;" "font-family:sans-serif;color:#999;\">" "<p>Bitte zuerst eine Verbindung herstellen.</p></body></html>"; libhttppp::HttpResponse curres; curres.setState(HTTP200); curres.setContentType("text/html"); curres.send(curreq, page.data(), page.size()); sendJsonError(curreq, 400, "Bitte zuerst eine Verbindung herstellen"); return; } // Render the document tree to HTML locally // Render the document tree to HTML std::string widgetHtml; { auto &doc = getDocState(sessionid); Loading @@ -788,64 +782,12 @@ void webedit::Api::handlePreviewPage(libhttppp::HttpRequest &curreq, widgetHtml = renderTree(doc.root); } try { // POST to the blog's /render endpoint with authid + html libhttppp::HttpUrl url(blogUrl + "/render"); libhttppp::HttpClient client(url); client.setTimeout(15); libhttppp::HttpRequest renderReq; renderReq.setRequestURL(url.getPath()); renderReq.setHeaderData("content-type")->push_back("application/x-www-form-urlencoded"); // URL-encode the form data std::string encodedAuthid, encodedHtml; libhttppp::HttpForm::urlEncode(authid, encodedAuthid); libhttppp::HttpForm::urlEncode(widgetHtml, encodedHtml); std::string formBody = "authid=" + encodedAuthid + "&html=" + encodedHtml; std::vector<char> postData(formBody.begin(), formBody.end()); std::vector<char> respData = client.Post(renderReq, postData); if (respData.empty()) { std::string errPage = "<!DOCTYPE html><html><body><p>Preview error: " "empty response from blog server</p></body></html>"; libhttppp::HttpResponse curres; curres.setState(HTTP200); curres.setContentType("text/html"); curres.send(curreq, errPage.data(), errPage.size()); return; } // Inject <base href> so relative paths (CSS, images, JS) resolve // against the blog server, not the editor server. std::string page(respData.begin(), respData.end()); std::string baseTag = "<base href=\"" + blogUrl + "/\">"; auto headPos = page.find("<head>"); if (headPos == std::string::npos) headPos = page.find("<head "); if (headPos != std::string::npos) { auto insertPos = page.find('>', headPos) + 1; page.insert(insertPos, baseTag); } else { // No <head> found — prepend base tag page = baseTag + page; } libhttppp::HttpResponse curres; curres.setState(HTTP200); curres.setContentType("text/html"); curres.send(curreq, page.data(), page.size()); } catch (const std::exception &e) { std::string errPage = "<!DOCTYPE html><html><body><p>Preview error: " + std::string(e.what()) + "</p></body></html>"; libhttppp::HttpResponse curres; curres.setState(HTTP200); curres.setContentType("text/html"); curres.send(curreq, errPage.data(), errPage.size()); } json_object *resp = json_object_new_object(); json_object_object_add(resp, "blogUrl", json_object_new_string(blogUrl.c_str())); json_object_object_add(resp, "authid", json_object_new_string(authid.c_str())); json_object_object_add(resp, "html", json_object_new_string(widgetHtml.c_str())); sendJson(curreq, resp); json_object_put(resp); } // --- Export XML --- Loading Loading
editor/html/js/preview.js +39 −5 Original line number Diff line number Diff line Loading @@ -19,11 +19,45 @@ var Preview = (function() { } function refresh() { // Fetch render data (blogUrl, authid, html) then POST directly // to the blog's /render endpoint inside the iframe. fetch('/api/preview/page') .then(function(r) { return r.json(); }) .then(function(data) { if (data.error) { var f = getIframe(); f.srcdoc = '<html><body style="display:flex;align-items:center;justify-content:center;height:100vh;margin:0;font-family:sans-serif;color:#999;background:#1e1e2e;"><p>' + data.error + '</p></body></html>'; return; } // Create a hidden form targeting the iframe and POST to blog/render var f = getIframe(); // Clear srcdoc so src takes effect f.removeAttribute('srcdoc'); // Reload full page preview with cache-busting timestamp f.src = '/api/preview/page?t=' + Date.now(); f.name = 'preview-iframe'; var form = document.createElement('form'); form.method = 'POST'; form.action = data.blogUrl + '/render'; form.target = 'preview-iframe'; form.style.display = 'none'; var addField = function(name, value) { var input = document.createElement('input'); input.type = 'hidden'; input.name = name; input.value = value; form.appendChild(input); }; addField('authid', data.authid); addField('html', data.html); document.body.appendChild(form); form.submit(); document.body.removeChild(form); }) .catch(function(err) { var f = getIframe(); f.srcdoc = '<html><body style="display:flex;align-items:center;justify-content:center;height:100vh;margin:0;font-family:sans-serif;color:#c00;background:#1e1e2e;"><p>Preview error: ' + err + '</p></body></html>'; }); } function clear() { Loading
editor/src/webedit_api.cpp +10 −68 Original line number Diff line number Diff line Loading @@ -757,7 +757,8 @@ void webedit::Api::handlePreview(libhttppp::HttpRequest &curreq, void webedit::Api::handlePreviewPage(libhttppp::HttpRequest &curreq, const std::string &sessionid) { // Find an active blog connection for rendering // Return JSON with blogUrl, authid, and rendered html so JS can POST // directly to the blog's /render endpoint. std::string authid, blogUrl; { std::lock_guard<std::mutex> lk(_connSessionMtx); Loading @@ -769,18 +770,11 @@ void webedit::Api::handlePreviewPage(libhttppp::HttpRequest &curreq, } if (authid.empty() || blogUrl.empty()) { std::string page = "<!DOCTYPE html><html><body style=\"display:flex;" "align-items:center;justify-content:center;height:100vh;margin:0;" "font-family:sans-serif;color:#999;\">" "<p>Bitte zuerst eine Verbindung herstellen.</p></body></html>"; libhttppp::HttpResponse curres; curres.setState(HTTP200); curres.setContentType("text/html"); curres.send(curreq, page.data(), page.size()); sendJsonError(curreq, 400, "Bitte zuerst eine Verbindung herstellen"); return; } // Render the document tree to HTML locally // Render the document tree to HTML std::string widgetHtml; { auto &doc = getDocState(sessionid); Loading @@ -788,64 +782,12 @@ void webedit::Api::handlePreviewPage(libhttppp::HttpRequest &curreq, widgetHtml = renderTree(doc.root); } try { // POST to the blog's /render endpoint with authid + html libhttppp::HttpUrl url(blogUrl + "/render"); libhttppp::HttpClient client(url); client.setTimeout(15); libhttppp::HttpRequest renderReq; renderReq.setRequestURL(url.getPath()); renderReq.setHeaderData("content-type")->push_back("application/x-www-form-urlencoded"); // URL-encode the form data std::string encodedAuthid, encodedHtml; libhttppp::HttpForm::urlEncode(authid, encodedAuthid); libhttppp::HttpForm::urlEncode(widgetHtml, encodedHtml); std::string formBody = "authid=" + encodedAuthid + "&html=" + encodedHtml; std::vector<char> postData(formBody.begin(), formBody.end()); std::vector<char> respData = client.Post(renderReq, postData); if (respData.empty()) { std::string errPage = "<!DOCTYPE html><html><body><p>Preview error: " "empty response from blog server</p></body></html>"; libhttppp::HttpResponse curres; curres.setState(HTTP200); curres.setContentType("text/html"); curres.send(curreq, errPage.data(), errPage.size()); return; } // Inject <base href> so relative paths (CSS, images, JS) resolve // against the blog server, not the editor server. std::string page(respData.begin(), respData.end()); std::string baseTag = "<base href=\"" + blogUrl + "/\">"; auto headPos = page.find("<head>"); if (headPos == std::string::npos) headPos = page.find("<head "); if (headPos != std::string::npos) { auto insertPos = page.find('>', headPos) + 1; page.insert(insertPos, baseTag); } else { // No <head> found — prepend base tag page = baseTag + page; } libhttppp::HttpResponse curres; curres.setState(HTTP200); curres.setContentType("text/html"); curres.send(curreq, page.data(), page.size()); } catch (const std::exception &e) { std::string errPage = "<!DOCTYPE html><html><body><p>Preview error: " + std::string(e.what()) + "</p></body></html>"; libhttppp::HttpResponse curres; curres.setState(HTTP200); curres.setContentType("text/html"); curres.send(curreq, errPage.data(), errPage.size()); } json_object *resp = json_object_new_object(); json_object_object_add(resp, "blogUrl", json_object_new_string(blogUrl.c_str())); json_object_object_add(resp, "authid", json_object_new_string(authid.c_str())); json_object_object_add(resp, "html", json_object_new_string(widgetHtml.c_str())); sendJson(curreq, resp); json_object_put(resp); } // --- Export XML --- Loading