mirror of
https://github.com/20kdc/OC-KittenOS.git
synced 2024-11-23 10:58:06 +11:00
Hopefully work out a corrected solution to the legal fun, and fix control, textedit, taskmgr, neoux, everest and glacier
taskmgr and textedit had issues with the DEL key neoux needed clipboard support that worked control... I forget glacier missed a pcall everest's launcher change support wasn't working compliance.lua was accused of being itself
This commit is contained in:
parent
380c325691
commit
de822181bc
11
README.md
11
README.md
@ -41,15 +41,18 @@ The contents of the repository/docs/licensing files represent a "full text" for
|
|||||||
|
|
||||||
It is assumed that this is sufficient.
|
It is assumed that this is sufficient.
|
||||||
|
|
||||||
A user with access to a package requiring newer licensing information that does not update their licensing package is assumed to have made a willing choice.
|
A separate package is used for each license such that the user must go out of their way to not download the license.
|
||||||
|
|
||||||
If you find this assumption to be incorrect, please request the removal of the affected packages.
|
|
||||||
|
|
||||||
The limitations of OpenComputers affect the available choices here, and having separate license copies for each package is not an available choice.
|
The limitations of OpenComputers affect the available choices here, and having separate license copies for each package is not an available choice.
|
||||||
|
|
||||||
Nor is having a separate license package for each individual license, unless you would prefer an unbrowsable repository.
|
Nor is having a separate license package for each individual license, unless you would prefer an unbrowsable repository.
|
||||||
|
|
||||||
The contents of the repository/docs/repo-authors file contains a full list of authorship and licensing information, per-file.
|
The contents of the repository/docs/repoauthors folder
|
||||||
|
is a human-readable per-package manifest of all files and their
|
||||||
|
licenses.
|
||||||
|
|
||||||
|
If you find this uncompliant with the license of a package,
|
||||||
|
please request the removal of the affected packages.
|
||||||
|
|
||||||
## About NOTE-TO-MS.asc
|
## About NOTE-TO-MS.asc
|
||||||
|
|
||||||
|
@ -114,10 +114,11 @@ local advPlusH = false
|
|||||||
|
|
||||||
local function advAsker(info, def, r, parent)
|
local function advAsker(info, def, r, parent)
|
||||||
info = unicode.safeTextFormat(info)
|
info = unicode.safeTextFormat(info)
|
||||||
|
local ww = math.max(25, unicode.len(info))
|
||||||
return function ()
|
return function ()
|
||||||
return 25, 2, nil, neoux.tcwindow(25, 2, {
|
return ww, 2, nil, neoux.tcwindow(ww, 2, {
|
||||||
neoux.tcrawview(1, 1, {info}),
|
neoux.tcrawview(1, 1, {info}),
|
||||||
neoux.tcfield(1, 2, 25, function (tx)
|
neoux.tcfield(1, 2, ww, function (tx)
|
||||||
def = tx or def
|
def = tx or def
|
||||||
return def
|
return def
|
||||||
end)
|
end)
|
||||||
|
@ -78,7 +78,7 @@ while true do
|
|||||||
end
|
end
|
||||||
if n[3] == "key" then
|
if n[3] == "key" then
|
||||||
if n[6] then
|
if n[6] then
|
||||||
if n[4] == 8 or n[4] == 127 then
|
if n[4] == 8 or n[5] == 211 then
|
||||||
if consistentProcList[camY] then
|
if consistentProcList[camY] then
|
||||||
kill(consistentProcList[camY][1])
|
kill(consistentProcList[camY][1])
|
||||||
end
|
end
|
||||||
|
@ -339,24 +339,24 @@ local function ev_key(ka, kc, down)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
-- Letters
|
-- Letters
|
||||||
if ka ~= 0 then
|
if ka == 8 or kc == 211 then
|
||||||
if ka == 8 then
|
if cursorX == 1 then
|
||||||
if cursorX == 1 then
|
if cursorY == 1 then
|
||||||
if cursorY == 1 then
|
return false
|
||||||
return false
|
|
||||||
end
|
|
||||||
local l = table.remove(lines, cursorY)
|
|
||||||
cursorY = cursorY - 1
|
|
||||||
cursorX = unicode.len(lines[cursorY]) + 1
|
|
||||||
lines[cursorY] = lines[cursorY] .. l
|
|
||||||
else
|
|
||||||
local a, b = splitCur()
|
|
||||||
a = unicode.sub(a, 1, unicode.len(a) - 1)
|
|
||||||
lines[cursorY] = a.. b
|
|
||||||
cursorX = cursorX - 1
|
|
||||||
end
|
end
|
||||||
return true
|
local l = table.remove(lines, cursorY)
|
||||||
|
cursorY = cursorY - 1
|
||||||
|
cursorX = unicode.len(lines[cursorY]) + 1
|
||||||
|
lines[cursorY] = lines[cursorY] .. l
|
||||||
|
else
|
||||||
|
local a, b = splitCur()
|
||||||
|
a = unicode.sub(a, 1, unicode.len(a) - 1)
|
||||||
|
lines[cursorY] = a.. b
|
||||||
|
cursorX = cursorX - 1
|
||||||
end
|
end
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
if ka ~= 0 then
|
||||||
putLetter(unicode.char(ka))
|
putLetter(unicode.char(ka))
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
@ -619,6 +619,18 @@ everestSessionProvider(function (pkg, pid, sendSig)
|
|||||||
end)
|
end)
|
||||||
-- THE EVEREST SESSION API ENDS
|
-- THE EVEREST SESSION API ENDS
|
||||||
|
|
||||||
|
local function startLauncher()
|
||||||
|
if not waitingShutdownCallback then
|
||||||
|
local lApp = "app-launcher"
|
||||||
|
if savingThrow then
|
||||||
|
lApp = lApp or savingThrow.getSetting("sys-everest.launcher")
|
||||||
|
end
|
||||||
|
if lApp then
|
||||||
|
neo.executeAsync(lApp)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
-- WM shortcuts are:
|
-- WM shortcuts are:
|
||||||
-- Alt-Z: Switch surface
|
-- Alt-Z: Switch surface
|
||||||
-- Alt-Enter: Launcher
|
-- Alt-Enter: Launcher
|
||||||
@ -678,12 +690,8 @@ local function key(ku, ka, kc, down)
|
|||||||
return
|
return
|
||||||
end
|
end
|
||||||
if ka == 13 then
|
if ka == 13 then
|
||||||
if down and (not waitingShutdownCallback) then
|
if down then
|
||||||
local lApp = "app-launcher"
|
startLauncher()
|
||||||
if savingThrow then
|
|
||||||
lApp = savingThrow.getSetting("sys-everest.launcher") or lApp
|
|
||||||
end
|
|
||||||
neo.executeAsync(lApp)
|
|
||||||
end
|
end
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
@ -743,7 +751,7 @@ while not shuttingDown do
|
|||||||
changeFocus(os)
|
changeFocus(os)
|
||||||
ns[6]("touch", lx, ly, ix, iy, s[5])
|
ns[6]("touch", lx, ly, ix, iy, s[5])
|
||||||
else
|
else
|
||||||
if s[5] == 1 and not waitingShutdownCallback then neo.executeAsync("app-launcher") end
|
if s[5] == 1 then startLauncher() end
|
||||||
end
|
end
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
|
@ -41,6 +41,7 @@ local settings = {
|
|||||||
password = "",
|
password = "",
|
||||||
["pub.clipboard"] = "",
|
["pub.clipboard"] = "",
|
||||||
["sys-init.shell"] = "sys-everest",
|
["sys-init.shell"] = "sys-everest",
|
||||||
|
["sys-everest.launcher"] = "app-launcher",
|
||||||
["run.sys-icecap"] = "yes",
|
["run.sys-icecap"] = "yes",
|
||||||
-- scr.w/h/d/t.<uuid>
|
-- scr.w/h/d/t.<uuid>
|
||||||
}
|
}
|
||||||
@ -343,7 +344,7 @@ end
|
|||||||
rescanDevs()
|
rescanDevs()
|
||||||
|
|
||||||
-- Save any settings made during the above (or just the language)
|
-- Save any settings made during the above (or just the language)
|
||||||
saveSettings()
|
pcall(saveSettings)
|
||||||
-- --
|
-- --
|
||||||
|
|
||||||
glacierDCProvider(function (pkg, pid, sendSig)
|
glacierDCProvider(function (pkg, pid, sendSig)
|
||||||
|
@ -8,23 +8,27 @@
|
|||||||
readBufSize = 2048
|
readBufSize = 2048
|
||||||
|
|
||||||
-- A function used for logging, usable by programs.
|
-- A function used for logging, usable by programs.
|
||||||
emergencyFunction = function () end
|
emergencyFunction = function (...)
|
||||||
|
computer.pushSignal("_kosneo_syslog", "kernel", ...)
|
||||||
|
if ocemu.log then
|
||||||
|
pcall(ocemu.log, ...)
|
||||||
|
end
|
||||||
|
end
|
||||||
-- Comment this out if you don't want programs to have
|
-- Comment this out if you don't want programs to have
|
||||||
-- access to ocemu's logger.
|
-- access to ocemu's logger.
|
||||||
ocemu = component.list("ocemu", true)()
|
ocemu = (component.list("ocemu", true)()) or (component.list("sandbox", true)())
|
||||||
if ocemu then
|
if ocemu then
|
||||||
ocemu = component.proxy(ocemu)
|
ocemu = component.proxy(ocemu)
|
||||||
emergencyFunction = ocemu.log
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- It is a really bad idea to remove this.
|
||||||
|
-- If the code inside this block even executes, then removing it is a security risk.
|
||||||
if load(string.dump(function()end)) then
|
if load(string.dump(function()end)) then
|
||||||
-- This is your first and only warning.
|
emergencyFunction("detected bytecode access, preventing (only remove this block if you trust every app ever on your KittenOS NEO system)")
|
||||||
-- allowBytecode has effects *outside the game.*
|
local oldLoad = load
|
||||||
-- If it is enabled, any program with load can take over your host system.
|
load = function (c, n, m, ...)
|
||||||
-- I refuse to allow KittenOS NEO to operate in this environment for your safety.
|
return oldLoad(c, n, "t", ...)
|
||||||
-- If you are truly unable to change it, tell someone who can.
|
end
|
||||||
emergencyFunction("Please set allowBytecode = false in OCEmu config.")
|
|
||||||
error("Please set allowBytecode = false in OC config.")
|
|
||||||
end
|
end
|
||||||
|
|
||||||
primaryDisk = component.proxy(computer.getBootAddress())
|
primaryDisk = component.proxy(computer.getBootAddress())
|
||||||
@ -378,15 +382,6 @@ function runProgramPolicy(ipkg, pkg, pid, ...)
|
|||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
-- This is hidden here to protect you.
|
|
||||||
-- I beg of you, don't remove it.
|
|
||||||
if load(string.dump(function()end)) then
|
|
||||||
local oldLoad = load
|
|
||||||
load = function (c, n, m, ...)
|
|
||||||
return oldLoad(c, n, "t", ...)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function retrieveAccess(perm, pkg, pid)
|
function retrieveAccess(perm, pkg, pid)
|
||||||
-- Return the access lib and the death callback.
|
-- Return the access lib and the death callback.
|
||||||
|
|
||||||
|
@ -35,7 +35,7 @@ newNeoux = function (event, neo)
|
|||||||
rtt = rt
|
rtt = rt
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
local tag = neo.requestAccess("x.neo.pub.base").showFileDialogAsync(forWrite)
|
local tag = neo.requireAccess("x.neo.pub.base", "filedialog").showFileDialogAsync(forWrite)
|
||||||
local f
|
local f
|
||||||
f = function (_, fd, tg, re)
|
f = function (_, fd, tg, re)
|
||||||
if fd == "filedialog" then
|
if fd == "filedialog" then
|
||||||
@ -347,7 +347,14 @@ newNeoux = function (event, neo)
|
|||||||
selectable = true,
|
selectable = true,
|
||||||
key = function (window, update, a, c, d, f)
|
key = function (window, update, a, c, d, f)
|
||||||
if d then
|
if d then
|
||||||
if a == 8 then
|
if c == 63 then
|
||||||
|
neo.requireAccess("x.neo.pub.globals", "clipboard").setSetting("clipboard", textprop())
|
||||||
|
elseif c == 64 then
|
||||||
|
local contents = neo.requireAccess("x.neo.pub.globals", "clipboard").getSetting("clipboard")
|
||||||
|
contents = contents:match("^[^\r\n]*")
|
||||||
|
textprop(contents)
|
||||||
|
update()
|
||||||
|
elseif a == 8 then
|
||||||
local str = textprop()
|
local str = textprop()
|
||||||
textprop(unicode.sub(str, 1, unicode.len(str) - 1))
|
textprop(unicode.sub(str, 1, unicode.len(str) - 1))
|
||||||
update()
|
update()
|
||||||
@ -359,6 +366,11 @@ newNeoux = function (event, neo)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
end,
|
end,
|
||||||
|
clipboard = function (window, update, contents)
|
||||||
|
contents = contents:match("^[^\r\n]*")
|
||||||
|
textprop(contents)
|
||||||
|
update()
|
||||||
|
end,
|
||||||
line = function (window, x, y, lind, bg, fg, selected)
|
line = function (window, x, y, lind, bg, fg, selected)
|
||||||
local fg1 = fg
|
local fg1 = fg
|
||||||
if selected then
|
if selected then
|
||||||
|
32
compliance.lua
Normal file
32
compliance.lua
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
-- KittenOS NEO Repository Compliance Check Tool
|
||||||
|
-- I'm still not a lawyer
|
||||||
|
local filesAccountedFor = {
|
||||||
|
["repository/data/app-claw/local.lua"] = 0,
|
||||||
|
["repository/inst.lua"] = 0
|
||||||
|
}
|
||||||
|
local f = io.popen("find repository/docs/repoauthors -type f", "r")
|
||||||
|
while true do
|
||||||
|
local s = f:read()
|
||||||
|
if not s then f:close() break end
|
||||||
|
filesAccountedFor[s] = s
|
||||||
|
local f2 = io.open(s, "r")
|
||||||
|
while true do
|
||||||
|
local s2 = f2:read()
|
||||||
|
if not s2 then
|
||||||
|
f2:close()
|
||||||
|
break
|
||||||
|
end
|
||||||
|
local st = s2:match("^[^:]+")
|
||||||
|
if st then
|
||||||
|
filesAccountedFor[st] = s
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
f = io.popen("find repository -type f", "r")
|
||||||
|
while true do
|
||||||
|
local s = f:read()
|
||||||
|
if not s then f:close() return end
|
||||||
|
if not filesAccountedFor[s] then
|
||||||
|
print("File wasn't accounted for: " .. s)
|
||||||
|
end
|
||||||
|
end
|
@ -5,42 +5,32 @@
|
|||||||
-- Authors: 20kdc
|
-- Authors: 20kdc
|
||||||
|
|
||||||
return {
|
return {
|
||||||
["licensing"] = {
|
|
||||||
desc = "Legal compliance package, dependency of everything in the repository",
|
|
||||||
v = 0,
|
|
||||||
deps = {
|
|
||||||
},
|
|
||||||
dirs = {
|
|
||||||
"docs",
|
|
||||||
"docs/licensing"
|
|
||||||
},
|
|
||||||
files = {
|
|
||||||
"docs/repo-authors",
|
|
||||||
"docs/licensing/Public Domain"
|
|
||||||
},
|
|
||||||
},
|
|
||||||
["app-eeprog"] = {
|
["app-eeprog"] = {
|
||||||
desc = "Example program: EEPROM programmer / copier",
|
desc = "Example program: EEPROM programmer / copier",
|
||||||
v = 0,
|
v = 0,
|
||||||
deps = {
|
deps = {
|
||||||
"neo",
|
"neo",
|
||||||
"licensing"
|
"zzz-license-pd"
|
||||||
},
|
},
|
||||||
dirs = {
|
dirs = {
|
||||||
"apps"
|
"apps",
|
||||||
|
"docs",
|
||||||
|
"docs/repoauthors"
|
||||||
},
|
},
|
||||||
files = {
|
files = {
|
||||||
"apps/app-eeprog.lua"
|
"apps/app-eeprog.lua",
|
||||||
|
"docs/repoauthors/app-eeprog"
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
["neo-docs"] = {
|
["neo-docs"] = {
|
||||||
desc = "KittenOS NEO system documentation",
|
desc = "KittenOS NEO system documentation",
|
||||||
v = 2,
|
v = 2,
|
||||||
deps = {
|
deps = {
|
||||||
"licensing"
|
"zzz-license-pd"
|
||||||
},
|
},
|
||||||
dirs = {
|
dirs = {
|
||||||
"docs"
|
"docs",
|
||||||
|
"docs/repoauthors"
|
||||||
},
|
},
|
||||||
files = {
|
files = {
|
||||||
"docs/an-intro",
|
"docs/an-intro",
|
||||||
@ -59,7 +49,8 @@ return {
|
|||||||
"docs/ul-neoux",
|
"docs/ul-neoux",
|
||||||
"docs/ul-brail",
|
"docs/ul-brail",
|
||||||
"docs/ul-bmp__",
|
"docs/ul-bmp__",
|
||||||
"docs/gp-pedan"
|
"docs/gp-pedan",
|
||||||
|
"docs/repoauthors/neo-docs"
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
["app-nbox2018"] = {
|
["app-nbox2018"] = {
|
||||||
@ -67,14 +58,17 @@ return {
|
|||||||
v = 0,
|
v = 0,
|
||||||
deps = {
|
deps = {
|
||||||
"neo",
|
"neo",
|
||||||
"licensing"
|
"zzz-license-pd"
|
||||||
},
|
},
|
||||||
dirs = {
|
dirs = {
|
||||||
"apps"
|
"apps",
|
||||||
|
"docs",
|
||||||
|
"docs/repoauthors"
|
||||||
},
|
},
|
||||||
files = {
|
files = {
|
||||||
"apps/app-nbox2018.lua",
|
"apps/app-nbox2018.lua",
|
||||||
"apps/app-nprt2018.lua"
|
"apps/app-nprt2018.lua",
|
||||||
|
"docs/repoauthors/app-nbox2018"
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
["svc-ghostie"] = {
|
["svc-ghostie"] = {
|
||||||
@ -82,14 +76,32 @@ return {
|
|||||||
v = 0,
|
v = 0,
|
||||||
deps = {
|
deps = {
|
||||||
"neo",
|
"neo",
|
||||||
"licensing"
|
"zzz-license-pd"
|
||||||
},
|
},
|
||||||
dirs = {
|
dirs = {
|
||||||
"apps"
|
"apps",
|
||||||
|
"docs",
|
||||||
|
"docs/repoauthors"
|
||||||
},
|
},
|
||||||
files = {
|
files = {
|
||||||
"apps/svc-ghostie.lua",
|
"apps/svc-ghostie.lua",
|
||||||
"apps/app-ghostcall.lua"
|
"apps/app-ghostcall.lua",
|
||||||
|
"docs/repoauthors/svc-ghostie"
|
||||||
|
},
|
||||||
|
},
|
||||||
|
-- licenses (MUST BE IMMUTABLE)
|
||||||
|
["zzz-license-pd"] = {
|
||||||
|
desc = "license file 'Public Domain'",
|
||||||
|
v = 0,
|
||||||
|
deps = {
|
||||||
|
"zzz-license",
|
||||||
|
},
|
||||||
|
dirs = {
|
||||||
|
"docs",
|
||||||
|
"docs/licensing"
|
||||||
|
},
|
||||||
|
files = {
|
||||||
|
"docs/licensing/Public Domain"
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -374,6 +374,15 @@ h.*(...):
|
|||||||
Hardware signals, by type, such
|
Hardware signals, by type, such
|
||||||
as "h.key_up"
|
as "h.key_up"
|
||||||
|
|
||||||
|
h._kosneo_syslog("kernel", ...):
|
||||||
|
System log entry. This is actually
|
||||||
|
generated by the kernel as part of
|
||||||
|
the emergency function processing.
|
||||||
|
Note the "kernel" component address.
|
||||||
|
The other parameters are the values
|
||||||
|
given to the emergency function.
|
||||||
|
You should tostring all of these.
|
||||||
|
|
||||||
With that, I hope I have documented
|
With that, I hope I have documented
|
||||||
the kernel's interface to programs.
|
the kernel's interface to programs.
|
||||||
|
|
||||||
|
2
repository/docs/repoauthors/app-eeprog
Normal file
2
repository/docs/repoauthors/app-eeprog
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
repository/apps/app-eeprog.lua: 20kdc, Public Domain
|
||||||
|
|
3
repository/docs/repoauthors/app-nbox2018
Normal file
3
repository/docs/repoauthors/app-nbox2018
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
repository/apps/app-nbox2018.lua: 20kdc, Public Domain
|
||||||
|
repository/apps/app-nprt2018.lua: 20kdc, Public Domain
|
||||||
|
|
@ -1,17 +1,9 @@
|
|||||||
repository/apps/app-eeprog.lua: 20kdc, Public Domain
|
|
||||||
repository/apps/app-ghostcall.lua: 20kdc, Public Domain
|
|
||||||
repository/apps/app-nbox2018.lua: 20kdc, Public Domain
|
|
||||||
repository/apps/app-nprt2018.lua: 20kdc, Public Domain
|
|
||||||
repository/apps/svc-ghostie.lua: 20kdc, Public Domain
|
|
||||||
repository/data/app-claw/local.lua: 20kdc, Public Domain
|
|
||||||
repository/docs/licensing/Public Domain: 20kdc, Public Domain
|
|
||||||
repository/docs/an-intro: 20kdc, Public Domain
|
repository/docs/an-intro: 20kdc, Public Domain
|
||||||
repository/docs/gp-pedan: 20kdc, Public Domain
|
repository/docs/gp-pedan: 20kdc, Public Domain
|
||||||
repository/docs/kn-intro: 20kdc, Public Domain
|
repository/docs/kn-intro: 20kdc, Public Domain
|
||||||
repository/docs/kn-perms: 20kdc, Public Domain
|
repository/docs/kn-perms: 20kdc, Public Domain
|
||||||
repository/docs/kn-refer: 20kdc, Public Domain
|
repository/docs/kn-refer: 20kdc, Public Domain
|
||||||
repository/docs/kn-sched: 20kdc, Public Domain
|
repository/docs/kn-sched: 20kdc, Public Domain
|
||||||
repository/docs/repo-authors: 20kdc, Public Domain
|
|
||||||
repository/docs/ul-bmp__: 20kdc, Public Domain
|
repository/docs/ul-bmp__: 20kdc, Public Domain
|
||||||
repository/docs/ul-brail: 20kdc, Public Domain
|
repository/docs/ul-brail: 20kdc, Public Domain
|
||||||
repository/docs/ul-event: 20kdc, Public Domain
|
repository/docs/ul-event: 20kdc, Public Domain
|
3
repository/docs/repoauthors/svc-ghostie
Normal file
3
repository/docs/repoauthors/svc-ghostie
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
repository/apps/app-ghostcall.lua: 20kdc, Public Domain
|
||||||
|
repository/apps/svc-ghostie.lua: 20kdc, Public Domain
|
||||||
|
|
1
repository/docs/repoauthors/zzz-license-pd
Normal file
1
repository/docs/repoauthors/zzz-license-pd
Normal file
@ -0,0 +1 @@
|
|||||||
|
repository/docs/licensing/Public Domain: 20kdc, Public Domain
|
Loading…
Reference in New Issue
Block a user