Tag creation for approved users

This commit is contained in:
mid 2024-07-18 22:53:44 +03:00
parent bccc0ef742
commit 61fd417013
8 changed files with 213 additions and 34 deletions

View File

@ -65,6 +65,31 @@ local handler = function(req, res)
res:statusCode(403)
end
res:write""
elseif req:path():match"^/addtags/?$" then
if verified and verified.privs >= DB.USER_PRIVS_APPROVED then
local pohst = req:post()
local returndata = {}
if pohst.csrf and DB.csrfverify(verified.id, Escapes.urlunescape(pohst.csrf)) then
local names = pohst["name[]"]
local tcs = pohst["tc[]"]
local adults = pohst["adult[]"]
if #names == #tcs and #names == #adults then
for tag = 1, #names do
local tagid = DB.tagadd(tostring(names[tag]), (tonumber(tcs[tag]) or 0) + 1, tostring(adults[tag]) == "1")
table.insert(returndata, tagid)
end
end
end
res:statusCode(200)
res:write(table.concat(returndata, ","))
else
res:statusCode(403)
res:write""
end
elseif req:path():match"^/verif/?$" then
res:addHeader("Set-Cookie", "sesh=" .. (req.querystring.q or "") .. "; SameSite=Lax; Secure; HttpOnly")
res:addHeader("Location", "/")
@ -183,4 +208,4 @@ local handler = function(req, res)
end
end
return handler
return handler

6
db.lua
View File

@ -416,7 +416,11 @@ local function tagadd(name, category, adultonly)
pingdb()
return not not SQLConn:execute("INSERT INTO tags (name, category, adultonly) VALUES ('" .. SQLConn:escape(name) .. "', " .. category .. ", " .. (adultonly and 1 or 0) .. ");")
local cur = SQLConn:execute("INSERT INTO tags (name, category, adultonly) VALUES ('" .. SQLConn:escape(name) .. "', " .. category .. ", " .. (adultonly and 1 or 0) .. ");")
if not cur then return nil end
return tonumber(SQLConn:getlastautoid())
end
local function regnewobj(ownerid, approved, published)

View File

