Loading editor/html/js/api.js +4 −0 Original line number Diff line number Diff line Loading @@ -417,6 +417,10 @@ var EditorApi = (function() { return request('GET', '/api/document/export-html'); }, listSections: function() { return request('GET', '/api/document/sections'); }, // Survey management listSurveys: function() { return request('GET', '/api/surveys'); Loading editor/html/js/properties.js +19 −1 Original line number Diff line number Diff line Loading @@ -357,7 +357,25 @@ var PropertiesPanel = (function() { group.appendChild(label); var input; if (field.type === 'survey_select') { if (field.type === 'section_select') { input = document.createElement('select'); input.name = field.key; var emptyOptSec = document.createElement('option'); emptyOptSec.value = ''; emptyOptSec.textContent = '-- Abschnitt wählen --'; input.appendChild(emptyOptSec); (function(sel, curVal) { EditorApi.listSections().then(function(resp) { (resp.sections || []).forEach(function(s, idx) { var o = document.createElement('option'); o.value = s.uuid; o.textContent = s.css_class ? s.css_class : ('Section ' + (idx + 1)); if (curVal === s.uuid) o.selected = true; sel.appendChild(o); }); }).catch(function() {}); })(input, value); } else if (field.type === 'survey_select') { input = document.createElement('select'); input.name = field.key; var emptyOptS = document.createElement('option'); Loading editor/src/webedit_api.cpp +58 −0 Original line number Diff line number Diff line Loading @@ -769,6 +769,12 @@ bool webedit::Api::handleRequest(libhttppp::HttpRequest &curreq, const int tid, return true; } // Route: /api/document/sections (GET) — lightweight section list for button section_select if (path == "/api/document/sections") { handleListDocumentSections(curreq, sessionid); return true; } // Route: /api/survey/names (GET) — lightweight list for widget dropdown if (path == "/api/survey/names") { handleListSurveyNames(curreq, sessionid); Loading Loading @@ -4104,6 +4110,58 @@ void webedit::Api::handleListSurveys(libhttppp::HttpRequest &curreq, json_object_put(resp); } static void collectSections(const blogi::webedit::EditPlugin *node, json_object *arr) { while (node) { if (node->getName() == "Section") { json_object *reqJson = json_object_new_object(); json_object *respJson = json_object_new_object(); json_object_object_add(reqJson, "action", json_object_new_string("read")); const_cast<blogi::webedit::EditPlugin*>(node)->JsonApi(reqJson, respJson); std::string cssClass; json_object *dataObj = nullptr; if (json_object_object_get_ex(respJson, "data", &dataObj)) { json_object *propsStr = nullptr; if (json_object_object_get_ex(dataObj, "properties", &propsStr)) { json_object *props = json_tokener_parse(json_object_get_string(propsStr)); if (props) { json_object *ccObj = nullptr; if (json_object_object_get_ex(props, "css_class", &ccObj)) { const char *cc = json_object_get_string(ccObj); if (cc && cc[0]) cssClass = cc; } json_object_put(props); } } } json_object_put(reqJson); json_object_put(respJson); json_object *entry = json_object_new_object(); json_object_object_add(entry, "uuid", json_object_new_string(node->getInstanceId().c_str())); json_object_object_add(entry, "css_class", json_object_new_string(cssClass.c_str())); json_object_array_add(arr, entry); } if (node->getChildElement()) collectSections(node->getChildElement(), arr); node = node->nextElement(); } } void webedit::Api::handleListDocumentSections(libhttppp::HttpRequest &curreq, const std::string &sessionid) { auto &doc = getDocState(sessionid); std::lock_guard<std::mutex> lk(doc.mtx); json_object *arr = json_object_new_array(); collectSections(doc.root, arr); json_object *resp = json_object_new_object(); json_object_object_add(resp, "sections", arr); sendJson(curreq, resp); json_object_put(resp); } void webedit::Api::handleListSurveyNames(libhttppp::HttpRequest &curreq, const std::string &sessionid) { std::string uid; Loading editor/src/webedit_api.h +3 −0 Original line number Diff line number Diff line Loading @@ -145,6 +145,9 @@ namespace webedit { std::mutex _connSessionMtx; std::map<std::string, ConnSession> _connSessions; // Document section list (for section_select widget field) void handleListDocumentSections(libhttppp::HttpRequest &curreq, const std::string &sessionid); // Survey management handlers void handleListSurveys(libhttppp::HttpRequest &curreq, const std::string &sessionid); void handleListSurveyNames(libhttppp::HttpRequest &curreq, const std::string &sessionid); Loading editor/widgets/button/button.cpp +67 −0 Original line number Diff line number Diff line Loading @@ -73,6 +73,12 @@ extern "C" { bool hidden = false; bool m_hidden = false; // Hover effect std::string hoverEffect = "none"; // none, move_up, glow, scale, fade, move_up_glow std::string hoverBgColor = ""; std::string hoverTextColor = ""; std::string hoverTransition = "0.2s ease"; // Custom CSS std::string customCss = ""; Loading Loading @@ -375,6 +381,11 @@ extern "C" { json_object *hf = json_object_array_get_idx(arr, json_object_array_length(arr) - 1); json_object_object_add(hf, "default", json_object_new_boolean(false)); } addField("hover_effect", "Hover Effect", "select", nullptr, "desktop", mkOpts({"none","move_up","glow","scale","fade","move_up_glow"})); addField("hover_bg_color", "Hover Background Color", "color_var", nullptr, "desktop"); addField("hover_text_color", "Hover Text Color", "color_var", nullptr, "desktop"); addField("hover_transition", "Hover Transition", "text", "0.2s ease", "desktop"); addField("custom_css", "Custom CSS", "textarea", ".we-xxx { color: red; }"); addField("g_event_enabled", "Enable Google Event", "checkbox"); addField("g_event_name", "Event Name", "text", "button_click", "all", nullptr, mkVisible("g_event_enabled", "true")); Loading Loading @@ -430,6 +441,12 @@ extern "C" { element->SetAttribute("g_event_value", gEventValue.c_str()); element->SetAttribute("g_consent_update", gConsentUpdate ? "true" : "false"); // Hover effect element->SetAttribute("hover_effect", hoverEffect.c_str()); element->SetAttribute("hover_bg_color", hoverBgColor.c_str()); element->SetAttribute("hover_text_color", hoverTextColor.c_str()); element->SetAttribute("hover_transition", hoverTransition.c_str()); if (!customCss.empty()) { tinyxml2::XMLElement* cssEl = doc->NewElement("CustomCss"); cssEl->SetText(customCss.c_str()); Loading Loading @@ -499,6 +516,16 @@ extern "C" { val = xml_data->Attribute("g_consent_update"); if (val) gConsentUpdate = (strcmp(val, "true") == 0); // Hover effect val = xml_data->Attribute("hover_effect"); if (val) hoverEffect = val; val = xml_data->Attribute("hover_bg_color"); if (val) hoverBgColor = val; val = xml_data->Attribute("hover_text_color"); if (val) hoverTextColor = val; val = xml_data->Attribute("hover_transition"); if (val && val[0]) hoverTransition = val; tinyxml2::XMLElement* cssEl = xml_data->FirstChildElement("CustomCss"); if (cssEl) { const char* cssVal = cssEl->GetText(); Loading Loading @@ -534,6 +561,10 @@ extern "C" { json_object_object_add(obj, "m_height", json_object_new_string(m_height.c_str())); json_object_object_add(obj, "hidden", json_object_new_boolean(hidden)); json_object_object_add(obj, "m_hidden", json_object_new_boolean(m_hidden)); json_object_object_add(obj, "hover_effect", json_object_new_string(hoverEffect.c_str())); json_object_object_add(obj, "hover_bg_color", json_object_new_string(hoverBgColor.c_str())); json_object_object_add(obj, "hover_text_color", json_object_new_string(hoverTextColor.c_str())); json_object_object_add(obj, "hover_transition", json_object_new_string(hoverTransition.c_str())); json_object_object_add(obj, "custom_css", json_object_new_string(customCss.c_str())); json_object_object_add(obj, "g_event_enabled", json_object_new_boolean(gEventEnabled)); json_object_object_add(obj, "g_event_name", json_object_new_string(gEventName.c_str())); Loading Loading @@ -643,6 +674,18 @@ extern "C" { m_hidden = json_object_get_boolean(val_obj); } } if (json_object_object_get_ex(obj, "hover_effect", &val_obj)) { val = json_object_get_string(val_obj); if (val) hoverEffect = val; } if (json_object_object_get_ex(obj, "hover_bg_color", &val_obj)) { val = json_object_get_string(val_obj); if (val) hoverBgColor = val; } if (json_object_object_get_ex(obj, "hover_text_color", &val_obj)) { val = json_object_get_string(val_obj); if (val) hoverTextColor = val; } if (json_object_object_get_ex(obj, "hover_transition", &val_obj)) { val = json_object_get_string(val_obj); if (val && val[0]) hoverTransition = val; } if (json_object_object_get_ex(obj, "custom_css", &val_obj)) { val = json_object_get_string(val_obj); if (val) customCss = val; Loading Loading @@ -758,6 +801,8 @@ extern "C" { style += " text-decoration: none; cursor: pointer;"; if (!width.empty()) style += " width: " + width + ";"; if (!height.empty()) style += " height: " + height + ";"; if (hoverEffect != "none") style += " transition: all " + hoverTransition + ";"; if (hidden) style += " display: none;"; a.setAttribute("style", style.c_str()); Loading @@ -783,7 +828,29 @@ extern "C" { if (!ml_backgroundColor.empty()) mobileLightCSS += " background-color: " + ml_backgroundColor + " !important;"; if (!ml_textColor.empty()) mobileLightCSS += " color: " + ml_textColor + " !important;"; // Hover effect CSS std::string hoverCSS; if (hoverEffect != "none") { std::string hoverProps; if (hoverEffect == "move_up" || hoverEffect == "move_up_glow") hoverProps += " transform: translateY(-4px);"; if (hoverEffect == "scale") hoverProps += " transform: scale(1.05);"; if (hoverEffect == "fade") hoverProps += " opacity: 0.75;"; if (hoverEffect == "glow" || hoverEffect == "move_up_glow") hoverProps += " box-shadow: 0 0 16px 4px " + backgroundColor + ";"; if (!hoverBgColor.empty()) hoverProps += " background-color: " + hoverBgColor + " !important;"; if (!hoverTextColor.empty()) hoverProps += " color: " + hoverTextColor + " !important;"; if (!hoverProps.empty()) hoverCSS = "." + responsiveClass + ":hover {" + hoverProps + " }"; } std::string allMediaCSS; if (!hoverCSS.empty()) allMediaCSS += hoverCSS + " "; if (!mobileCSS.empty()) { allMediaCSS += "@media (max-width: 768px) { ." + responsiveClass + " {" + mobileCSS + " } } "; } Loading Loading
editor/html/js/api.js +4 −0 Original line number Diff line number Diff line Loading @@ -417,6 +417,10 @@ var EditorApi = (function() { return request('GET', '/api/document/export-html'); }, listSections: function() { return request('GET', '/api/document/sections'); }, // Survey management listSurveys: function() { return request('GET', '/api/surveys'); Loading
editor/html/js/properties.js +19 −1 Original line number Diff line number Diff line Loading @@ -357,7 +357,25 @@ var PropertiesPanel = (function() { group.appendChild(label); var input; if (field.type === 'survey_select') { if (field.type === 'section_select') { input = document.createElement('select'); input.name = field.key; var emptyOptSec = document.createElement('option'); emptyOptSec.value = ''; emptyOptSec.textContent = '-- Abschnitt wählen --'; input.appendChild(emptyOptSec); (function(sel, curVal) { EditorApi.listSections().then(function(resp) { (resp.sections || []).forEach(function(s, idx) { var o = document.createElement('option'); o.value = s.uuid; o.textContent = s.css_class ? s.css_class : ('Section ' + (idx + 1)); if (curVal === s.uuid) o.selected = true; sel.appendChild(o); }); }).catch(function() {}); })(input, value); } else if (field.type === 'survey_select') { input = document.createElement('select'); input.name = field.key; var emptyOptS = document.createElement('option'); Loading
editor/src/webedit_api.cpp +58 −0 Original line number Diff line number Diff line Loading @@ -769,6 +769,12 @@ bool webedit::Api::handleRequest(libhttppp::HttpRequest &curreq, const int tid, return true; } // Route: /api/document/sections (GET) — lightweight section list for button section_select if (path == "/api/document/sections") { handleListDocumentSections(curreq, sessionid); return true; } // Route: /api/survey/names (GET) — lightweight list for widget dropdown if (path == "/api/survey/names") { handleListSurveyNames(curreq, sessionid); Loading Loading @@ -4104,6 +4110,58 @@ void webedit::Api::handleListSurveys(libhttppp::HttpRequest &curreq, json_object_put(resp); } static void collectSections(const blogi::webedit::EditPlugin *node, json_object *arr) { while (node) { if (node->getName() == "Section") { json_object *reqJson = json_object_new_object(); json_object *respJson = json_object_new_object(); json_object_object_add(reqJson, "action", json_object_new_string("read")); const_cast<blogi::webedit::EditPlugin*>(node)->JsonApi(reqJson, respJson); std::string cssClass; json_object *dataObj = nullptr; if (json_object_object_get_ex(respJson, "data", &dataObj)) { json_object *propsStr = nullptr; if (json_object_object_get_ex(dataObj, "properties", &propsStr)) { json_object *props = json_tokener_parse(json_object_get_string(propsStr)); if (props) { json_object *ccObj = nullptr; if (json_object_object_get_ex(props, "css_class", &ccObj)) { const char *cc = json_object_get_string(ccObj); if (cc && cc[0]) cssClass = cc; } json_object_put(props); } } } json_object_put(reqJson); json_object_put(respJson); json_object *entry = json_object_new_object(); json_object_object_add(entry, "uuid", json_object_new_string(node->getInstanceId().c_str())); json_object_object_add(entry, "css_class", json_object_new_string(cssClass.c_str())); json_object_array_add(arr, entry); } if (node->getChildElement()) collectSections(node->getChildElement(), arr); node = node->nextElement(); } } void webedit::Api::handleListDocumentSections(libhttppp::HttpRequest &curreq, const std::string &sessionid) { auto &doc = getDocState(sessionid); std::lock_guard<std::mutex> lk(doc.mtx); json_object *arr = json_object_new_array(); collectSections(doc.root, arr); json_object *resp = json_object_new_object(); json_object_object_add(resp, "sections", arr); sendJson(curreq, resp); json_object_put(resp); } void webedit::Api::handleListSurveyNames(libhttppp::HttpRequest &curreq, const std::string &sessionid) { std::string uid; Loading
editor/src/webedit_api.h +3 −0 Original line number Diff line number Diff line Loading @@ -145,6 +145,9 @@ namespace webedit { std::mutex _connSessionMtx; std::map<std::string, ConnSession> _connSessions; // Document section list (for section_select widget field) void handleListDocumentSections(libhttppp::HttpRequest &curreq, const std::string &sessionid); // Survey management handlers void handleListSurveys(libhttppp::HttpRequest &curreq, const std::string &sessionid); void handleListSurveyNames(libhttppp::HttpRequest &curreq, const std::string &sessionid); Loading
editor/widgets/button/button.cpp +67 −0 Original line number Diff line number Diff line Loading @@ -73,6 +73,12 @@ extern "C" { bool hidden = false; bool m_hidden = false; // Hover effect std::string hoverEffect = "none"; // none, move_up, glow, scale, fade, move_up_glow std::string hoverBgColor = ""; std::string hoverTextColor = ""; std::string hoverTransition = "0.2s ease"; // Custom CSS std::string customCss = ""; Loading Loading @@ -375,6 +381,11 @@ extern "C" { json_object *hf = json_object_array_get_idx(arr, json_object_array_length(arr) - 1); json_object_object_add(hf, "default", json_object_new_boolean(false)); } addField("hover_effect", "Hover Effect", "select", nullptr, "desktop", mkOpts({"none","move_up","glow","scale","fade","move_up_glow"})); addField("hover_bg_color", "Hover Background Color", "color_var", nullptr, "desktop"); addField("hover_text_color", "Hover Text Color", "color_var", nullptr, "desktop"); addField("hover_transition", "Hover Transition", "text", "0.2s ease", "desktop"); addField("custom_css", "Custom CSS", "textarea", ".we-xxx { color: red; }"); addField("g_event_enabled", "Enable Google Event", "checkbox"); addField("g_event_name", "Event Name", "text", "button_click", "all", nullptr, mkVisible("g_event_enabled", "true")); Loading Loading @@ -430,6 +441,12 @@ extern "C" { element->SetAttribute("g_event_value", gEventValue.c_str()); element->SetAttribute("g_consent_update", gConsentUpdate ? "true" : "false"); // Hover effect element->SetAttribute("hover_effect", hoverEffect.c_str()); element->SetAttribute("hover_bg_color", hoverBgColor.c_str()); element->SetAttribute("hover_text_color", hoverTextColor.c_str()); element->SetAttribute("hover_transition", hoverTransition.c_str()); if (!customCss.empty()) { tinyxml2::XMLElement* cssEl = doc->NewElement("CustomCss"); cssEl->SetText(customCss.c_str()); Loading Loading @@ -499,6 +516,16 @@ extern "C" { val = xml_data->Attribute("g_consent_update"); if (val) gConsentUpdate = (strcmp(val, "true") == 0); // Hover effect val = xml_data->Attribute("hover_effect"); if (val) hoverEffect = val; val = xml_data->Attribute("hover_bg_color"); if (val) hoverBgColor = val; val = xml_data->Attribute("hover_text_color"); if (val) hoverTextColor = val; val = xml_data->Attribute("hover_transition"); if (val && val[0]) hoverTransition = val; tinyxml2::XMLElement* cssEl = xml_data->FirstChildElement("CustomCss"); if (cssEl) { const char* cssVal = cssEl->GetText(); Loading Loading @@ -534,6 +561,10 @@ extern "C" { json_object_object_add(obj, "m_height", json_object_new_string(m_height.c_str())); json_object_object_add(obj, "hidden", json_object_new_boolean(hidden)); json_object_object_add(obj, "m_hidden", json_object_new_boolean(m_hidden)); json_object_object_add(obj, "hover_effect", json_object_new_string(hoverEffect.c_str())); json_object_object_add(obj, "hover_bg_color", json_object_new_string(hoverBgColor.c_str())); json_object_object_add(obj, "hover_text_color", json_object_new_string(hoverTextColor.c_str())); json_object_object_add(obj, "hover_transition", json_object_new_string(hoverTransition.c_str())); json_object_object_add(obj, "custom_css", json_object_new_string(customCss.c_str())); json_object_object_add(obj, "g_event_enabled", json_object_new_boolean(gEventEnabled)); json_object_object_add(obj, "g_event_name", json_object_new_string(gEventName.c_str())); Loading Loading @@ -643,6 +674,18 @@ extern "C" { m_hidden = json_object_get_boolean(val_obj); } } if (json_object_object_get_ex(obj, "hover_effect", &val_obj)) { val = json_object_get_string(val_obj); if (val) hoverEffect = val; } if (json_object_object_get_ex(obj, "hover_bg_color", &val_obj)) { val = json_object_get_string(val_obj); if (val) hoverBgColor = val; } if (json_object_object_get_ex(obj, "hover_text_color", &val_obj)) { val = json_object_get_string(val_obj); if (val) hoverTextColor = val; } if (json_object_object_get_ex(obj, "hover_transition", &val_obj)) { val = json_object_get_string(val_obj); if (val && val[0]) hoverTransition = val; } if (json_object_object_get_ex(obj, "custom_css", &val_obj)) { val = json_object_get_string(val_obj); if (val) customCss = val; Loading Loading @@ -758,6 +801,8 @@ extern "C" { style += " text-decoration: none; cursor: pointer;"; if (!width.empty()) style += " width: " + width + ";"; if (!height.empty()) style += " height: " + height + ";"; if (hoverEffect != "none") style += " transition: all " + hoverTransition + ";"; if (hidden) style += " display: none;"; a.setAttribute("style", style.c_str()); Loading @@ -783,7 +828,29 @@ extern "C" { if (!ml_backgroundColor.empty()) mobileLightCSS += " background-color: " + ml_backgroundColor + " !important;"; if (!ml_textColor.empty()) mobileLightCSS += " color: " + ml_textColor + " !important;"; // Hover effect CSS std::string hoverCSS; if (hoverEffect != "none") { std::string hoverProps; if (hoverEffect == "move_up" || hoverEffect == "move_up_glow") hoverProps += " transform: translateY(-4px);"; if (hoverEffect == "scale") hoverProps += " transform: scale(1.05);"; if (hoverEffect == "fade") hoverProps += " opacity: 0.75;"; if (hoverEffect == "glow" || hoverEffect == "move_up_glow") hoverProps += " box-shadow: 0 0 16px 4px " + backgroundColor + ";"; if (!hoverBgColor.empty()) hoverProps += " background-color: " + hoverBgColor + " !important;"; if (!hoverTextColor.empty()) hoverProps += " color: " + hoverTextColor + " !important;"; if (!hoverProps.empty()) hoverCSS = "." + responsiveClass + ":hover {" + hoverProps + " }"; } std::string allMediaCSS; if (!hoverCSS.empty()) allMediaCSS += hoverCSS + " "; if (!mobileCSS.empty()) { allMediaCSS += "@media (max-width: 768px) { ." + responsiveClass + " {" + mobileCSS + " } } "; } Loading