Commit 2080a142 authored by Jan Köster's avatar Jan Köster
Browse files

test

parent a8c573b6
Loading
Loading
Loading
Loading
+14 −3
Changes for editor/html/js/survey-manager.js: 14 added lines, 3 removed lines.
Original line number Diff line number Diff line
@@ -282,6 +282,8 @@ var SurveyManager = (function() {
    var _surveys    = [];
    var _current    = null;   // {id, name, title, xml, questions:[]}
    var _editingQ   = null;   // current question being edited
    var _selectMode = false;  // true when opened with a callback for selection
    var _onSelect   = null;   // callback(selectedName) in select mode

    // -------------------------------------------------------------------------
    // DOM helpers
@@ -297,7 +299,14 @@ var SurveyManager = (function() {
            item.className = 'sm-survey-item' + (_current && _current.id === s.id ? ' active' : '');
            item.textContent = s.name;
            item.setAttribute('data-id', s.id);
            item.addEventListener('click', function() { loadSurvey(s.id); });
            item.addEventListener('click', function() {
                if (_selectMode) {
                    if (_onSelect) _onSelect(s.name);
                    document.getElementById('survey-manager-dialog').close();
                } else {
                    loadSurvey(s.id);
                }
            });
            list.appendChild(item);
        });
    }
@@ -782,10 +791,12 @@ var SurveyManager = (function() {
    // Dialog open
    // -------------------------------------------------------------------------

    function open() {
    function open(initialName, onSelectCallback) {
        _selectMode = typeof onSelectCallback === 'function';
        _onSelect   = _selectMode ? onSelectCallback : null;
        _current = null;
        _selectedQId = null;
        el('sm-form-area').style.display = 'none';
        el('sm-form-area').style.display = _selectMode ? 'none' : 'none';
        loadSurveys();
        document.getElementById('survey-manager-dialog').showModal();
    }
+405 −0
Changes for editor/src/webedit_api.cpp: 405 added lines, 0 removed lines.
Original line number Diff line number Diff line
@@ -3874,3 +3874,408 @@ webedit::Api::NodeContext webedit::Api::findNodeContextInChildren(

    return {};
}

// ---------------------------------------------------------------------------
// Survey proxy handler — forwards a survey command to the connected blog
// ---------------------------------------------------------------------------

void webedit::Api::handleSurveyProxy(libhttppp::HttpRequest &curreq,
                                      const std::string &sessionid,
                                      const std::string &connId) {
    std::string authid, blogUrl;
    {
        std::lock_guard<std::mutex> lk(_connSessionMtx);
        auto it = _connSessions.find(connId);
        if (it == _connSessions.end()) {
            sendJsonError(curreq, 403, "Not connected");
            return;
        }
        authid  = it->second.authid;
        blogUrl = it->second.blogUrl;
    }

    std::string body = getRequestBody(curreq);
    json_object *cmd = json_tokener_parse(body.c_str());
    if (!cmd) {
        sendJsonError(curreq, 400, "Invalid JSON");
        return;
    }

    json_object *requestArr = json_object_new_array();

    json_object *authCmd = json_object_new_object();
    json_object_object_add(authCmd, "authid", json_object_new_string(authid.c_str()));
    json_object_array_add(requestArr, authCmd);
    json_object_array_add(requestArr, cmd);

    try {
        json_object *resp = blogApiCall(blogUrl, requestArr);
        json_object_put(requestArr);

        if (!resp) {
            sendJsonError(curreq, 502, "No response from blog");
            return;
        }

        json_object *result = json_object_new_object();
        if (json_object_is_type(resp, json_type_array)) {
            size_t rLen = json_object_array_length(resp);
            for (size_t i = 0; i < rLen; ++i) {
                json_object *item = json_object_array_get_idx(resp, i);
                json_object *sObj = nullptr;
                if (json_object_object_get_ex(item, "status", &sObj)) {
                    json_object_get(item);
                    json_object_put(result);
                    result = item;
                    break;
                }
            }
        }
        json_object_put(resp);
        sendJson(curreq, result);
        json_object_put(result);
    } catch (const std::exception &e) {
        json_object_put(requestArr);
        sendJsonError(curreq, 502, std::string("Survey proxy failed: ") + e.what());
    }
}

// ---------------------------------------------------------------------------
// Survey XML → blogi JSON conversion
//
// Editor XML format:
//   <Survey name="..." title="...">
//     <Question id="..." parent_id="..." type="..." label="..." order_idx="0">
//       <Option value="...">label text</Option>
//     </Question>
//   </Survey>
//
// Blogi JSON format expected by save_survey:
//   { "id":"", "title":"...", "description":"",
//     "questions":[{"id":"...","type":"...","text":"...","required":false,
//                   "order":0, "options":[{"value":"...","label":"..."}]}] }
// ---------------------------------------------------------------------------

json_object* webedit::Api::surveyXmlToJson(const std::string &name,
                                            const std::string &title,
                                            const std::string &xml) {
    json_object *jtpl = json_object_new_object();
    json_object_object_add(jtpl, "id",          json_object_new_string(name.c_str()));
    json_object_object_add(jtpl, "title",       json_object_new_string(title.c_str()));
    json_object_object_add(jtpl, "description", json_object_new_string(""));

    json_object *jquestions = json_object_new_array();

    if (!xml.empty()) {
        tinyxml2::XMLDocument doc;
        if (doc.Parse(xml.c_str(), xml.size()) == tinyxml2::XML_SUCCESS) {
            tinyxml2::XMLElement *surveyEl = doc.FirstChildElement("Survey");
            if (surveyEl) {
                int orderIdx = 0;
                for (tinyxml2::XMLElement *qEl = surveyEl->FirstChildElement("Question");
                     qEl; qEl = qEl->NextSiblingElement("Question")) {

                    json_object *jq = json_object_new_object();

                    const char *qid    = qEl->Attribute("id");
                    const char *qtype  = qEl->Attribute("type");
                    const char *qlabel = qEl->Attribute("label");

                    int qorder = orderIdx;
                    qEl->QueryIntAttribute("order_idx", &qorder);

                    json_object_object_add(jq, "id",       json_object_new_string(qid    ? qid    : ""));
                    json_object_object_add(jq, "type",     json_object_new_string(qtype  ? qtype  : "text"));
                    json_object_object_add(jq, "text",     json_object_new_string(qlabel ? qlabel : ""));
                    json_object_object_add(jq, "required", json_object_new_boolean(false));
                    json_object_object_add(jq, "order",    json_object_new_int(qorder));

                    json_object *jopts = json_object_new_array();
                    for (tinyxml2::XMLElement *optEl = qEl->FirstChildElement("Option");
                         optEl; optEl = optEl->NextSiblingElement("Option")) {
                        json_object *jo = json_object_new_object();
                        const char *val = optEl->Attribute("value");
                        const char *lbl = optEl->GetText();
                        json_object_object_add(jo, "value", json_object_new_string(val ? val : ""));
                        json_object_object_add(jo, "label", json_object_new_string(lbl ? lbl : ""));
                        json_object_array_add(jopts, jo);
                    }
                    json_object_object_add(jq, "options", jopts);
                    json_object_array_add(jquestions, jq);
                    ++orderIdx;
                }
            }
        }
    }

    json_object_object_add(jtpl, "questions", jquestions);
    return jtpl;
}

void webedit::Api::autoPublishSurveysForDocument(const std::string &docXml,
                                                  const std::string &authid,
                                                  const std::string &blogUrl,
                                                  const std::string &ownerUid) {
    if (docXml.empty() || authid.empty() || blogUrl.empty()) return;

    tinyxml2::XMLDocument doc;
    if (doc.Parse(docXml.c_str(), docXml.size()) != tinyxml2::XML_SUCCESS) return;

    std::vector<std::string> surveyNames;
    std::function<void(tinyxml2::XMLElement*)> walk = [&](tinyxml2::XMLElement *el) {
        if (!el) return;
        const char *sn = el->Attribute("survey_name");
        if (sn && sn[0]) {
            std::string name(sn);
            bool found = false;
            for (auto &s : surveyNames) { if (s == name) { found = true; break; } }
            if (!found) surveyNames.push_back(name);
        }
        for (auto *child = el->FirstChildElement(); child; child = child->NextSiblingElement())
            walk(child);
    };
    walk(doc.RootElement());

    if (surveyNames.empty()) return;

    for (const auto &sname : surveyNames) {
        dbpp::SQL sql;
        dbpp::DBResult res;
        std::vector<char> eOwner, eName;
        sql << "SELECT id, name, title, xml FROM survey_templates WHERE owner_uid='"
            << dbpp::SQL::escaped(eOwner, ownerUid.c_str()) << "' AND name='"
            << dbpp::SQL::escaped(eName, sname.c_str()) << "' LIMIT 1;";

        int rows = _db.getDb().exec(sql, res);
        if (rows < 1) continue;

        std::string sname2 = res[0][1];
        std::string stitle = res[0][2];
        std::string sxml   = res[0][3] ? res[0][3] : "";

        json_object *jsurvey = surveyXmlToJson(sname2, stitle, sxml);

        json_object *requestArr = json_object_new_array();

        json_object *authCmd = json_object_new_object();
        json_object_object_add(authCmd, "authid", json_object_new_string(authid.c_str()));
        json_object_array_add(requestArr, authCmd);

        json_object *saveCmd = json_object_new_object();
        json_object_object_add(saveCmd, "command",  json_object_new_string("save_survey"));
        json_object_object_add(saveCmd, "name",     json_object_new_string(sname2.c_str()));
        json_object_object_add(saveCmd, "survey",   jsurvey);
        json_object_array_add(requestArr, saveCmd);

        try {
            json_object *resp = blogApiCall(blogUrl, requestArr);
            if (resp) json_object_put(resp);
        } catch (...) {
            std::cerr << "autoPublishSurveys: failed to push survey '" << sname2 << "'" << std::endl;
        }
        json_object_put(requestArr);
    }
}

// ---------------------------------------------------------------------------
// Survey management handlers
// ---------------------------------------------------------------------------

void webedit::Api::handleListSurveys(libhttppp::HttpRequest &curreq,
                                      const std::string &sessionid) {
    std::string uid;
    _session.getData(sessionid, "uid", uid);
    std::string jsonStr = _db.listSurveys(uid);
    json_object *resp = json_object_new_object();
    json_object *arr = json_tokener_parse(jsonStr.c_str());
    json_object_object_add(resp, "surveys", arr ? arr : json_object_new_array());
    sendJson(curreq, resp);
    json_object_put(resp);
}

void webedit::Api::handleListSurveyNames(libhttppp::HttpRequest &curreq,
                                          const std::string &sessionid) {
    std::string uid;
    _session.getData(sessionid, "uid", uid);
    std::string jsonStr = _db.listSurveyNames(uid);
    json_object *resp = json_object_new_object();
    json_object *arr = json_tokener_parse(jsonStr.c_str());
    json_object_object_add(resp, "surveys", arr ? arr : json_object_new_array());
    sendJson(curreq, resp);
    json_object_put(resp);
}

void webedit::Api::handleGetSurvey(libhttppp::HttpRequest &curreq,
                                    const std::string &sessionid,
                                    const std::string &surveyId) {
    std::string uid;
    _session.getData(sessionid, "uid", uid);
    std::string jsonStr = _db.getSurvey(uid, surveyId);
    json_object *resp = json_tokener_parse(jsonStr.c_str());
    if (!resp) {
        sendJsonError(curreq, 404, "Survey not found");
        return;
    }
    sendJson(curreq, resp);
    json_object_put(resp);
}

void webedit::Api::handleSaveSurvey(libhttppp::HttpRequest &curreq,
                                     const std::string &sessionid) {
    std::string uid;
    _session.getData(sessionid, "uid", uid);

    std::string body = getRequestBody(curreq);
    json_object *req = json_tokener_parse(body.c_str());
    if (!req) {
        sendJsonError(curreq, 400, "Invalid JSON");
        return;
    }

    json_object *idObj = nullptr, *nameObj = nullptr, *titleObj = nullptr, *xmlObj = nullptr;
    json_object_object_get_ex(req, "id",    &idObj);
    json_object_object_get_ex(req, "name",  &nameObj);
    json_object_object_get_ex(req, "title", &titleObj);
    json_object_object_get_ex(req, "xml",   &xmlObj);

    std::string name  = nameObj  ? json_object_get_string(nameObj)  : "";
    std::string title = titleObj ? json_object_get_string(titleObj) : "";
    std::string xml   = xmlObj   ? json_object_get_string(xmlObj)   : "<Survey/>";

    if (name.empty()) {
        json_object_put(req);
        sendJsonError(curreq, 400, "Name is required");
        return;
    }

    std::string surveyId;
    if (idObj && strlen(json_object_get_string(idObj)) > 0) {
        surveyId = json_object_get_string(idObj);
        _db.updateSurvey(uid, surveyId, name, title, xml);
    } else {
        surveyId = _db.saveSurvey(uid, name, title, xml);
    }

    json_object *resp = json_object_new_object();
    json_object_object_add(resp, "status", json_object_new_string("ok"));
    json_object_object_add(resp, "id",     json_object_new_string(surveyId.c_str()));
    sendJson(curreq, resp);
    json_object_put(resp);
    json_object_put(req);
}

void webedit::Api::handleDeleteSurvey(libhttppp::HttpRequest &curreq,
                                       const std::string &sessionid,
                                       const std::string &surveyId) {
    std::string uid;
    _session.getData(sessionid, "uid", uid);
    _db.deleteSurvey(uid, surveyId);
    json_object *resp = json_object_new_object();
    json_object_object_add(resp, "status", json_object_new_string("ok"));
    sendJson(curreq, resp);
    json_object_put(resp);
}

void webedit::Api::handlePublishSurveyToConnection(libhttppp::HttpRequest &curreq,
                                                    const std::string &sessionid) {
    std::string uid;
    _session.getData(sessionid, "uid", uid);

    std::string body = getRequestBody(curreq);
    json_object *req = json_tokener_parse(body.c_str());
    if (!req) {
        sendJsonError(curreq, 400, "Invalid JSON");
        return;
    }

    json_object *surveyIdObj = nullptr, *connIdObj = nullptr;
    json_object_object_get_ex(req, "survey_id", &surveyIdObj);
    json_object_object_get_ex(req, "conn_id",   &connIdObj);

    std::string surveyId = surveyIdObj ? json_object_get_string(surveyIdObj) : "";
    std::string connId   = connIdObj   ? json_object_get_string(connIdObj)   : "";
    json_object_put(req);

    if (surveyId.empty() || connId.empty()) {
        sendJsonError(curreq, 400, "survey_id and conn_id required");
        return;
    }

    std::string surveyJson = _db.getSurvey(uid, surveyId);
    json_object *surveyData = json_tokener_parse(surveyJson.c_str());
    if (!surveyData) {
        sendJsonError(curreq, 404, "Survey not found");
        return;
    }

    json_object *nameObj = nullptr, *titleObj = nullptr, *xmlObj = nullptr;
    json_object_object_get_ex(surveyData, "name",  &nameObj);
    json_object_object_get_ex(surveyData, "title", &titleObj);
    json_object_object_get_ex(surveyData, "xml",   &xmlObj);

    std::string sname  = nameObj  ? json_object_get_string(nameObj)  : "";
    std::string stitle = titleObj ? json_object_get_string(titleObj) : "";
    std::string sxml   = xmlObj   ? json_object_get_string(xmlObj)   : "";
    json_object_put(surveyData);

    if (sname.empty()) {
        sendJsonError(curreq, 400, "Survey has no name");
        return;
    }

    std::string authid, blogUrl;
    {
        std::lock_guard<std::mutex> lk(_connSessionMtx);
        auto it = _connSessions.find(connId);
        if (it == _connSessions.end()) {
            sendJsonError(curreq, 403, "Not connected. Bitte zuerst verbinden.");
            return;
        }
        authid  = it->second.authid;
        blogUrl = it->second.blogUrl;
    }

    json_object *jsurvey = surveyXmlToJson(sname, stitle, sxml);

    json_object *requestArr = json_object_new_array();

    json_object *authCmd = json_object_new_object();
    json_object_object_add(authCmd, "authid", json_object_new_string(authid.c_str()));
    json_object_array_add(requestArr, authCmd);

    json_object *saveCmd = json_object_new_object();
    json_object_object_add(saveCmd, "command",  json_object_new_string("save_survey"));
    json_object_object_add(saveCmd, "name",     json_object_new_string(sname.c_str()));
    json_object_object_add(saveCmd, "survey",   jsurvey);
    json_object_array_add(requestArr, saveCmd);

    try {
        json_object *pubResp = blogApiCall(blogUrl, requestArr);
        json_object_put(requestArr);

        std::string status = "error";
        std::string message = "No response";
        if (pubResp && json_object_is_type(pubResp, json_type_array)) {
            size_t rLen = json_object_array_length(pubResp);
            for (size_t i = 0; i < rLen; ++i) {
                json_object *item = json_object_array_get_idx(pubResp, i);
                json_object *sObj = nullptr, *mObj = nullptr;
                if (json_object_object_get_ex(item, "status", &sObj))
                    status = json_object_get_string(sObj);
                if (json_object_object_get_ex(item, "message", &mObj))
                    message = json_object_get_string(mObj);
            }
        }
        if (pubResp) json_object_put(pubResp);

        json_object *resp = json_object_new_object();
        json_object_object_add(resp, "status",  json_object_new_string(status.c_str()));
        json_object_object_add(resp, "message", json_object_new_string(message.c_str()));
        sendJson(curreq, resp);
        json_object_put(resp);

    } catch (const std::exception &e) {
        json_object_put(requestArr);
        sendJsonError(curreq, 502, std::string("Publish failed: ") + e.what());
    }
}