@ -1,7 +1,7 @@
{% title = BigGlobe.cfg.sitename .. " - generic object database inspired by imageboards" %}
{% function content() %}
<div style="text-align:center;height:50%;margin-top:25%;">
<div style="text-align:center;height:50%;margin-top:25vh;">
<div style="max-width:15cm;display:inline-block;">
<h1>{{ Escapes.htmlescape(BigGlobe.cfg.sitename) }}</h1>
<form action="/search" method="GET" id="searchform">
@ -81,4 +81,4 @@
{% end %}
{# base.inc
{# base.inc

View File

@ -4,6 +4,8 @@
obj = DB.getobj(DB.objshowid(request:path():sub(7, 7 + 31)))
end
local filecount = 0
if obj then
local virt = "/objd/" .. DB.objhideid(obj.id) .. "/"
local phys = DB.urltophysical(virt)
@ -13,6 +15,10 @@
if f ~= "." and f ~= ".." then
local attribs = LFS.attributes(phys .. f)
table.insert(files, {name = f, size = attribs.size, modtime = attribs.modification, phys = phys .. f, virt = virt .. f})
if f ~= ".thumb.jpg" then
filecount = filecount + 1
end
end
end
@ -41,7 +47,7 @@
<div style="display:inline-block;width:40%;vertical-align:top;">
<h2 style="margin-bottom:0;">{{ Escapes.htmlescape(obj.name) }}{% if verified and verified.id == obj.owner then %} <a href="/obje/{{ DB.objhideid(obj.id) }}">(edit)</a>{% end %}</h2>
<h5 style="margin-top:0;"><sup><a href="/user/{{ obj.owner }}">{{ Escapes.htmlescape(owner.displayname) }}</a> <span class="dt">{{ obj.createtime }}</span></sup></h5>
<p>Object has {{#files-1}} files:</p>
<p>Object has {{filecount}} files:</p>
<ul id="filelist">
{% for _,f in pairs(files) do %}
{% if f.name ~= ".thumb.jpg" then %}<li><a onmouseenter="hover(this);" download href="{{ Escapes.htmlescape(f.virt) }}">{{ Escapes.htmlescape(f.name) }}</a> ({{ (f.size + 1023) // 1024 }}kB)</li>{% end %}
@ -159,4 +165,4 @@
{% end %}
{% end %}
{# base.inc
{# base.inc

View File

@ -147,7 +147,7 @@
</div>
<div style="display:inline-block;vertical-align:top;width:49%;">
<h2>Tags</h2>
<div style="position:relative;width:80%;" class="tagbox" data-formid="editform" data-formparaminputid="objtagsparam">
<div style="position:relative;width:80%;" class="tagbox" data-formid="editform" data-formparaminputid="objtagsparam" data-unknowntags="">
<p{% if tagcount > 0 then %} style="visibility:hidden;"{% end %}>Enter tags...</p>
{% for _, tag in pairs(tags) do %}
<div class="tag tc{{ tag.category }}" data-tagid="{{ tag.id }}">{{ Escapes.htmlescape(tag.name) }}</div>
@ -175,15 +175,28 @@
</div>
</form>
<div id="newtagsform" class="hidden">
<p>There are unknown tags in your submission. Please assign them categories.</p>
<table>{% --[[
<select style="border: 0;"><option value="a">A</option><option value="b">B</option></select>
]] %}</table>
<button>Submit</button>
</div>
<script>
{% if BigGlobe.cfg.enable18plus then %}
function upd(over18) {
document.querySelector(".tagbox").setAttribute("data-over18", +over18 != -1)
}
var activeo18 = "-1"
var activeo18
if(window.localStorage.getItem("o18")) {
activeo18 = window.localStorage.getItem("o18")
}
if(!activeo18 || activeo18 == 1) {
activeo18 = 0; // Make "Show 18+", not "Only 18+"
}
upd(activeo18)
document.querySelector("ul.over18 input[type=\"radio\"][value=\"" + activeo18 + "\"]").checked = true
{% end %}
@ -330,13 +343,92 @@
xhr.send(formData)
}
function fixnewtags(afterfunc) {
var newtagsform = document.querySelector("div#newtagsform")
var tbl = newtagsform.querySelector("table")
var newTags = document.querySelectorAll("div.tagbox div.tag.tcNew")
for(var i = 0; i < newTags.length; i++) {
var row = tbl.insertRow(-1)
var sel = document.createElement("select")
{% for tc = 1, #BigGlobe.cfg.tc do if BigGlobe.cfg.tc[tc].name ~= "" then %}
{
var opt = document.createElement("option")
opt.textContent = "{{ Escapes.htmlescape(BigGlobe.cfg.tc[tc].name) }}"
opt.value = {{ tc - 1 }}
sel.appendChild(opt)
}
{% end end %}
var adult = document.createElement("input")
adult.type = "checkbox"
adult.checked = false
adult.id = "boobietc" + i
var adultLbl = document.createElement("label")
adultLbl.htmlFor = adult.id
adultLbl.textContent = "18+"
row.insertCell(-1).appendChild(sel)
row.insertCell(-1).textContent = newTags[i].textContent
var c = row.insertCell(-1)
c.appendChild(adult)
c.appendChild(adultLbl)
}
newtagsform.classList.toggle("hidden")
var btn = newtagsform.querySelector("button")
btn.onclick = function() {
btn.toggleAttribute("disabled")
var xhr = new XMLHttpRequest()
xhr.open("POST", "/addtags", true)
var formData = new FormData()
formData.append("csrf", document.querySelector("[name='csrf']").value)
for(var i = 0; i < newTags.length; i++) {
formData.append("name[]", tbl.rows[i].cells[1].textContent)
formData.append("tc[]", tbl.rows[i].cells[0].querySelector("select").value)
formData.append("adult[]", tbl.rows[i].cells[2].querySelector("input").checked ? "1" : "0")
}
xhr.onreadystatechange = function() {
if(xhr.readyState == XMLHttpRequest.DONE && xhr.status == 200) {
var resp = xhr.responseText.split(",")
// Set data-tagids so they object may be updated with the new tags
for(var i = 0; i < newTags.length; i++) {
newTags[i].setAttribute("data-tagid", resp[i])
}
tagbox_updateformdata()
afterfunc()
}
}
xhr.send(formData);
}
}
document.getElementById("editform").addEventListener("submit", function(ev) {
ev.preventDefault()
files = Array.from(document.querySelectorAll("input[type='file']"))
files.pop() // don't count empty input
files.pop() // don't count empty input box
doremoves()
var newTags = document.querySelectorAll("div.tagbox div.tag.tcNew")
if(newTags.length != 0) {
fixnewtags(doremoves)
} else {
doremoves()
}
})
</script>
<script src="/static/tagbox.js"></script>
@ -348,4 +440,4 @@
{% end %}
{% end %}
{# base.inc
{# base.inc

View File

@ -153,7 +153,15 @@ function Request:parseMultipartFormData(data)
}
end
else
output[name] = itemdata
if name:sub(#name - 1) == "[]" then
if output[name] then
table.insert(output[name], itemdata)
else
output[name] = {itemdata}
end
else
output[name] = itemdata
end
end
end
end

View File

@ -11,6 +11,7 @@ div.tagbox {
user-select: contain;
padding: 0.4em;
border-radius: 3px;
box-sizing: border-box;
}
div.tagbox::after {
content: "penis";
@ -113,6 +114,7 @@ input[type='text'], input[type='number'] {
border: 1px solid gray;
border-radius: 3px;
font-size: 1em;
box-sizing: border-box;
}
input::placeholder {
color: #C0C0C0;
@ -125,6 +127,7 @@ ul.over18 {
padding: 0 0.4em 0 0.4em;
width: 100%;
text-align: center;
box-sizing: border-box;
}
ul.over18 label {
cursor: pointer;
@ -210,4 +213,27 @@ footer {
margin-top: 2em;
width: 100%;
text-align: center;
}
}
div.tag.tcNew::before {
content: "New";
}
div#newtagsform {
position: fixed;
width: 50vw;
height: 50vh;
top: 25vh;
left: 25vw;
background: white;
border: 1px solid black;
padding: 1em;
}
div#newtagsform select {
border: 0;
}
.hidden {
visibility: hidden;
}

View File

@ -1,13 +1,28 @@
function createtag(name, tc, tagid) {
var newtag = document.createElement("div")
newtag.classList.toggle("tag")
newtag.classList.toggle("tc" + (tc || "New"))
if(tagid) {
newtag.setAttribute("data-tagid", tagid)
}
newtag.innerText = name
return newtag
}
var acTimer;
document.getElementById(document.querySelector("div.tagbox").getAttribute("data-formid")).addEventListener("submit", function(ev) {
function tagbox_updateformdata() {
document.getElementById(document.querySelector("div.tagbox").getAttribute("data-formparaminputid")).value = Array.from(document.querySelectorAll("div.tagbox div.tag")).map(function(a) {return a.getAttribute("data-tagid")}).join(",")
})
}
document.getElementById(document.querySelector("div.tagbox").getAttribute("data-formid")).addEventListener("submit", tagbox_updateformdata)
document.querySelector("div.tagbox").onclick = function(ev) {
this.querySelector('span').focus()
}
var UnknownTagsMode = document.querySelector("div.tagbox").getAttribute("data-unknowntags") !== null
document.querySelector("div.tagbox span").onkeydown = function(ev) {
if(ev.keyCode == 13) {
ev.preventDefault()
@ -15,16 +30,18 @@ document.querySelector("div.tagbox span").onkeydown = function(ev) {
if(document.querySelector("div.tagbox span").innerText.length) {
var from = document.querySelector("div.autocomplete > div.tag.selected") || document.querySelectorAll("div.autocomplete > div.tag")[0]
if(!document.querySelector("div.tagbox div.tag[data-tagid='"+from.getAttribute("data-tagid")+"']")) {
from.classList.remove("selected")
document.querySelector("div.tagbox span").innerText = ""
document.querySelector("div.tagbox").insertBefore(from, document.querySelector("div.tagbox span"))
document.querySelector("div.tagbox").insertBefore(document.createTextNode("\n"), document.querySelector("div.tagbox span"))
var ac = document.querySelector(".autocomplete")
ac.style.visibility = "hidden"
if(from) {
if(!document.querySelector("div.tagbox div.tag[data-tagid='"+from.getAttribute("data-tagid")+"']")) {
from.classList.remove("selected")
document.querySelector("div.tagbox").insertBefore(from, document.querySelector("div.tagbox span"))
document.querySelector("div.tagbox").insertBefore(document.createTextNode("\n"), document.querySelector("div.tagbox span"))
}
}
document.querySelector("div.tagbox span").innerText = ""
var ac = document.querySelector(".autocomplete")
ac.style.visibility = "hidden"
} else {
document.getElementById(document.querySelector("div.tagbox").getAttribute("data-formid")).querySelector("input[type='submit']").click()
}
@ -75,16 +92,17 @@ document.querySelector("div.tagbox span").oninput = function(ev) {
var ac = document.querySelector(".autocomplete")
ac.style.visibility = ""
while(ac.firstChild) ac.removeChild(ac.firstChild)
if(ajax.responseText == "") {
if(!UnknownTagsMode && ajax.responseText == "") {
ac.innerText = "No such tags found"
} else ajax.responseText.split("\n").slice(0, -1).forEach(function(line) {
var newtag = document.createElement("div")
newtag.classList.toggle("tag")
newtag.classList.toggle("tc" + line.split(",")[2])
newtag.setAttribute("data-tagid", line.split(",")[0])
newtag.innerText = line.split(",")[1]
ac.insertBefore(newtag, null)
})
} else {
ajax.responseText.split("\n").slice(0, -1).forEach(function(line) {
ac.insertBefore(createtag(line.split(",")[1], line.split(",")[2], line.split(",")[0]), null)
})
if(UnknownTagsMode) {
ac.insertBefore(createtag(document.querySelector("div.tagbox span").innerText, null, null), null);
}
}
}
}
ajax.send()
@ -97,4 +115,4 @@ document.querySelector("div.tagbox span").oninput = function(ev) {
clearTimeout(acTimer)
acTimer = undefined
}
}
}