Index: backend/pgsql.t
==================================================================
--- backend/pgsql.t
+++ backend/pgsql.t
@@ -302,10 +302,24 @@
now(), now(), array[]::bigint[], array[]::bigint[]
) returning id
]]; -- TODO array handling
};
+ post_destroy_prepare = {
+ params = {uint64}, cmd = true, sql = [[
+ update parsav_posts set
+ parent = (select parent from parsav_posts where id = $1::bigint limit 1)
+ where parent = $1::bigint
+ ]]
+ };
+
+ post_destroy = {
+ params = {uint64}, cmd = true, sql = [[
+ delete from parsav_posts where id = $1::bigint
+ ]]
+ };
+
post_fetch = {
params = {uint64}, sql = [[
select a.origin is null,
p.id, p.author, p.subject, p.acl, p.body,
extract(epoch from p.posted )::bigint,
@@ -775,10 +789,33 @@
end
return powers
end
+
+local txdo = terra(src: &lib.store.source)
+ var res = lib.pq.PQexec([&lib.pq.PGconn](src.handle), 'begin')
+ if lib.pq.PQresultStatus(res) == lib.pq.PGRES_COMMAND_OK then
+ lib.dbg('beginning pgsql transaction')
+ return true
+ else
+ lib.warn('backend pgsql - failed to begin transaction: \n', lib.pq.PQresultErrorMessage(res))
+ return false
+ end
+end
+
+local txdone = terra(src: &lib.store.source)
+ var res = lib.pq.PQexec([&lib.pq.PGconn](src.handle), 'end')
+ if lib.pq.PQresultStatus(res) == lib.pq.PGRES_COMMAND_OK then
+ lib.dbg('completing pgsql transaction')
+ return true
+ else
+ lib.warn('backend pgsql - failed to complete transaction: \n', lib.pq.PQresultErrorMessage(res))
+ return false
+ end
+end
+
local b = `lib.store.backend {
id = "pgsql";
open = [terra(src: &lib.store.source): &opaque
lib.report('connecting to postgres database: ', src.string.ptr)
var [con] = lib.pq.PQconnectdb(src.string.ptr)
@@ -805,10 +842,12 @@
return con
end];
close = [terra(src: &lib.store.source) lib.pq.PQfinish([&lib.pq.PGconn](src.handle)) end];
+
+ tx_enter = txdo, tx_complete = txdone;
conprep = [terra(src: &lib.store.source, mode: lib.store.prepmode.t)
var [con] = [&lib.pq.PGconn](src.handle)
if mode == lib.store.prepmode.full then [prep]
elseif mode == lib.store.prepmode.conf or
@@ -839,32 +878,10 @@
return true
else
lib.warn('backend pgsql - failed to obliterate database: \n', lib.pq.PQresultErrorMessage(res))
return false
end
- end];
-
- tx_enter = [terra(src: &lib.store.source)
- var res = lib.pq.PQexec([&lib.pq.PGconn](src.handle), 'begin')
- if lib.pq.PQresultStatus(res) == lib.pq.PGRES_COMMAND_OK then
- lib.dbg('beginning pgsql transaction')
- return true
- else
- lib.warn('backend pgsql - failed to begin transaction: \n', lib.pq.PQresultErrorMessage(res))
- return false
- end
- end];
-
- tx_complete = [terra(src: &lib.store.source)
- var res = lib.pq.PQexec([&lib.pq.PGconn](src.handle), 'end')
- if lib.pq.PQresultStatus(res) == lib.pq.PGRES_COMMAND_OK then
- lib.dbg('completing pgsql transaction')
- return true
- else
- lib.warn('backend pgsql - failed to complete transaction: \n', lib.pq.PQresultErrorMessage(res))
- return false
- end
end];
conf_get = [terra(src: &lib.store.source, key: rawstring)
var r = queries.conf_get.exec(src, key)
if r.sz == 0 then return [lib.mem.ptr(int8)] { ptr = nil, ct = 0 } else
@@ -1016,10 +1033,20 @@
if r.sz == 0 then return 0 end
defer r:free()
var id = r:int(uint64,0,0)
return id
end];
+
+ post_destroy = [terra(
+ src: &lib.store.source,
+ post: uint64
+ ): {}
+ txdo(src)
+ queries.post_destroy_prepare.exec(src, post)
+ queries.post_destroy.exec(src, post)
+ txdone(src)
+ end];
post_fetch = [terra(
src: &lib.store.source,
post: uint64
): lib.mem.ptr(lib.store.post)
Index: parsav.t
==================================================================
--- parsav.t
+++ parsav.t
@@ -6,18 +6,26 @@
lib = {
init = {}, util = util;
load = function(lst)
for _, l in pairs(lst) do
+ io.stdout:write(' · processing module \27[1m' .. l ..'\27[m… ')
local path = {}
for m in l:gmatch('([^:]+)') do path[#path+1]=m end
local tgt = lib
for i=1,#path-1 do
if tgt[path[i]] == nil then tgt[path[i]] = {} end
tgt = tgt[path[i]]
end
- tgt[path[#path]:gsub('-','_')] = terralib.loadfile(l:gsub(':','/') .. '.t')()
+ local chunk = terralib.loadfile(l:gsub(':','/') .. '.t')
+ if chunk ~= nil then
+ tgt[path[#path]:gsub('-','_')] = chunk()
+ print(' \27[1m[ \27[32mok\27[;1m ]\27[m')
+ else
+ print(' \27[1m[\27[31mfail\27[;1m]\27[m')
+ os.exit(2)
+ end
end
end;
loadlib = function(name,hdr)
local p = config.pkg[name]
-- for _,v in pairs(p.dylibs) do
@@ -433,10 +441,11 @@
'render:docpage';
'render:conf:profile';
'render:conf:sec';
+ 'render:conf:users';
'render:conf';
'route';
}
do
@@ -611,9 +620,11 @@
for _,p in pairs(config.pkg) do util.append(linkargs, p.linkargs) end
local linkargs_d = linkargs -- controller is not multithreaded
if config.posix then
linkargs_d[#linkargs_d+1] = '-pthread'
end
-holler('linking with args',util.dump(linkargs))
-terralib.saveobj('parsavd'..suffix, { main = entry_daemon }, linkargs_d, target)
+holler(' → linking \27[1mparsav\27[m with "' .. table.concat(linkargs,' ') .. '"')
terralib.saveobj('parsav' ..suffix, { main = lib.mgtool }, linkargs, target)
+
+holler(' → linking \27[1mparsavd\27[m with "' .. table.concat(linkargs_d,' ') .. '"')
+terralib.saveobj('parsavd'..suffix, { main = entry_daemon }, linkargs_d, target)
ADDED render/conf/users.t
Index: render/conf/users.t
==================================================================
--- render/conf/users.t
+++ render/conf/users.t
@@ -0,0 +1,42 @@
+-- vim: ft=terra
+local pstr = lib.mem.ptr(int8)
+local pref = lib.mem.ref(int8)
+
+local terra cs(s: rawstring)
+ return pstr { ptr = s, ct = lib.str.sz(s) }
+end
+
+local terra
+render_conf_users(co: &lib.srv.convo, path: lib.mem.ptr(pref)): pstr
+ if path.ct == 2 then
+ var uid, ok = lib.math.shorthand.parse(path(1).ptr,path(1).ct)
+ var user = co.srv:actor_fetch_uid(uid)
+ if not user then goto e404 end
+ var islinkct = false
+ var cinp: lib.str.acc
+ var clnk: lib.str.acc clnk:compose('
')
+
+ var cinpp = cinp:finalize() defer cinpp:free()
+ var clnkp: pstr
+ if islinkct then clnkp = clnk:finalize() else
+ clnk:free()
+ clnkp = pstr { ptr='', ct=0 }
+ end
+ var pg = data.view.conf_user_ctl {
+ name = cs(user(0).handle);
+ inputcontent = cinpp;
+ linkcontent = clnkp;
+ }
+ var ret = pg:tostr()
+ if islinkct then clnkp:free() end
+ return ret
+ else
+
+ end
+ do return pstr.null() end
+ ::e404:: co:complain(404, 'not found', 'there is no user or resource by that identifier on this server')
+
+ do return pstr.null() end
+end
+
+return render_conf_users
Index: route.t
==================================================================
--- route.t
+++ route.t
@@ -177,16 +177,16 @@
return
end
defer post:free()
if path.ct == 3 then
- if path(2):cmp(lib.str.lit 'edit') then
- if post(0).author ~= co.who.id then
- co:complain(403, 'forbidden', 'you cannot edit other people\'s posts')
- return
- end
-
+ var lnk: lib.str.acc lnk:compose('/post/', path(1))
+ var lnkp = lnk:finalize() defer lnkp:free()
+ if post(0).author ~= co.who.id then
+ co:complain(403, 'forbidden', 'you cannot alter other people\'s posts')
+ return
+ elseif path(2):cmp(lib.str.lit 'edit') then
if meth == method.get then
lib.render.compose(co, post.ptr, nil)
return
elseif meth == method.post then
var newbody = co:postv('post')._0
@@ -194,28 +194,45 @@
var newsubj = co:postv('subject')._0
if newbody ~= nil then post(0).body = newbody end
if newacl ~= nil then post(0).acl = newacl end
if newsubj ~= nil then post(0).subject = newsubj end
post(0):save(true)
-
- var lnk: lib.str.acc lnk:compose('/post/', path(1))
- co:reroute(lnk.buf)
- lnk:free()
+ co:reroute(lnkp.ptr)
end
return
+ elseif path(2):cmp(lib.str.lit 'del') then
+ if meth == method.get then
+ var conf = data.view.confirm {
+ title = lib.str.plit 'delete post';
+ query = lib.str.plit 'are you sure you want to delete this post?';
+ cancel = lnkp
+ }
+ var body = conf:tostr() defer body:free()
+ co:stdpage([lib.srv.convo.page] {
+ title = lib.str.plit 'post :: delete';
+ class = lib.str.plit 'query';
+ body = body; cache = false;
+ })
+ return
+ elseif meth == method.post then
+ var act = co:ppostv('act')
+ if act:cmp(lib.str.plit 'confirm') then
+ post(0).source:post_destroy(post(0).id)
+ co:reroute('/') -- TODO maybe return to parent or conversation if possible
+ return
+ else goto badop end
+ end
else goto badurl end
end
- if meth == method.post then
- co:complain(405, 'invalid operation', 'the operation you have attempted on this post is not meaningful')
- return
- end
+ if meth == method.post then goto badop end
lib.render.tweet_page(co, path, post.ptr)
do return end
- ::badurl:: co:complain(404, 'invalid URL', 'this URL does not reference extant content or functionality')
+ ::badurl:: do co:complain(404, 'invalid URL', 'this URL does not reference extant content or functionality') return end
+ ::badop :: do co:complain(405, 'invalid operation', 'the operation you have attempted on this post is not meaningful') return end
end
terra http.configure(co: &lib.srv.convo, path: hpath, meth: method.t)
var msg = pstring.null()
if meth == method.post and path.ct >= 1 then
@@ -228,11 +245,16 @@
if co.who.nym ~= nil and @co.who.nym == 0 then co.who.nym = nil end
co.who.source:actor_save(co.who)
msg = lib.str.plit 'profile changes saved'
--user_refresh = true -- not really necessary here, actually
elseif path(1):cmp(lib.str.lit 'srv') then
+ if not co.who.rights.powers.config() then goto nopriv end
+ elseif path(1):cmp(lib.str.lit 'brand') then
+ if not co.who.rights.powers.rebrand() then goto nopriv end
elseif path(1):cmp(lib.str.lit 'users') then
+ if not co.who.rights.powers:affect_users() then goto nopriv end
+
elseif path(1):cmp(lib.str.lit 'sec') then
var act = co:ppostv('act')
if act:cmp(lib.str.plit 'invalidate') then
lib.dbg('setting user\'s cookie validation time to now')
co.who.source:auth_sigtime_user_alter(co.who.id, lib.osclock.time(nil))
@@ -252,10 +274,13 @@
co:reroute(go)
return
end
end
lib.render.conf(co,path,msg)
+ do return end
+
+ ::nopriv:: co:complain(403,'insufficient privileges','you do not have the necessary powers to perform this action')
end
do local branches = quote end
local filename, flen = symbol(&int8), symbol(intptr)
local page = symbol(lib.http.page)
Index: static/style.scss
==================================================================
--- static/style.scss
+++ static/style.scss
@@ -70,10 +70,11 @@
color: tone(25%);
text-shadow: 1px 1px black;
text-decoration: none;
text-align: center;
cursor: default;
+ user-select: none;
background: linear-gradient(to bottom,
tone(-47%),
tone(-50%) 15%,
tone(-50%) 75%,
tone(-53%)
@@ -341,10 +342,11 @@
display: block;
width: 4in;
margin:auto;
padding: 0.5in;
text-align: center;
+ menu:first-of-type { margin-top: 0.3in; }
}
div.login {
@extend %box;
width: 4in;
@@ -524,10 +526,11 @@
padding-left: 0.2in;
text-shadow: 0 2px 0 black;
}
}
+menu { all: unset; display: block; }
body.conf main {
display: grid;
grid-template-columns: 2in 1fr;
grid-template-rows: max-content 1fr;
> menu {
@@ -608,11 +611,15 @@
flex-flow: column;
float: right;
width: 40%;
margin-left: 0.1in;
}
- > %button { display: block; margin: 2px; flex-grow: 1 }
+ > %button {
+ flex-basis: 0;
+ flex-grow: 1;
+ display: block; margin: 2px;
+ }
}
.check-panel {
display: flex;
flex-flow: row wrap;
Index: store.t
==================================================================
--- store.t
+++ store.t
@@ -337,10 +337,11 @@
-- uid: uint64
-- timestamp: timepoint
post_save: {&m.source, &m.post} -> {}
post_create: {&m.source, &m.post} -> uint64
+ post_destroy: {&m.source, uint64} -> {}
post_fetch: {&m.source, uint64} -> lib.mem.ptr(m.post)
post_enum_author_uid: {&m.source, uint64, m.range} -> lib.mem.ptr(lib.mem.ptr(m.post))
post_attach_ctl: {&m.source, uint64, uint64, bool} -> {}
-- attaches or detaches an existing database artifact
-- post id: uint64
ADDED view/conf-user-ctl.tpl
Index: view/conf-user-ctl.tpl
==================================================================
--- view/conf-user-ctl.tpl
+++ view/conf-user-ctl.tpl
@@ -0,0 +1,9 @@
+
Index: view/confirm.tpl
==================================================================
--- view/confirm.tpl
+++ view/confirm.tpl
@@ -1,9 +1,9 @@
-
Index: view/load.lua
==================================================================
--- view/load.lua
+++ view/load.lua
@@ -15,10 +15,11 @@
'conf';
'conf-profile';
'conf-sec';
'conf-sec-credmg';
+ 'conf-user-ctl';
}
local ingest = function(filename)
local hnd = io.open(path..'/'..filename)
local txt = hnd:read('*a')