Loading data/template/default/index.html +1 −1 Original line number Diff line number Diff line Loading @@ -12,7 +12,7 @@ <body> <div id="header"> <!-- header image --> <img alt="headerimage" width="1280" height="150" id="banner" src="${PREFIX}/theme/public/header.webp" > <img alt="headerimage" width="1280" height="150" id="banner" src="${LOGO_URL}" > </div> <!-- Navbar--> <div id="headnav"> Loading src/CMakeLists.txt +2 −0 Original line number Diff line number Diff line Loading @@ -29,6 +29,8 @@ target_link_libraries(blogidev PUBLIC dbpp::dbpp uuidp::uuidp tinyxml2::tinyxml2 mediadb_client cjson ) if(${CMAKE_HOST_SYSTEM_NAME} STREQUAL "Windows") Loading src/blogi.cpp +1 −1 Original line number Diff line number Diff line Loading @@ -1173,7 +1173,7 @@ void blogi::Blogi::Settings(libhttppp::HttpRequest& curreq,const int tid,const s jformdata = SettingsMail(*PlgArgs->database[tid], curreq, sessiondid); }else if(url.compare(0, PlgArgs->config->buildurl("settings/theme").length(), PlgArgs->config->buildurl("settings/theme")) == 0){ jformdata = PlgArgs->theme->SettingsTheme(tid, curreq); jformdata = PlgArgs->theme->SettingsTheme(tid, curreq, sessiondid); }else if(url.compare(0, PlgArgs->config->buildurl("settings/language").length(), PlgArgs->config->buildurl("settings/language")) == 0){ jformdata = SettingsLanguage(*PlgArgs->database[tid], curreq); Loading src/theme.cpp +186 −3 Original line number Diff line number Diff line Loading @@ -44,6 +44,7 @@ #include "conf.h" #include "compress.h" #include "i18n.h" #include "session.h" blogi::Theme::Theme(blogi::ThemeConfig& config,bool formated) : _Config(config){ auto trimlines = [&] (const std::string in){ Loading Loading @@ -163,6 +164,31 @@ blogi::Theme::~Theme(){ // Destructor implementation } void blogi::Theme::ensureMediaClient(const int tid, const std::string &token) { if (_MdbClients.empty()) _MdbClients.resize(_Config.TDatabase.size()); std::string mdbUrl; try { getConfig(*_Config.TDatabase[tid], "MEDIADB_URL", mdbUrl); } catch (...) {} if (mdbUrl.empty()) return; if (!_MdbClients[tid]) _MdbClients[tid] = std::make_unique<mediadb::Client>(mdbUrl, false, true); if (!token.empty()) _MdbClients[tid]->set_token(token); } std::string blogi::Theme::getLogoUrl(const int tid) const { std::string mediaId, mediaExt; try { getConfig(*_Config.TDatabase[tid], "THEME_LOGO_MEDIA_ID", mediaId); } catch (...) {} try { getConfig(*_Config.TDatabase[tid], "THEME_LOGO_MEDIA_EXT", mediaExt); } catch (...) {} if (!mediaId.empty()) { if (mediaExt.empty()) mediaExt = "webp"; return _Config.config.buildurl("media/getimage/" + mediaId + "." + mediaExt); } // Fallback to theme default return _Config.config.getprefix() + "/theme/public/header.webp"; } void blogi::Theme::renderPage(const int tid,const char *name,libhtmlpp::HtmlPage& page, libhtmlpp::HtmlElement &index){ std::string htmlfile=_Config.Theme; #ifdef Windows Loading Loading @@ -193,6 +219,7 @@ void blogi::Theme::renderPage(const int tid,const char *name,libhtmlpp::HtmlPage std::map<std::string, std::string> variable_map = { {"PREFIX", _Config.config.getprefix()}, {"SITEURL", _Config.config.getsiteurl()}, {"LOGO_URL", getLogoUrl(tid)}, }; // Inline CSS content as template variables to eliminate render-blocking requests Loading Loading @@ -553,9 +580,19 @@ void blogi::Theme::injectThemeColors(const int tid, libhtmlpp::HtmlElement *inde head->appendChild(css.parse()); } json_object* blogi::Theme::SettingsTheme(const int tid, libhttppp::HttpRequest &req){ json_object* blogi::Theme::SettingsTheme(const int tid, libhttppp::HttpRequest &req, const std::string &sessionid){ bool saved = false; bool reset = false; std::string logoMsg; // Get auth token for mediadb std::string authToken; if (!sessionid.empty()) { try { Session sess(*_Config.TDatabase[tid]); sess.getSessionData(sessionid, "authid", authToken); } catch (...) {} } // Handle form POST — save dark and light color fields libhttppp::HttpForm form; Loading @@ -564,10 +601,110 @@ json_object* blogi::Theme::SettingsTheme(const int tid, libhttppp::HttpRequest & const char *suffixes[] = {"BG","SURFACE","SURFACE_A","TEXT","TEXT_MUTED","ACCENT","ACCENT_DARK","BORDER","BG_ALT","ERROR","ERROR_BG","ERROR_TEXT","SUCCESS_BG","SUCCESS_TEXT","NAV_ACTIVE","NAV_HOVER","NAV_BORDER"}; const char *profiles[] = {"dark", "light"}; // Check for reset request // Check for reset / logo delete for(const auto &cur : form.urlData()){ if(cur.key == "theme_reset" && cur.value == "1"){ reset = true; } if(cur.key == "delete_logo" && !cur.value.empty()){ // Delete logo from mediadb and clear config std::string oldMediaId; try { getConfig(*_Config.TDatabase[tid], "THEME_LOGO_MEDIA_ID", oldMediaId); } catch (...) {} if (!oldMediaId.empty()) { try { ensureMediaClient(tid, authToken); if (_MdbClients[tid]) _MdbClients[tid]->delete_media(oldMediaId); } catch (...) {} setConfig(*_Config.TDatabase[tid], "THEME_LOGO_MEDIA_ID", ""); setConfig(*_Config.TDatabase[tid], "THEME_LOGO_MEDIA_EXT", ""); } logoMsg = "Logo deleted!"; saved = true; } } // Handle logo file upload (multipart) for (const auto &part : form.multipartData()) { std::string fieldName, filename; for (const auto &disp : part.dispositions) { if (disp.key == "name") fieldName = disp.value; if (disp.key == "filename") filename = disp.value; } if (fieldName == "theme_logo" && !filename.empty() && !part.value.empty()) { try { ensureMediaClient(tid, authToken); if (!_MdbClients[tid]) { logoMsg = "MediaDB not configured (set MEDIADB_URL)"; break; } // Find or create a "theme" album in the first available store std::string albumId; std::string mdbStores; try { getConfig(*_Config.TDatabase[tid], "MEDIADB_STORES", mdbStores); } catch (...) {} // Parse first store ID from JSON array std::string storeId; if (!mdbStores.empty()) { json_object *jstores = json_tokener_parse(mdbStores.c_str()); if (jstores && json_object_is_type(jstores, json_type_array) && json_object_array_length(jstores) > 0) { storeId = json_object_get_string(json_object_array_get_idx(jstores, 0)); } if (jstores) json_object_put(jstores); } if (storeId.empty()) { // Try single store config try { getConfig(*_Config.TDatabase[tid], "MEDIADB_STORE", storeId); } catch (...) {} } if (storeId.empty()) { // List stores and use first one auto stores = _MdbClients[tid]->list_stores(); if (!stores.empty()) storeId = stores[0].id; } if (storeId.empty()) { logoMsg = "No MediaDB store available"; break; } // Find or create "blogi-theme" album auto albums = _MdbClients[tid]->list_albums(storeId); for (const auto &a : albums) { if (a.name == "blogi-theme") { albumId = a.id; break; } } if (albumId.empty()) { auto newAlbum = _MdbClients[tid]->create_album(storeId, "blogi-theme"); albumId = newAlbum.id; } // Delete old logo if exists std::string oldMediaId; try { getConfig(*_Config.TDatabase[tid], "THEME_LOGO_MEDIA_ID", oldMediaId); } catch (...) {} if (!oldMediaId.empty()) { try { _MdbClients[tid]->delete_media(oldMediaId); } catch (...) {} } // Upload std::vector<std::uint8_t> payload( reinterpret_cast<const std::uint8_t*>(part.value.data()), reinterpret_cast<const std::uint8_t*>(part.value.data()) + part.value.size()); auto mi = _MdbClients[tid]->upload(albumId, filename, payload); // Determine extension std::string ext = "webp"; size_t dp = mi.filename.rfind('.'); if (dp != std::string::npos) ext = mi.filename.substr(dp + 1); setConfig(*_Config.TDatabase[tid], "THEME_LOGO_MEDIA_ID", mi.id); setConfig(*_Config.TDatabase[tid], "THEME_LOGO_MEDIA_EXT", ext); logoMsg = "Logo uploaded!"; saved = true; } catch (std::exception &e) { logoMsg = std::string("Upload failed: ") + e.what(); } break; } } Loading Loading @@ -621,9 +758,15 @@ json_object* blogi::Theme::SettingsTheme(const int tid, libhttppp::HttpRequest & json_object *jroot = json_object_new_object(); json_object_object_add(jroot, "title", json_object_new_string("Theme Settings")); if(saved){ if(saved && !logoMsg.empty()){ json_object_object_add(jroot, "message", json_object_new_string(logoMsg.c_str())); json_object_object_add(jroot, "message_type", json_object_new_string("success")); } else if(saved){ json_object_object_add(jroot, "message", json_object_new_string("Saved!")); json_object_object_add(jroot, "message_type", json_object_new_string("success")); } else if(!logoMsg.empty()){ json_object_object_add(jroot, "message", json_object_new_string(logoMsg.c_str())); json_object_object_add(jroot, "message_type", json_object_new_string("error")); } if(reset){ json_object_object_add(jroot, "message", json_object_new_string("Reset to defaults!")); Loading @@ -633,9 +776,49 @@ json_object* blogi::Theme::SettingsTheme(const int tid, libhttppp::HttpRequest & json_object *jform = json_object_new_object(); json_object_object_add(jform, "method", json_object_new_string("POST")); json_object_object_add(jform, "action", json_object_new_string(req.getRequestURL().c_str())); json_object_object_add(jform, "enctype", json_object_new_string("multipart/form-data")); json_object *jfields = json_object_new_array(); // Section header: Logo { std::string logoUrl = getLogoUrl(tid); std::string curMediaId; try { getConfig(*_Config.TDatabase[tid], "THEME_LOGO_MEDIA_ID", curMediaId); } catch (...) {} std::string logoLabel = "Logo"; if (!curMediaId.empty()) logoLabel += " — <img src=\"" + logoUrl + "\" style=\"max-height:60px;max-width:200px;vertical-align:middle;margin-left:8px;\" />"; else logoLabel += " (using theme default)"; json_object *jf = json_object_new_object(); json_object_object_add(jf, "type", json_object_new_string("heading")); json_object_object_add(jf, "label", json_object_new_string(logoLabel.c_str())); json_object_array_add(jfields, jf); } // Logo file upload { json_object *jf = json_object_new_object(); json_object_object_add(jf, "name", json_object_new_string("theme_logo")); json_object_object_add(jf, "type", json_object_new_string("file")); json_object_object_add(jf, "label", json_object_new_string("Upload Logo")); json_object_array_add(jfields, jf); } // Delete logo checkbox (only if a logo is set) { std::string curMediaId; try { getConfig(*_Config.TDatabase[tid], "THEME_LOGO_MEDIA_ID", curMediaId); } catch (...) {} if (!curMediaId.empty()) { json_object *jf = json_object_new_object(); json_object_object_add(jf, "name", json_object_new_string("delete_logo")); json_object_object_add(jf, "type", json_object_new_string("checkbox")); json_object_object_add(jf, "label", json_object_new_string("Delete Logo (revert to theme default)")); json_object_object_add(jf, "value", json_object_new_string("1")); json_object_array_add(jfields, jf); } } // Section header: Dark { json_object *jf = json_object_new_object(); Loading src/theme.h +13 −5 Original line number Diff line number Diff line Loading @@ -27,6 +27,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include <string> #include <vector> #include <memory> #include <netplus/socket.h> #include <netplus/connection.h> Loading @@ -36,6 +37,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include <dbpp/database.h> #include <json-c/json.h> #include <mediadb/client.h> #include "conf.h" #include "auth.h" Loading Loading @@ -85,12 +87,18 @@ namespace blogi { /// Build a <style> block with CSS custom properties from the active theme profile void injectThemeColors(const int tid, libhtmlpp::HtmlElement *index); /// Settings UI for theme colors (returns JSON form descriptor) json_object* SettingsTheme(const int tid, libhttppp::HttpRequest &req); /// Settings UI for theme colors + logo upload (returns JSON form descriptor) json_object* SettingsTheme(const int tid, libhttppp::HttpRequest &req, const std::string &sessionid); /// Get the logo URL (uploaded or theme default) std::string getLogoUrl(const int tid) const; private: void ensureMediaClient(const int tid, const std::string &token = ""); ThemeConfig _Config; std::vector<ThemeFiles> _PublicFiles; bool _Formated; mutable std::vector<std::unique_ptr<mediadb::Client>> _MdbClients; }; }; Loading
data/template/default/index.html +1 −1 Original line number Diff line number Diff line Loading @@ -12,7 +12,7 @@ <body> <div id="header"> <!-- header image --> <img alt="headerimage" width="1280" height="150" id="banner" src="${PREFIX}/theme/public/header.webp" > <img alt="headerimage" width="1280" height="150" id="banner" src="${LOGO_URL}" > </div> <!-- Navbar--> <div id="headnav"> Loading
src/CMakeLists.txt +2 −0 Original line number Diff line number Diff line Loading @@ -29,6 +29,8 @@ target_link_libraries(blogidev PUBLIC dbpp::dbpp uuidp::uuidp tinyxml2::tinyxml2 mediadb_client cjson ) if(${CMAKE_HOST_SYSTEM_NAME} STREQUAL "Windows") Loading
src/blogi.cpp +1 −1 Original line number Diff line number Diff line Loading @@ -1173,7 +1173,7 @@ void blogi::Blogi::Settings(libhttppp::HttpRequest& curreq,const int tid,const s jformdata = SettingsMail(*PlgArgs->database[tid], curreq, sessiondid); }else if(url.compare(0, PlgArgs->config->buildurl("settings/theme").length(), PlgArgs->config->buildurl("settings/theme")) == 0){ jformdata = PlgArgs->theme->SettingsTheme(tid, curreq); jformdata = PlgArgs->theme->SettingsTheme(tid, curreq, sessiondid); }else if(url.compare(0, PlgArgs->config->buildurl("settings/language").length(), PlgArgs->config->buildurl("settings/language")) == 0){ jformdata = SettingsLanguage(*PlgArgs->database[tid], curreq); Loading
src/theme.cpp +186 −3 Original line number Diff line number Diff line Loading @@ -44,6 +44,7 @@ #include "conf.h" #include "compress.h" #include "i18n.h" #include "session.h" blogi::Theme::Theme(blogi::ThemeConfig& config,bool formated) : _Config(config){ auto trimlines = [&] (const std::string in){ Loading Loading @@ -163,6 +164,31 @@ blogi::Theme::~Theme(){ // Destructor implementation } void blogi::Theme::ensureMediaClient(const int tid, const std::string &token) { if (_MdbClients.empty()) _MdbClients.resize(_Config.TDatabase.size()); std::string mdbUrl; try { getConfig(*_Config.TDatabase[tid], "MEDIADB_URL", mdbUrl); } catch (...) {} if (mdbUrl.empty()) return; if (!_MdbClients[tid]) _MdbClients[tid] = std::make_unique<mediadb::Client>(mdbUrl, false, true); if (!token.empty()) _MdbClients[tid]->set_token(token); } std::string blogi::Theme::getLogoUrl(const int tid) const { std::string mediaId, mediaExt; try { getConfig(*_Config.TDatabase[tid], "THEME_LOGO_MEDIA_ID", mediaId); } catch (...) {} try { getConfig(*_Config.TDatabase[tid], "THEME_LOGO_MEDIA_EXT", mediaExt); } catch (...) {} if (!mediaId.empty()) { if (mediaExt.empty()) mediaExt = "webp"; return _Config.config.buildurl("media/getimage/" + mediaId + "." + mediaExt); } // Fallback to theme default return _Config.config.getprefix() + "/theme/public/header.webp"; } void blogi::Theme::renderPage(const int tid,const char *name,libhtmlpp::HtmlPage& page, libhtmlpp::HtmlElement &index){ std::string htmlfile=_Config.Theme; #ifdef Windows Loading Loading @@ -193,6 +219,7 @@ void blogi::Theme::renderPage(const int tid,const char *name,libhtmlpp::HtmlPage std::map<std::string, std::string> variable_map = { {"PREFIX", _Config.config.getprefix()}, {"SITEURL", _Config.config.getsiteurl()}, {"LOGO_URL", getLogoUrl(tid)}, }; // Inline CSS content as template variables to eliminate render-blocking requests Loading Loading @@ -553,9 +580,19 @@ void blogi::Theme::injectThemeColors(const int tid, libhtmlpp::HtmlElement *inde head->appendChild(css.parse()); } json_object* blogi::Theme::SettingsTheme(const int tid, libhttppp::HttpRequest &req){ json_object* blogi::Theme::SettingsTheme(const int tid, libhttppp::HttpRequest &req, const std::string &sessionid){ bool saved = false; bool reset = false; std::string logoMsg; // Get auth token for mediadb std::string authToken; if (!sessionid.empty()) { try { Session sess(*_Config.TDatabase[tid]); sess.getSessionData(sessionid, "authid", authToken); } catch (...) {} } // Handle form POST — save dark and light color fields libhttppp::HttpForm form; Loading @@ -564,10 +601,110 @@ json_object* blogi::Theme::SettingsTheme(const int tid, libhttppp::HttpRequest & const char *suffixes[] = {"BG","SURFACE","SURFACE_A","TEXT","TEXT_MUTED","ACCENT","ACCENT_DARK","BORDER","BG_ALT","ERROR","ERROR_BG","ERROR_TEXT","SUCCESS_BG","SUCCESS_TEXT","NAV_ACTIVE","NAV_HOVER","NAV_BORDER"}; const char *profiles[] = {"dark", "light"}; // Check for reset request // Check for reset / logo delete for(const auto &cur : form.urlData()){ if(cur.key == "theme_reset" && cur.value == "1"){ reset = true; } if(cur.key == "delete_logo" && !cur.value.empty()){ // Delete logo from mediadb and clear config std::string oldMediaId; try { getConfig(*_Config.TDatabase[tid], "THEME_LOGO_MEDIA_ID", oldMediaId); } catch (...) {} if (!oldMediaId.empty()) { try { ensureMediaClient(tid, authToken); if (_MdbClients[tid]) _MdbClients[tid]->delete_media(oldMediaId); } catch (...) {} setConfig(*_Config.TDatabase[tid], "THEME_LOGO_MEDIA_ID", ""); setConfig(*_Config.TDatabase[tid], "THEME_LOGO_MEDIA_EXT", ""); } logoMsg = "Logo deleted!"; saved = true; } } // Handle logo file upload (multipart) for (const auto &part : form.multipartData()) { std::string fieldName, filename; for (const auto &disp : part.dispositions) { if (disp.key == "name") fieldName = disp.value; if (disp.key == "filename") filename = disp.value; } if (fieldName == "theme_logo" && !filename.empty() && !part.value.empty()) { try { ensureMediaClient(tid, authToken); if (!_MdbClients[tid]) { logoMsg = "MediaDB not configured (set MEDIADB_URL)"; break; } // Find or create a "theme" album in the first available store std::string albumId; std::string mdbStores; try { getConfig(*_Config.TDatabase[tid], "MEDIADB_STORES", mdbStores); } catch (...) {} // Parse first store ID from JSON array std::string storeId; if (!mdbStores.empty()) { json_object *jstores = json_tokener_parse(mdbStores.c_str()); if (jstores && json_object_is_type(jstores, json_type_array) && json_object_array_length(jstores) > 0) { storeId = json_object_get_string(json_object_array_get_idx(jstores, 0)); } if (jstores) json_object_put(jstores); } if (storeId.empty()) { // Try single store config try { getConfig(*_Config.TDatabase[tid], "MEDIADB_STORE", storeId); } catch (...) {} } if (storeId.empty()) { // List stores and use first one auto stores = _MdbClients[tid]->list_stores(); if (!stores.empty()) storeId = stores[0].id; } if (storeId.empty()) { logoMsg = "No MediaDB store available"; break; } // Find or create "blogi-theme" album auto albums = _MdbClients[tid]->list_albums(storeId); for (const auto &a : albums) { if (a.name == "blogi-theme") { albumId = a.id; break; } } if (albumId.empty()) { auto newAlbum = _MdbClients[tid]->create_album(storeId, "blogi-theme"); albumId = newAlbum.id; } // Delete old logo if exists std::string oldMediaId; try { getConfig(*_Config.TDatabase[tid], "THEME_LOGO_MEDIA_ID", oldMediaId); } catch (...) {} if (!oldMediaId.empty()) { try { _MdbClients[tid]->delete_media(oldMediaId); } catch (...) {} } // Upload std::vector<std::uint8_t> payload( reinterpret_cast<const std::uint8_t*>(part.value.data()), reinterpret_cast<const std::uint8_t*>(part.value.data()) + part.value.size()); auto mi = _MdbClients[tid]->upload(albumId, filename, payload); // Determine extension std::string ext = "webp"; size_t dp = mi.filename.rfind('.'); if (dp != std::string::npos) ext = mi.filename.substr(dp + 1); setConfig(*_Config.TDatabase[tid], "THEME_LOGO_MEDIA_ID", mi.id); setConfig(*_Config.TDatabase[tid], "THEME_LOGO_MEDIA_EXT", ext); logoMsg = "Logo uploaded!"; saved = true; } catch (std::exception &e) { logoMsg = std::string("Upload failed: ") + e.what(); } break; } } Loading Loading @@ -621,9 +758,15 @@ json_object* blogi::Theme::SettingsTheme(const int tid, libhttppp::HttpRequest & json_object *jroot = json_object_new_object(); json_object_object_add(jroot, "title", json_object_new_string("Theme Settings")); if(saved){ if(saved && !logoMsg.empty()){ json_object_object_add(jroot, "message", json_object_new_string(logoMsg.c_str())); json_object_object_add(jroot, "message_type", json_object_new_string("success")); } else if(saved){ json_object_object_add(jroot, "message", json_object_new_string("Saved!")); json_object_object_add(jroot, "message_type", json_object_new_string("success")); } else if(!logoMsg.empty()){ json_object_object_add(jroot, "message", json_object_new_string(logoMsg.c_str())); json_object_object_add(jroot, "message_type", json_object_new_string("error")); } if(reset){ json_object_object_add(jroot, "message", json_object_new_string("Reset to defaults!")); Loading @@ -633,9 +776,49 @@ json_object* blogi::Theme::SettingsTheme(const int tid, libhttppp::HttpRequest & json_object *jform = json_object_new_object(); json_object_object_add(jform, "method", json_object_new_string("POST")); json_object_object_add(jform, "action", json_object_new_string(req.getRequestURL().c_str())); json_object_object_add(jform, "enctype", json_object_new_string("multipart/form-data")); json_object *jfields = json_object_new_array(); // Section header: Logo { std::string logoUrl = getLogoUrl(tid); std::string curMediaId; try { getConfig(*_Config.TDatabase[tid], "THEME_LOGO_MEDIA_ID", curMediaId); } catch (...) {} std::string logoLabel = "Logo"; if (!curMediaId.empty()) logoLabel += " — <img src=\"" + logoUrl + "\" style=\"max-height:60px;max-width:200px;vertical-align:middle;margin-left:8px;\" />"; else logoLabel += " (using theme default)"; json_object *jf = json_object_new_object(); json_object_object_add(jf, "type", json_object_new_string("heading")); json_object_object_add(jf, "label", json_object_new_string(logoLabel.c_str())); json_object_array_add(jfields, jf); } // Logo file upload { json_object *jf = json_object_new_object(); json_object_object_add(jf, "name", json_object_new_string("theme_logo")); json_object_object_add(jf, "type", json_object_new_string("file")); json_object_object_add(jf, "label", json_object_new_string("Upload Logo")); json_object_array_add(jfields, jf); } // Delete logo checkbox (only if a logo is set) { std::string curMediaId; try { getConfig(*_Config.TDatabase[tid], "THEME_LOGO_MEDIA_ID", curMediaId); } catch (...) {} if (!curMediaId.empty()) { json_object *jf = json_object_new_object(); json_object_object_add(jf, "name", json_object_new_string("delete_logo")); json_object_object_add(jf, "type", json_object_new_string("checkbox")); json_object_object_add(jf, "label", json_object_new_string("Delete Logo (revert to theme default)")); json_object_object_add(jf, "value", json_object_new_string("1")); json_object_array_add(jfields, jf); } } // Section header: Dark { json_object *jf = json_object_new_object(); Loading
src/theme.h +13 −5 Original line number Diff line number Diff line Loading @@ -27,6 +27,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include <string> #include <vector> #include <memory> #include <netplus/socket.h> #include <netplus/connection.h> Loading @@ -36,6 +37,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include <dbpp/database.h> #include <json-c/json.h> #include <mediadb/client.h> #include "conf.h" #include "auth.h" Loading Loading @@ -85,12 +87,18 @@ namespace blogi { /// Build a <style> block with CSS custom properties from the active theme profile void injectThemeColors(const int tid, libhtmlpp::HtmlElement *index); /// Settings UI for theme colors (returns JSON form descriptor) json_object* SettingsTheme(const int tid, libhttppp::HttpRequest &req); /// Settings UI for theme colors + logo upload (returns JSON form descriptor) json_object* SettingsTheme(const int tid, libhttppp::HttpRequest &req, const std::string &sessionid); /// Get the logo URL (uploaded or theme default) std::string getLogoUrl(const int tid) const; private: void ensureMediaClient(const int tid, const std::string &token = ""); ThemeConfig _Config; std::vector<ThemeFiles> _PublicFiles; bool _Formated; mutable std::vector<std::unique_ptr<mediadb::Client>> _MdbClients; }; };