Index: backend/pgsql.t ================================================================== --- backend/pgsql.t +++ backend/pgsql.t @@ -60,10 +60,36 @@ (a.origin is null and $1::text = a.handle or $1::text = ('@' || a.handle)) ]]; }; + + actor_save = { + params = { + uint64, --id + rawstring, --nym + rawstring, --handle + rawstring, --bio + rawstring, --epithet + rawstring, --avataruri + uint64, --avatarid + uint16, --rank + uint32 --quota + }, cmd = true, sql = [[ + update parsav_actors set + nym = $2::text, + handle = $3::text, + bio = $4::text, + epithet = $5::text, + avataruri = $6::text, + avatarid = $7::bigint, + rank = $8::smallint, + quota = $9::integer + --invites are controlled by their own specialized routines + where id = $1::bigint + ]]; + }; actor_create = { params = { rawstring, rawstring, uint64, lib.store.timepoint, rawstring, rawstring, lib.mem.ptr(uint8), @@ -825,11 +851,14 @@ var r = queries.actor_enum.exec(src) if r.sz == 0 then return [lib.mem.ptr(&lib.store.actor)] { ct = 0, ptr = nil } else defer r:free() var mem = lib.mem.heapa([&lib.store.actor], r.sz) - for i=0,r.sz do mem.ptr[i] = row_to_actor(&r, i).ptr end + for i=0,r.sz do + mem.ptr[i] = row_to_actor(&r, i).ptr + mem.ptr[i].source = src + end return [lib.mem.ptr(&lib.store.actor)] { ct = r.sz, ptr = mem.ptr } end end]; actor_enum_local = [terra(src: &lib.store.source) @@ -836,11 +865,14 @@ var r = queries.actor_enum_local.exec(src) if r.sz == 0 then return [lib.mem.ptr(&lib.store.actor)] { ct = 0, ptr = nil } else defer r:free() var mem = lib.mem.heapa([&lib.store.actor], r.sz) - for i=0,r.sz do mem.ptr[i] = row_to_actor(&r, i).ptr end + for i=0,r.sz do + mem.ptr[i] = row_to_actor(&r, i).ptr + mem.ptr[i].source = src + end return [lib.mem.ptr(&lib.store.actor)] { ct = r.sz, ptr = mem.ptr } end end]; actor_auth_how = [terra( @@ -936,11 +968,14 @@ var r = pqr { sz = 0 } var A,B,C,D = rg:matrix() -- :/ r = queries.timeline_instance_fetch.exec(src,A,B,C,D) var ret: lib.mem.ptr(lib.mem.ptr(lib.store.post)) ret:init(r.sz) - for i=0,r.sz do ret.ptr[i] = row_to_post(&r, i) end -- MUST FREE ALL + for i=0,r.sz do + ret.ptr[i] = row_to_post(&r, i) -- MUST FREE ALL + ret.ptr[i].ptr.source = src + end return ret end]; post_enum_author_uid = [terra( @@ -951,16 +986,29 @@ var r = pqr { sz = 0 } var A,B,C,D = rg:matrix() -- :/ r = queries.post_enum_author_uid.exec(src,A,B,C,D,uid) var ret: lib.mem.ptr(lib.mem.ptr(lib.store.post)) ret:init(r.sz) - for i=0,r.sz do ret.ptr[i] = row_to_post(&r, i) end -- MUST FREE ALL + for i=0,r.sz do + ret.ptr[i] = row_to_post(&r, i) -- MUST FREE ALL + ret.ptr[i].ptr.source = src + end return ret end]; actor_powers_fetch = getpow; + actor_save = [terra( + src: &lib.store.source, + ac: &lib.store.actor + ): {} + queries.actor_save.exec(src, + ac.id, ac.nym, ac.handle, + ac.bio, ac.epithet, ac.avatar, + ac.avatarid, ac.rights.rank, ac.rights.quota) + end]; + actor_save_privs = privupdate; actor_create = [terra( src: &lib.store.source, ac: &lib.store.actor Index: parsav.t ================================================================== --- parsav.t +++ parsav.t @@ -560,11 +560,11 @@ end local holler = print local suffix = config.exe and '' or ('.'..config.outform) local out = 'parsavd' .. suffix -local linkargs = {} +local linkargs = {'-O4'} local target = config.tgttrip and terralib.newtarget { Triple = config.tgttrip; CPU = config.tgtcpu; FloatABIHard = config.tgthf; } or nil Index: render/conf.t ================================================================== --- render/conf.t +++ render/conf.t @@ -37,11 +37,11 @@ end end end local terra -render_conf([co], [path]) +render_conf([co], [path], notify: pstr) var menu: lib.str.acc menu:init(64):lpush('
') defer menu:free() -- build menu do var p = co.who.rights.powers if p.config() then menu:lpush 'server settings' end @@ -60,11 +60,19 @@ var pg = data.view.conf { menu = mptr; panel = panel; } - var pgt = pg:tostr() defer pgt:free() + var pgt: pstr + if notify:ref() then + var fnpg: lib.str.acc + fnpg:compose('
', notify, '
') + pg:append(&fnpg) + pgt = fnpg:finalize() + else pgt = pg:tostr() end + defer pgt:free() + co:stdpage([lib.srv.convo.page] { title = 'configure'; body = pgt; class = lib.str.plit 'conf'; cache = false; }) Index: render/profile.t ================================================================== --- render/profile.t +++ render/profile.t @@ -5,24 +5,22 @@ end local terra render_profile(co: &lib.srv.convo, actor: &lib.store.actor) var aux: lib.str.acc - var auxp: pstr if co.aid ~= 0 and co.who.id == actor.id then - auxp = lib.str.plit 'alter' + aux:compose('alter') elseif co.aid ~= 0 then aux:compose('followchat') if co.who.rights.powers:affect_users() then aux:lpush('control') end - auxp = aux:finalize() else aux:compose('remote follow') - auxp = aux:finalize() end + var auxp = aux:finalize() var avistr: lib.str.acc if actor.origin == 0 then avistr:compose('/avi/',actor.handle) end var timestr: int8[26] lib.osclock.ctime_r(&actor.knownsince, ×tr[0]) @@ -32,11 +30,11 @@ var sn_follows = cs(lib.math.decstr_friendly(stats.follows, sn_posts.ptr - 1)) var sn_followers = cs(lib.math.decstr_friendly(stats.followers, sn_follows.ptr - 1)) var sn_mutuals = cs(lib.math.decstr_friendly(stats.mutuals, sn_followers.ptr - 1)) var bio = lib.str.plit "tall, dark, and mysterious" if actor.bio ~= nil then - bio = lib.html.sanitize(cs(actor.bio), false) + bio = lib.smackdown.html(cs(actor.bio)) end var fullname = lib.render.nym(actor,0) defer fullname:free() var profile = data.view.profile { nym = fullname; bio = bio; @@ -52,11 +50,11 @@ auxbtn = auxp; } var ret = profile:tostr() if actor.origin == 0 then avistr:free() end - if not (co.aid ~= 0 and co.who.id == actor.id) then auxp:free() end + auxp:free() if actor.bio ~= nil then bio:free() end return ret end return render_profile Index: route.t ================================================================== --- route.t +++ route.t @@ -174,12 +174,39 @@ else co:complain(404, 'no such documentation', 'invalid documentation URL') end end -terra http.configure(co: &lib.srv.convo, path: hpath) - lib.render.conf(co,path) +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 + var user_refresh = false var fail = false + if path(1):cmp(lib.str.lit 'profile') then + co.who.bio = co:postv('bio')._0 + co.who.nym = co:postv('nym')._0 + if co.who.bio ~= nil and @co.who.bio == 0 then co.who.bio = nil end + 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 + elseif path(1):cmp(lib.str.lit 'users') then + + end + + if user_refresh then -- refresh the user info for the renderer + var usr = co.srv:actor_fetch_uid(co.who.id) + lib.mem.heapf(co.who) + co.who = usr.ptr + end + var go,golen = co:getv('go') + if not fail and go ~= nil then + co:reroute(go) + return + end + end + lib.render.conf(co,path,msg) end do local branches = quote end local filename, flen = symbol(&int8), symbol(intptr) local page = symbol(lib.http.page) @@ -272,14 +299,14 @@ elseif path.ptr[0]:cmp(lib.str.lit('doc')) then if meth ~= method.get and meth ~= method.head then goto wrongmeth end http.documentation(co, path) elseif path.ptr[0]:cmp(lib.str.lit('conf')) then if co.aid == 0 then goto unauth end - http.configure(co,path) + http.configure(co,path,meth) else goto notfound end return end ::wrongmeth:: co:complain(405, 'method not allowed', 'that method is not meaningful for this endpoint') do return end ::notfound:: co:complain(404, 'not found', 'no such resource available') do return end ::unauth:: co:complain(401, 'unauthorized', 'this content is not available at your clearance level') do return end end Index: static/style.scss ================================================================== --- static/style.scss +++ static/style.scss @@ -559,12 +559,39 @@ box-sizing: border-box; padding: 0.08in 0.1in; border: 1px solid black; background: tone(-55%); } + textarea { resize: vertical; min-height: 2in; } input, textarea, .txtbox { display: block; width: 100%; } button { float: right; width: 50%; } } } + +@keyframes flashup { + 0% { opacity: 0; transform: scale(0.8); } + 10% { opacity: 1; transform: scale(1.1); } + 80% { opacity: 1; transform: scale(1); } + 100% { opacity: 0; transform: scale(0.9) translateY(-0.12in); display: none; } +} +.flashmsg { + display: block; + position: fixed; + top: 1.3in; + max-width: 3in; + padding: 0.5in 0.2in; + left: 0; right: 0; + text-align: center; + text-shadow: 0 0 15px tone(10%); + margin: auto; + background: linear-gradient(to bottom, tone(-49%), tone(-43%,-0.1)); + border: 1px solid tone(0%); + border-radius: 3px; + box-shadow: 0 0 50px tone(-55%); + color: white; + animation: ease forwards flashup; + //cubic-bezier(0.4, 0.63, 0.6, 0.31) + animation-duration: 3s; +} Index: store.t ================================================================== --- store.t +++ store.t @@ -55,11 +55,11 @@ struct m.rights { rank: uint16 -- lower = more powerful except 0 = regular user -- creating staff automatically assigns rank immediately below you quota: uint32 -- # of allowed tweets per day; 0 = no limit - invites: intptr -- # of people left this user can invite + invites: uint32 -- # of people left this user can invite powers: m.powerset } terra m.rights_default() @@ -81,10 +81,11 @@ handle: str origin: uint64 bio: str epithet: str avatar: str + avatarid: uint64 knownsince: m.timepoint rights: m.rights key: lib.mem.ptr(uint8) -- ephemera @@ -249,10 +250,11 @@ conf_get: {&m.source, rawstring} -> lib.mem.ptr(int8) conf_set: {&m.source, rawstring, rawstring} -> {} conf_reset: {&m.source, rawstring} -> {} actor_create: {&m.source, &m.actor} -> uint64 + actor_save: {&m.source, &m.actor} -> {} actor_save_privs: {&m.source, &m.actor} -> {} actor_fetch_xid: {&m.source, lib.mem.ptr(int8)} -> lib.mem.ptr(m.actor) actor_fetch_uid: {&m.source, uint64} -> lib.mem.ptr(m.actor) actor_notif_fetch_uid: {&m.source, uint64} -> lib.mem.ptr(m.notif) actor_enum: {&m.source} -> lib.mem.ptr(&m.actor)