Loading plugins/google/google.cpp +74 −20 Original line number Diff line number Diff line Loading @@ -2,6 +2,7 @@ #include <algorithm> #include <regex> #include <chrono> #include <set> #include <string> #include <thread> #include <atomic> Loading Loading @@ -84,16 +85,19 @@ namespace blogi { form.parse(req); bool saved = false; bool syncNow = false; // Collect all form data in one pass before applying std::set<std::string> inForm; // review_in_form_* hidden fields std::set<std::string> checked; // approve_* checkboxes that are checked for(const auto &cur : form.urlData()){ if(cur.key.compare(0, 8, "approve_") == 0) { std::string author = cur.key.substr(8); dbpp::SQL sql; dbpp::DBResult res; sql << "UPDATE google_reviews SET approved=" << (cur.value == "1" ? "true" : "false") << " WHERE author_name='" << author << "';"; Args->database[tid]->exec(sql, res); saved = true; if(cur.key.compare(0, 15, "review_in_form_") == 0) { inForm.insert(cur.key.substr(15)); } else if(cur.key.compare(0, 8, "approve_") == 0 && cur.value == "on") { checked.insert(cur.key.substr(8)); } else if(cur.key == "sync_now") { syncNow = true; } else if(cur.key == "google_notify_email") { _notifyEmail = cur.value; setConfig(*Args->database[tid], "GOOGLE_REVIEW_NOTIFY_EMAIL", cur.value); Loading @@ -116,11 +120,35 @@ namespace blogi { } } // Apply approvals: reset only the reviews that were shown in the form, // then mark the checked ones as approved. if(!inForm.empty()) { std::vector<char> buf; for(const auto &author : inForm) { buf.clear(); bool isApproved = checked.count(author) > 0; dbpp::SQL sql; dbpp::DBResult res; sql << "UPDATE google_reviews SET approved=" << (isApproved ? "true" : "false") << " WHERE author_name='" << dbpp::SQL::escaped(buf, author.c_str()) << "';"; Args->database[tid]->exec(sql, res); } saved = true; } json_object *jroot = json_object_new_object(); json_object_object_add(jroot, "title", json_object_new_string("Google Reviews")); if(saved) { json_object_object_add(jroot, "message", json_object_new_string("Saved!")); if(syncNow) { if(!_apiKey.empty() && !_placeId.empty()) { _fetchFromGoogle(tid); json_object_object_add(jroot, "message", json_object_new_string("Synchronisierung abgeschlossen!")); } else { json_object_object_add(jroot, "message", json_object_new_string("API Key und Place ID müssen zuerst gespeichert werden.")); json_object_object_add(jroot, "message_type", json_object_new_string("error")); } } else if(saved) { json_object_object_add(jroot, "message", json_object_new_string("Gespeichert!")); json_object_object_add(jroot, "message_type", json_object_new_string("success")); } Loading Loading @@ -152,27 +180,53 @@ namespace blogi { for(int i = 0; i < ncount; ++i) { std::string author = res[i][0]; int rating = atoi(res[i][1]); bool approved = atoi(res[i][3]) == 1; std::string text = res[i][2] ? res[i][2] : ""; bool approved = res[i][3] && atoi(res[i][3]) == 1; json_object *jf = json_object_new_object(); std::string key = "approve_" + author; json_object_object_add(jf, "name", json_object_new_string(key.c_str())); // Hidden sentinel so the form handler knows which reviews were on screen json_object *jhidden = json_object_new_object(); json_object_object_add(jhidden, "name", json_object_new_string(("review_in_form_" + author).c_str())); json_object_object_add(jhidden, "type", json_object_new_string("hidden")); json_object_object_add(jhidden, "value", json_object_new_string("1")); json_object_array_add(jfields, jhidden); // Approval checkbox with author, rating and a text excerpt as label std::string excerpt = text.size() > 80 ? text.substr(0, 77) + "..." : text; std::string label = author + " (" + std::to_string(rating) + "★)"; if(!excerpt.empty()) label += " – " + excerpt; std::string label = "Review by " + author + " (" + std::to_string(rating) + " stars)"; json_object *jf = json_object_new_object(); json_object_object_add(jf, "name", json_object_new_string(("approve_" + author).c_str())); json_object_object_add(jf, "label", json_object_new_string(label.c_str())); json_object_object_add(jf, "type", json_object_new_string("checkbox")); if(approved) { json_object_object_add(jf, "value", json_object_new_string("1")); json_object_object_add(jf, "value", json_object_new_string("on")); } json_object_array_add(jfields, jf); } json_object_object_add(jform, "fields", jfields); json_object_object_add(jform, "submit", json_object_new_string("Save Changes")); json_object_object_add(jform, "submit", json_object_new_string("Speichern")); json_object_object_add(jroot, "form", jform); // Sync Now button (rendered as a separate form below the main form) json_object *jsyncform = json_object_new_object(); json_object_object_add(jsyncform, "method", json_object_new_string("POST")); json_object_object_add(jsyncform, "action", json_object_new_string(Args->config->buildurl("settings/google").c_str())); json_object *jsyncfields = json_object_new_array(); { json_object *jf = json_object_new_object(); json_object_object_add(jf, "name", json_object_new_string("sync_now")); json_object_object_add(jf, "type", json_object_new_string("hidden")); json_object_object_add(jf, "value", json_object_new_string("1")); json_object_array_add(jsyncfields, jf); } json_object_object_add(jsyncform, "fields", jsyncfields); json_object_object_add(jsyncform, "submit", json_object_new_string("Jetzt synchronisieren")); json_object_object_add(jroot, "sync_form", jsyncform); return jroot; } Loading Loading @@ -267,11 +321,11 @@ namespace blogi { if (_stop.load()) break; if (!_apiKey.empty() && !_placeId.empty()) _fetchFromGoogle(); _fetchFromGoogle(0); } } void _fetchFromGoogle() { void _fetchFromGoogle(int tid) { try { std::string path = "/maps/api/place/details/json?place_id=" + _placeId + "&fields=reviews&key=" + _apiKey; Loading Loading @@ -313,7 +367,7 @@ namespace blogi { timeStr = std::to_string(json_object_get_int64(jtime)); if (!author.empty()) _processReview(0, author, rating, text, timeStr); _processReview(tid, author, rating, text, timeStr); } } } Loading Loading
plugins/google/google.cpp +74 −20 Original line number Diff line number Diff line Loading @@ -2,6 +2,7 @@ #include <algorithm> #include <regex> #include <chrono> #include <set> #include <string> #include <thread> #include <atomic> Loading Loading @@ -84,16 +85,19 @@ namespace blogi { form.parse(req); bool saved = false; bool syncNow = false; // Collect all form data in one pass before applying std::set<std::string> inForm; // review_in_form_* hidden fields std::set<std::string> checked; // approve_* checkboxes that are checked for(const auto &cur : form.urlData()){ if(cur.key.compare(0, 8, "approve_") == 0) { std::string author = cur.key.substr(8); dbpp::SQL sql; dbpp::DBResult res; sql << "UPDATE google_reviews SET approved=" << (cur.value == "1" ? "true" : "false") << " WHERE author_name='" << author << "';"; Args->database[tid]->exec(sql, res); saved = true; if(cur.key.compare(0, 15, "review_in_form_") == 0) { inForm.insert(cur.key.substr(15)); } else if(cur.key.compare(0, 8, "approve_") == 0 && cur.value == "on") { checked.insert(cur.key.substr(8)); } else if(cur.key == "sync_now") { syncNow = true; } else if(cur.key == "google_notify_email") { _notifyEmail = cur.value; setConfig(*Args->database[tid], "GOOGLE_REVIEW_NOTIFY_EMAIL", cur.value); Loading @@ -116,11 +120,35 @@ namespace blogi { } } // Apply approvals: reset only the reviews that were shown in the form, // then mark the checked ones as approved. if(!inForm.empty()) { std::vector<char> buf; for(const auto &author : inForm) { buf.clear(); bool isApproved = checked.count(author) > 0; dbpp::SQL sql; dbpp::DBResult res; sql << "UPDATE google_reviews SET approved=" << (isApproved ? "true" : "false") << " WHERE author_name='" << dbpp::SQL::escaped(buf, author.c_str()) << "';"; Args->database[tid]->exec(sql, res); } saved = true; } json_object *jroot = json_object_new_object(); json_object_object_add(jroot, "title", json_object_new_string("Google Reviews")); if(saved) { json_object_object_add(jroot, "message", json_object_new_string("Saved!")); if(syncNow) { if(!_apiKey.empty() && !_placeId.empty()) { _fetchFromGoogle(tid); json_object_object_add(jroot, "message", json_object_new_string("Synchronisierung abgeschlossen!")); } else { json_object_object_add(jroot, "message", json_object_new_string("API Key und Place ID müssen zuerst gespeichert werden.")); json_object_object_add(jroot, "message_type", json_object_new_string("error")); } } else if(saved) { json_object_object_add(jroot, "message", json_object_new_string("Gespeichert!")); json_object_object_add(jroot, "message_type", json_object_new_string("success")); } Loading Loading @@ -152,27 +180,53 @@ namespace blogi { for(int i = 0; i < ncount; ++i) { std::string author = res[i][0]; int rating = atoi(res[i][1]); bool approved = atoi(res[i][3]) == 1; std::string text = res[i][2] ? res[i][2] : ""; bool approved = res[i][3] && atoi(res[i][3]) == 1; json_object *jf = json_object_new_object(); std::string key = "approve_" + author; json_object_object_add(jf, "name", json_object_new_string(key.c_str())); // Hidden sentinel so the form handler knows which reviews were on screen json_object *jhidden = json_object_new_object(); json_object_object_add(jhidden, "name", json_object_new_string(("review_in_form_" + author).c_str())); json_object_object_add(jhidden, "type", json_object_new_string("hidden")); json_object_object_add(jhidden, "value", json_object_new_string("1")); json_object_array_add(jfields, jhidden); // Approval checkbox with author, rating and a text excerpt as label std::string excerpt = text.size() > 80 ? text.substr(0, 77) + "..." : text; std::string label = author + " (" + std::to_string(rating) + "★)"; if(!excerpt.empty()) label += " – " + excerpt; std::string label = "Review by " + author + " (" + std::to_string(rating) + " stars)"; json_object *jf = json_object_new_object(); json_object_object_add(jf, "name", json_object_new_string(("approve_" + author).c_str())); json_object_object_add(jf, "label", json_object_new_string(label.c_str())); json_object_object_add(jf, "type", json_object_new_string("checkbox")); if(approved) { json_object_object_add(jf, "value", json_object_new_string("1")); json_object_object_add(jf, "value", json_object_new_string("on")); } json_object_array_add(jfields, jf); } json_object_object_add(jform, "fields", jfields); json_object_object_add(jform, "submit", json_object_new_string("Save Changes")); json_object_object_add(jform, "submit", json_object_new_string("Speichern")); json_object_object_add(jroot, "form", jform); // Sync Now button (rendered as a separate form below the main form) json_object *jsyncform = json_object_new_object(); json_object_object_add(jsyncform, "method", json_object_new_string("POST")); json_object_object_add(jsyncform, "action", json_object_new_string(Args->config->buildurl("settings/google").c_str())); json_object *jsyncfields = json_object_new_array(); { json_object *jf = json_object_new_object(); json_object_object_add(jf, "name", json_object_new_string("sync_now")); json_object_object_add(jf, "type", json_object_new_string("hidden")); json_object_object_add(jf, "value", json_object_new_string("1")); json_object_array_add(jsyncfields, jf); } json_object_object_add(jsyncform, "fields", jsyncfields); json_object_object_add(jsyncform, "submit", json_object_new_string("Jetzt synchronisieren")); json_object_object_add(jroot, "sync_form", jsyncform); return jroot; } Loading Loading @@ -267,11 +321,11 @@ namespace blogi { if (_stop.load()) break; if (!_apiKey.empty() && !_placeId.empty()) _fetchFromGoogle(); _fetchFromGoogle(0); } } void _fetchFromGoogle() { void _fetchFromGoogle(int tid) { try { std::string path = "/maps/api/place/details/json?place_id=" + _placeId + "&fields=reviews&key=" + _apiKey; Loading Loading @@ -313,7 +367,7 @@ namespace blogi { timeStr = std::to_string(json_object_get_int64(jtime)); if (!author.empty()) _processReview(0, author, rating, text, timeStr); _processReview(tid, author, rating, text, timeStr); } } } Loading