aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authormetamuffin <metamuffin@disroot.org>2024-11-04 20:05:04 +0100
committermetamuffin <metamuffin@disroot.org>2024-11-04 20:06:25 +0100
commitc78ca2592dd35703d4f61f4c0b78481135e029eb (patch)
treebff207ffc5aacc357f475f561d083dacf89828ad
parent7dc2354e497c472fca0ebf6e75abe672dd9278b4 (diff)
downloadabrechenbarkeit-c78ca2592dd35703d4f61f4c0b78481135e029eb.tar
abrechenbarkeit-c78ca2592dd35703d4f61f4c0b78481135e029eb.tar.bz2
abrechenbarkeit-c78ca2592dd35703d4f61f4c0b78481135e029eb.tar.zst
a
-rwxr-xr-xabrechenbarkeit.lua152
-rw-r--r--locale/en.ini1
2 files changed, 90 insertions, 63 deletions
diff --git a/abrechenbarkeit.lua b/abrechenbarkeit.lua
index deaaf34..d29cf20 100755
--- a/abrechenbarkeit.lua
+++ b/abrechenbarkeit.lua
@@ -15,7 +15,7 @@
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
-]]--
+]] --
local function escape(s)
return s:gsub("<", "&lt;"):gsub("<", "&lt;")
@@ -63,17 +63,50 @@ local function load_config()
return config
end
+local function load_translations(langs)
+ local t = {}
+ for _, lcode in ipairs(langs) do
+ local file = io.open(string.format("locale/%s.ini", lcode), "r")
+ if file ~= nil then
+ for l in file:lines("l") do
+ if l ~= "" then
+ local key, value = string.match(l, "^([^=]+)=([^=]*)")
+ if key ~= nil and value ~= nil then
+ t[key] = value
+ end
+ end
+ end
+ end
+ end
+ return t
+end
+
local config = load_config()
local path = os.getenv("PATH_INFO")
local method = os.getenv("REQUEST_METHOD")
local query = parse_query(os.getenv("QUERY_STRING"))
+local translations = load_translations({ "en", config.language })
local stylesheet = io.open("style.css"):read("a")
local script = io.open("script.js"):read("a")
+local function format(template, params)
+ return string.gsub(template, "{([%w\\.!]+)}", function(n)
+ local raw = n:sub(1,1) ~= "!"
+ if raw then n = n:sub(2) end
+ local s = format(params[n] or translations[n] or "NIL TEMPLATE", params)
+ return raw and s or escape(s)
+ end)
+end
+
+local function format_amount(amount, tag, classes)
+ local s = string.format("%.02f%s", amount / 100, config.unit or "€")
+ if tag == nil then return s end
+ return string.format([[<%s class="amount-%s %s">%s</%s>"]], tag, amount >= 0 and "pos" or "neg", s, tag, classes or "")
+end
+
local function get_user_theme(username)
local c = ""
- local js = ""
if username == "_jeb" then
c = "html { animation: 2s jeb infinite; }"
c = c .. "@keyframes jeb {\n"
@@ -84,24 +117,29 @@ local function get_user_theme(username)
elseif username == "Dinnerbone" then
c = "html { transform: scale(-1); } "
end
- return c, js
+ return c
end
-local function respond(status, title, body)
- local themecss, themejs = get_user_theme(path and path:sub(2))
+local function format_duration(t)
+ if t > 86400 then return string.format("%d day%s", t / 86400, math.floor(t / 86400) ~= 1 and "s" or "") end
+ if t > 3600 then return string.format("%d hour%s", t / 3600, math.floor(t / 3600) ~= 1 and "s" or "") end
+ if t > 60 then return string.format("%d minute%s", t / 60, math.floor(t / 60) ~= 1 and "s" or "") end
+ return string.format("%d seconds", t)
+end
+local function respond(status, title, body)
print(string.format("Status: %d", status))
print("Content-Type: text/html")
print("")
- print(string.format([[
+ print(format([[
<!DOCTYPE html>
<html><head>
- <title>%s</title>
+ <title>{title}</title>
<meta charset="utf-8" />
- <style>%s</style>
- <style>%s</style>
- <script>%s</script>
- %s
+ <style>{style}</style>
+ <style>{user_style}</style>
+ <script>{script}</script>
+ {head_extra}
</head>
<body>
<nav>
@@ -110,23 +148,21 @@ local function respond(status, title, body)
<path d="M3 4.5a.5.5 0 0 1 .5-.5h6a.5.5 0 1 1 0 1h-6a.5.5 0 0 1-.5-.5m0 2a.5.5 0 0 1 .5-.5h6a.5.5 0 1 1 0 1h-6a.5.5 0 0 1-.5-.5m0 2a.5.5 0 0 1 .5-.5h6a.5.5 0 1 1 0 1h-6a.5.5 0 0 1-.5-.5m0 2a.5.5 0 0 1 .5-.5h6a.5.5 0 0 1 0 1h-6a.5.5 0 0 1-.5-.5m0 2a.5.5 0 0 1 .5-.5h6a.5.5 0 0 1 0 1h-6a.5.5 0 0 1-.5-.5M11.5 4a.5.5 0 0 0 0 1h1a.5.5 0 0 0 0-1zm0 2a.5.5 0 0 0 0 1h1a.5.5 0 0 0 0-1zm0 2a.5.5 0 0 0 0 1h1a.5.5 0 0 0 0-1zm0 2a.5.5 0 0 0 0 1h1a.5.5 0 0 0 0-1zm0 2a.5.5 0 0 0 0 1h1a.5.5 0 0 0 0-1z"/>
<path d="M2.354.646a.5.5 0 0 0-.801.13l-.5 1A.5.5 0 0 0 1 2v13H.5a.5.5 0 0 0 0 1h15a.5.5 0 0 0 0-1H15V2a.5.5 0 0 0-.053-.224l-.5-1a.5.5 0 0 0-.8-.13L13 1.293l-.646-.647a.5.5 0 0 0-.708 0L11 1.293l-.646-.647a.5.5 0 0 0-.708 0L9 1.293 8.354.646a.5.5 0 0 0-.708 0L7 1.293 6.354.646a.5.5 0 0 0-.708 0L5 1.293 4.354.646a.5.5 0 0 0-.708 0L3 1.293zm-.217 1.198.51.51a.5.5 0 0 0 .707 0L4 1.707l.646.647a.5.5 0 0 0 .708 0L6 1.707l.646.647a.5.5 0 0 0 .708 0L8 1.707l.646.647a.5.5 0 0 0 .708 0L10 1.707l.646.647a.5.5 0 0 0 .708 0L12 1.707l.646.647a.5.5 0 0 0 .708 0l.509-.51.137.274V15H2V2.118z"/>
</svg>
- Abrechenbarkeit</a>
- <a href="/?log">Log</a>
- <a href="/?products">Products</a>
- <a href="/?about">About</a>
+ {appname}
+ </a>
+ <a href="/?log">{log}</a>
+ <a href="/?products">{products}</a>
+ <a href="/?about">{about}</a>
</nav>
- ]],
- escape(title),
- stylesheet, -- style.css
- themecss, -- theme for user
- script, -- script.js
- config.head_extra or ""
- ))
+ ]], {
+ title = escape(title),
+ style = stylesheet,
+ user_style = get_user_theme(path and path:sub(2)),
+ script = script,
+ head_extra = config.head_extra or ""
+ }))
body()
- print(string.format(
- "<script>%s</script></body></html>",
- themejs
- ))
+ print("</body></html>")
end
local function error_box(message)
@@ -149,13 +185,6 @@ local function form_data()
return parse_query(io.read())
end
-local function format_duration(t)
- if t > 86400 then return string.format("%d day%s", t / 86400, math.floor(t / 86400) ~= 1 and "s" or "") end
- if t > 3600 then return string.format("%d hour%s", t / 3600, math.floor(t / 3600) ~= 1 and "s" or "") end
- if t > 60 then return string.format("%d minute%s", t / 60, math.floor(t / 60) ~= 1 and "s" or "") end
- return string.format("%d seconds", t)
-end
-
local function read_log()
local log = io.open("log", "r")
if log == nil then
@@ -285,14 +314,15 @@ local function r_user_post(username)
end
log:flush()
log:close()
- return string.format([[
- <div class="notif"><p>Transaction successful: <strong class="amount-%s">%.02f€</strong> (%s)</p></div>
- <audio src="%s" autoplay></audio>
- ]],
- amount >= 0 and "pos" or "neg", amount / 100,
- escape(comment),
- config.transaction_sound or ""
- )
+ return format([[
+ <div class="notif"><p>Transaction successful: {amount} ({!comment})</p></div>
+ <audio src="{sound}" autoplay></audio>
+ ]], {
+ sign = amount >= 0 and "pos" or "neg",
+ amount = format_amount(amount, "strong"),
+ comment = escape(comment),
+ sound = config.transaction_sound or ""
+ })
end
local function r_user(username)
@@ -301,24 +331,20 @@ local function r_user(username)
notif = r_user_post(username)
end
return respond(200, string.format("Abrechenbarheit: %s", username), function()
- print(string.format("<h1>%s</h1>", username))
+ print(format("<h1>{username}</h1>", { username = username }))
local balance = balances()[username]
local last_txn = last_txns()[username]
local new_user = balance == nil
balance = balance or 0
if notif then print(notif) end
if new_user then
- print([[
- <div class="notif"><p><i>This user account does not exist yet. It will only be created after the first transaction.</i></p></div>
- ]])
+ print([[<div class="notif"><p><i>{user.lazy_creation}</i></p></div>]])
else
print([[<div class="backgroundbox userinfo">]])
- print(string.format([[
- Current balance:<br><span class="amount-%s balance-value">%.02f€</span><br>
- ]], balance >= 0 and "pos" or "neg", balance / 100))
- print(string.format([[
- Last transaction added %s ago. <a href="/%s?log">View user log</a>
- ]], format_duration(os.time() - last_txn), username))
+ print(format([[{user.balance}: <br>{amount}<br>]],
+ { sign = balance >= 0 and "pos" or "neg", amount = format_amount(balance, "span", "balance-value") }))
+ print(format([[{user.last_txn} <a href="/{username}?log">{user.view_log}</a>]],
+ { time = format_duration(os.time() - last_txn), username = urlencode(username) }))
print([[</div>]])
end
print([[<div class="transactions container firstchildlarge">]])
@@ -336,35 +362,35 @@ local function r_user(username)
end
end
print("</div>")
- print([[
+ print(format([[
<form class="transaction box backgroundbox" action="" method="POST">
- <h3>Create Transaction</h3>
+ <h3>{user.form.transaction}</h3>
<label for="amount">Amount (ct): </label>
<input type="number" name="amount" id="amount" />
<label for="comment">Comment: </label>
<input type="text" name="comment" id="comment" />
- <input type="submit" value="Update" class="amount-ntr button" />
+ <input type="submit" value="{user.form.transaction.submit}" class="amount-ntr button" />
</form>
<form class="transaction box backgroundbox" action="" method="POST" id="buy_product">
- <h3>Buy Product</h3>
+ <h3>{user.form.buy}</h3>
<input type="text" name="negate_pcount" value="1" hidden />
<label for="pcount">Count: </label>
<input type="number" name="pcount" id="pcount" value="1" />
- <label for="pcode">Product Barcode: </label>
+ <label for="pcode">{field.barcode}: </label>
<input type="text" name="pcode" id="pcode" />
- <input class="amount-neg button" type="submit" value="Buy" />
+ <input class="amount-neg button" type="submit" value="{user.form.buy.submit}" />
</form>
<form class="transaction box backgroundbox" action="" method="POST" id="buy_product">
- <h3>Restock Product</h3>
- <label for="pcount">Count: </label>
+ <h3>{user.form.restock}</h3>
+ <label for="pcount">{field.count}: </label>
<input type="number" name="pcount" id="pcount" value="1" />
- <label for="amount">Upstream price: </label>
+ <label for="amount">{field.upstream_price}: </label>
<input type="number" name="amount" id="amount" />
- <label for="pcode">Product Barcode: </label>
+ <label for="pcode">{field.barcode}: </label>
<input type="text" name="pcode" id="pcode" />
- <input type="submit" value="Restock" class="button amount-pos" />
+ <input type="submit" value="{user.form.restock.submit}" class="button amount-pos" />
</form>
- ]])
+ ]]))
print("</div>")
end)
end
diff --git a/locale/en.ini b/locale/en.ini
index 33705f5..1deac2a 100644
--- a/locale/en.ini
+++ b/locale/en.ini
@@ -35,3 +35,4 @@ user.form.transaction.submit=Update
user.form.transaction=Create Transaction
user.last_txn=Last transaction added %s.
user.view_log=View user log
+user.lazy_creation=This user account does not exist yet. It will only be created after the first transaction.