Index: acl.t
==================================================================
--- acl.t
+++ acl.t
@@ -1,1 +1,29 @@
-- vim: ft=terra
+local m = {
+ agentkind = lib.enum {
+ 'user', 'circle'
+ };
+}
+
+struct m.agent {
+ kind: m.agentkind.t
+ id: uint64
+}
+
+terra m.eval(expr: lib.str.t, agent: m.agent)
+
+end
+
+terra lib.store.post:save(ctupdate: bool)
+-- this post handles the messy details of registering a post's
+-- circles and actors, and increments the edit-count if ctupdate
+-- is true, which is should be in almost all cases.
+ if ctupdate then
+ self.chgcount = self.chgcount + 1
+ self.edited = lib.osclock.time(nil)
+ end
+ -- TODO extract mentions from body, circles from acl
+ self.source:post_save(self)
+end
+
+return m
Index: backend/pgsql.t
==================================================================
--- backend/pgsql.t
+++ backend/pgsql.t
@@ -169,11 +169,11 @@
(select count(tweets.*)::bigint from tweets),
(select count(follows.*)::bigint from follows),
(select count(followers.*)::bigint from followers),
(select count(mutuals.*)::bigint from mutuals)
)
- ]]):gsub('<(%w+)>',function(r) return tostring(lib.store.relation[r]) end)
+ ]]):gsub('<(%w+)>',function(r) return tostring(lib.store.relation.idvmap[r]) end)
};
actor_auth_how = {
params = {rawstring, lib.store.inet}, sql = [[
with mts as (select a.kind from parsav_auth as a
@@ -191,11 +191,11 @@
(select count(*) from mts where kind = 'trust') > 0
]]; -- cheat
};
actor_session_fetch = {
- params = {uint64, lib.store.inet}, sql = [[
+ params = {uint64, lib.store.inet, int64}, sql = [[
select a.id, a.nym, a.handle, a.origin, a.bio,
a.avataruri, a.rank, a.quota, a.key, a.epithet,
extract(epoch from a.knownsince)::bigint,
coalesce(a.handle || '@' || s.domain,
'@' || a.handle) as xid,
@@ -211,11 +211,14 @@
from parsav_auth au
left join parsav_actors a on au.uid = a.id
left join parsav_servers s on a.origin = s.id
where au.aid = $1::bigint and au.blacklist = false and
- (au.netmask is null or au.netmask >> $2::inet)
+ (au.netmask is null or au.netmask >> $2::inet) and
+ ($3::bigint = 0 or --slightly abusing the epoch time fmt here, but
+ ((a.authtime is null or a.authtime <= to_timestamp($3::bigint)) and
+ (au.valperiod is null or au.valperiod <= to_timestamp($3::bigint))))
]];
};
actor_powers_fetch = {
params = {uint64}, sql = [[
@@ -236,10 +239,25 @@
delete from parsav_rights where
actor = $1::bigint and
key = $2::text
]]
};
+
+ auth_sigtime_user_fetch = {
+ params = {uint64}, sql = [[
+ select extract(epoch from authtime)::bigint
+ from parsav_actors where id = $1::bigint
+ ]];
+ };
+
+ auth_sigtime_user_alter = {
+ params = {uint64,int64}, cmd = true, sql = [[
+ update parsav_actors set
+ authtime = to_timestamp($2::bigint)
+ where id = $1::bigint
+ ]];
+ };
auth_create_pw = {
params = {uint64, lib.mem.ptr(uint8)}, cmd = true, sql = [[
insert into parsav_auth (uid, name, kind, cred) values (
$1::bigint,
@@ -254,10 +272,25 @@
delete from parsav_auth where
((uid = 0 and name = $1::text) or uid = $2::bigint) and
kind like $3::text
]]
};
+
+ post_save = {
+ params = {
+ uint64, uint32, int64;
+ rawstring, rawstring, rawstring;
+ }, cmd = true, sql = [[
+ update parsav_posts set
+ subject = $4::text,
+ acl = $5::text,
+ body = $6::text,
+ chgcount = $2::integer,
+ edited = to_timestamp($3::bigint)
+ where id = $1::bigint
+ ]]
+ };
post_create = {
params = {uint64, rawstring, rawstring, rawstring}, sql = [[
insert into parsav_posts (
author, subject, acl, body,
@@ -268,18 +301,33 @@
$3::text, $4::text,
now(), now(), array[]::bigint[], array[]::bigint[]
) returning id
]]; -- TODO array handling
};
+
+ 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,
+ extract(epoch from p.discovered)::bigint,
+ extract(epoch from p.edited )::bigint,
+ p.parent, p.convoheaduri, p.chgcount
+ from parsav_posts as p
+ inner join parsav_actors as a on p.author = a.id
+ where p.id = $1::bigint
+ ]];
+ };
post_enum_author_uid = {
params = {uint64,uint64,uint64,uint64, uint64}, sql = [[
select a.origin is null,
p.id, p.author, p.subject, p.acl, p.body,
extract(epoch from p.posted )::bigint,
extract(epoch from p.discovered)::bigint,
- p.parent, p.convoheaduri
+ extract(epoch from p.edited )::bigint,
+ p.parent, p.convoheaduri, p.chgcount
from parsav_posts as p
inner join parsav_actors as a on p.author = a.id
where p.author = $5::bigint and
($1::bigint = 0 or p.posted <= to_timestamp($1::bigint)) and
($2::bigint = 0 or to_timestamp($2::bigint) < p.posted)
@@ -296,11 +344,12 @@
params = {uint64, uint64, uint64, uint64}, sql = [[
select true,
p.id, p.author, p.subject, p.acl, p.body,
extract(epoch from p.posted )::bigint,
extract(epoch from p.discovered)::bigint,
- p.parent, null::text
+ extract(epoch from p.edited )::bigint,
+ p.parent, null::text, p.chgcount
from parsav_posts as p
inner join parsav_actors as a on p.author = a.id
where
($1::bigint = 0 or p.posted <= to_timestamp($1::bigint)) and
($2::bigint = 0 or to_timestamp($2::bigint) < p.posted) and
@@ -567,13 +616,13 @@
var cvhu: rawstring, cvhlen: intptr
if r:null(row,3)
then subj = nil sblen = 0
else subj = r:string(row,3) sblen = r:len(row,3)+1
end
- if r:null(row,9)
+ if r:null(row,10)
then cvhu = nil cvhlen = 0
- else cvhu = r:string(row,9) cvhlen = r:len(row,9)+1
+ else cvhu = r:string(row,10) cvhlen = r:len(row,10)+1
end
var p = [ lib.str.encapsulate(lib.store.post, {
subject = { `subj, `sblen };
acl = {`r:string(row,4), `r:len(row,4)+1};
body = {`r:string(row,5), `r:len(row,5)+1};
@@ -581,13 +630,18 @@
}) ]
p.ptr.id = r:int(uint64,row,1)
p.ptr.author = r:int(uint64,row,2)
p.ptr.posted = r:int(uint64,row,6)
p.ptr.discovered = r:int(uint64,row,7)
- if r:null(row,8)
+ p.ptr.edited = r:int(uint64,row,8)
+ if r:null(row,9)
then p.ptr.parent = 0
- else p.ptr.parent = r:int(uint64,row,8)
+ else p.ptr.parent = r:int(uint64,row,9)
+ end
+ if r:null(row,11)
+ then p.ptr.chgcount = 0
+ else p.ptr.chgcount = r:int(uint32,row,11)
end
p.ptr.localpost = r:bool(row,0)
return p
end
@@ -920,13 +974,14 @@
end];
actor_session_fetch = [terra(
src: &lib.store.source,
aid: uint64,
- ip : lib.store.inet
+ ip : lib.store.inet,
+ issuetime: lib.store.timepoint
): { lib.stat(lib.store.auth), lib.mem.ptr(lib.store.actor) }
- var r = queries.actor_session_fetch.exec(src, aid, ip)
+ var r = queries.actor_session_fetch.exec(src, aid, ip, issuetime)
if r.sz == 0 then goto fail end
do defer r:free()
if r:null(0,0) then goto fail end
@@ -961,10 +1016,21 @@
if r.sz == 0 then return 0 end
defer r:free()
var id = r:int(uint64,0,0)
return id
end];
+
+ post_fetch = [terra(
+ src: &lib.store.source,
+ post: uint64
+ ): lib.mem.ptr(lib.store.post)
+ var r = queries.post_fetch.exec(src, post)
+ if r.sz == 0 then return [lib.mem.ptr(lib.store.post)].null() end
+ var p = row_to_post(&r, 0)
+ p.ptr.source = src
+ return p
+ end];
timeline_instance_fetch = [terra(src: &lib.store.source, rg: lib.store.range)
var r = pqr { sz = 0 }
var A,B,C,D = rg:matrix() -- :/
r = queries.timeline_instance_fetch.exec(src,A,B,C,D)
@@ -1108,10 +1174,36 @@
if detach
then queries.post_attach_ctl_del.exec(src,post,artifact)
else queries.post_attach_ctl_ins.exec(src,post,artifact)
end
end];
+
+ post_save = [terra(
+ src: &lib.store.source,
+ post: &lib.store.post
+ ): {}
+ queries.post_save.exec(src,
+ post.id, post.chgcount, post.edited,
+ post.subject, post.acl, post.body)
+ end];
+
+ auth_sigtime_user_fetch = [terra(
+ src: &lib.store.source,
+ uid: uint64
+ ): lib.store.timepoint
+ var r = queries.auth_sigtime_user_fetch.exec(src, uid)
+ if r.sz > 0 then defer r:free()
+ var t = r:int(int64,0,0)
+ return t
+ else return 0 end
+ end];
+
+ auth_sigtime_user_alter = [terra(
+ src: &lib.store.source,
+ uid: uint64,
+ time: lib.store.timepoint
+ ): {} queries.auth_sigtime_user_alter.exec(src, uid, time) end];
actor_auth_register_uid = nil; -- TODO better support non-view based auth
}
return b
Index: backend/schema/pgsql-auth.sql
==================================================================
--- backend/schema/pgsql-auth.sql
+++ backend/schema/pgsql-auth.sql
@@ -44,8 +44,12 @@
-- uid = null, kind = trust, cidr = (untrusted IP range)
valperiod timestamp default now(),
-- cookies bearing timestamps earlier than this point in time
-- will be considered invalid and will not grant access
+
+ comment text,
+ -- a field the user can use to identify the specific credential,
+ -- in order to aid credential management
unique(name,kind,cred)
);
Index: backend/schema/pgsql.sql
==================================================================
--- backend/schema/pgsql.sql
+++ backend/schema/pgsql.sql
@@ -26,11 +26,11 @@
id bigint primary key default (1+random()*(2^63-1))::bigint,
nym text,
handle text not null, -- nym [@handle@origin]
origin bigint references parsav_servers(id)
on delete cascade, -- null origin = local actor
- knownsince timestamp,
+ knownsince timestamp not null default now(),
bio text,
avatarid bigint, -- artifact id, null if remote
avataruri text, -- null if local
rank smallint not null default 0,
quota integer not null default 1000,
@@ -58,10 +58,12 @@
subject text,
acl text not null default 'all', -- just store the script raw 🤷
body text,
posted timestamp not null,
discovered timestamp not null,
+ chgcount integer not null default 0,
+ edited timestamp,
parent bigint not null default 0, -- if post: part of conversation; if chatroom: top-level post
circles bigint[], -- TODO at edit or creation, iterate through each circle
mentions bigint[], -- a user has, check if it can see her post, and if so add
artifacts bigint[],
Index: cmdparse.t
==================================================================
--- cmdparse.t
+++ cmdparse.t
@@ -23,12 +23,17 @@
local incr = desc.inc or 0
options.entries[#options.entries + 1] = {
field = o, type = (consume > 0) and &rawstring or
(incr > 0) and uint or bool
}
- helpstr = helpstr .. string.format(' -%s --%s: %s\n',
- desc[1], sanitize(o), desc[2])
+ if desc[1] then
+ helpstr = helpstr .. string.format(' -%s --%s: %s\n',
+ desc[1], sanitize(o), desc[2])
+ else
+ helpstr = helpstr .. string.format(' --%s: %s\n',
+ sanitize(o), desc[2])
+ end
end
for o,desc in pairs(tbl) do
local flag = desc[1]
local consume = desc.consume or 0
local incr = desc.inc or 0
@@ -50,12 +55,14 @@
elseif incr > 0 then
ch = quote [self].[o] = [self].[o] + incr end
else ch = quote
[self].[o] = true
end end
- shortcases[#shortcases + 1] = quote
- case [int8]([string.byte(flag)]) then [ch] end
+ if flag ~= nil then
+ shortcases[#shortcases + 1] = quote
+ case [int8]([string.byte(flag)]) then [ch] end
+ end
end
longcases[#longcases + 1] = quote
if lib.str.cmp([arg]+2, [sanitize(o)]) == 0 then [ch] goto [skip] end
end
end
Index: config.lua
==================================================================
--- config.lua
+++ config.lua
@@ -46,14 +46,20 @@
feat = {};
debug = u.tobool(default('parsav_enable_debug',true));
backends = defaultlist('parsav_backends', 'pgsql');
braingeniousmode = false;
embeds = {
+ -- TODO with gzip compression, svg is dramatically superior to webp
+ -- we should have a build-time option to serve svg so instances
+ -- proxied behind nginx can serve svgz, or possibly just straight-up
+ -- add support for content-encoding headers and pre-compress the
+ -- damn things before compiling
{'style.css', 'text/css'};
{'default-avatar.webp', 'image/webp'};
{'padlock.webp', 'image/webp'};
{'warn.webp', 'image/webp'};
+ {'query.webp', 'image/webp'};
};
}
if os.getenv('parsav_let_me_be_an_idiot') == "i know what i'm doing" then
conf.braingeniousmode = true -- SOUND GENERAL QUARTERS
end
Index: math.t
==================================================================
--- math.t
+++ math.t
@@ -1,9 +1,11 @@
-- vim: ft=terra
local m = {
shorthand = {maxlen = 14}
}
+
+local pstring = lib.mem.ptr(int8)
-- swap in place -- faster on little endian
m.netswap_ip = macro(function(ty, src, dest)
if ty:astype().type ~= 'integer' then error('bad type') end
local bytes = ty:astype().bytes
@@ -184,7 +186,60 @@
buf = buf - 1
@buf = 0x30
end
return buf
end
+
+terra m.ndigits(n: intptr, base: intptr): intptr
+ var c = base
+ var i = 1
+ while true do
+ if n < c then return i end
+ c = c * base
+ i = i + 1
+ end
+end
+
+terra m.fsz_parse(f: pstring): {intptr, bool}
+-- take a string representing a file size and return {nbytes, true}
+-- or {0, false} if the parse fails
+ if f.ct == 0 then f.ct = lib.str.sz(f.ptr) end
+ var sz: intptr = 0
+ for i = 0, f.ct do
+ if f(i) == @',' then goto skip end
+ if f(i) >= 0x30 and f(i) <= 0x39 then
+ sz = sz * 10
+ sz = sz + f(i) - 0x30
+ else
+ if i+1 == f.ct or f(i) == 0 then return sz, true end
+ if i+2 == f.ct or f(i+1) == 0 then
+ if f(i) == @'b' then return sz/8, true end -- bits
+ else
+ var s: intptr = 0
+ if i+3 == f.ct or f(i+2) == 0 then
+ s = i + 1
+ elseif (i+4 == f.ct or f(i+3) == 0) and f(i+1) == @'i' then
+ -- grudgingly tolerate ~mebibits~ and its ilk, without
+ -- affecting the result in any way
+ s = i + 2
+ else return 0, false end
+
+ if f(s) == @'b' then sz = sz/8 -- bits
+ elseif f(s) ~= @'B' then return 0, false end -- wth
+ end
+ var c = f(i)
+ if c >= @'A' and c <= @'Z' then c = c - 0x20 end
+ switch c do -- normal char literal syntax doesn't work here, leads to llvm error (!!)
+ case [uint8]([string.byte('k')]) then return sz * [1024ULL ^ 1], true end
+ case [uint8]([string.byte('m')]) then return sz * [1024ULL ^ 2], true end
+ case [uint8]([string.byte('g')]) then return sz * [1024ULL ^ 3], true end
+ case [uint8]([string.byte('t')]) then return sz * [1024ULL ^ 4], true end
+ case [uint8]([string.byte('e')]) then return sz * [1024ULL ^ 5], true end
+ case [uint8]([string.byte('y')]) then return sz * [1024ULL ^ 6], true end
+ else return sz, true
+ end
+ end
+ ::skip::end
+ return sz, true
+end
return m
Index: mgtool.t
==================================================================
--- mgtool.t
+++ mgtool.t
@@ -299,10 +299,12 @@
lib.bail('you are attempting to completely obliterate all data! make sure you have selected your target correctly. if you really want to do this, pass the confirmation string ', &cfmstr[0])
elseif dbmode.arglist.ct == 2 then
if lib.str.cmp(dbmode.arglist(1), cfmstr) == 0 then
lib.warn('completely obliterating all data!')
dlg:obliterate_everything()
+ elseif lib.str.cmp(dbmode.arglist(1), 'print-confirmation-string') == 0 then
+ lib.io.send(1, cfmstr, lib.str.sz(cfmstr))
else
lib.bail('you passed an incorrect confirmation string; pass ', &cfmstr[0], ' if you really want to destroy everything')
end
else goto cmderr end
else goto cmderr end
@@ -331,19 +333,23 @@
if cfmode.arglist.ct == 1 then
if lib.str.cmp(cfmode.arglist(0),'chsec') == 0 then
var sec: int8[65] gensec(&sec[0])
dlg:conf_set('server-secret', &sec[0])
lib.report('server secret reset')
- -- FIXME notify server to reload its config
elseif lib.str.cmp(cfmode.arglist(0),'refresh') == 0 then
- -- TODO notify server to reload config
+ cfmode.no_notify = false -- duh
else goto cmderr end
elseif cfmode.arglist.ct == 3 and
lib.str.cmp(cfmode.arglist(0),'set') == 0 then
dlg:conf_set(cfmode.arglist(1),cfmode.arglist(2))
lib.report('parameter set')
else goto cmderr end
+
+ -- successful commands fall through
+ if not cfmode.no_notify then
+ dlg:ipc_send(lib.ipc.cmd.cfgrefresh,0)
+ end
else
srv:setup(cnf)
srv:conprep(lib.store.prepmode.full)
if lib.str.cmp(mode.arglist(0),'mkroot') == 0 then
var cfmode: pbasic cfmode:parse(mode.arglist.ct, &mode.arglist(0))
@@ -366,11 +372,12 @@
var epithets = array(
'root', 'god', 'regional jehovah', 'titan king',
'king of olympus', 'cyberpharaoh', 'electric ellimist',
"rampaging c'tan", 'deathless tweetlord', 'postmaster',
'faerie queene', 'lord of the posts', 'ruthless cybercrat',
- 'general secretary', 'commissar', 'kwisatz haderach'
+ 'general secretary', 'commissar', 'kwisatz haderach',
+ 'dedicated hyperturing'
-- feel free to add more
)
root.epithet = epithets[lib.crypt.random(intptr,0,[epithets.type.N])]
root.rights.powers:fill() -- grant omnipotence
root.rights.rank = 1
Index: parsav.t
==================================================================
--- parsav.t
+++ parsav.t
@@ -127,11 +127,10 @@
in val end
return q
end);
proc = {
fork = terralib.externfunction('fork', {} -> int);
- daemonize = terralib.externfunction('daemon', {int,int} -> {});
exit = terralib.externfunction('exit', int -> {});
getenv = terralib.externfunction('getenv', rawstring -> rawstring);
exec = terralib.externfunction('execv', {rawstring,&rawstring} -> int);
execp = terralib.externfunction('execvp', {rawstring,&rawstring} -> int);
};
@@ -140,10 +139,11 @@
recv = terralib.externfunction('read', {int, rawstring, intptr} -> ptrdiff);
close = terralib.externfunction('close', {int} -> int);
say = macro(function(msg) return `lib.io.send(2, msg, [#(msg:asvalue())]) end);
fmt = terralib.externfunction('printf',
terralib.types.funcpointer({rawstring},{int},true));
+ ttyp = terralib.externfunction('isatty', int -> int);
};
str = { sz = terralib.externfunction('strlen', rawstring -> intptr) };
copy = function(tbl)
local new = {}
for k,v in pairs(tbl) do new[k] = v end
@@ -275,10 +275,23 @@
end
end
end
return q
end)
+ terra set:setbit(i: intptr, val: bool)
+ if val then
+ self._store[i/8] = self._store[i/8] or (1 << (i % 8))
+ else
+ self._store[i/8] = self._store[i/8] and not (1 << (i % 8))
+ end
+ end
+ set.bits = {}
+ set.idvmap = {}
+ for i,v in ipairs(tbl) do
+ set.idvmap[v] = i
+ set.bits[v] = quote var b: set b:clear() b:setbit(i, true) in b end
+ end
set.metamethods.__add = macro(function(self,other)
local new = symbol(set)
local q = quote var [new] new:clear() end
for i = 0, bytes - 1 do
q = quote [q]
@@ -294,10 +307,31 @@
q = quote [q]
new._store[i] = self._store[i] and other._store[i]
end
end
return quote [q] in new end
+ end)
+ set.metamethods.__eq = macro(function(self,other)
+ local rt = symbol(bool)
+ local fb if #tbl % 8 == 0 then fb = bytes - 1 else fb = bytes - 2 end
+ local q = quote rt = true end
+ for i = 0, fb do
+ q = quote
+ if self._store[i] ~= other._store[i] then rt = false else [q] end
+ end
+ end
+ -- we need to mask out any extraneous bits the values might have, as we
+ -- don't want the kind of noise introduced by :fill() to affect comparison
+ if #tbl % 8 ~= 0 then
+ local last = #tbl-1
+ local msk = (2 ^ (#tbl % 8)) - 1
+ q = quote
+ if (self._store [last] and [uint8](msk)) ~=
+ (other._store[last] and [uint8](msk)) then rt = false else [q] end
+ end
+ end
+ return quote var [rt]; [q] in rt end
end)
set.metamethods.__not = macro(function(self)
local new = symbol(set)
local q = quote var [new] new:clear() end
for i = 0, bytes - 1 do
@@ -347,11 +381,11 @@
lib.net = lib.loadlib('mongoose','mongoose.h')
lib.pq = lib.loadlib('libpq','libpq-fe.h')
lib.load {
'mem', 'math', 'str', 'file', 'crypt', 'ipc';
- 'http', 'html', 'session', 'tpl', 'store';
+ 'http', 'html', 'session', 'tpl', 'store', 'acl';
'smackdown'; -- md-alike parser
}
local be = {}
@@ -389,19 +423,20 @@
'srv';
'render:nav';
'render:nym';
'render:login';
'render:profile';
-
'render:compose';
'render:tweet';
- 'render:userpage';
+ 'render:tweet-page';
+ 'render:user-page';
'render:timeline';
'render:docpage';
'render:conf:profile';
+ 'render:conf:sec';
'render:conf';
'route';
}
do
Index: render/compose.t
==================================================================
--- render/compose.t
+++ render/compose.t
@@ -1,30 +1,31 @@
-- vim: ft=terra
local terra
-render_compose(co: &lib.srv.convo, edit: &lib.store.post)
+render_compose(co: &lib.srv.convo, edit: &lib.store.post, acc: &lib.str.acc)
var target, tgtlen = co:getv('to')
var form: data.view.compose
+ form = data.view.compose {
+ handle = co.who.handle;
+ circles = ''; -- TODO: list user's circles, rooms, and saved aclexps
+ }
if edit == nil then
- form = data.view.compose {
- content = lib.coalesce(target, '');
- acl = lib.trn(target == nil, 'all', 'mentioned'); -- TODO default acl setting?
- handle = co.who.handle;
- circles = ''; -- TODO: list user's circles, rooms, and saved aclexps
- }
+ form.content = lib.coalesce(target, '')
+ form.acl = lib.trn(target == nil, 'all', 'mentioned') -- TODO default acl setting?
+ else
+ form.content = lib.coalesce(edit.body, '')
+ form.acl = edit.acl
end
+ if acc ~= nil then form:append(acc) return end
+
var cotxt = form:tostr() defer cotxt:free()
- var doc = data.view.docskel {
- instance = co.srv.cfg.instance;
+ var doc = [lib.srv.convo.page] {
title = lib.str.plit 'compose';
body = cotxt;
class = lib.str.plit 'compose';
- navlinks = co.navbar;
+ cache = true;
}
- var hdrs = array(
- lib.http.header { 'Content-Type', 'text/html; charset=UTF-8' }
- )
- doc:send(co.con,200,[lib.mem.ptr(lib.http.header)] {ct = 1, ptr = &hdrs[0]})
+ co:stdpage(doc)
end
return render_compose
Index: render/conf/profile.t
==================================================================
--- render/conf/profile.t
+++ render/conf/profile.t
@@ -6,15 +6,14 @@
return pstr { ptr = s, ct = lib.str.sz(s) }
end
local terra
render_conf_profile(co: &lib.srv.convo, path: lib.mem.ptr(pref)): pstr
-
var c = data.view.conf_profile {
handle = cs(co.who.handle);
nym = cs(lib.coalesce(co.who.nym,''));
bio = cs(lib.coalesce(co.who.bio,''));
}
return c:tostr()
end
return render_conf_profile
ADDED render/conf/sec.t
Index: render/conf/sec.t
==================================================================
--- render/conf/sec.t
+++ render/conf/sec.t
@@ -0,0 +1,25 @@
+-- vim: ft=terra
+local pstr = lib.mem.ptr(int8)
+local pref = lib.mem.ref(int8)
+local terra
+render_conf_sec(co: &lib.srv.convo, path: lib.mem.ptr(pref)): pstr
+ var time: lib.store.timepoint = co.who.source:auth_sigtime_user_fetch(co.who.id)
+ var tstr: int8[26]
+ lib.osclock.ctime_r(&time, &tstr[0])
+ var body = data.view.conf_sec {
+ lastreset = pstr {
+ ptr = &tstr[0], ct = lib.str.sz(&tstr[0])
+ }
+ }
+
+ if co.srv.cfg.credmgd then
+ var a: lib.str.acc a:init(768)
+ body:append(&a)
+ var credmgr = data.view.conf_sec_credmg {
+ credlist = ''
+ }
+ credmgr:append(&a)
+ return a:finalize()
+ else return body:tostr() end
+end
+return render_conf_sec
Index: render/docpage.t
==================================================================
--- render/docpage.t
+++ render/docpage.t
@@ -69,11 +69,11 @@
pushbranches(list: &lib.str.acc, idx: intptr, ps: lib.store.powerset): {}
var [pages] = array([allpages])
var started = false
for i=0,[pages.type.N] do
if pages[i].parent == idx+1 and (pages[i].priv:sz() == 0 or
- (ps and pages[i].priv):sz() > 0) then
+ (ps and pages[i].priv) == pages[i].priv) then
if not started then
started = true
list:lpush('