Loading src/blogi.cpp +143 −0 Original line number Diff line number Diff line Loading @@ -656,6 +656,105 @@ static void renderSettingsJson(json_object *jroot, libhtmlpp::HtmlString &out, c out << "</select>"; }else if(strcmp(type,"file")==0){ out << "<input class=\"set-input\" type=\"file\" name=\"" << name << "\" />"; }else if(strcmp(type,"media_url")==0){ // Media URL browser: hidden input + preview + Browse button + modal std::string mid = std::string("murl_") + name; out << "<div style=\"display:flex;align-items:center;gap:10px;flex-wrap:wrap;\">"; out << "<input type=\"hidden\" id=\"" << mid << "\" name=\"" << name << "\" value=\"" << value << "\" />"; if(value && value[0] != '\0'){ out << "<img id=\"" << mid << "_prev\" src=\"" << value << "\" style=\"max-height:60px;max-width:200px;border-radius:4px;border:1px solid #555;\" />"; } else { out << "<img id=\"" << mid << "_prev\" style=\"display:none;max-height:60px;max-width:200px;border-radius:4px;border:1px solid #555;\" />"; } out << "<button type=\"button\" class=\"set-btn\" onclick=\"openSettingsMediaBrowser('" << mid << "')\">Browse…</button>"; out << "<button type=\"button\" class=\"set-btn\" style=\"font-size:0.85em;\" onclick=\"document.getElementById('" << mid << "').value='';var p=document.getElementById('" << mid << "_prev');p.style.display='none';p.src='';\">Clear</button>"; out << "</div>"; // Emit the media browser modal + logic once out << "<script>" << "if(!window._settingsMediaBrowserReady){" << "window._settingsMediaBrowserReady=true;" // Modal overlay << "var mo=document.createElement('div');mo.id='smb-overlay';" << "mo.style.cssText='display:none;position:fixed;top:0;left:0;width:100%;height:100%;background:rgba(0,0,0,0.6);z-index:9999;justify-content:center;align-items:center;';" << "var md=document.createElement('div');md.style.cssText='background:var(--blogi-surface,#31363b);border-radius:8px;width:620px;max-height:80vh;display:flex;flex-direction:column;overflow:hidden;box-shadow:0 8px 32px rgba(0,0,0,0.5);';" // Header << "var hdr=document.createElement('div');hdr.style.cssText='display:flex;justify-content:space-between;align-items:center;padding:12px 16px;border-bottom:1px solid var(--blogi-border,#3a3f44);';" << "var ht=document.createElement('span');ht.id='smb-title';ht.textContent='Select Image';ht.style.cssText='font-weight:600;color:var(--blogi-text,#eee);';" << "var hx=document.createElement('button');hx.textContent='\\u00d7';hx.style.cssText='background:none;border:none;font-size:1.4em;color:var(--blogi-text-muted,#aaa);cursor:pointer;';" << "hx.onclick=function(){mo.style.display='none';};" << "hdr.appendChild(ht);hdr.appendChild(hx);md.appendChild(hdr);" // Content area << "var ct=document.createElement('div');ct.id='smb-content';ct.style.cssText='padding:12px 16px;overflow-y:auto;flex:1;';" << "md.appendChild(ct);mo.appendChild(md);document.body.appendChild(mo);" // State << "var _smbTarget='';" // Fetch helper << "function smbFetch(url,cb){" << "fetch(url,{credentials:'same-origin'}).then(function(r){return r.json();}).then(cb).catch(function(e){ct.textContent='Error: '+e;});" << "}" // Open browser << "window.openSettingsMediaBrowser=function(tid){" << "_smbTarget=tid;" << "mo.style.display='flex';" << "ct.innerHTML='<em>Loading albums…</em>';" << "ht.textContent='Select Album';" << "smbFetch('settings/api/media/albums',function(d){" << "ct.innerHTML='';" << "if(!d.albums||!d.albums.length){ct.textContent='No albums found.';return;}" << "d.albums.forEach(function(a){" << "var b=document.createElement('div');" << "b.style.cssText='padding:8px 12px;margin:4px 0;background:var(--blogi-bg-alt,#2a2e32);border-radius:4px;cursor:pointer;color:var(--blogi-text,#eee);';" << "b.textContent=a.name+' ('+a.media_count+')';" << "b.onmouseover=function(){b.style.background='var(--blogi-accent,#3ad43a)';b.style.color='#000';};" << "b.onmouseout=function(){b.style.background='var(--blogi-bg-alt,#2a2e32)';b.style.color='var(--blogi-text,#eee)';};" << "b.onclick=function(){smbLoadAlbum(a.id,a.name);};" << "ct.appendChild(b);" << "});" << "});" << "};" // Load album images << "function smbLoadAlbum(aid,aname){" << "ct.innerHTML='<em>Loading…</em>';" << "ht.textContent=aname||'Select Image';" << "smbFetch('settings/api/media/images?album_id='+encodeURIComponent(aid),function(d){" << "ct.innerHTML='';" // Back button << "var bk=document.createElement('div');" << "bk.textContent='\\u2190 Back to albums';" << "bk.style.cssText='cursor:pointer;color:var(--blogi-accent,#3ad43a);margin-bottom:10px;font-size:0.9em;';" << "bk.onclick=function(){openSettingsMediaBrowser(_smbTarget);};" << "ct.appendChild(bk);" << "if(!d.media||!d.media.length){var em=document.createElement('p');em.textContent='No images in this album.';ct.appendChild(em);return;}" // Grid << "var g=document.createElement('div');" << "g.style.cssText='display:grid;grid-template-columns:repeat(auto-fill,minmax(100px,1fr));gap:8px;';" << "d.media.forEach(function(m){" << "var c=document.createElement('div');" << "c.style.cssText='cursor:pointer;border-radius:4px;overflow:hidden;border:2px solid transparent;aspect-ratio:1;display:flex;align-items:center;justify-content:center;background:var(--blogi-bg-alt,#2a2e32);';" << "if(m.kind==='image'){" << "var im=document.createElement('img');" << "im.src=m.url+'?w=100&h=100';im.style.cssText='max-width:100%;max-height:100%;object-fit:cover;';" << "c.appendChild(im);" << "}else{" << "var sp=document.createElement('span');sp.textContent=m.filename;sp.style.cssText='font-size:0.75em;color:var(--blogi-text-muted,#aaa);text-align:center;padding:4px;word-break:break-all;';" << "c.appendChild(sp);" << "}" << "c.onmouseover=function(){c.style.borderColor='var(--blogi-accent,#3ad43a)';};" << "c.onmouseout=function(){c.style.borderColor='transparent';};" << "c.onclick=function(){" << "var hd=document.getElementById(_smbTarget);" << "hd.value=m.url;" << "var pv=document.getElementById(_smbTarget+'_prev');" << "pv.src=m.url;pv.style.display='inline-block';" << "mo.style.display='none';" << "};" << "g.appendChild(c);" << "});" << "ct.appendChild(g);" << "});" << "}" << "}" << "</script>"; }else if(strcmp(type,"color")==0){ // Custom RGBA color picker std::string uid = std::string("clr_") + name; Loading Loading @@ -1099,6 +1198,50 @@ void blogi::Blogi::settingsApi(libhttppp::HttpRequest& curreq,const int tid,cons } json_object_object_add(jresponse,"success",json_object_new_boolean(1)); json_object_object_add(jresponse,"nav",jnav); }else if(apipath == "media/albums" || apipath == "media/images"){ // Proxy media commands to the media plugin via its JsonApi std::string authid; try { Session sess(*PlgArgs->database[tid]); sess.getSessionData(sessiondid, "authid", authid); } catch(...) {} json_object *jreq = json_object_new_object(); json_object *jresp = json_object_new_object(); if(apipath == "media/albums"){ json_object_object_add(jreq, "command", json_object_new_string("media_list_albums")); } else { json_object_object_add(jreq, "command", json_object_new_string("media_list_media")); // Parse album_id from query string std::string url = curreq.getRequestURL(); size_t qp = url.find('?'); if(qp != std::string::npos){ std::string qs = url.substr(qp + 1); size_t ep = qs.find("album_id="); if(ep != std::string::npos){ std::string aid = qs.substr(ep + 9); size_t amp = aid.find('&'); if(amp != std::string::npos) aid = aid.substr(0, amp); json_object_object_add(jreq, "album_id", json_object_new_string(aid.c_str())); } } } uuid::uuid uid(authid.c_str()); for(const blogi::Plugin::PluginData *curplg=BlogiPlg->getFirstPlugin(); curplg; curplg=curplg->getNextPlg()){ curplg->getInstace()->JsonApi(tid, jreq, jresp, uid); if(json_object_object_get(jresp, "status") != nullptr) break; } json_object_object_add(jresponse,"success",json_object_new_boolean(1)); // Merge plugin response into our response json_object_object_foreach(jresp, k, v){ json_object_object_add(jresponse, k, json_object_get(v)); } json_object_put(jreq); json_object_put(jresp); }else{ json_object_object_add(jresponse,"success",json_object_new_boolean(0)); json_object_object_add(jresponse,"error",json_object_new_string("unknown api endpoint")); Loading src/theme.cpp +2 −2 Original line number Diff line number Diff line Loading @@ -724,8 +724,8 @@ json_object* blogi::Theme::SettingsTheme(const int tid, libhttppp::HttpRequest & json_object *jf = json_object_new_object(); json_object_object_add(jf, "name", json_object_new_string("logo_url")); json_object_object_add(jf, "type", json_object_new_string("text")); json_object_object_add(jf, "label", json_object_new_string("Logo URL (e.g. /media/getimage/UUID.webp) — leave empty for theme default")); json_object_object_add(jf, "type", json_object_new_string("media_url")); json_object_object_add(jf, "label", json_object_new_string("Logo Image — leave empty for theme default")); json_object_object_add(jf, "value", json_object_new_string(curUrl.c_str())); json_object_array_add(jfields, jf); } Loading Loading
src/blogi.cpp +143 −0 Original line number Diff line number Diff line Loading @@ -656,6 +656,105 @@ static void renderSettingsJson(json_object *jroot, libhtmlpp::HtmlString &out, c out << "</select>"; }else if(strcmp(type,"file")==0){ out << "<input class=\"set-input\" type=\"file\" name=\"" << name << "\" />"; }else if(strcmp(type,"media_url")==0){ // Media URL browser: hidden input + preview + Browse button + modal std::string mid = std::string("murl_") + name; out << "<div style=\"display:flex;align-items:center;gap:10px;flex-wrap:wrap;\">"; out << "<input type=\"hidden\" id=\"" << mid << "\" name=\"" << name << "\" value=\"" << value << "\" />"; if(value && value[0] != '\0'){ out << "<img id=\"" << mid << "_prev\" src=\"" << value << "\" style=\"max-height:60px;max-width:200px;border-radius:4px;border:1px solid #555;\" />"; } else { out << "<img id=\"" << mid << "_prev\" style=\"display:none;max-height:60px;max-width:200px;border-radius:4px;border:1px solid #555;\" />"; } out << "<button type=\"button\" class=\"set-btn\" onclick=\"openSettingsMediaBrowser('" << mid << "')\">Browse…</button>"; out << "<button type=\"button\" class=\"set-btn\" style=\"font-size:0.85em;\" onclick=\"document.getElementById('" << mid << "').value='';var p=document.getElementById('" << mid << "_prev');p.style.display='none';p.src='';\">Clear</button>"; out << "</div>"; // Emit the media browser modal + logic once out << "<script>" << "if(!window._settingsMediaBrowserReady){" << "window._settingsMediaBrowserReady=true;" // Modal overlay << "var mo=document.createElement('div');mo.id='smb-overlay';" << "mo.style.cssText='display:none;position:fixed;top:0;left:0;width:100%;height:100%;background:rgba(0,0,0,0.6);z-index:9999;justify-content:center;align-items:center;';" << "var md=document.createElement('div');md.style.cssText='background:var(--blogi-surface,#31363b);border-radius:8px;width:620px;max-height:80vh;display:flex;flex-direction:column;overflow:hidden;box-shadow:0 8px 32px rgba(0,0,0,0.5);';" // Header << "var hdr=document.createElement('div');hdr.style.cssText='display:flex;justify-content:space-between;align-items:center;padding:12px 16px;border-bottom:1px solid var(--blogi-border,#3a3f44);';" << "var ht=document.createElement('span');ht.id='smb-title';ht.textContent='Select Image';ht.style.cssText='font-weight:600;color:var(--blogi-text,#eee);';" << "var hx=document.createElement('button');hx.textContent='\\u00d7';hx.style.cssText='background:none;border:none;font-size:1.4em;color:var(--blogi-text-muted,#aaa);cursor:pointer;';" << "hx.onclick=function(){mo.style.display='none';};" << "hdr.appendChild(ht);hdr.appendChild(hx);md.appendChild(hdr);" // Content area << "var ct=document.createElement('div');ct.id='smb-content';ct.style.cssText='padding:12px 16px;overflow-y:auto;flex:1;';" << "md.appendChild(ct);mo.appendChild(md);document.body.appendChild(mo);" // State << "var _smbTarget='';" // Fetch helper << "function smbFetch(url,cb){" << "fetch(url,{credentials:'same-origin'}).then(function(r){return r.json();}).then(cb).catch(function(e){ct.textContent='Error: '+e;});" << "}" // Open browser << "window.openSettingsMediaBrowser=function(tid){" << "_smbTarget=tid;" << "mo.style.display='flex';" << "ct.innerHTML='<em>Loading albums…</em>';" << "ht.textContent='Select Album';" << "smbFetch('settings/api/media/albums',function(d){" << "ct.innerHTML='';" << "if(!d.albums||!d.albums.length){ct.textContent='No albums found.';return;}" << "d.albums.forEach(function(a){" << "var b=document.createElement('div');" << "b.style.cssText='padding:8px 12px;margin:4px 0;background:var(--blogi-bg-alt,#2a2e32);border-radius:4px;cursor:pointer;color:var(--blogi-text,#eee);';" << "b.textContent=a.name+' ('+a.media_count+')';" << "b.onmouseover=function(){b.style.background='var(--blogi-accent,#3ad43a)';b.style.color='#000';};" << "b.onmouseout=function(){b.style.background='var(--blogi-bg-alt,#2a2e32)';b.style.color='var(--blogi-text,#eee)';};" << "b.onclick=function(){smbLoadAlbum(a.id,a.name);};" << "ct.appendChild(b);" << "});" << "});" << "};" // Load album images << "function smbLoadAlbum(aid,aname){" << "ct.innerHTML='<em>Loading…</em>';" << "ht.textContent=aname||'Select Image';" << "smbFetch('settings/api/media/images?album_id='+encodeURIComponent(aid),function(d){" << "ct.innerHTML='';" // Back button << "var bk=document.createElement('div');" << "bk.textContent='\\u2190 Back to albums';" << "bk.style.cssText='cursor:pointer;color:var(--blogi-accent,#3ad43a);margin-bottom:10px;font-size:0.9em;';" << "bk.onclick=function(){openSettingsMediaBrowser(_smbTarget);};" << "ct.appendChild(bk);" << "if(!d.media||!d.media.length){var em=document.createElement('p');em.textContent='No images in this album.';ct.appendChild(em);return;}" // Grid << "var g=document.createElement('div');" << "g.style.cssText='display:grid;grid-template-columns:repeat(auto-fill,minmax(100px,1fr));gap:8px;';" << "d.media.forEach(function(m){" << "var c=document.createElement('div');" << "c.style.cssText='cursor:pointer;border-radius:4px;overflow:hidden;border:2px solid transparent;aspect-ratio:1;display:flex;align-items:center;justify-content:center;background:var(--blogi-bg-alt,#2a2e32);';" << "if(m.kind==='image'){" << "var im=document.createElement('img');" << "im.src=m.url+'?w=100&h=100';im.style.cssText='max-width:100%;max-height:100%;object-fit:cover;';" << "c.appendChild(im);" << "}else{" << "var sp=document.createElement('span');sp.textContent=m.filename;sp.style.cssText='font-size:0.75em;color:var(--blogi-text-muted,#aaa);text-align:center;padding:4px;word-break:break-all;';" << "c.appendChild(sp);" << "}" << "c.onmouseover=function(){c.style.borderColor='var(--blogi-accent,#3ad43a)';};" << "c.onmouseout=function(){c.style.borderColor='transparent';};" << "c.onclick=function(){" << "var hd=document.getElementById(_smbTarget);" << "hd.value=m.url;" << "var pv=document.getElementById(_smbTarget+'_prev');" << "pv.src=m.url;pv.style.display='inline-block';" << "mo.style.display='none';" << "};" << "g.appendChild(c);" << "});" << "ct.appendChild(g);" << "});" << "}" << "}" << "</script>"; }else if(strcmp(type,"color")==0){ // Custom RGBA color picker std::string uid = std::string("clr_") + name; Loading Loading @@ -1099,6 +1198,50 @@ void blogi::Blogi::settingsApi(libhttppp::HttpRequest& curreq,const int tid,cons } json_object_object_add(jresponse,"success",json_object_new_boolean(1)); json_object_object_add(jresponse,"nav",jnav); }else if(apipath == "media/albums" || apipath == "media/images"){ // Proxy media commands to the media plugin via its JsonApi std::string authid; try { Session sess(*PlgArgs->database[tid]); sess.getSessionData(sessiondid, "authid", authid); } catch(...) {} json_object *jreq = json_object_new_object(); json_object *jresp = json_object_new_object(); if(apipath == "media/albums"){ json_object_object_add(jreq, "command", json_object_new_string("media_list_albums")); } else { json_object_object_add(jreq, "command", json_object_new_string("media_list_media")); // Parse album_id from query string std::string url = curreq.getRequestURL(); size_t qp = url.find('?'); if(qp != std::string::npos){ std::string qs = url.substr(qp + 1); size_t ep = qs.find("album_id="); if(ep != std::string::npos){ std::string aid = qs.substr(ep + 9); size_t amp = aid.find('&'); if(amp != std::string::npos) aid = aid.substr(0, amp); json_object_object_add(jreq, "album_id", json_object_new_string(aid.c_str())); } } } uuid::uuid uid(authid.c_str()); for(const blogi::Plugin::PluginData *curplg=BlogiPlg->getFirstPlugin(); curplg; curplg=curplg->getNextPlg()){ curplg->getInstace()->JsonApi(tid, jreq, jresp, uid); if(json_object_object_get(jresp, "status") != nullptr) break; } json_object_object_add(jresponse,"success",json_object_new_boolean(1)); // Merge plugin response into our response json_object_object_foreach(jresp, k, v){ json_object_object_add(jresponse, k, json_object_get(v)); } json_object_put(jreq); json_object_put(jresp); }else{ json_object_object_add(jresponse,"success",json_object_new_boolean(0)); json_object_object_add(jresponse,"error",json_object_new_string("unknown api endpoint")); Loading
src/theme.cpp +2 −2 Original line number Diff line number Diff line Loading @@ -724,8 +724,8 @@ json_object* blogi::Theme::SettingsTheme(const int tid, libhttppp::HttpRequest & json_object *jf = json_object_new_object(); json_object_object_add(jf, "name", json_object_new_string("logo_url")); json_object_object_add(jf, "type", json_object_new_string("text")); json_object_object_add(jf, "label", json_object_new_string("Logo URL (e.g. /media/getimage/UUID.webp) — leave empty for theme default")); json_object_object_add(jf, "type", json_object_new_string("media_url")); json_object_object_add(jf, "label", json_object_new_string("Logo Image — leave empty for theme default")); json_object_object_add(jf, "value", json_object_new_string(curUrl.c_str())); json_object_array_add(jfields, jf); } Loading