diff --git a/README.md b/README.md index 73e4911..359e279 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,8 @@ XRForge is a tiny, powerful tool to interconnect XR experiences via 3D portals...like a federated viverse.com

-An open, seamless XR web built from simple URLs and directories, without gatekeepers.
+An open, seamless XR web built from simple URLs and directories, without gatekeepers, built upon Next Generation Internet (NGI) specs:

+
diff --git a/src/.init.lua b/src/.init.lua index 0dd58f4..8ac293b 100644 --- a/src/.init.lua +++ b/src/.init.lua @@ -14,8 +14,6 @@ HidePath('/usr/share/ssl/') -- create app = require("soakbean") { - bin = "./xrforge.com", - bindir = arg[-1]:gsub("/[a-zA-Z0-9-_ ]+.com$","/"), cache = { items = {}, url = {} }, plugin = {}, opts = {}, @@ -62,11 +60,13 @@ app.plugin.moderation = app.addPlugin('plugin/moderation') app.plugin.xrfragments = app.addPlugin('plugin/xrfragments') app.plugin.janusxr = app.addPlugin('plugin/janusxr') app.plugin.clouddrive = app.addPlugin('plugin/clouddrive') +app.plugin.markdown = app.addPlugin('plugin/markdown') +app.plugin.rssatom = app.addPlugin('plugin/rssatom') app.plugin.federation = app.addPlugin('plugin/federation') app.plugin.manyfold = app.addPlugin('plugin/manyfold') +app.plugin.peertube = app.addPlugin('plugin/peertube') app.plugin.git = app.addPlugin('plugin/git') app.plugin.webhook = app.addPlugin('plugin/webhook') -app.plugin.markdown = app.addPlugin('plugin/markdown') app.plugin.staticwebgen = app.addPlugin('plugin/staticwebgen') -- should always be last app.runcmd(app) -- run clicmds @@ -92,7 +92,11 @@ app.post('^/save', function(req,res,next) -- setup inline POST endpoint end) app -- -.use( require("middleware/httpauth")() ) +.use( require("middleware/httpauth")({ + user = app.opts.admin_user, + pass = app.opts.admin_pass, + path = "^/admin" +})) .use( require("json").middleware() ) -- try plug'n'play json API middleware .use( require('page/dir').middleware ) -- directory browser .use( app.router( app.url ) ) -- try url router diff --git a/src/.lua/config.lua b/src/.lua/config.lua index 87b5fd1..df9874f 100644 --- a/src/.lua/config.lua +++ b/src/.lua/config.lua @@ -7,6 +7,8 @@ return function(app) baseurl="/", admin_title = "admin panel", admin_subtitle = "Manage your XR experiences here", + admin_user = "admin", + admin_pass = "admin" } local configfile = "xrforge.lua" diff --git a/src/.lua/soakbean.lua b/src/.lua/soakbean.lua index 5d15066..3ce5672 100644 --- a/src/.lua/soakbean.lua +++ b/src/.lua/soakbean.lua @@ -181,7 +181,8 @@ sb = { run = function(req) local next = function() end local k = 0 - local req = { + local internal = req + req = req or { param={}, method=GetMethod(), host=GetHost(), @@ -195,7 +196,7 @@ sb = { _header={}, _body="" } - local params = GetParams() + local params = internal and {} or GetParams() if params ~= nil then for i,p in pairs(params) do req.param[ p[1] ] = p[2] end end @@ -238,6 +239,7 @@ sb = { response = function() return function(req,res,next) + sb.pub('response', {req=req, res=res, next=next}) if res._status ~= nil then SetStatus(res._status) for k,v in pairs(res._header) do @@ -293,7 +295,7 @@ sb = { end print("\nUsage: " .. app.bin .. " [opts]\n\n") for k,v in pairs(app.cmd) do - print("\t" .. app.bin .. " " .. k .. "\t\t" .. v.info ) + print( string.format("\t%s %-15s %s", app.bin, k, v.info ) ) end print("") os.exit() @@ -304,6 +306,8 @@ sb = { return function(data) local app = {} setmetatable(app,sb) + sb.bin = arg[-1]:gsub(".*/","") + sb.bindir = arg[-1]:gsub("/[a-zA-Z0-9-_ ]+.com$","/") sb.app = app sb.data = data sb.data.url = {} diff --git a/src/.lua/util.lua b/src/.lua/util.lua index 174b2e9..4a41541 100644 --- a/src/.lua/util.lua +++ b/src/.lua/util.lua @@ -96,6 +96,8 @@ util.template = function(content,data) mdata.url = mdata.url or util.getURL() mdata.title = mdata.title or app.opts.subtitle mdata.header_include = mdata.header_include or '' + mdata.footerlinks = {} + app.pub('util.template', nil, mdata) mdata.header = app.tpl( LoadAsset('page/header.html'), mdata ) if not mdata.janusxr then mdata.janusxr = app.tpl( LoadAsset('page/janusxr.html'), mdata ) @@ -328,5 +330,21 @@ function util.stripPath(str) return str:gsub("^./",""):gsub("^/",""):gsub("/$","") end +-- WARNING: this is a destructive function which renders the redbean server inactive +-- only to be used in tests or staticwebgen plugin +util.queueServerRequest = function(method, path, params, host, headers, body, scheme, port) + GetMethod = function() return method end + GetPath = function() return path end + GetParams = function() return params or {} end -- note: { {"key","value"} } format + GetHost = function() return host or 'localhost' end + GetHeaders = function() return headers or {} end + GetScheme = function() return scheme or 'https' end + GetBody = function() return body or '' end + GetPort = function() return port or 80 end + SetStatus = function() end + Write = function() end + SetHeader = function() end + Route = function() end +end return util diff --git a/src/cmd/generate.lua b/src/cmd/generate.lua index 8d065f0..510032e 100644 --- a/src/cmd/generate.lua +++ b/src/cmd/generate.lua @@ -2,6 +2,4 @@ return function(app,argv) print('generating..') -- do nothing here, as this is handled by the src/plugin/staticwebgen.lua -- AFTER all plugins are loaded - print('TODO: disabled for now..not stable') - os.exit() end diff --git a/src/cmd/help.lua b/src/cmd/help.lua index 1f3076a..443eac3 100644 --- a/src/cmd/help.lua +++ b/src/cmd/help.lua @@ -7,7 +7,7 @@ return function(app,argv) io.write("Flags:\n\n") foreach( app.opts, function(k,v) if not k:match("/") and not k:match("^help") and not k:match("updated") then - io.write( " " .. k .. "='" .. v .. "'\n") + io.write( " " .. k .. "='" .. v:gsub("\n","") .. "'\n") end end) io.write("\n> NOTE: flags override flags in xrforge.lua configfile]\n") diff --git a/src/cmd/server.lua b/src/cmd/server.lua index e815841..ad8501f 100644 --- a/src/cmd/server.lua +++ b/src/cmd/server.lua @@ -4,6 +4,7 @@ return function(app,argv) print("starting server") if not util.contains(arg,"--nobrowser") then if arg[1] == 'admin' then + print("ℹ️ admin login/pass is " .. app.opts.admin_user .. "/" .. app.opts.admin_pass) LaunchBrowser('/admin') else LaunchBrowser('/') diff --git a/src/css/xrforge.max.css b/src/css/xrforge.max.css index 8c38e4c..76f78a4 100644 --- a/src/css/xrforge.max.css +++ b/src/css/xrforge.max.css @@ -54,11 +54,15 @@ body{ font-size:14px; } +img.icon.invert, +img.icon.link, +img.icon.play, +img.icon.download, img.icon.list-tree{ filter: invert(1); } -img.icon.play{ - filter: invert(1); +img.icon.download, +img.icon.play,{ transform: translate(0,-1px); } diff --git a/src/img/download.svg b/src/img/download.svg new file mode 100644 index 0000000..7a45f84 --- /dev/null +++ b/src/img/download.svg @@ -0,0 +1,16 @@ + + + + \ No newline at end of file diff --git a/src/img/drive.svg b/src/img/drive.svg new file mode 100644 index 0000000..1cc3be1 --- /dev/null +++ b/src/img/drive.svg @@ -0,0 +1,22 @@ + + + + + \ No newline at end of file diff --git a/src/img/link.svg b/src/img/link.svg new file mode 100644 index 0000000..f8786a6 --- /dev/null +++ b/src/img/link.svg @@ -0,0 +1,20 @@ + + + + + \ No newline at end of file diff --git a/src/myverse/janusxr/helloworld/janusxr.xml b/src/myverse/janusxr/helloworld/janusxr.xml deleted file mode 100644 index b5018a9..0000000 --- a/src/myverse/janusxr/helloworld/janusxr.xml +++ /dev/null @@ -1,5 +0,0 @@ -world 1 title - - - - diff --git a/src/myverse/janusxr.png b/src/myverse/janusxr_tutorial.png similarity index 100% rename from src/myverse/janusxr.png rename to src/myverse/janusxr_tutorial.png diff --git a/src/myverse/janusxr_tutorial/AFRAME.jpg b/src/myverse/janusxr_tutorial/AFRAME.jpg new file mode 100644 index 0000000..272b50c Binary files /dev/null and b/src/myverse/janusxr_tutorial/AFRAME.jpg differ diff --git a/src/myverse/janusxr_tutorial/AFRAME/README.md b/src/myverse/janusxr_tutorial/AFRAME/README.md new file mode 100644 index 0000000..ed5b01a --- /dev/null +++ b/src/myverse/janusxr_tutorial/AFRAME/README.md @@ -0,0 +1,6 @@ +# AFRAME hello world + +[XRForge's](https://xrforge.isvery.ninja/) [JanusXR](https://coderofsalvation.github.io/janus-guide/)-viewer ([janusweb](https://github.com/jbaicoianu/janusweb)) can detect [AFRAME](https://aframe.io) markup in HTML/XML/even binary files(!). +This basically extends [the seamless JanusXR portalsystem](https://coderofsalvation.github.io/janus-guide/#/home/about) by interconnecting otherwise immersively-disjointed ecosystems like [AFRAME](https://aframe.io), by [reprojecting them in 3D](https://coderofsalvation.github.io/janus-guide/#/home/about). + +Currently only geometry markup and models are supported. diff --git a/src/myverse/janusxr_tutorial/AFRAME/index.html b/src/myverse/janusxr_tutorial/AFRAME/index.html new file mode 100644 index 0000000..f94228c --- /dev/null +++ b/src/myverse/janusxr_tutorial/AFRAME/index.html @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/src/myverse/janusxr_tutorial/AFRAME/tree2.glb b/src/myverse/janusxr_tutorial/AFRAME/tree2.glb new file mode 100644 index 0000000..86f7a6e Binary files /dev/null and b/src/myverse/janusxr_tutorial/AFRAME/tree2.glb differ diff --git a/src/myverse/janusxr_tutorial/helloworld.jpg b/src/myverse/janusxr_tutorial/helloworld.jpg new file mode 100644 index 0000000..5c50ad1 Binary files /dev/null and b/src/myverse/janusxr_tutorial/helloworld.jpg differ diff --git a/src/myverse/janusxr_tutorial/helloworld/README.md b/src/myverse/janusxr_tutorial/helloworld/README.md new file mode 100644 index 0000000..72de5f2 --- /dev/null +++ b/src/myverse/janusxr_tutorial/helloworld/README.md @@ -0,0 +1,5 @@ +# JanusXR JML + +[XRForge's](https://xrforge.isvery.ninja/) uses a [JanusXR](https://coderofsalvation.github.io/janus-guide/)-viewer called ([janusweb](https://github.com/jbaicoianu/janusweb)). + +> JanusWeb supports various URLs, containing [JML](https://coderofsalvation.github.io/janus-guide/#/build/room), [XRFragments](https://xrfragment.org), RSS/Atom, HTML, and so on..basically an in-browser VR browser diff --git a/src/myverse/janusxr_tutorial/helloworld/janusxr.xml b/src/myverse/janusxr_tutorial/helloworld/janusxr.xml new file mode 100644 index 0000000..125930b --- /dev/null +++ b/src/myverse/janusxr_tutorial/helloworld/janusxr.xml @@ -0,0 +1,10 @@ +JanusXR hello world + + + + + + + + + diff --git a/src/myverse/janusxr/tronverse b/src/myverse/janusxr_tutorial/tronverse similarity index 100% rename from src/myverse/janusxr/tronverse rename to src/myverse/janusxr_tutorial/tronverse diff --git a/src/myverse/janusxr/tronverse.png b/src/myverse/janusxr_tutorial/tronverse.png similarity index 100% rename from src/myverse/janusxr/tronverse.png rename to src/myverse/janusxr_tutorial/tronverse.png diff --git a/src/myverse/tronverse/tron/README.md b/src/myverse/tronverse/tron/README.md index ac93bdd..171ef65 100644 --- a/src/myverse/tronverse/tron/README.md +++ b/src/myverse/tronverse/tron/README.md @@ -1,7 +1,8 @@ # Tron World 1 -This is a test +This is a glTF world which is only 65 kilobytes in size.
+Say what?
+
+> Fitting an entire virtual world into 65 KB is like trying to condense the whole Marvel Cinematic Universe into a single tweet. It’s the digital equivalent of trying to build a full-scale, functioning Disneyland inside a hollowed-out peanut shell. -> This is a blockquote - -And [this](https://xrfragment.org) is an URL. +Read more about [best practices at xrfragment.org](https://xrfragment.org/#Best%20practices) diff --git a/src/myverse/tronverse/tron/tron.glb b/src/myverse/tronverse/tron/tron.glb index d60f133..0446ff4 100644 Binary files a/src/myverse/tronverse/tron/tron.glb and b/src/myverse/tronverse/tron/tron.glb differ diff --git a/src/myverse/x3d/test_world.x3d b/src/myverse/x3d/test_world.x3d deleted file mode 100644 index 2861716..0000000 --- a/src/myverse/x3d/test_world.x3d +++ /dev/null @@ -1,52 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/myverse/x3d/test_world.x3d.glb b/src/myverse/x3d/test_world.x3d.glb deleted file mode 100644 index 8097701..0000000 Binary files a/src/myverse/x3d/test_world.x3d.glb and /dev/null differ diff --git a/src/page/about.html b/src/page/about.html index 01be761..1e92259 100644 --- a/src/page/about.html +++ b/src/page/about.html @@ -6,7 +6,7 @@ header h2{ font-weight: normal; } header{ - background-image: linear-gradient(0deg,#0000,#101,#0000), url(/myverse/tronverse/tron/tron.png); + background-image: linear-gradient(0deg,#0000,#101,#0000), url(myverse/tronverse/tron/tron.png); background-position-x: 0%; background-position-y: 0%; background-repeat: repeat; @@ -20,7 +20,7 @@ header{ div#hint{ float:right; - background: url(/img/blinkhint.svg); + background: url(img/blinkhint.svg); height: 129px; background-size: cover; display:inline-block; @@ -28,7 +28,7 @@ div#hint{ } div#badges{ - background: url(/img/badges.png); + background: url(img/badges.png); height: 90px; background-repeat:no-repeat; background-size: contain; @@ -39,7 +39,7 @@ div#badges{ } div#xrecosystem{ - background: #000 url(/img/xrecosystem.png); + background: #000 url(img/xrecosystem.png); background-repeat: no-repeat; height: 200px; background-size: cover; @@ -67,7 +67,7 @@ div#xrecosystem{
- + Check the sourcecode
docker run codeforge.org/coderofsalvation/xrforge:latest
@@ -79,7 +79,7 @@ div#xrecosystem{
- + chat with community
irc.isvery.ninja port 443 via ObsidianIRC
@@ -347,7 +347,7 @@ div#xrecosystem{

JanusXR: The deep spatial web (within the web)

diff --git a/src/page/admin/index.lua b/src/page/admin/index.lua index 93f6a83..ddc2bd1 100644 --- a/src/page/admin/index.lua +++ b/src/page/admin/index.lua @@ -66,7 +66,7 @@ end) function pluginUI(seen,data) return function(name,plugin) if plugin.hide or seen[ plugin.name ] then return end -- already processed - table.insert( data.tabs, plugin.name ) -- insert tabname + table.insert( data.tabs, plugin.tab or plugin.name ) -- insert tabname seen[ plugin.name ] = true local plugindata = {plugin=plugin} plugindata.opts = "" @@ -88,6 +88,7 @@ function saveData() end end) if saveconfig then + app.set('app.opts', app.opts) data.notification = '
updated
' else data.notification = '
no changes detected
' diff --git a/src/page/dir.html b/src/page/dir.html index 40238a3..029b863 100644 --- a/src/page/dir.html +++ b/src/page/dir.html @@ -23,41 +23,37 @@ end} local item = items[i] local icon = 'list-tree' local thumbnail = '' - if #item.thumbnail > 0 then thumbnail = 'style="background-image: url(' .. EscapeFragment(item.thumbnail) .. ');"' end + if #item.thumbnail > 0 then thumbnail = 'style="background-image: url(' .. EscapeFragment( util.stripPath(item.thumbnail) ) .. ');"' end if item.view3D or item.portal3D == false then icon = 'play' end print([[ ]]) end end} - {if parent.parent == nil then - for i=1,5 do - local urls = app.opts[ 'plugin_federation_url_' .. i] - if #urls > 0 then - local title = urls:gsub(' .*','') - local fediurl = '/?url=' .. EscapeFragment(title) - local thumburl = urls:gsub('.* ','') - local icon = 'list-tree' - local thumbnail = '' - if #thumburl > 0 then thumbnail = 'style="background-image: url(' .. EscapeFragment(thumburl) .. ');"' end - print([[ - - ]]) - end + {if itemsremote then + for i=1,#itemsremote do + local item = itemsremote[i] + local icon = 'link' + local thumbnail = '' + if #item.thumbnail > 0 then thumbnail = 'style="background-image: url(' .. EscapeFragment( util.stripPath(item.thumbnail) ) .. ');"' end + if item.view3D or item.portal3D == false then icon = 'play' end + print([[ + + ]]) end end}
diff --git a/src/page/dir.lua b/src/page/dir.lua index 132d2bd..d777100 100644 --- a/src/page/dir.lua +++ b/src/page/dir.lua @@ -6,17 +6,17 @@ return { middleware = function(req,res,next) local rootpath = "/" .. app.opts.plugin_diskscan_dirname - local urlpath = GetPath() - res.items = {} - res.template = res.template or 'page/dir.html' + local urlpath = req.url + local isroot = urlpath == "/" or urlpath:match("^"..rootpath.."[/]?$") + res.items = {} + res.template = res.template or 'page/dir.html' local isdir = urlpath:sub(-1) == "/" local nslashes = util.countChar('/',urlpath) local match = function(a,b) return util.stripPath(a) == util.stripPath(b) end local urlhash = urlpath .. util.dump( req.param ) - if urlpath == "/" - or urlpath:match("^"..rootpath) - and not urlpath:gsub(".*/",""):match("%.") + if (isroot or urlpath:match("^"..rootpath)) + and not urlpath:gsub(".*/",""):match("%.") -- no regular file with extension and not req.param.url then local redirect = false @@ -26,10 +26,10 @@ return { foreach( app.cache.items, function(k,v) if req.authenticated or app.plugin.moderation.isAllowed(v) then if v.parent then - trace( "path = " .. util.stripPath(v.path) .. " " .. - "parent = " .. util.stripPath(v.parent.path) .. " " .. - "matchpath = " .. util.stripPath(matchpath) - ) +-- trace( "path = " .. util.stripPath(v.path) .. " " .. +-- "parent = " .. util.stripPath(v.parent.path) .. " " .. +-- "matchpath = " .. util.stripPath(matchpath) +-- ) if match(v.path, matchpath) then -- show parent folder if any local parent = app.cache.items[ v.parent.id ] if v.portal then @@ -45,7 +45,6 @@ return { -- add items from dir if match(v.parent.path, matchpath) then if v.dir or v.view3D then - trace("-- dir item: " .. v.path ) local parent = app.cache.items[ v.parent.id ] res.items[ v.id ] = v res.parent = parent @@ -57,32 +56,35 @@ return { local data = util.merge({ url = req.param.url, -- query hint to render janus.url in iframe (index.html) - html_items='', items = {}, parent=res.parent },{ opts = app.opts }) local i = 0 foreach( res.items, function(k,v) - -- position items for janusxr JML (dir.html) + -- add extra metadata for janusxr JML (janusxr.html and dir.html) table.insert(data.items, util.merge({x = i*2, i = i},v) ) i=i+1 if v.view3D then - data.item = v - data.title = data.item.title - data.parent = v.parent - local port = ':' .. GetPort() - if port == 80 then port = '' end - data.url = GetScheme() .. '://' .. GetHost() .. port .. v.url - if v.janusxr and v.janusxr.jml then - -- what to do here? no generated portals? - data.janusxr = app.tpl( util.readfile(v.path), data ) + local isMarkupExperience = function(v) + return v.janusxr and (v.janusxr.jml or v.janusxr.aframe) + end + -- prioritize markup experience above binary 3D files as experience containers + if not data.item or (isMarkupExperience(v) and not isMarkupExperience(data.item)) then + data.item = v + data.title = data.item.title + data.parent = v.parent + data.url = app.opts.baseurl .. util.stripPath(v.url) -- because we are running in iframe + if v.janusxr and (v.janusxr.jml or v.janusxr.aframe) then + -- what to do here? no generated portals? + data.janusxr = app.tpl( util.readfile(v.path), data ) + end end end end) if data.item then res.template = 'page/index.html' end - app.pub("dir", nil, {res=res,data=data, urlpath=urlpath, isdir=isdir}) + app.pub("dir", nil, {res=res,data=data, urlpath=urlpath, isroot=isroot, isdir=isdir}) if not redirect then app.cache.url[ urlhash ] = util.templateFile( res.template, data) end diff --git a/src/page/footer.html b/src/page/footer.html index ad6f19b..b3ad9c1 100644 --- a/src/page/footer.html +++ b/src/page/footer.html @@ -17,13 +17,20 @@ NLNET
- View from XR Device
- Content overview
- About
+ View from XR Device
+ Content overview
+ About
- Sourcecode
- +
+ Sourcecode
+ {if footerlinks then + for i,v in pairs(footerlinks) do + print([[ + ]] .. v.title .. [[
+ ]]) + end + end} diff --git a/src/page/header.html b/src/page/header.html index ddf66d7..be1e8ac 100644 --- a/src/page/header.html +++ b/src/page/header.html @@ -5,7 +5,7 @@ ${opts.plugin_global_title} - + ${opts.plugin_global_headertags}
]]) @@ -31,9 +31,14 @@ end} if( url == document.location.href ) return try{ // handle external urls via ?url= let urlObj = new URL(url) - url = document.location.origin + document.location.pathname + '?url=' + url + if( document.location.origin != urlObj.origin ){ + url = document.location.origin + document.location.pathname + '?url=' + url + } }catch(e){} - window.history.replaceState( url, iframe.contentDocument.title, url ); + const isFileWithExtension = url.replace( new RegExp('./','g'),"").match("\\.") + if( !isFileWithExtension ){ + window.history.replaceState( url, iframe.contentDocument.title, url ); + } setTimeout( () => { document.title = iframe.contentDocument.title }, 1000 ) diff --git a/src/page/janusxr.html b/src/page/janusxr.html index a57e473..2c7300c 100644 --- a/src/page/janusxr.html +++ b/src/page/janusxr.html @@ -14,6 +14,13 @@ end} end end end} + {if itemsremote and #itemsremote > 0 then + for i,v in pairs(itemsremote) do + if #v.thumbnail > 0 then + print('\t') + end + end + end} {if showfediportals then for i=1,5 do local urls = app.opts[ 'plugin_federation_url_' .. i] @@ -49,9 +56,6 @@ end} end} {if not item and items and #items > 0 then - local portals = {} - for i,v in pairs(items) do table.insert(portals, v) end - if parent then table.insert(portals, p) end for i,v in pairs(items) do local width = 3.2 local center = -((#items*width)/1.3) + (i*width) @@ -65,27 +69,24 @@ end} addPortal('dir',center, v, url,i, -4, thumb, '0 0 0', '0.2 0.2 0.2') end end} - {if showfediportals and not item then - local portals = {} - for i=1,5 do - local urls = app.opts[ 'plugin_federation_url_' .. i] - if #urls > 0 then - local fediurl = urls:gsub(' .*','') - table.insert(portals, fediurl ) - end - end - if #portals > 0 then - print([[ - - ]]) - end - for i,fediurl in pairs(portals) do + {if not item and itemsremote and #itemsremote > 0 then + for i,v in pairs(itemsremote) do local width = 3.2 - local center = -((#portals*width)/1.3) + (i*width) - local thumb = ' thumb_id="federated' .. i .. '"' - addPortal('federated',center, {title=fediurl}, fediurl,i, 4, thumb, '0 180 0', '0.1 0.1 0.1') + local center = -((#itemsremote*width)/1.3) + (i*width) + local url = v.url + local thumb = ' shader_id="defaultportal"' + if #v.thumbnail > 0 then + thumb = ' thumb_id="imgremote' .. i .. '" ' + end + if v.dir then url = url .. "/" end + if v.redirect then url = v.redirect end -- dont wrap standalone glb's in janusxr + addPortal('remote',center, v, url,i, 4, thumb, '0 180 0', '0.2 0.2 0.2') end + print([[ + + ]]) end} + ${opts.plugin_janusxr_jml} diff --git a/src/plugin/diskscan.lua b/src/plugin/diskscan.lua index 219962b..f63d567 100644 --- a/src/plugin/diskscan.lua +++ b/src/plugin/diskscan.lua @@ -8,7 +8,8 @@ end) -- this registers the plugin UI /funcs return { - name = "Local Content", + name = "Files", + tab = " Files", info = '', opts = { { diff --git a/src/plugin/federation.lua b/src/plugin/federation.lua index 5105495..0705157 100644 --- a/src/plugin/federation.lua +++ b/src/plugin/federation.lua @@ -11,70 +11,70 @@ app.on('set', function(k,v) end) app.on('dir', function(opts) - local me = app.plugin.federation - if opts.urlpath == '/' then - print_r(me.portals) - --foreach( me.portals, function(k,portal) - -- print("JAAA") - -- table.insert( opts.data.items,{ - -- url = portal.url, - -- name = portal.name, - -- thumbnail = portal.thumbnail - -- }) - --end) + local showportals = app.opts.plugin_federation_strategy == 'always' or + (app.opts.plugin_federation_strategy == 'homepage' and opts.isroot) + if showportals then + opts.data.itemsremote = opts.data.itemsremote or {} + local itemsremote = opts.data.itemsremote + for i=1,5 do + local urls = app.opts[ 'plugin_federation_url_' .. i] + if #urls > 0 then + local url = urls:gsub(' .*','') + local thumburl = urls:match(" ") and urls:gsub('.* ','') or nil + table.insert(itemsremote, { + title = url:gsub(".*//",""), + thumbnail = thumburl, + url = '/janusweb/index.html#janus.url=' .. EscapeFragment(url) + }) + end + end end end) local plugin = { name = 'Federation', + tab = " Federation", info = 'Add links to other (janus)XR(forges), and have them show up as extra portals in portalrooms', opts = { -- TODO populate this based on xrforge-federation.json - { - key='app.opts.plugin_federation_title', - title='Title', - info="Displayed above federated portals", - default="Federated neighbourhoods", - values='' - }, { key='app.opts.plugin_federation_strategy', title='Display portals', info="When to show federated portals?", - default="always", + default="homepage", values={always='always',homepage='only homepage'} }, { key='app.opts.plugin_federation_url_1', title='Federated URL 1', - info="URL + Thumbnail URL (space separated)", + info="URL and/or Thumbnail URL (space separated)", default="https://xrforge.isvery.ninja https://i.imgur.com/x7fzn9v.png", values='' }, { key='app.opts.plugin_federation_url_2', title='Federated URL 2', - info="URL + Thumbnail URL (space separated)", - default="https://www.janusxr.org/new/ https://archive.org/download/janusxr-1/janusxr.png", + info="URL and/or Thumbnail URL (space separated)", + default="https://www.janusxr.org https://www.janusvr.com/images/logos/logotextoverlight.png", values='' }, { key='app.opts.plugin_federation_url_3', title='Federated URL 3', - info="URL + Thumbnail URL (space separated)", + info="URL and/or Thumbnail URL (space separated)", default="", values='' }, { key='app.opts.plugin_federation_url_4', title='Federated URL 4', - info="URL + Thumbnail URL (space separated)", + info="URL and/or Thumbnail URL (space separated)", default="", values='' }, { key='app.opts.plugin_federation_url_5', title='Federated URL 5', - info="URL + Thumbnail URL (space separated)", + info="URL and/or Thumbnail URL (space separated)", default="", values='' } diff --git a/src/plugin/git.lua b/src/plugin/git.lua index 7d60bbb..27e8bc7 100644 --- a/src/plugin/git.lua +++ b/src/plugin/git.lua @@ -26,7 +26,8 @@ end) -- this registers the plugin UI local plugin = { name = 'Git', - info = '', + info = 'Git repositories will be cloned before xrforge starts scanning for experiences', + tab = " Git", opts = { { key='app.opts.plugin_git_repo_1', diff --git a/src/plugin/global.lua b/src/plugin/global.lua index f50e40e..0600680 100644 --- a/src/plugin/global.lua +++ b/src/plugin/global.lua @@ -10,6 +10,38 @@ end) app.on('set', function(k,v) end) +app.on('init', function() + if app.opts.plugin_global_credible_exit == 'platform' then + if not util.fileExist( app.bin ) then -- check for binary in local filesystem + os.execute("ln -fs " .. arg[-1] .. " .") + os.execute("ln -fs " .. arg[-1] .. " " .. app.bin:gsub(".com",".zip")) + end + else + if not util.fileExist( app.bin ) then + os.execute("test -L " .. app.bin .. " && rm " .. app.bin ) -- delete symlink + end + end +end) + +app.on('util.template', function(data) + if data.urlpath then + local isAdminPath = data.urlpath:match("^/admin") + if app.opts.plugin_global_credible_exit == 'platform' or + (app.opts.plugin_global_credible_exit == 'platformadmin' and isAdminPath) + then + data.footerlinks = data.footerlinks or {} + table.insert(data.footerlinks, { + title=" Download data", + url="/" .. app.bin:gsub(".com",".zip") + }) + table.insert(data.footerlinks, { + title=" Download data+platform", + url="/" .. app.bin + }) + end + end +end) + -- this registers the plugin UI local plugin = { name = "Global settings", @@ -27,13 +59,13 @@ local plugin = { key='app.opts.plugin_global_subtitle', title='Subtitle', info='Slogan here', - default = "Portable XR experiences for Phone, VRheadset & desktop.", + default = "Interconnected XR experiences for Phone, VRheadset & desktop.", values='' }, { key='app.opts.plugin_global_topmenu_mapname', title='Overview link', - info='Top-right link (overview page)', + info='Top-right link (overview page)', default = "map", values='' }, @@ -41,14 +73,21 @@ local plugin = { key='app.opts.plugin_global_homepage', title='Homepage', info='Display 3D or 2D experiences on homepage', - default = "2D", + default = "3D", values={ ['3D'] = '3D', ['2D'] = '2D' }, }, + { + key='app.opts.plugin_global_credible_exit', + title='Credible exit', + info='How can users reproduce experiences locally?', + default = "platform", + values={ platform="download platform+data", platformadmin="download platform+data (admin only)" } + }, { key='app.opts.plugin_global_topmenu_html', title='Menu html', info='Add extra menu items here', - default = 'about', + default = 'about', control='textarea', values='' }, @@ -64,6 +103,13 @@ local plugin = { control='textarea', values='' }, + { + key='app.opts.plugin_global_remote_title', + title='Title', + info="Displayed above remote portals", + default="Remote / Federated", + values='' + }, }, middleware = { homepage_2D_or_3D = function(req,res,next) diff --git a/src/plugin/janusxr.lua b/src/plugin/janusxr.lua index 48ae3e8..83b9024 100644 --- a/src/plugin/janusxr.lua +++ b/src/plugin/janusxr.lua @@ -2,6 +2,7 @@ local util = require('util') local pattern_fireboxroom = "[Ff][Ii][Rr][Ee][Bb][Oo][Xx][Rr][Oo][Oo][Mm][ ]?.*>" local pattern_title = "<[Tt][Ii][Tt][Ll][Ee]>(.*)<" +local pattern_aframe = " 100kb local data = util.readfile( exp.file.fullpath) if data then - if data:match(pattern_fireboxroom) then - table.insert(exp.tag, "janusxr") + if data:match(pattern_fireboxroom) or + data:match(pattern_aframe) then local title = data:match(pattern_title) if title then exp.title = title:gsub("<.*","") @@ -41,8 +42,13 @@ app.on('scan_experience_file', function(exp) app.pub("scan_experience_janusxr", nil, {exp=exp, item=item}) -- be nice to other plugins app.cache.items[ exp.file.ino ] = exp exp.view3D = true + exp.parent.redirect = exp.url -- mark parent to redirect to this standalone url instead (not glb assets e.g.) exp.janusxr = {} print("✅ └─ plugin/janusxr: " .. exp.file.name) + if data:match(pattern_aframe) and app.opts.plugin_janusxr_wrap_aframe == 'enabled' then + exp.janusxr.aframe = true + print("✅ └─ plugin/janusxr: AFRAME markup detected") + end end else -- symlink to directory @@ -79,31 +85,31 @@ local plugin = { }, { key='app.opts.plugin_janusxr_wrap_3dfile', - title='wrap 3D file', + title='VR wrap 3D file', info='render 3D files in janusxr.xml room', default="room_plane", values=util.merge(rooms,{disabled="-- disabled --"}) }, { key='app.opts.plugin_janusxr_wrap_image', - title='wrap images', + title='VR wrap images', info='render images in janusxr.xml room', default="room_plane", values=util.merge(rooms,{disabled="-- disabled --"}) }, { key='app.opts.plugin_janusxr_wrap_video', - title='wrap videos', + title='VR wrap videos', info='render videos in janusxr.xml room', default="room_plane", values=util.merge(rooms,{disabled="-- disabled --"}) }, { key='app.opts.plugin_janusxr_wrap_aframe', - title='wrap AFRAME', - info='render AFRAME markup in janusxr.xml room', - default="room_plane", - values=util.merge(rooms,{disabled="-- disabled --"}) + title='AFRAME support', + info='render AFRAME (geometry) markup when detected in URLs', + default="enabled", + values={enabled="enabled",disabled="disabled"} }, { key='app.opts.plugin_janusxr_portalroom', diff --git a/src/plugin/manyfold.lua b/src/plugin/manyfold.lua index 481a06c..625b2de 100644 --- a/src/plugin/manyfold.lua +++ b/src/plugin/manyfold.lua @@ -13,6 +13,7 @@ end) -- this registers the plugin UI local plugin = { name = "Manyfold", + tab = " Manyfold", info = '', opts = { { diff --git a/src/plugin/peertube.lua b/src/plugin/peertube.lua new file mode 100644 index 0000000..5d54ec2 --- /dev/null +++ b/src/plugin/peertube.lua @@ -0,0 +1,85 @@ +local util = require('util') + +app.on('scan_experience', function(exp) + --print_r(exp) +end) + +app.on('scan_experience_file', function(exp) +end) + +app.on('set', function(k,v) +end) + +app.on('dir', function(opts) + local showportals = app.opts.plugin_peertube_strategy == 'always' or + (app.opts.plugin_peertube_strategy == 'homepage' and opts.isroot) + if showportals then + opts.data.itemsremote = opts.data.itemsremote or {} + local itemsremote = opts.data.itemsremote + for i=1,5 do + local urls = app.opts[ 'plugin_peertube_url_' .. i] + if #urls > 0 then + local url = urls:gsub(' .*','') + local thumburl = urls:match(" ") and urls:gsub('.* ','') or nil + table.insert(itemsremote, { + title = url:gsub(".*//",""), + thumbnail = thumburl, + url = '/janusweb/index.html#janus.url=' .. EscapeFragment(url) + }) + end + end + end +end) + +-- this registers the plugin UI +local plugin = { + name = "Peertube", + tab = " Peertube", + info = "disabled Peertube videos/channels/livestreams can be rendered as rooms in JanusWeb", + opts = { + { + key='app.opts.plugin_peertube_strategy', + title='Display portals', + info="When to show peertube portals?", + default="homepage", + values={always='always',homepage='only homepage'} + }, + { + key='app.opts.plugin_peertube_url_1', + title='Peertube URL 1', + info="URL and/or Thumbnail URL (space separated)", + default="https://tilvids.com/c/arthurpizza https://oss.iqrator.org/wp-content/uploads/2026/01/00-16-510x510.png", + values='https://tilvids.com/c/arthurpizza https://oss.iqrator.org/wp-content/uploads/2026/01/00-16-510x510.png' + }, + { + key='app.opts.plugin_peertube_url_2', + title='Peertube URL 2', + info="URL and/or Thumbnail URL (space separated)", + default="", + values='' + }, + { + key='app.opts.plugin_peertube_url_3', + title='Peertube URL 3', + info="URL and/or Thumbnail URL (space separated)", + default="", + values='' + }, + { + key='app.opts.plugin_peertube_url_4', + title='Peertube URL 4', + info="URL and/or Thumbnail URL (space separated)", + default="", + values='' + }, + { + key='app.opts.plugin_peertube_url_5', + title='Peertube URL 5', + info="URL and/or Thumbnail URL (space separated)", + default="", + values='' + } + } +} + +return plugin diff --git a/src/plugin/rssatom.lua b/src/plugin/rssatom.lua new file mode 100644 index 0000000..9efc7f5 --- /dev/null +++ b/src/plugin/rssatom.lua @@ -0,0 +1,85 @@ +local util = require('util') + +app.on('scan_experience', function(exp) + --print_r(exp) +end) + +app.on('scan_experience_file', function(exp) +end) + +app.on('set', function(k,v) +end) + +app.on('dir', function(opts) + local showportals = app.opts.plugin_rssatom_strategy == 'always' or + (app.opts.plugin_rssatom_strategy == 'homepage' and opts.isroot) + if showportals then + opts.data.itemsremote = opts.data.itemsremote or {} + local itemsremote = opts.data.itemsremote + for i=1,5 do + local urls = app.opts[ 'plugin_rssatom_url_' .. i] + if #urls > 0 then + local url = urls:gsub(' .*','') + local thumburl = urls:match(" ") and urls:gsub('.* ','') or nil + table.insert(itemsremote, { + title = url:gsub(".*//",""), + thumbnail = thumburl, + url = '/janusweb/index.html#janus.url=' .. EscapeFragment(url) + }) + end + end + end +end) + +-- this registers the plugin UI +local plugin = { + name = "RSS Atom", + tab = " RSS Atom", + info = "disabled RSS feeds can be rendered as rooms in JanusWeb", + opts = { + { + key='app.opts.plugin_rssatom_strategy', + title='Display portals', + info="When to show rss portals?", + default="homepage", + values={always='always',homepage='only homepage'} + }, + { + key='app.opts.plugin_rssatom_url_1', + title='RSS/Atom URL 1', + info="URL and/or Thumbnail URL (space separated)", + default="http://mastodon.online/@lvk.rss https://pbs.twimg.com/profile_images/1604566357206601730/dlRNo-V-_400x400.jpg", + values='http://mastodon.online/@lvk.rss https://pbs.twimg.com/profile_images/1604566357206601730/dlRNo-V-_400x400.jpg' + }, + { + key='app.opts.plugin_rssatom_url_2', + title='RSS/Atom URL 2', + info="URL and/or Thumbnail URL (space separated)", + default="https://nlnet.nl/feed.atom https://nlnet.nl/logo/history/logo-640x732-pre-2024.jpg", + values='https://nlnet.nl/feed.atom https://nlnet.nl/logo/history/logo-640x732-pre-2024.jpg' + }, + { + key='app.opts.plugin_rssatom_url_3', + title='RSS/Atom URL 3', + info="URL and/or Thumbnail URL (space separated)", + default="", + values='' + }, + { + key='app.opts.plugin_rssatom_url_4', + title='RSS/Atom URL 4', + info="URL and/or Thumbnail URL (space separated)", + default="", + values='' + }, + { + key='app.opts.plugin_rssatom_url_5', + title='RSS/Atom URL 5', + info="URL and/or Thumbnail URL (space separated)", + default="", + values='' + } + } +} + +return plugin diff --git a/src/plugin/staticwebgen.lua b/src/plugin/staticwebgen.lua index ef27542..63a5c1c 100644 --- a/src/plugin/staticwebgen.lua +++ b/src/plugin/staticwebgen.lua @@ -8,32 +8,10 @@ app.on('set', function(k,v) end) app.on('init', function() - local outdir = 'www' - local url = {"/"} + local me = app.plugin.staticwebgen if util.contains(argv, 'generate') then - if util.fileExist(outdir) then - print("[!] folder 'www' was already detected..please delete it") - os.exit() - end - local selfbinary = '../' .. argv[-1] - os.execute("mkdir www") - os.execute("cp -Lr " .. app.opts.plugin_diskscan_dir .. " " .. outdir .. "/.") - os.execute("cd " .. outdir .. " && unzip " .. selfbinary .. " 'img/*' 'css/*' 'janusweb/*' 'shader/*'") - foreach( app.cache.items, function(k,v) - if v.dir then - table.insert(url, v.url ) - end - end) - foreach( url, function(k,url) - url = url:gsub("//","/") - util.fetchServer( url, nil, function(req,res,next) - print( outdir .. url .. '/index.html') - util.writefile( outdir .. url .. '/index.html', res.body ) - end) - end) + me.generate() end - --print("files generated in 'www' folder") - --os.exit() end) -- this registers the plugin UI @@ -77,7 +55,47 @@ local plugin = { default="", values='' } - } + }, + + done = function() + print("✅ finished generating 'www' folder") + os.exit(0) + end, + + generate = function() + local outdir = 'www' + local root = app.opts.plugin_diskscan_dirname + local urls = {"/","/about", "/" .. root } + local urlLast = '' + if util.fileExist(outdir) then + print("[!] folder 'www' was already detected..please delete it") + os.exit() + end + local selfbinary = '../' .. argv[-1] + os.execute("mkdir www www/about") + os.execute("cp -Lr " .. app.opts.plugin_diskscan_dir .. " " .. outdir .. "/.") + os.execute("cd " .. outdir .. " && unzip " .. selfbinary .. " 'img/*' 'css/*' 'janusweb/*' 'shader/*'") + foreach( app.cache.items, function(k,v) + if v.dir then + table.insert(urls, v.url ) + urlLast = v.url + end + end) + + app.on('response', function(opts) + local body = opts.res._body -- capture the output + local url = opts.req.url:gsub("//","/") + util.writefile( outdir .. url .. '/index.html', body ) + end) + + foreach( urls, function(k,url) + url = url:gsub("//","/") + print("saving " .. url) + util.queueServerRequest('GET', url, nil, nil, nil, nil, nil, 8080) + app.run() + end) + app.plugin.staticwebgen.done() + end } return plugin diff --git a/src/plugin/webhook.lua b/src/plugin/webhook.lua index 83f693c..9836bbd 100644 --- a/src/plugin/webhook.lua +++ b/src/plugin/webhook.lua @@ -13,6 +13,7 @@ end) -- this registers the plugin UI local plugin = { name = 'Webhooks disabled', + tab = " Webhooks", info = '', opts = { { diff --git a/src/test/main.lua b/src/test/main.lua index 474c5a9..b772149 100644 --- a/src/test/main.lua +++ b/src/test/main.lua @@ -1,17 +1,9 @@ local lust = require 'test/lust' +local util = require 'util' local describe, it, expect = lust.describe, lust.it, lust.expect -- usage: https://github.com/bjornbytes/lust -lust.queueServerRequest = function(method, path, params, host, headers, body, scheme, port) - GetMethod = function() return method end - GetPath = function() return path end - GetParams = function() return params or {} end -- note: { {"key","value"} } format - GetHost = function() return host or 'localhost' end - GetHeaders = function() return headers or {} end - GetScheme = function() return scheme or 'https' end - GetBody = function() return body or '' end - GetPort = function() return port or 80 end -end +lust.queueServerRequest = util.queueServerRequest require('test/plugin_global')(lust) require('test/plugin_git')(lust) diff --git a/src/util/xrforge.sh b/src/util/xrforge.sh index e89da1d..69f5afd 100755 --- a/src/util/xrforge.sh +++ b/src/util/xrforge.sh @@ -47,6 +47,7 @@ build(){ download janusweb cd src zip -yr ../result/bin/xrforge.com * .lua .init.lua .args + test -n "$ARCH" && cp ../result/bin/xrforge.com ../result/bin/xrforge-$ARCH.com } rebuild(){