refactored everything to plugins (todo: fix memoize tiles)
This commit is contained in:
parent
1014554be6
commit
d36dfa30b2
20 changed files with 241 additions and 229 deletions
2
run.sh
2
run.sh
|
|
@ -1,2 +1,4 @@
|
||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
|
source ./.env
|
||||||
|
rebuild
|
||||||
result/bin/xrforge.com server
|
result/bin/xrforge.com server
|
||||||
|
|
|
||||||
|
|
@ -23,20 +23,39 @@ app = require("soakbean") {
|
||||||
cmd={
|
cmd={
|
||||||
help = {file="cmd/help.lua", info="display all possible flags"},
|
help = {file="cmd/help.lua", info="display all possible flags"},
|
||||||
server = {file="cmd/server.lua", info="starts the webserver"},
|
server = {file="cmd/server.lua", info="starts the webserver"},
|
||||||
createplugin = {file="cmd/createplugin.lua", info="generates skeleton plugin"}
|
createplugin = {file="cmd/createplugin.lua", info="generates skeleton plugin"},
|
||||||
|
generate = {file="cmd/generate.lua", info="generate www-folder with XR experiences"}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
-- api skeleton (implemented via soakbean's app.on(...))
|
-- api skeleton (implemented via soakbean's app.on(...))
|
||||||
app.set = function(k,v) end
|
app.set = function(k,v) end
|
||||||
app.init = function() end
|
app.init = function() end
|
||||||
app.scan = require('scan').scan
|
|
||||||
|
|
||||||
-- load config
|
-- load config
|
||||||
app.opts = require('config')(app)
|
app.opts = require('config')(app)
|
||||||
|
|
||||||
-- load plugins
|
-- load system plugins
|
||||||
app.plugin.janusxr = require('plugin/janusxr')
|
app.plugin.moderation = require('plugin/moderation')
|
||||||
|
app.plugin.diskscan = require('plugin/diskscan')
|
||||||
|
app.plugin.manyfold = require('plugin/manyfold')
|
||||||
|
app.plugin.janusxr = require('plugin/janusxr')
|
||||||
|
|
||||||
|
-- ensure plugin config-defaults
|
||||||
|
foreach( app.plugin, function(name,plugin)
|
||||||
|
foreach( plugin.opts, function(k,opt)
|
||||||
|
local key = opt.key:gsub("app.opts.","")
|
||||||
|
if not app.opts[ key ] then
|
||||||
|
app.opts[ key ] = opt.default
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
|
||||||
|
-- set order preference (optional)
|
||||||
|
app.plugin[1] = app.plugin.diskscan
|
||||||
|
app.plugin[2] = app.plugin.moderation
|
||||||
|
app.plugin[3] = app.plugin.janusxr
|
||||||
|
app.plugin[4] = app.plugin.manyfold
|
||||||
|
|
||||||
app.runcmd(app) -- run clicmds
|
app.runcmd(app) -- run clicmds
|
||||||
|
|
||||||
|
|
@ -67,12 +86,13 @@ app --
|
||||||
.use( function(req,next) Route() end) -- fallback default redbean fileserver
|
.use( function(req,next) Route() end) -- fallback default redbean fileserver
|
||||||
|
|
||||||
app.on('init', function()
|
app.on('init', function()
|
||||||
if assert(unix.fork()) == 0 then
|
if assert(unix.fork()) == 0 then
|
||||||
|
app.plugin.diskscan.scan()
|
||||||
|
print_r(app.cache)
|
||||||
--os.execute("ls ; sleep 5")
|
--os.execute("ls ; sleep 5")
|
||||||
app.scan()
|
|
||||||
print("ok")
|
print("ok")
|
||||||
--local ok, headers, body = Fetch('http://127.0.0.1:8080/test')
|
--local ok, headers, body = Fetch('http://127.0.0.1:8080/test')
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
|
|
||||||
function OnHttpRequest() app.run() end
|
function OnHttpRequest() app.run() end
|
||||||
|
|
|
||||||
|
|
@ -5,17 +5,10 @@ return function(app)
|
||||||
local config = { -- default
|
local config = { -- default
|
||||||
name = "XRForge",
|
name = "XRForge",
|
||||||
baseurl="/",
|
baseurl="/",
|
||||||
title="My XRForge experiences",
|
title="Your XR Forge",
|
||||||
subtitle="Portable XR experiences for Phone, VRheadset & desktop.",
|
subtitle="Portable XR experiences for Phone, VRheadset & desktop.",
|
||||||
admin_title = "XRForge Admin",
|
admin_title = "XRForge Admin",
|
||||||
admin_subtitle = "Manage your XR experiences here",
|
admin_subtitle = "Manage your XR experiences here",
|
||||||
disk_dir = ".",
|
|
||||||
hide_keywords = "wip beta -v[0-9] customerX",
|
|
||||||
manyfold_instance_1 = "https://xrforge.isvery.ninja",
|
|
||||||
manyfold_instance_2 = "",
|
|
||||||
manyfold_instance_3 = "",
|
|
||||||
sync_interval = 30,
|
|
||||||
sync_strategy = "recent"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
local configfile = "xrforge.lua"
|
local configfile = "xrforge.lua"
|
||||||
|
|
|
||||||
|
|
@ -31,25 +31,25 @@ local dir = {}
|
||||||
|
|
||||||
dir.scan = function(path)
|
dir.scan = function(path)
|
||||||
local stat = unix.stat(path)
|
local stat = unix.stat(path)
|
||||||
if stat == nil then
|
if not stat or not unix.S_ISDIR( stat:mode() ) then
|
||||||
return ServeError(404,"does not exist")
|
return nil,nil, "not a dir"
|
||||||
else
|
end -- let redbean take care of serving static file
|
||||||
if not unix.S_ISDIR( stat:mode() ) then return false end -- let redbean take care of serving static file
|
state,err = assert(unix.opendir(path))
|
||||||
end
|
return stat, state, err
|
||||||
state = assert(unix.opendir(path))
|
|
||||||
return stat, state
|
|
||||||
end
|
end
|
||||||
|
|
||||||
dir.foreach = function(path,cb)
|
dir.foreach = function(path,cb)
|
||||||
local stat, state = dir.scan(path)
|
local stat, state, err = dir.scan(path)
|
||||||
for name, kind, ino, off in state do
|
if not err then
|
||||||
if name == "." then goto continue end
|
for name, kind, ino, off in state do
|
||||||
attr,err = unix.stat(path..'/'..name, unix.AT_SYMLINK_NOFOLLOW)
|
if name == "." then goto continue end
|
||||||
if err then print("error: ".. name .. ": " ..err) end
|
attr,err = unix.stat(path..'/'..name, unix.AT_SYMLINK_NOFOLLOW)
|
||||||
cb(path,stat,state,name,kind,ino,off,attr, path .. "/" ..name)
|
if err then print("error: ".. name .. ": " ..err) end
|
||||||
::continue::
|
cb(path,stat,state,name,kind,ino,off,attr, path .. "/" ..name)
|
||||||
|
::continue::
|
||||||
|
end
|
||||||
end
|
end
|
||||||
return state
|
return state,err
|
||||||
end
|
end
|
||||||
|
|
||||||
dir.serve = function(folder) -- raw redbean function (use dir.foreach in soakbean)
|
dir.serve = function(folder) -- raw redbean function (use dir.foreach in soakbean)
|
||||||
|
|
@ -97,7 +97,11 @@ dir.serve = function(folder) -- raw redbean function (use dir.foreach in soakbea
|
||||||
Write('<th class=hide r>ctim\r\n')
|
Write('<th class=hide r>ctim\r\n')
|
||||||
Write('<tbody>\r\n')
|
Write('<tbody>\r\n')
|
||||||
|
|
||||||
local stat, state = dir.scan(path)
|
local stat, state, err = dir.scan(path)
|
||||||
|
if stat == nil then
|
||||||
|
return ServeError(404,"does not exist")
|
||||||
|
end
|
||||||
|
|
||||||
dir.foreach(path, function(path,stat,state,name,kind,ino,off,attr,fullpath)
|
dir.foreach(path, function(path,stat,state,name,kind,ino,off,attr,fullpath)
|
||||||
Write('<tr>\r\n')
|
Write('<tr>\r\n')
|
||||||
Write('<td>')
|
Write('<td>')
|
||||||
|
|
|
||||||
1
src/.lua/page
Symbolic link
1
src/.lua/page
Symbolic link
|
|
@ -0,0 +1 @@
|
||||||
|
../page
|
||||||
|
|
@ -3,7 +3,7 @@ return function(app,argv)
|
||||||
|
|
||||||
--
|
--
|
||||||
-- 1. put the code below in a file ('xrforge.myplugin.lua' e.g.)
|
-- 1. put the code below in a file ('xrforge.myplugin.lua' e.g.)
|
||||||
-- 2. make sure to require it at the top of xrforge.lua (require('xrforge.myplugin') e.g.)
|
-- 2. in xrforge.lua make sure to add (at the top): app.plugin.myplugin = require('xrforge.myplugin')
|
||||||
-- 3. profit!
|
-- 3. profit!
|
||||||
--
|
--
|
||||||
|
|
||||||
|
|
@ -38,7 +38,10 @@ local plugin = {
|
||||||
default="bar",
|
default="bar",
|
||||||
values=""
|
values=""
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
myfunc = function()
|
||||||
|
-- call me via app.plugin.myplugin.myfunc()
|
||||||
|
end
|
||||||
}
|
}
|
||||||
|
|
||||||
return plugin
|
return plugin
|
||||||
|
|
|
||||||
4
src/cmd/generate.lua
Normal file
4
src/cmd/generate.lua
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
return function(app,argv)
|
||||||
|
app.plugin.diskscan.scan()
|
||||||
|
os.exit()
|
||||||
|
end
|
||||||
|
|
@ -184,14 +184,14 @@ ul{
|
||||||
.menu{
|
.menu{
|
||||||
text-align: right;
|
text-align: right;
|
||||||
}
|
}
|
||||||
|
.menu,
|
||||||
|
.menu *{
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
.menu a {
|
.menu a {
|
||||||
padding-right:30px;
|
padding-right:30px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.menu br {
|
|
||||||
display:none
|
|
||||||
}
|
|
||||||
|
|
||||||
.grid .tile{ border-radius:5px; }
|
.grid .tile{ border-radius:5px; }
|
||||||
.grid .tile .text{ padding:3px 0px; }
|
.grid .tile .text{ padding:3px 0px; }
|
||||||
.grid .tile .img{
|
.grid .tile .img{
|
||||||
|
|
|
||||||
|
|
@ -33,9 +33,14 @@ body{
|
||||||
form table tr > td:nth-child(3) {
|
form table tr > td:nth-child(3) {
|
||||||
display:none;
|
display:none;
|
||||||
}
|
}
|
||||||
.menu br {
|
.menu{
|
||||||
display:initial !important;
|
position:absolute;
|
||||||
|
right:20px;
|
||||||
}
|
}
|
||||||
|
.menu a{
|
||||||
|
padding-right:20px !important;
|
||||||
|
}
|
||||||
|
.menu a.xl{ display:none; }
|
||||||
}
|
}
|
||||||
|
|
||||||
.grid .tile .text{
|
.grid .tile .text{
|
||||||
|
|
|
||||||
|
|
@ -10,145 +10,6 @@ ${notification}
|
||||||
|
|
||||||
<form method="POST">
|
<form method="POST">
|
||||||
|
|
||||||
<div class="tab">
|
|
||||||
<div class="label col2">
|
|
||||||
Local content
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="margin1">
|
|
||||||
<table width="100%">
|
|
||||||
<tr>
|
|
||||||
<th width="165"/>
|
|
||||||
<th />
|
|
||||||
<th />
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
Directory
|
|
||||||
</td>
|
|
||||||
<td >
|
|
||||||
<input type="text" value="${opts.disk_dir}"></input>
|
|
||||||
</td>
|
|
||||||
<td class="col3">
|
|
||||||
Directory to scan for experiences (use '.' for current)
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<br/>
|
|
||||||
<br/>
|
|
||||||
|
|
||||||
<div class="tab">
|
|
||||||
<div class="label col2">
|
|
||||||
Hide content
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="margin1">
|
|
||||||
<table width="100%">
|
|
||||||
<tr>
|
|
||||||
<th width="165"/>
|
|
||||||
<th />
|
|
||||||
<th />
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
Keywords
|
|
||||||
</td>
|
|
||||||
<td >
|
|
||||||
<input type="text" name="opts.hide_keywords" value="${opts.hide_keywords}"></input>
|
|
||||||
</td>
|
|
||||||
<td class="col3">
|
|
||||||
Space-separated, <a href="http://lua-users.org/wiki/PatternsTutorial" target="_blank">lua patterns</a> are allowed
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<br/>
|
|
||||||
<br/>
|
|
||||||
|
|
||||||
<div class="tab">
|
|
||||||
<div class="label col2">
|
|
||||||
Manyfold instances
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="margin1">
|
|
||||||
<table width="100%">
|
|
||||||
<tr>
|
|
||||||
<th width="165"/>
|
|
||||||
<th />
|
|
||||||
<th />
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
Instance URL 1
|
|
||||||
</td>
|
|
||||||
<td >
|
|
||||||
<input type="text" name="opts.manyfold_instance_1" value="${opts.manyfold_instance_1}"></input>
|
|
||||||
</td>
|
|
||||||
<td class="col3">
|
|
||||||
<a href="https://manyfold.app/" target="_blank">Manyfold</a> URL goes here (https://xrforge.isvery.ninja e.g.)
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
Instance URL 2
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<input type="text" name="opts.manyfold_instance_2" value="${opts.manyfold_instance_2}"></input>
|
|
||||||
</td>
|
|
||||||
<td class="col3">
|
|
||||||
<a href="https://manyfold.app/" target="_blank">Manyfold</a> URL goes here (https://xrforge.isvery.ninja e.g.)
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
Instance URL 3
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<input type="text" name="opts.manyfold_instance_3" value="${opts.manyfold_instance_3}"></input>
|
|
||||||
</td>
|
|
||||||
<td class="col3">
|
|
||||||
<a href="https://manyfold.app/" target="_blank">Manyfold</a> URL goes here (https://xrforge.isvery.ninja e.g.)
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
Sync strategy
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<select value="${opts.manyfold_sync_strategy}">
|
|
||||||
<option value="recent" default>only recent</option>
|
|
||||||
<option value="all+recent">everything + recent</option>
|
|
||||||
<option value="disabled">disabled</option>
|
|
||||||
</select>
|
|
||||||
</td>
|
|
||||||
<td class="col3">
|
|
||||||
Specify how manyfold instances should be synced.
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
Sync interval
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<select value="${opts.manyfold_sync_interval}">
|
|
||||||
<option value="30">every 30 mins</option>
|
|
||||||
<option value="15" selected>every 15 mins</option>
|
|
||||||
<option value="5">every 5 minutes</option>
|
|
||||||
</select>
|
|
||||||
</td>
|
|
||||||
<td class="col3">
|
|
||||||
Specify how often manyfold instances should be checked for recent items.
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<br/>
|
|
||||||
<br/>
|
|
||||||
${plugins}
|
${plugins}
|
||||||
|
|
||||||
<div style="margin-left:187px">
|
<div style="margin-left:187px">
|
||||||
|
|
|
||||||
|
|
@ -14,11 +14,13 @@ function pluginInput(opt)
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
]]
|
]]
|
||||||
|
local value = app.opts[ opt.key:gsub("^app.opts.","") ]
|
||||||
if type(opt.values) == 'table' then
|
if type(opt.values) == 'table' then
|
||||||
html = html .. util.template('<select name="${key}" value="${value}">\n', {
|
html = html .. util.template('<select name="${key}" value="${value}">\n', {
|
||||||
key = opt.key,
|
key = opt.key,
|
||||||
value = app.opts[key]
|
value = value
|
||||||
})
|
})
|
||||||
|
print(opt.key:gsub("^app.opts.","") )
|
||||||
foreach( opt.values, function(k,v)
|
foreach( opt.values, function(k,v)
|
||||||
html = html .. util.template('<option value="${v}">${k}</option>\n',{
|
html = html .. util.template('<option value="${v}">${k}</option>\n',{
|
||||||
k=k,
|
k=k,
|
||||||
|
|
@ -29,7 +31,7 @@ function pluginInput(opt)
|
||||||
else
|
else
|
||||||
html = html .. util.template('<input type="text" name="${key}" value="${value}"></input>',{
|
html = html .. util.template('<input type="text" name="${key}" value="${value}"></input>',{
|
||||||
key = opt.key,
|
key = opt.key,
|
||||||
value = app.opts[key]
|
value = value
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
html = html .. util.template([[
|
html = html .. util.template([[
|
||||||
|
|
@ -41,14 +43,17 @@ function pluginInput(opt)
|
||||||
return html
|
return html
|
||||||
end
|
end
|
||||||
|
|
||||||
function pluginUI(name,plugin)
|
function pluginUI(seen)
|
||||||
local plugindata = util.flatten(plugin,"plugin")
|
return function(name,plugin)
|
||||||
plugindata.opts = ""
|
if seen[ plugin.name ] then return end -- already processed
|
||||||
foreach(plugin.opts, function(k,opt)
|
seen[ plugin.name ] = true
|
||||||
plugindata.opts = plugindata.opts .. pluginInput(opt)
|
local plugindata = util.flatten(plugin,"plugin")
|
||||||
end)
|
plugindata.opts = ""
|
||||||
print_r(plugindata)
|
foreach(plugin.opts, function(k,opt)
|
||||||
data.plugins = data.plugins .. util.template( LoadAsset('/page/admin/plugin.html'), plugindata)
|
plugindata.opts = plugindata.opts .. pluginInput(opt)
|
||||||
|
end)
|
||||||
|
data.plugins = data.plugins .. util.template( LoadAsset('/page/admin/plugin.html'), plugindata)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function saveData()
|
function saveData()
|
||||||
|
|
@ -70,7 +75,8 @@ function saveData()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
foreach( app.plugin, pluginUI )
|
local seen = {}
|
||||||
|
foreach( app.plugin, pluginUI(seen) )
|
||||||
saveData()
|
saveData()
|
||||||
local html = util.template( LoadAsset("page/admin/index.html"),data)
|
local html = util.template( LoadAsset("page/admin/index.html"),data)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
<div class="tab">
|
<div class="tab">
|
||||||
<div class="label col2">${plugin.name} <b class="tag">plugin</b></div>
|
<div class="label col2">${plugin.name}</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="margin1">
|
<div class="margin1">
|
||||||
<table width="100%">
|
<table width="100%">
|
||||||
|
|
|
||||||
|
|
@ -7,24 +7,7 @@ ${header}
|
||||||
<hr style="margin-top:0">
|
<hr style="margin-top:0">
|
||||||
|
|
||||||
<div class="grid">
|
<div class="grid">
|
||||||
<div class="tile">
|
${tiles}
|
||||||
<div class="img" style="background-image:url(https://xrforge.isvery.ninja/models/dpjnj8pt1cjc/model_files/bzjvdm7l523l.png?derivative=preview);"></div>
|
|
||||||
<div class="text">
|
|
||||||
<img src="img/folder.svg" class="icon"/> Item 1
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="tile">
|
|
||||||
<div class="img" style="background-image:url(https://xrforge.isvery.ninja/models/dpjnj8pt1cjc/model_files/bzjvdm7l523l.png?derivative=preview);"></div>
|
|
||||||
<div class="text">
|
|
||||||
<img src="img/folder.svg" class="icon"/> Item 2
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="tile">
|
|
||||||
<div class="img" style="background-image:url(https://xrforge.isvery.ninja/models/dpjnj8pt1cjc/model_files/bzjvdm7l523l.png?derivative=preview);"></div>
|
|
||||||
<div class="text">
|
|
||||||
<img src="img/folder.svg" class="icon"/> Item 3
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
${footer}
|
${footer}
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,16 @@
|
||||||
local util = require("util")
|
local util = require("util")
|
||||||
|
local tile = require('page/partial/tile')
|
||||||
|
|
||||||
if not app.cache['experiences.html'] then
|
if not app.cache['experiences.html'] then
|
||||||
|
|
||||||
|
print("processing! " .. util.count(app.cache.items) .. " items")
|
||||||
app.cache['experiences.html'] = util.memoize(
|
app.cache['experiences.html'] = util.memoize(
|
||||||
function()
|
function()
|
||||||
print("processing!")
|
local data = util.merge({tiles=''},{ opts = app.opts })
|
||||||
local data = util.merge({foo='1'},{ opts = app.opts })
|
foreach(app.cache.items, function(k,v)
|
||||||
local html = util.template( LoadAsset("page/experiences.html"),app)
|
data.tiles = data.tiles .. tile( util.flatten(v,"item") )
|
||||||
|
end)
|
||||||
|
local html = util.template( LoadAsset("page/experiences.html"),data)
|
||||||
return html
|
return html
|
||||||
end,
|
end,
|
||||||
60*5 -- re-run every 5 mins (otherwise return cache)
|
60*5 -- re-run every 5 mins (otherwise return cache)
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
<base href="${opts.baseurl}" />
|
<base href="${opts.baseurl}" />
|
||||||
<title>XRForge - selfsovereign interoperable XR experiences</title>
|
<title>XRForge - selfsovereign interoperable XR experiences</title>
|
||||||
<link rel="stylesheet" href="css/xrforge.css"></link>
|
<link rel="stylesheet" href="css/xrforge.css"></link>
|
||||||
|
|
@ -25,10 +25,8 @@
|
||||||
</td>
|
</td>
|
||||||
<td class="menu">
|
<td class="menu">
|
||||||
<div class="margin1L">
|
<div class="margin1L">
|
||||||
<a href="/experiences">Experiences</a><br/>
|
<!-- important: no spaces between anchor tags for older browsers without flexbox -->
|
||||||
<a href="/about">About</a><br/>
|
<a href="/experiences">experiences</a><a href="/about">about</a><a class="xl" href="/howto">Howto</a></div>
|
||||||
<a href="/howto">Howto</a><br/>
|
|
||||||
</div>
|
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
|
|
|
||||||
12
src/page/partial/tile.lua
Normal file
12
src/page/partial/tile.lua
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
local util = require('util')
|
||||||
|
|
||||||
|
return function(opts)
|
||||||
|
return util.template([[
|
||||||
|
<div class="tile">
|
||||||
|
<div class="img" style="background-image:url(https://xrforge.isvery.ninja/models/dpjnj8pt1cjc/model_files/bzjvdm7l523l.png?derivative=preview);"></div>
|
||||||
|
<div class="text">
|
||||||
|
<img src="img/folder.svg" class="icon"/> ${item.title}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
]],opts)
|
||||||
|
end
|
||||||
|
|
@ -1,8 +1,18 @@
|
||||||
local dir = require "opendirectory"
|
local dir = require "opendirectory"
|
||||||
|
|
||||||
local scan
|
-- this registers the plugin UI /funcs
|
||||||
scan = {
|
return {
|
||||||
|
name = "Content (from disk)",
|
||||||
|
info = 'XRForge can search in (unnested) folders for XR/image/video files.',
|
||||||
|
opts = {
|
||||||
|
{
|
||||||
|
key='app.opts.plugin_diskscan_dir',
|
||||||
|
title='Directory',
|
||||||
|
info="Directory to scan for experiences (use '.' for current) ",
|
||||||
|
default=".",
|
||||||
|
values=''
|
||||||
|
}
|
||||||
|
},
|
||||||
shouldHideFile = function(name)
|
shouldHideFile = function(name)
|
||||||
return name == ".."
|
return name == ".."
|
||||||
or name == "util" -- repository
|
or name == "util" -- repository
|
||||||
|
|
@ -12,12 +22,15 @@ scan = {
|
||||||
end,
|
end,
|
||||||
|
|
||||||
scan = function()
|
scan = function()
|
||||||
|
local me = app.plugin.diskscan
|
||||||
|
local folder = app.opts.plugin_diskscan_dir
|
||||||
-- scan dirs
|
-- scan dirs
|
||||||
local dref = dir.foreach( app.opts.disk_dir, function(path,stat,state,name,kind,ino,off,attr,fullpath)
|
local dref, derr= dir.foreach( folder, function(path,stat,state,name,kind,ino,off,attr,fullpath)
|
||||||
if kind == unix.DT_DIR and not scan.shouldHideFile(name) then
|
if (kind == unix.DT_DIR or kind == unix.DT_LNK) and not me.shouldHideFile(name) then
|
||||||
local experience = {
|
local experience = {
|
||||||
path = fullpath,
|
path = fullpath,
|
||||||
name = name,
|
name = name,
|
||||||
|
title = name,
|
||||||
tag = {},
|
tag = {},
|
||||||
file = {stat=stat,state=state,kind=kind,ino=ino,off=off,attr=attr,fullpath=fullpath,name=name,path=path}
|
file = {stat=stat,state=state,kind=kind,ino=ino,off=off,attr=attr,fullpath=fullpath,name=name,path=path}
|
||||||
}
|
}
|
||||||
|
|
@ -25,18 +38,18 @@ scan = {
|
||||||
print("ℹ️ ".. fullpath)
|
print("ℹ️ ".. fullpath)
|
||||||
|
|
||||||
-- scan files
|
-- scan files
|
||||||
local fref = dir.foreach(fullpath, function(path,stat,state,name,kind,ino,off,attr, fullpath)
|
local fref, ferr = dir.foreach(fullpath, function(path,stat,state,name,kind,ino,off,attr, fullpath)
|
||||||
if kind ~= unix.DT_DIR and not scan.shouldHideFile(name) then
|
if kind ~= unix.DT_DIR and not me.shouldHideFile(name) then
|
||||||
experience.file = {stat=stat,state=state,kind=kind,ino=ino,off=off,attr=attr,fullpath=fullpath,name=name,path=path}
|
experience.file = {stat=stat,state=state,kind=kind,ino=ino,off=off,attr=attr,fullpath=fullpath,name=name,path=path}
|
||||||
app.pub("scan_experience_file", nil, experience)
|
app.pub("scan_experience_file", nil, experience)
|
||||||
local size = attr:size()
|
local size = attr:size()
|
||||||
trace("ℹ️ ".. fullpath)
|
trace("ℹ️ ".. fullpath)
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
fref:close()
|
if not ferr then fref:close() end
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
dref:close()
|
if not derr then dref:close() end
|
||||||
|
|
||||||
foreach( app.cache.items, function(k,v)
|
foreach( app.cache.items, function(k,v)
|
||||||
-- remove file attributes (which are closed already)
|
-- remove file attributes (which are closed already)
|
||||||
|
|
@ -46,5 +59,3 @@ scan = {
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
}
|
}
|
||||||
|
|
||||||
return scan
|
|
||||||
|
|
@ -11,12 +11,17 @@ app.on('scan_experience_file', function(exp)
|
||||||
local size = exp.file.attr:size()
|
local size = exp.file.attr:size()
|
||||||
if exp.file.name:match(".glb") and not util.contains(exp.tag,"xrfragment") then
|
if exp.file.name:match(".glb") and not util.contains(exp.tag,"xrfragment") then
|
||||||
print("✅ └─ plugin/janusxr: generating janusxr.xml")
|
print("✅ └─ plugin/janusxr: generating janusxr.xml")
|
||||||
|
table.insert(exp.tag, "janusxr")
|
||||||
|
app.pub("scan_experience_janusxr", nil, {exp=exp, item=item}) -- be nice to other plugins
|
||||||
|
table.insert( app.cache.items, exp)
|
||||||
elseif size < 1000 then -- skip large binary files
|
elseif size < 1000 then -- skip large binary files
|
||||||
local data = util.readfile( exp.file.fullpath)
|
local data = util.readfile( exp.file.fullpath)
|
||||||
if data:match(pattern_fireboxroom) then
|
if data:match(pattern_fireboxroom) then
|
||||||
table.insert(exp.tag, "janusxr")
|
table.insert(exp.tag, "janusxr")
|
||||||
local title = data:match(pattern_title)
|
local title = data:match(pattern_title)
|
||||||
if title then exp.title = title:gsub("<.*","") end
|
if title then
|
||||||
|
exp.title = title:gsub("<.*","")
|
||||||
|
end
|
||||||
app.pub("scan_experience_janusxr", nil, {exp=exp, item=item}) -- be nice to other plugins
|
app.pub("scan_experience_janusxr", nil, {exp=exp, item=item}) -- be nice to other plugins
|
||||||
table.insert( app.cache.items, exp)
|
table.insert( app.cache.items, exp)
|
||||||
print("✅ └─ plugin/janusxr: " .. exp.file.name)
|
print("✅ └─ plugin/janusxr: " .. exp.file.name)
|
||||||
|
|
@ -31,8 +36,15 @@ end)
|
||||||
-- this registers the plugin UI
|
-- this registers the plugin UI
|
||||||
local plugin = {
|
local plugin = {
|
||||||
name = "JanusXR",
|
name = "JanusXR",
|
||||||
info = '<a href="https://coderofsalvation.github.io/janus-guide" target="_blank">janusxr</a> allows composing (interconnected) 3D experiences (via portals) via its JML markup language. It also supports AFRAME (geometry)markup.',
|
info = '<a href="https://coderofsalvation.github.io/janus-guide" target="_blank">JanusXR</a> connects XR experiences via XML portals (+AFRAME (geometry)markup also supported).',
|
||||||
opts = {
|
opts = {
|
||||||
|
{
|
||||||
|
key='app.opts.plugin_janusxr_janusweb_endpoint',
|
||||||
|
title='backend URL',
|
||||||
|
info='Where janusweb (editor) can save its JML/assets while livecoding',
|
||||||
|
default="/janusxr",
|
||||||
|
values=''
|
||||||
|
},
|
||||||
{
|
{
|
||||||
key='app.opts.plugin_janusxr_wrap_image',
|
key='app.opts.plugin_janusxr_wrap_image',
|
||||||
title='wrap images',
|
title='wrap images',
|
||||||
|
|
|
||||||
64
src/plugin/manyfold.lua
Normal file
64
src/plugin/manyfold.lua
Normal file
|
|
@ -0,0 +1,64 @@
|
||||||
|
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)
|
||||||
|
print("content was updated")
|
||||||
|
end)
|
||||||
|
|
||||||
|
-- this registers the plugin UI
|
||||||
|
local plugin = {
|
||||||
|
name = "Manyfold",
|
||||||
|
info = '',
|
||||||
|
opts = {
|
||||||
|
{
|
||||||
|
key='app.opts.plugin_manyfold_instance_1',
|
||||||
|
title='Instance 1 URL',
|
||||||
|
info='<a href="https://manyfold.app/" target="_blank">Manyfold</a> URL goes here (https://xrforge.isvery.ninja e.g.)',
|
||||||
|
default="https://xrforge.isvery.ninja",
|
||||||
|
values=''
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key='app.opts.plugin_manyfold_instance_2',
|
||||||
|
title='Instance 2 URL',
|
||||||
|
info='<a href="https://manyfold.app/" target="_blank">Manyfold</a> URL goes here (https://xrforge.isvery.ninja e.g.)',
|
||||||
|
default="",
|
||||||
|
values=''
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key='app.opts.plugin_manyfold_instance_3',
|
||||||
|
title='Instance 3 URL',
|
||||||
|
info='<a href="https://manyfold.app/" target="_blank">Manyfold</a> URL goes here (https://xrforge.isvery.ninja e.g.)',
|
||||||
|
default="",
|
||||||
|
values=''
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key='app.opts.plugin_manyfold_sync_strategy',
|
||||||
|
title='Instance 3 URL',
|
||||||
|
info="Specify how content from manyfold instances should be fetched",
|
||||||
|
default="recent",
|
||||||
|
values={recent='only recent',all_recent='everything + recent', disabled='disabled'}
|
||||||
|
},
|
||||||
|
--{
|
||||||
|
-- key='app.opts.plugin_manyfold_sync_strategy',
|
||||||
|
-- title='Instance 3 URL',
|
||||||
|
-- info='Specify how content from manyfold instances should be fetched",
|
||||||
|
-- default="recent",
|
||||||
|
-- values={recent='only recent',all_recent='everything + recent', disabled='disabled'}
|
||||||
|
--},
|
||||||
|
--{
|
||||||
|
-- key='app.opts.plugin_manyfold_sync_interval',
|
||||||
|
-- title='Instance 3 URL',
|
||||||
|
-- info='Specify how often xrforge should check for recent items #serveronly'
|
||||||
|
-- default="recent",
|
||||||
|
-- values={recent='only recent',all_recent='everything + recent', disabled='disabled'}
|
||||||
|
--},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return plugin
|
||||||
29
src/plugin/moderation.lua
Normal file
29
src/plugin/moderation.lua
Normal file
|
|
@ -0,0 +1,29 @@
|
||||||
|
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)
|
||||||
|
print("content was updated")
|
||||||
|
end)
|
||||||
|
|
||||||
|
-- this registers the plugin UI
|
||||||
|
local plugin = {
|
||||||
|
name = "Moderation",
|
||||||
|
info = 'Hide certain experiences from the listing (client work e.g.)',
|
||||||
|
opts = {
|
||||||
|
{
|
||||||
|
key='app.opts.plugin_moderation_mutekeywords',
|
||||||
|
title='Mute keywords',
|
||||||
|
info='Space-separated, <a href="http://lua-users.org/wiki/PatternsTutorial" target="_blank">lua patterns</a> are allowed',
|
||||||
|
default = "wip beta -v[0-9] customerX",
|
||||||
|
values=''
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return plugin
|
||||||
Loading…
Add table
Reference in a new issue