parsav  Check-in [d3fe1d11af]

Overview
Comment:add full relationship control screen, more timelines, minor fixes and cleanups
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA3-256: d3fe1d11afdd8440eb8b89506bde63da2235b9caeaf337d075a2995c223d5731
User & Date: lexi on 2021-01-13 00:04:54
Other Links: manifest | tags
Context
2021-01-13
13:57
implement some sanctions check-in: 60040f3ca3 user: lexi tags: trunk
00:04
add full relationship control screen, more timelines, minor fixes and cleanups check-in: d3fe1d11af user: lexi tags: trunk
2021-01-11
23:33
various cleanups, notices no longer originated from self check-in: 1ba4bbc92f user: lexi tags: trunk
Changes

Modified backend/pgsql.t from [c81652b018] to [bd352a7166].

1515
1516
1517
1518
1519
1520
1521
1522



1523
1524
1525


















1526
1527
1528
1529
1530
1531
1532
....
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
		post: uint64
	): bool
		var q = queries.post_reacts_fetch_uid.exec(src,uid,post,'like')
		if q.sz > 0 then q:free() return true end
		return false
	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)


















		
		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) -- MUST FREE ALL
			ret.ptr[i].ptr.source = src
		end

................................................................................

		var res = queries.actor_rel_enum.exec(src,relator,relatee)
		var recip = queries.actor_rel_enum.exec(src,relatee,relator)

		if res.sz > 0 then defer res:free()
			for i = 0, res.sz do
				var bit = res:int(uint16, i, 0)-1
				if bit < [#lib.store.relation.members] then r.rel:setbit(bit, true)
					else lib.warn('unknown relationship type in database') end
			end
		end

		if recip.sz > 0 then defer recip:free()
			for i = 0, recip.sz do
				var bit = recip:int(uint16, i, 0)-1
				if bit < [#lib.store.relation.members] then r.recip:setbit(bit, true)
					else lib.warn('unknown relationship type in database') end
			end
		end

		return r
	end];








|
>
>
>



>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







 







|







|







1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
....
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
		post: uint64
	): bool
		var q = queries.post_reacts_fetch_uid.exec(src,uid,post,'like')
		if q.sz > 0 then q:free() return true end
		return false
	end];

	timeline_instance_fetch = [terra(
		src: &lib.store.source,
		rg: lib.store.range
	): lib.mem.lstptr(lib.store.post)
		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) -- MUST FREE ALL
			ret.ptr[i].ptr.source = src
		end

		return ret
	end];

	timeline_actor_fetch_uid = [terra(
		src: &lib.store.source,
		uid: uint64,
		rg: lib.store.range
	): lib.mem.lstptr(lib.store.post)
		var r = pqr { sz = 0 }
		var A,B,C,D = rg:matrix() -- :/
		r = queries.timeline_actor_fetch.exec(src,uid,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) -- MUST FREE ALL
			ret.ptr[i].ptr.source = src
		end

................................................................................

		var res = queries.actor_rel_enum.exec(src,relator,relatee)
		var recip = queries.actor_rel_enum.exec(src,relatee,relator)

		if res.sz > 0 then defer res:free()
			for i = 0, res.sz do
				var bit = res:int(uint16, i, 0)-1
				if bit < [#lib.store.relation.members] then r.rel:setbit(bit,true)
					else lib.warn('unknown relationship type in database') end
			end
		end

		if recip.sz > 0 then defer recip:free()
			for i = 0, recip.sz do
				var bit = recip:int(uint16, i, 0)-1
				if bit < [#lib.store.relation.members] then r.recip:setbit(bit,true)
					else lib.warn('unknown relationship type in database') end
			end
		end

		return r
	end];

Modified doc/acl.md from [53956f184b] to [c0f2a21443].

29
30
31
32
33
34
35
36

* `deny groupies allow +illuminati`: permits access to the illuminati, but excluding those members who are groupies
* `+illuminati deny groupies`: allows access to everyone but groupies (unless they're in the illuminati)
* `@eve @alice@nowhere.tld deny @bob @trent@witches.live`: grants access to eve and alice, but locks out bob and trent
* `<grand duke> #4th-intl<comrade>`: restricts the post to the eyes of the Fourth International's secret cabal of anointed comrades and the grand dukes of the Empire
* `deny ~%3`: blocks a post from being seen by anyone with a staff rank level below 3

**limitations:** to inhibit potential denial-of-service attacks, ACL expressions can be a maximum of 128 characters, can contain at most 16 words, and cannot trigger queries against other servers. all information needed to evaluate an ACL expression must be known locally. this is particularly relevant with respect to rooms.







|
29
30
31
32
33
34
35
36

* `deny groupies allow +illuminati`: permits access to the illuminati, but excluding those members who are groupies
* `+illuminati deny groupies`: allows access to everyone but groupies (unless they're in the illuminati)
* `@eve @alice@nowhere.tld deny @bob @trent@witches.live`: grants access to eve and alice, but locks out bob and trent
* `<grand duke> #4th-intl<comrade>`: restricts the post to the eyes of the Fourth International's secret cabal of anointed comrades and the grand dukes of the Empire
* `deny ~%3`: blocks a post from being seen by anyone with a staff rank level below 3

**limitations:** to inhibit potential denial-of-service attacks, ACL expressions can be a maximum of 256 characters, can contain at most 16 words, and cannot trigger queries against other servers. all information needed to evaluate an ACL expression must be known locally. this is particularly relevant with respect to rooms.

Modified parsav.t from [392d24dbd5] to [d3351b7874].

240
241
242
243
244
245
246





247
248
249
250
251
252
253
...
265
266
267
268
269
270
271


272
273
274
275
276
277
278
...
306
307
308
309
310
311
312


























313
314
315
316
317
318
319
320
321
322
323
		}
	}
	n.name = string.format("stat<%s>", ty.name)
	n.stat_basetype = ty
	return n
end)
lib.enum = function(tbl)





	local ty = uint8
	if #tbl >= 2^32 then ty = uint64 -- hey, can't be too safe
	elseif #tbl >= 2^16 then ty = uint32
	elseif #tbl >= 2^8 then ty = uint16 end
	local o = { t = ty, members = tbl }
	local strings = {}
	for i, name in ipairs(tbl) do
................................................................................
	local o = {}
	for i, name in ipairs(tbl) do o[name] = i end
	local struct set { _store: uint8[bytes] }
	local struct bit { _v: intptr _set: &set}
	terra set:clear() for i=0,bytes do self._store[i] = 0 end end
	terra set:fill() for i=0,bytes do self._store[i] = 0xFF end end
	set.members = tbl


	set.name = string.format('set<%s>', table.concat(tbl, '|'))
	set.metamethods.__entrymissing = macro(function(val, obj)
		if o[val] == nil then error('value ' .. val .. ' not in set') end
		return `bit { _v=[o[val] - 1], _set = &(obj) }
	end)
	terra set:sz()
		var ct: intptr = 0
................................................................................
	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-1], 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]







>
>
>
>
>







 







>
>







 







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>

<

<







240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
...
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
...
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346

347

348
349
350
351
352
353
354
		}
	}
	n.name = string.format("stat<%s>", ty.name)
	n.stat_basetype = ty
	return n
end)
lib.enum = function(tbl)
	if type(tbl) == 'string' then -- shorthand syntax
		local t = {}
		for w in tbl:gmatch('(%g+)') do t[#t+1] = w end
		tbl = t
	end
	local ty = uint8
	if #tbl >= 2^32 then ty = uint64 -- hey, can't be too safe
	elseif #tbl >= 2^16 then ty = uint32
	elseif #tbl >= 2^8 then ty = uint16 end
	local o = { t = ty, members = tbl }
	local strings = {}
	for i, name in ipairs(tbl) do
................................................................................
	local o = {}
	for i, name in ipairs(tbl) do o[name] = i end
	local struct set { _store: uint8[bytes] }
	local struct bit { _v: intptr _set: &set}
	terra set:clear() for i=0,bytes do self._store[i] = 0 end end
	terra set:fill() for i=0,bytes do self._store[i] = 0xFF end end
	set.members = tbl
	set.idvmap = o
	set.null = quote var s: set s:clear() in s end
	set.name = string.format('set<%s>', table.concat(tbl, '|'))
	set.metamethods.__entrymissing = macro(function(val, obj)
		if o[val] == nil then error('value ' .. val .. ' not in set') end
		return `bit { _v=[o[val] - 1], _set = &(obj) }
	end)
	terra set:sz()
		var ct: intptr = 0
................................................................................
	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
	local qksetexp = function(self,idx,rhs)
		local ch,bit
		if terralib.isconstant(idx) then
			ch = math.floor(idx/8)
			bit = idx%8
		else
			ch = `[idx]/8
			bit = `[idx]%8
		end

		if terralib.isconstant(rhs) then
			local b = rhs:asvalue()
			if b == true then return quote
				self._store[ch] = self._store[ch] or (1 << bit)
			end elseif b == false then return quote
				self._store[ch] = self._store[ch] and not (1 << bit)
			end end
		else
			return quote self:setbit([ch], rhs) end
		end
	end
	set.metamethods.__update = macro(qksetexp)
	set.metamethods.__setentry = macro(function(field,self,rhs)
		return `self:setbit([o[field]-1],rhs)
		--return qksetexp(self, o[field], rhs)
	end)
	set.bits = {}

	for i,v in ipairs(tbl) do

		set.bits[v] = quote var b: set b:clear() b:setbit([i-1], 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]

Modified render/conf/users.t from [46d40d63a3] to [24aad61532].

136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
...
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
		terror fear blood slime slab warp waggle tit boob bird derp
		birb goat horde masto mastodon social global tweet post
		house home prison jail box pit hole haven town trump putin
		truth liberty zone land ranch butt butts sex pimp cop mail
		slut goblin goblins no good bad only gtfo electro electric
		dragon space mars earth venus neptune pluto saturn star
		moon lunar catastrophe catastro cuck honk war lap cuddle
		planet
	]]
	var tlds = splitwords [[
		tld club town space xxx house land ranch horse com io online
		shop site vip ltd win men lgbt cat adult army analytics art
		associates bar bible biz black blog broker cam camp careers
		catering church city coop dad date dating direct diy dog
		duck dot enterprises esq estate expert express fail farm foo
................................................................................
		forsale fun fund forum foundation gay global golf gop guru
		group hangout hot industries international info investments
		jobs land law life limited live lol mom network now party
		porn productions pub rehab rocks school sex sexy singles
		social software solutions spot store sucks supplies cuck
		uwu systems university vacations ventures wang website work
		wow wtf world xyz soy live gym park worship orb zone mail
		war honk derp planet
	]]
	var sub = rnd(uint8,0,10) == 0
	if sub then a:ppush(words[rnd(intptr,0,[words.type.N])]):lpush('.') end
	a:ppush(words[rnd(intptr,0,[words.type.N])])
	if rnd(uint8,0,3) == 0 or not sub then
		a:ppush(words[rnd(intptr,0,[words.type.N])])
	end







|







 







|







136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
...
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
		terror fear blood slime slab warp waggle tit boob bird derp
		birb goat horde masto mastodon social global tweet post
		house home prison jail box pit hole haven town trump putin
		truth liberty zone land ranch butt butts sex pimp cop mail
		slut goblin goblins no good bad only gtfo electro electric
		dragon space mars earth venus neptune pluto saturn star
		moon lunar catastrophe catastro cuck honk war lap cuddle
		planet pride
	]]
	var tlds = splitwords [[
		tld club town space xxx house land ranch horse com io online
		shop site vip ltd win men lgbt cat adult army analytics art
		associates bar bible biz black blog broker cam camp careers
		catering church city coop dad date dating direct diy dog
		duck dot enterprises esq estate expert express fail farm foo
................................................................................
		forsale fun fund forum foundation gay global golf gop guru
		group hangout hot industries international info investments
		jobs land law life limited live lol mom network now party
		porn productions pub rehab rocks school sex sexy singles
		social software solutions spot store sucks supplies cuck
		uwu systems university vacations ventures wang website work
		wow wtf world xyz soy live gym park worship orb zone mail
		war honk derp planet pride
	]]
	var sub = rnd(uint8,0,10) == 0
	if sub then a:ppush(words[rnd(intptr,0,[words.type.N])]):lpush('.') end
	a:ppush(words[rnd(intptr,0,[words.type.N])])
	if rnd(uint8,0,3) == 0 or not sub then
		a:ppush(words[rnd(intptr,0,[words.type.N])])
	end

Modified render/docpage.t from [704653b9d0] to [2df16a2b60].

30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
	local par = 0
	if t.meta.parent then par = topicidxt[t.meta.parent] end
	local restrict = symbol(lib.store.powerset)
	local setbits = quote restrict:clear() end
	if t.meta.priv then
		if type(t.meta.priv) ~= 'table' then t.meta.priv = {t.meta.priv} end
		for _,v in pairs(t.meta.priv) do
			setbits = quote [setbits]; (restrict.[v] << true) end
		end
	end
	allpages[i] = quote var [restrict]; [setbits] in pgpair {
		name = [v];
		parent = par;
		priv = restrict;
		title = [t.meta.title];







|







30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
	local par = 0
	if t.meta.parent then par = topicidxt[t.meta.parent] end
	local restrict = symbol(lib.store.powerset)
	local setbits = quote restrict:clear() end
	if t.meta.priv then
		if type(t.meta.priv) ~= 'table' then t.meta.priv = {t.meta.priv} end
		for _,v in pairs(t.meta.priv) do
			setbits = quote [setbits]; restrict.[v] = true end
		end
	end
	allpages[i] = quote var [restrict]; [setbits] in pgpair {
		name = [v];
		parent = par;
		priv = restrict;
		title = [t.meta.title];

Modified render/media-gallery.t from [1f18c83945] to [880ce531e5].

8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
...
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155

local terra 
render_media_gallery(co: &lib.srv.convo, path: lib.mem.ptr(lib.mem.ref(int8)), uid: uint64, acc: &lib.str.acc)
 -- note that when calling this function, path must be adjusted so that path(0)
 -- eq "media"
	var owner = false
	if co.aid ~= 0 and co.who.id == uid then owner = true end
	var ou = co.srv:actor_fetch_uid(uid)
	if not ou then goto e404 end
	do defer ou:free()
		var pfx = pstr.null()
		if not owner then
			var pa = co:stra(32)
			pa:lpush('/')
			if ou(0).origin ~= 0 then pa:lpush('@') end
			pa:push(ou(0).xid,0)
			pfx = pa:finalize()
		end

		if path.ct >= 3 and path(1):cmp('a') then
			var id, idok = lib.math.shorthand.parse(path(2).ptr, path(2).ct)
			if not idok then goto e404 end
			var art = co.srv:artifact_fetch(uid, id)
................................................................................

			if folders.ct > 0 then
				var fa = co:stra(128)
				var fldr = co:pgetv('folder')
				for i=0,folders.ct do
					var ule = lib.html.urlenc(&co.srv.pool,folders(i), true) -- defer ule:free()
					var san = lib.html.sanitize(&co.srv.pool,folders(i), true) -- defer san:free()
					fa:lpush('<a href="'):ppush(pfx):lpush('/media?folder='):ppush(ule)
						:lpush('">'):ppush(san):lpush('</a>')
					lib.dbg('checking folder ',{fldr.ptr,fldr.ct},' against ',{folders(i).ptr,folders(i).ct})
					if fldr:ref() and folders(i):cmp(fldr)
						then folder = folders(i) lib.dbg('folder match ',{fldr.ptr,fldr.ct})
						else folders(i):free()
					end
				end
				fa:lpush('<hr>')
				view.folders = fa:finalize()
				folders:free()
			end

			if owner then
				view.menu = '<a class="pos" href="/media/upload">upload</a><hr>'
			end

			var md = co.srv:artifact_enum_uid(uid, folder)
			var gallery: lib.str.acc gallery:pool(&co.srv.pool,256)
			var files: lib.str.acc files:pool(&co.srv.pool,256) 
			for i=0,md.ct do
				var desc = lib.smackdown.html(&co.srv.pool,pstr{md(i)(0).desc,0}, true) --defer desc:free()







|
|
|




|
|







 







|













|







8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
...
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155

local terra 
render_media_gallery(co: &lib.srv.convo, path: lib.mem.ptr(lib.mem.ref(int8)), uid: uint64, acc: &lib.str.acc)
 -- note that when calling this function, path must be adjusted so that path(0)
 -- eq "media"
	var owner = false
	if co.aid ~= 0 and co.who.id == uid then owner = true end
	var ou = co:uid2actor(uid)
	if ou == nil then goto e404 end
	do -- defer ou:free()
		var pfx = pstr.null()
		if not owner then
			var pa = co:stra(32)
			pa:lpush('/')
			if ou.origin ~= 0 then pa:lpush('@') end
			pa:push(ou.xid,0)
			pfx = pa:finalize()
		end

		if path.ct >= 3 and path(1):cmp('a') then
			var id, idok = lib.math.shorthand.parse(path(2).ptr, path(2).ct)
			if not idok then goto e404 end
			var art = co.srv:artifact_fetch(uid, id)
................................................................................

			if folders.ct > 0 then
				var fa = co:stra(128)
				var fldr = co:pgetv('folder')
				for i=0,folders.ct do
					var ule = lib.html.urlenc(&co.srv.pool,folders(i), true) -- defer ule:free()
					var san = lib.html.sanitize(&co.srv.pool,folders(i), true) -- defer san:free()
					fa:lpush('<a class="button" href="'):ppush(pfx):lpush('/media?folder='):ppush(ule)
						:lpush('">'):ppush(san):lpush('</a>')
					lib.dbg('checking folder ',{fldr.ptr,fldr.ct},' against ',{folders(i).ptr,folders(i).ct})
					if fldr:ref() and folders(i):cmp(fldr)
						then folder = folders(i) lib.dbg('folder match ',{fldr.ptr,fldr.ct})
						else folders(i):free()
					end
				end
				fa:lpush('<hr>')
				view.folders = fa:finalize()
				folders:free()
			end

			if owner then
				view.menu = '<a class="pos button" href="/media/upload">upload</a><hr>'
			end

			var md = co.srv:artifact_enum_uid(uid, folder)
			var gallery: lib.str.acc gallery:pool(&co.srv.pool,256)
			var files: lib.str.acc files:pool(&co.srv.pool,256) 
			for i=0,md.ct do
				var desc = lib.smackdown.html(&co.srv.pool,pstr{md(i)(0).desc,0}, true) --defer desc:free()

Modified render/profile.t from [4c6c4c1279] to [446c5d468d].

1
2
3
4
5
6




















































































7
8
9
10
11
12
13

14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
























32
33
34
35
36
37
38
..
97
98
99
100
101
102
103


104
105
106
107
108
109
110
111
112
113
114
-- vim: ft=terra
local pstr = lib.mem.ptr(int8)
local terra cs(s: rawstring)
	return pstr { ptr = s, ct = lib.str.sz(s) }
end





















































































local terra 
render_profile(
	co: &lib.srv.convo,
	actor: &lib.store.actor,
	relationship: &lib.store.relationship
): pstr
	var aux: lib.str.acc

	var followed = false -- FIXME
	if co.aid ~= 0 and co.who.id == actor.id then
		aux:pcompose(&co.srv.pool,'<a accesskey="a" class="button" href="/conf/profile?go=/@',actor.handle,'">alter</a>')
	elseif co.aid ~= 0 then
		if not relationship.rel.follow() then
			aux:pcompose(&co.srv.pool,'<button accesskey="f" method="post" class="pos" name="act" value="follow">follow</button>')
		elseif relationship.rel.follow() then
			aux:pcompose(&co.srv.pool,'<button accesskey="f" method="post" class="neg" name="act" value="unfollow">unfollow</button>')
		end
		aux:lpush(' <a accesskey="h" class="button" href="/'):push(actor.xid,0):lpush('/chat">chat</a>')
		if co.who.rights.powers:affect_users() and co.who:overpowers(actor) then
			aux:lpush(' <a accesskey="n" class="button" href="/conf/users/'):shpush(actor.id):lpush('">control</a>')
		end
	else
		aux:pcompose(&co.srv.pool,' <a accesskey="f" class="button" href="/', actor.xid, '/follow">remote follow</a>')
	end
	var auxp = aux:finalize()
	var timestr: int8[26] lib.osclock.ctime_r(&actor.knownsince, &timestr[0])

























	var strfbuf: int8[28*4]
	var stats = co.srv:actor_stats(actor.id)
		var sn_posts     = cs(lib.math.decstr_friendly(stats.posts, &strfbuf[ [strfbuf.type.N - 1] ]))
		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))
................................................................................
		nfollowers = sn_followers, nmutuals = sn_mutuals;
		tweetday = cs(timestr);
		timephrase = lib.trn(actor.origin == 0, pstr 'joined', pstr 'known since');

		remarks = '';

		auxbtn = auxp;


	}
	if comments.sz > 0 then profile.remarks = comments:finalize() end

	var ret = profile:poolstr(&co.srv.pool)
	-- auxp:free() 
	--if actor.bio ~= nil then bio:free() end
	--if comments.sz > 0 then profile.remarks:free() end
	return ret
end

return render_profile






>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>






<
>


|

<
<
<
<
<
<




|



>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







 







>
>











1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96

97
98
99
100
101






102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
...
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
-- vim: ft=terra
local pstr = lib.mem.ptr(int8)
local terra cs(s: rawstring)
	return pstr { ptr = s, ct = lib.str.sz(s) }
end

local relkinds = {
	pos = {
		{ id = 'follow', start = {
			text = 'follow';
			desc = "this user's posts will appear in your timeline";
		}, stop = {
			text = 'unfollow';
			desc = "this user's posts will no longer appear in your timeline";
		}};

		{ id = 'sub', start = {
			text = 'subscribe';
			desc = "you will get a notification whenever this user posts";
		}, stop = {
			text = 'unsubscribe';
			desc = "you will no longer get notifications when this user posts";
		}};
	};
	neg = {
		{ id = 'mute', start = {
			text = 'mute';
			desc = "this user's posts will no longer appear anywhere";
		}, stop = {
			text = 'unmute';
			desc = "this user's posts will once again be visible to you";
		}};

		{ id = 'exclude', start = {
			text = 'exclude';
			desc = "this user will not be able to see your posts";
		}, stop = {
			text = 'reinclude';
			desc = "this user will again be able to see your posts";
		}};

		{ id = 'avoid', start = {
			text = 'avoid';
			desc = "this user's posts will not appear on your timeline even if you follow them and you will not receive notices from them, but they will still be visible in threads";
		}, stop = {
			text = 'reconcile';
			desc = "this user will once again be able to originate notices to you, and will be shown on your timeline";
		}};

		{ id = 'collapse', start = {
			text = 'collapse';
			desc = "this user's posts will still appear but will be collapsed by default, so you only see the text if you choose to expand each post";
		}, stop = {
			text = 'expand';
			desc = "this user's posts will no longer be collapsed";
		}};

		{ id = 'disemvowel', start = {
			text = 'disemvowel'; -- translations should not translate this literally
			desc = "this user's posts will be ritually mutilated in a humorous fashion as appropriate to the script in which they are written; e.g. the removal of vowels in roman text and deletion of kana in japanese text";
		}, stop = {
			text = 're-emvowel';
			desc = "this user's posts will once again appear normally";
		}};

		{ id = 'block', start = {
			text = 'block';
			desc = "this user will not be able to interact with you in any fashion and they will be forced to unfollow you";
		}, stop = {
			text = 'unblock';
			desc = "this user will once again be able to interact with you";
		}};
	};
}

local function
btnhtml(kind)
	local function sb(class)
		local b = kind[class]
		return string.format('<div class="opt%s">' ..
			'<button name="act" value="%s">%s</button>' ..
			'<p>%s</p>' .. 
		'</div>',
			(class == 'stop' and ' on' or ''),
			(class == 'stop' and 'un' or '') .. kind.id,
			b.text, b.desc)
	end
	return sb('start'), sb('stop')
end

local terra 
render_profile(
	co: &lib.srv.convo,
	actor: &lib.store.actor,
	relationship: &lib.store.relationship
): pstr

	var aux = co:stra(128)
	var followed = false -- FIXME
	if co.aid ~= 0 and co.who.id == actor.id then
		aux:lpush('<a accesskey="a" class="button" href="/conf/profile?go=/@'):push(actor.handle,0):lpush('">alter</a>')
	elseif co.aid ~= 0 then






		if co.who.rights.powers:affect_users() and co.who:overpowers(actor) then
			aux:lpush(' <a accesskey="n" class="button" href="/conf/users/'):shpush(actor.id):lpush('">control</a>')
		end
	else
		aux:lpush(' <a accesskey="f" class="button" href="/'):push(actor.xid,0):lpush('/follow">remote follow</a>')
	end
	var auxp = aux:finalize()
	var timestr: int8[26] lib.osclock.ctime_r(&actor.knownsince, &timestr[0])

	var relbtns = co:stra(256)
	var sancbtns = co:stra(256)
	[(function()
		local allkinds = {}
		for kind, rels in pairs(relkinds) do
			for i,v in ipairs(rels) do
				v.kind = kind
				allkinds[#allkinds + 1] = v
			end
		end
		local br = {}
		for i,v in ipairs(allkinds) do
			local off, on = btnhtml(v)
			local target = v.kind == 'pos' and relbtns or sancbtns
			br[#br+1] = quote
				if relationship.rel.[v.id]()
					then target:ppush(lib.str.plit([on]))
					else target:ppush(lib.str.plit([off]))
				end
			end
		end
		return br
	end)()]

	var strfbuf: int8[28*4]
	var stats = co.srv:actor_stats(actor.id)
		var sn_posts     = cs(lib.math.decstr_friendly(stats.posts, &strfbuf[ [strfbuf.type.N - 1] ]))
		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))
................................................................................
		nfollowers = sn_followers, nmutuals = sn_mutuals;
		tweetday = cs(timestr);
		timephrase = lib.trn(actor.origin == 0, pstr 'joined', pstr 'known since');

		remarks = '';

		auxbtn = auxp;
		relations = relbtns:finalize();
		sanctions = sancbtns:finalize();
	}
	if comments.sz > 0 then profile.remarks = comments:finalize() end

	var ret = profile:poolstr(&co.srv.pool)
	-- auxp:free() 
	--if actor.bio ~= nil then bio:free() end
	--if comments.sz > 0 then profile.remarks:free() end
	return ret
end

return render_profile

Modified render/timeline.t from [9997258dca] to [91a054f52e].

1
2

3







4
5
6
7
8
9
10
11


12
13
14
15
16
17
18
19
20
21
22
23
24





25
26
27
28
29














30
31
32




33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
-- vim: ft=terra
local modes = lib.enum {'follow','mutual','srvlocal','fediglobal','circle'}

local terra 







render_timeline(co: &lib.srv.convo, modestr: lib.mem.ref(int8))
	var mode = modes.srvlocal
	var circle: uint64 = 0
	-- if     modestr:cmpl('local') then mode = modes.srvlocal
	-- elseif modestr:cmpl('mutual') then mode = modes.mutual
	-- elseif modestr:cmpl('global') then mode = modes.fediglobal
	-- elseif modestr:cmpl('circle') then mode = modes.circle
	-- end



	var stoptime = lib.osclock.time(nil)

	var posts = [lib.mem.vec(lib.mem.ptr(lib.store.post))] { 
		sz = 0, run = 0
	}
	if mode == modes.follow then
	elseif mode == modes.srvlocal then
		posts = co.srv:timeline_instance_fetch(lib.store.range {
			mode = 1; -- T->I
			from_time = stoptime;
			to_idx = 64;
		})





	elseif mode == modes.fediglobal then
	elseif mode == modes.circle then
	end

	var acc = co:stra(1024)














	acc:lpush('<div id="tl" data-live="10">')
	var newest: lib.store.timepoint = 0
	for i = 0, posts.sz do




		lib.render.tweet(co, posts(i).ptr, &acc)
		var t = lib.math.biggest(lib.math.biggest(posts(i).ptr.posted, posts(i).ptr.discovered),posts(i).ptr.edited)
		if t > newest then newest = t end
		posts(i):free()
	end
	posts:free()
	acc:lpush('</div>')

	var doc = [lib.srv.convo.page] {
		title = 'timeline';
		body = acc:finalize();
		class = 'timeline';
		cache = false;
	}
	co:livepage(doc,newest)
	--doc.body:free()
end
return render_timeline

|
>

>
>
>
>
>
>
>

|

|
|
|
|
|
>
>






<
<
|
|
|
|
<
>
>
>
>
>
|




>
>
>
>
>
>
>
>
>
>
>
>
>
>



>
>
>
>



|

|












1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27


28
29
30
31

32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
-- vim: ft=terra
local pstr = lib.str.t
local modes = lib.enum [[follow mutual local fedi circle]]
local terra 
requires_login(m: modes.t): bool
	return m == modes.follow
	    or m == modes.mutual
	    or m == modes.circle
end

local terra 
render_timeline(co: &lib.srv.convo, modestr: lib.mem.ref(int8))
	var mode = modes.follow
	var circle: uint64 = 0
	if     modestr:cmp('local') then mode = [modes['local']]
	elseif modestr:cmp('mutual') then mode = modes.mutual
	elseif modestr:cmp('fedi') then mode = modes.fedi
	elseif modestr:cmp('circle') then mode = modes.circle
	end
	if requires_login(mode) and co.aid == 0 then mode = [modes['local']] end


	var stoptime = lib.osclock.time(nil)

	var posts = [lib.mem.vec(lib.mem.ptr(lib.store.post))] { 
		sz = 0, run = 0
	}


	var fetchmode = lib.store.range {
		mode = 1; -- T->I
		from_time = stoptime;
		to_idx = 64;

	}
	if mode == modes.follow or mode == modes.mutual then
		posts = co.srv:timeline_actor_fetch_uid(co.who.id,fetchmode)
	elseif mode == [modes['local']] then
		posts = co.srv:timeline_instance_fetch(fetchmode)
	elseif mode == modes.fedi then
	elseif mode == modes.circle then
	end

	var acc = co:stra(1024)
	var modelabels = arrayof(pstr, 'followed', 'mutuals', 'local instance', 'fediverse', 'circle')
	var modelinks = arrayof(pstr, [modes.members])
	acc:lpush('<div style="text-align: right"><em>showing ')
	for i=0, [modelabels.type.N] do
		if co.aid ~= 0 or not requires_login(i) then
			if i > 0 then acc:lpush(' ยท ') end
			if i == mode then
				acc:lpush('<strong>'):ppush(modelabels[i]):lpush('</strong>')
			else
				acc:lpush('<a href="/tl/'):ppush(modelinks[i]):lpush('">'):ppush(modelabels[i]):lpush('</a>')
			end
		end
	end
	acc:lpush('</em></div>')
	acc:lpush('<div id="tl" data-live="10">')
	var newest: lib.store.timepoint = 0
	for i = 0, posts.sz do
		if mode == modes.mutual and posts(i).ptr.author ~= co.who.id then
			var author = co:uid2actor(posts(i).ptr.author)
			if not author.relationship.recip.follow() then goto skip end
		end
		lib.render.tweet(co, posts(i).ptr, &acc)
		var t = lib.math.biggest(lib.math.biggest(posts(i).ptr.posted, posts(i).ptr.discovered),posts(i).ptr.edited)
		if t > newest then newest = t end
		::skip:: posts(i):free()
	end
	if posts.run > 0 then posts:free() end
	acc:lpush('</div>')

	var doc = [lib.srv.convo.page] {
		title = 'timeline';
		body = acc:finalize();
		class = 'timeline';
		cache = false;
	}
	co:livepage(doc,newest)
	--doc.body:free()
end
return render_timeline

Modified render/tweet.t from [57f4f6bb5e] to [fad3f537df].

24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
		if p.author == co.actorcache(j).ptr.id then author    = co.actorcache(j).ptr end
		if p.rtdby  == co.actorcache(j).ptr.id then retweeter = co.actorcache(j).ptr end
		if author ~= nil and (p.rtdby == 0 or retweeter ~= nil) then
			goto foundauth
		end
	end
	if author == nil then
		author = co.actorcache:insert(co.srv:actor_fetch_uid(p.author)).ptr
	end
	if p.rtdby ~= 0 and retweeter == nil then
		retweeter = co.actorcache:insert(co.srv:actor_fetch_uid(p.rtdby)).ptr
	end

	::foundauth::
	var timestr: int8[26] lib.osclock.ctime_r(&p.posted, &timestr[0])
	for i=0,26 do if timestr[i] == @'\n' then timestr[i] = 0 break end end -- ๐Ÿ™„

	var bhtml = lib.smackdown.html(&co.srv.pool, [lib.mem.ptr(int8)] {ptr=p.body,ct=0},false)







|


|







24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
		if p.author == co.actorcache(j).ptr.id then author    = co.actorcache(j).ptr end
		if p.rtdby  == co.actorcache(j).ptr.id then retweeter = co.actorcache(j).ptr end
		if author ~= nil and (p.rtdby == 0 or retweeter ~= nil) then
			goto foundauth
		end
	end
	if author == nil then
		author = co.actorcache:insert(co:uid2actor_live(p.author)).ptr
	end
	if p.rtdby ~= 0 and retweeter == nil then
		retweeter = co.actorcache:insert(co:uid2actor_live(p.rtdby)).ptr
	end

	::foundauth::
	var timestr: int8[26] lib.osclock.ctime_r(&p.posted, &timestr[0])
	for i=0,26 do if timestr[i] == @'\n' then timestr[i] = 0 break end end -- ๐Ÿ™„

	var bhtml = lib.smackdown.html(&co.srv.pool, [lib.mem.ptr(int8)] {ptr=p.body,ct=0},false)

Modified render/user-page.t from [d4b538d774] to [b87f9f8b97].

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
..
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
-- vim: ft=terra
local terra 
render_userpage(
	co          : &lib.srv.convo,
	actor       : &lib.store.actor,
	relationship: &lib.store.relationship
): {}
	var ti: lib.str.acc 
	if co.aid ~= 0 and co.who.id == actor.id then
		ti:compose('my profile')
	else
		ti:compose('profile :: ', actor.handle)
	end
	var tiptr = ti:finalize()

	var acc: lib.str.acc acc:pool(&co.srv.pool, 1024)
	var pftxt = lib.render.profile(co,actor,relationship) --defer pftxt:free()
	acc:ppush(pftxt)

	var stoptime = lib.osclock.time(nil)
	var posts = co.srv:post_enum_author_uid(actor.id, lib.store.range {
................................................................................
		posts(i):free()
	end
	posts:free()
	acc:lpush('</div>')

	var bdf = acc:finalize()
	co:livepage([lib.srv.convo.page] {
		title = tiptr; body = bdf;
		class = 'profile';
		cache = false;
	}, newest)

	tiptr:free()
	--bdf:free()
end

return render_userpage







|

|

|

<







 







|




|




1
2
3
4
5
6
7
8
9
10
11
12
13

14
15
16
17
18
19
20
..
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
-- vim: ft=terra
local terra 
render_userpage(
	co          : &lib.srv.convo,
	actor       : &lib.store.actor,
	relationship: &lib.store.relationship
): {}
	var ti: lib.str.t
	if co.aid ~= 0 and co.who.id == actor.id then
		ti = 'my profile'
	else
		ti = co:qstr('profile :: ', actor.handle)
	end


	var acc: lib.str.acc acc:pool(&co.srv.pool, 1024)
	var pftxt = lib.render.profile(co,actor,relationship) --defer pftxt:free()
	acc:ppush(pftxt)

	var stoptime = lib.osclock.time(nil)
	var posts = co.srv:post_enum_author_uid(actor.id, lib.store.range {
................................................................................
		posts(i):free()
	end
	posts:free()
	acc:lpush('</div>')

	var bdf = acc:finalize()
	co:livepage([lib.srv.convo.page] {
		title = ti; body = bdf;
		class = 'profile';
		cache = false;
	}, newest)

	--tiptr:free()
	--bdf:free()
end

return render_userpage

Modified route.t from [57b2f599e2] to [cc19a11396].

12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
terra http.actor_profile(co: &lib.srv.convo, actor: &lib.store.actor, meth: method.t)
	var rel: lib.store.relationship
	if co.aid ~= 0 then
		rel = co.srv:actor_rel_calc(co.who.id, actor.id)
		if meth == method.post then
			var act = co:ppostv('act')
			if rel.recip.block() then
				if act:cmp( 'follow') or act:cmp( 'subscribe') then
					co:complain(403,'blocked','you cannot follow a user you are blocked by') return
				end
			end
			if act:cmp( 'block') and not rel.rel.block() then
				(rel.rel.block << true) ; (rel.recip.follow << false)
				co.srv:actor_rel_create([lib.store.relation.idvmap.block], co.who.id, actor.id)
				co.srv:actor_rel_destroy([lib.store.relation.idvmap.follow], actor.id, co.who.id)
			else
				[(function()
					local tests = quote co:complain(400,'bad request','the action you have attempted on this user is not meaningful') return end
					for i,v in ipairs(lib.store.relation.members) do
						tests = quote
							if [v ~= 'block'] and act:cmp(([v])) and not rel.rel.[v]() then -- rely on dead code elimination :/
								(rel.rel.[v] << true)
								co.srv:actor_rel_create([lib.store.relation.idvmap[v]], co.who.id, actor.id)
							elseif act:cmp((['un'..v])) and rel.rel.[v]() then
								(rel.rel.[v] << false)
								co.srv:actor_rel_destroy([lib.store.relation.idvmap[v]], co.who.id, actor.id)
							else [tests] end
						end
					end
					return tests
				end)()]
			end







|



|
|


|




|
|

|
|







12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
terra http.actor_profile(co: &lib.srv.convo, actor: &lib.store.actor, meth: method.t)
	var rel: lib.store.relationship
	if co.aid ~= 0 then
		rel = co.srv:actor_rel_calc(co.who.id, actor.id)
		if meth == method.post then
			var act = co:ppostv('act')
			if rel.recip.block() then
				if act:cmp('follow') or act:cmp('subscribe') then
					co:complain(403,'blocked','you cannot follow a user you are blocked by') return
				end
			end
			if act:cmp('block') and not rel.rel.block() then
				rel.rel.block = true  rel.recip.follow = false
				co.srv:actor_rel_create([lib.store.relation.idvmap.block], co.who.id, actor.id)
				co.srv:actor_rel_destroy([lib.store.relation.idvmap.follow], actor.id, co.who.id)
			elseif not act:cmp('report') then
				[(function()
					local tests = quote co:complain(400,'bad request','the action you have attempted on this user is not meaningful') return end
					for i,v in ipairs(lib.store.relation.members) do
						tests = quote
							if [v ~= 'block'] and act:cmp(lib.str.plit([v])) and not rel.rel.[v]() then -- rely on dead code elimination :/
								rel.rel.[v] = true
								co.srv:actor_rel_create([lib.store.relation.idvmap[v]], co.who.id, actor.id)
							elseif act:cmp(lib.str.plit(['un'..v])) and rel.rel.[v]() then
								rel.rel.[v] = false
								co.srv:actor_rel_destroy([lib.store.relation.idvmap[v]], co.who.id, actor.id)
							else [tests] end
						end
					end
					return tests
				end)()]
			end

Modified srv.t from [826b7b2edb] to [6a947d38ca].

45
46
47
48
49
50
51





52
53
54
55
56
57
58
59
60
61
62
63

64



65
66
67
68
69
70
71
...
168
169
170
171
172
173
174






























175
176
177
178
179
180
181
			for j=0, lst.ct do all:push(lst.ptr[j]) end
			lst:free()
		end
	end
	return all
end






terra srv:timeline_instance_fetch(r: lib.store.range): lib.mem.vec(lib.mem.ptr(lib.store.post))
	var all: lib.mem.vec(lib.mem.ptr(lib.store.post)) all:init(64)
	for i=0,self.sources.ct do var src = self.sources.ptr + i
		if src.handle ~= nil and src.backend.timeline_instance_fetch ~= nil then
			var lst = src:timeline_instance_fetch(r)
			all:assure(all.sz + lst.ct)
			for j=0, lst.ct do all:push(lst.ptr[j]) end
			lst:free()
		end
	end
	return all
end





srv.metamethods.__methodmissing = macro(function(meth, self, ...)
	local primary, ptr, stat, simple, oid = 0,1,2,3,4
	local tk, rt = primary
	local expr = {...}
	for _,f in pairs(lib.store.backend.entries) do
		local fn = f.field or f[1]
		local ft = f.type or f[2]
................................................................................
		mgr: &lib.net.mg_mgr
		peer: lib.net.mg_addr
	}
	terra getpeer(con: &lib.net.mg_connection)
		return [&strucheader](con).peer
	end
end































terra convo:rawpage(code: uint16, pg: convo.page, hdrs: lib.mem.ptr(lib.http.header))
	var doc = data.view.docskel {
		instance = self.srv.cfg.instance;
		title = pg.title;
		body = pg.body;
		class = pg.class;







>
>
>
>
>
|
|
|
|
|
|
|
|
|
|
|
|
>

>
>
>







 







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
...
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
			for j=0, lst.ct do all:push(lst.ptr[j]) end
			lst:free()
		end
	end
	return all
end

local function deftlfetch(fnname, ...)
	local args = {}
	for i,ty in ipairs{...} do args[#args + 1] = symbol(ty) end
	args[#args + 1] = symbol(lib.store.range)
	fnname = 'timeline_' .. fnname
	srv.methods[fnname] = terra(self: &srv, [args]): lib.mem.vec(lib.mem.ptr(lib.store.post))
		var all: lib.mem.vec(lib.mem.ptr(lib.store.post)) all:init(64)
		for i=0,self.sources.ct do var src = self.sources.ptr + i
			if src.handle ~= nil and src.backend.[fnname] ~= nil then
				var lst = src:[fnname]([args])
				all:assure(all.sz + lst.ct)
				for j=0, lst.ct do all:push(lst.ptr[j]) end
				lst:free()
			end
		end
		return all
	end
end

deftlfetch('instance_fetch')
deftlfetch('actor_fetch_uid', uint64)

srv.metamethods.__methodmissing = macro(function(meth, self, ...)
	local primary, ptr, stat, simple, oid = 0,1,2,3,4
	local tk, rt = primary
	local expr = {...}
	for _,f in pairs(lib.store.backend.entries) do
		local fn = f.field or f[1]
		local ft = f.type or f[2]
................................................................................
		mgr: &lib.net.mg_mgr
		peer: lib.net.mg_addr
	}
	terra getpeer(con: &lib.net.mg_connection)
		return [&strucheader](con).peer
	end
end

terra convo:uid2actor_live(uid: uint64)
	var actor = self.srv:actor_fetch_uid(uid)
	if actor:ref() then
		if self.aid ~= 0 and self.who.id ~= uid then
			actor(0).relationship = self.srv:actor_rel_calc(self.who.id, uid)
		else -- defensive branch
			actor(0).relationship = lib.store.relationship {
				agent = 0, patient = uid;
				rel   = [lib.store.relation.null],
				recip = [lib.store.relation.null],
			}
		end
	end
	return actor
end

terra convo:uid2actor(uid: uint64)
	var actor: &lib.store.actor = nil
	for j = 0, self.actorcache.top do
		if uid == self.actorcache(j).ptr.id then
			actor = self.actorcache(j).ptr
			break
		end
	end
	if actor == nil then
		actor = self.actorcache:insert(self:uid2actor_live(uid)).ptr
	end
	return actor
end

terra convo:rawpage(code: uint16, pg: convo.page, hdrs: lib.mem.ptr(lib.http.header))
	var doc = data.view.docskel {
		instance = self.srv.cfg.instance;
		title = pg.title;
		body = pg.body;
		class = pg.class;

Added static/followreq.svg version [8263feb69d].



















































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->

<svg
   xmlns:dc="http://purl.org/dc/elements/1.1/"
   xmlns:cc="http://creativecommons.org/ns#"
   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
   xmlns:svg="http://www.w3.org/2000/svg"
   xmlns="http://www.w3.org/2000/svg"
   xmlns:xlink="http://www.w3.org/1999/xlink"
   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
   width="20"
   height="20"
   viewBox="0 0 5.2916664 5.2916665"
   version="1.1"
   id="svg8"
   inkscape:version="0.92.4 (5da689c313, 2019-01-14)"
   sodipodi:docname="followreq.svg">
  <defs
     id="defs2">
    <linearGradient
       id="linearGradient1647"
       inkscape:collect="always">
      <stop
         id="stop1643"
         offset="0"
         style="stop-color:#9db3ff;stop-opacity:1" />
      <stop
         id="stop1645"
         offset="1"
         style="stop-color:#0036e4;stop-opacity:0.98431373" />
    </linearGradient>
    <linearGradient
       inkscape:collect="always"
       id="linearGradient1609">
      <stop
         style="stop-color:#f6f8ff;stop-opacity:1"
         offset="0"
         id="stop1605" />
      <stop
         style="stop-color:#3c6aff;stop-opacity:0.98431373"
         offset="1"
         id="stop1607" />
    </linearGradient>
    <linearGradient
       id="linearGradient1603"
       inkscape:collect="always">
      <stop
         id="stop1599"
         offset="0"
         style="stop-color:#557dff;stop-opacity:1" />
      <stop
         id="stop1601"
         offset="1"
         style="stop-color:#557dff;stop-opacity:0.97647059" />
    </linearGradient>
    <linearGradient
       id="linearGradient1597"
       inkscape:collect="always">
      <stop
         id="stop1591"
         offset="0"
         style="stop-color:#001868;stop-opacity:1" />
      <stop
         style="stop-color:#3565ff;stop-opacity:0.77254903"
         offset="0.30000001"
         id="stop1593" />
      <stop
         id="stop1595"
         offset="1"
         style="stop-color:#0032d4;stop-opacity:0" />
    </linearGradient>
    <linearGradient
       inkscape:collect="always"
       id="linearGradient1589">
      <stop
         style="stop-color:#557eff;stop-opacity:0.50980395"
         offset="0"
         id="stop1585" />
      <stop
         style="stop-color:#557dff;stop-opacity:0.02352941"
         offset="1"
         id="stop1587" />
    </linearGradient>
    <linearGradient
       inkscape:collect="always"
       id="linearGradient1054">
      <stop
         style="stop-color:#ff73e0;stop-opacity:1"
         offset="0"
         id="stop1050" />
      <stop
         style="stop-color:#ff00cb;stop-opacity:0.98431373"
         offset="1"
         id="stop1052" />
    </linearGradient>
    <linearGradient
       id="linearGradient1034"
       inkscape:collect="always">
      <stop
         id="stop1030"
         offset="0"
         style="stop-color:#ff55dd;stop-opacity:0.50869566" />
      <stop
         id="stop1032"
         offset="1"
         style="stop-color:#ff55dd;stop-opacity:0.02173913" />
    </linearGradient>
    <linearGradient
       id="linearGradient1019"
       inkscape:collect="always">
      <stop
         id="stop1013"
         offset="0"
         style="stop-color:#680054;stop-opacity:0.01304348" />
      <stop
         style="stop-color:#ff40d9;stop-opacity:0.23913044"
         offset="0.30000001"
         id="stop1015" />
      <stop
         id="stop1017"
         offset="1"
         style="stop-color:#d400aa;stop-opacity:0;" />
    </linearGradient>
    <linearGradient
       id="linearGradient1005"
       inkscape:collect="always">
      <stop
         id="stop1001"
         offset="0"
         style="stop-color:#fff6fd;stop-opacity:1" />
      <stop
         id="stop1003"
         offset="1"
         style="stop-color:#ff3cd7;stop-opacity:0.98260868" />
    </linearGradient>
    <linearGradient
       inkscape:collect="always"
       id="linearGradient979">
      <stop
         style="stop-color:#680054;stop-opacity:1"
         offset="0"
         id="stop975" />
      <stop
         id="stop983"
         offset="0.30000001"
         style="stop-color:#d400aa;stop-opacity:0.77254902;" />
      <stop
         style="stop-color:#d400aa;stop-opacity:0;"
         offset="1"
         id="stop977" />
    </linearGradient>
    <linearGradient
       inkscape:collect="always"
       id="linearGradient971">
      <stop
         style="stop-color:#ff55dd;stop-opacity:1;"
         offset="0"
         id="stop967" />
      <stop
         style="stop-color:#ff55dd;stop-opacity:0.97826087"
         offset="1"
         id="stop969" />
    </linearGradient>
    <linearGradient
       inkscape:collect="always"
       id="linearGradient963">
      <stop
         style="stop-color:#c839ac;stop-opacity:0.00392157"
         offset="0"
         id="stop959" />
      <stop
         style="stop-color:#852572;stop-opacity:1"
         offset="1"
         id="stop961" />
    </linearGradient>
    <linearGradient
       inkscape:collect="always"
       id="linearGradient943">
      <stop
         style="stop-color:#f4d7ee;stop-opacity:1;"
         offset="0"
         id="stop939" />
      <stop
         style="stop-color:#f4d7ee;stop-opacity:0;"
         offset="1"
         id="stop941" />
    </linearGradient>
    <linearGradient
       inkscape:collect="always"
       id="linearGradient954">
      <stop
         style="stop-color:#ffffff;stop-opacity:1;"
         offset="0"
         id="stop950" />
      <stop
         style="stop-color:#ffffff;stop-opacity:0;"
         offset="1"
         id="stop952" />
    </linearGradient>
    <linearGradient
       inkscape:collect="always"
       id="linearGradient938">
      <stop
         style="stop-color:#d9fff6;stop-opacity:1;"
         offset="0"
         id="stop934" />
      <stop
         style="stop-color:#d9fff6;stop-opacity:0;"
         offset="1"
         id="stop936" />
    </linearGradient>
    <linearGradient
       inkscape:collect="always"
       id="linearGradient1403">
      <stop
         style="stop-color:#ccaaff;stop-opacity:1;"
         offset="0"
         id="stop1399" />
      <stop
         style="stop-color:#ccaaff;stop-opacity:0;"
         offset="1"
         id="stop1401" />
    </linearGradient>
    <linearGradient
       id="linearGradient1395"
       inkscape:collect="always">
      <stop
         id="stop1391"
         offset="0"
         style="stop-color:#ff1616;stop-opacity:1" />
      <stop
         id="stop1393"
         offset="1"
         style="stop-color:#ff1d1d;stop-opacity:0" />
    </linearGradient>
    <linearGradient
       inkscape:collect="always"
       id="linearGradient1383">
      <stop
         style="stop-color:#980000;stop-opacity:1;"
         offset="0"
         id="stop1379" />
      <stop
         style="stop-color:#980000;stop-opacity:0;"
         offset="1"
         id="stop1381" />
    </linearGradient>
    <linearGradient
       inkscape:collect="always"
       id="linearGradient832">
      <stop
         style="stop-color:#ffcfcf;stop-opacity:1;"
         offset="0"
         id="stop828" />
      <stop
         style="stop-color:#ffcfcf;stop-opacity:0;"
         offset="1"
         id="stop830" />
    </linearGradient>
    <radialGradient
       inkscape:collect="always"
       xlink:href="#linearGradient832"
       id="radialGradient834"
       cx="3.2286437"
       cy="286.62921"
       fx="3.2286437"
       fy="286.62921"
       r="1.0866126"
       gradientTransform="matrix(1.8608797,0.8147617,-0.38242057,0.87343168,106.71446,33.692223)"
       gradientUnits="userSpaceOnUse" />
    <radialGradient
       inkscape:collect="always"
       xlink:href="#linearGradient1383"
       id="radialGradient1385"
       cx="4.1787109"
       cy="286.89261"
       fx="4.1787109"
       fy="286.89261"
       r="1.2260786"
       gradientTransform="matrix(1.7016464,0,0,1.6348586,-2.9319775,-182.10895)"
       gradientUnits="userSpaceOnUse" />
    <radialGradient
       inkscape:collect="always"
       xlink:href="#linearGradient1395"
       id="radialGradient1389"
       gradientUnits="userSpaceOnUse"
       gradientTransform="matrix(0.66230313,-1.6430738,1.0154487,0.40931507,-290.06307,177.39489)"
       cx="4.02069"
       cy="287.79269"
       fx="4.02069"
       fy="287.79269"
       r="1.0866126" />
    <linearGradient
       inkscape:collect="always"
       xlink:href="#linearGradient1403"
       id="linearGradient1405"
       x1="8.3939333"
       y1="288.1091"
       x2="7.0158253"
       y2="287.32819"
       gradientUnits="userSpaceOnUse" />
    <linearGradient
       inkscape:collect="always"
       xlink:href="#linearGradient938"
       id="linearGradient940"
       x1="7.609839"
       y1="288.73215"
       x2="7.609839"
       y2="283.78305"
       gradientUnits="userSpaceOnUse" />
    <linearGradient
       inkscape:collect="always"
       xlink:href="#linearGradient954"
       id="linearGradient956"
       x1="3.0150654"
       y1="285.94464"
       x2="3.0150654"
       y2="282.40109"
       gradientUnits="userSpaceOnUse" />
    <linearGradient
       inkscape:collect="always"
       xlink:href="#linearGradient954"
       id="linearGradient1138"
       gradientUnits="userSpaceOnUse"
       x1="3.0150654"
       y1="285.94464"
       x2="3.0150654"
       y2="284.62277" />
    <radialGradient
       inkscape:collect="always"
       xlink:href="#linearGradient1005"
       id="radialGradient945"
       cx="6.1517248"
       cy="285.09021"
       fx="6.1517248"
       fy="285.09021"
       r="1.3844374"
       gradientTransform="matrix(2.4674713,0,0,2.4674669,-5.8821073,-417.49152)"
       gradientUnits="userSpaceOnUse" />
    <radialGradient
       inkscape:collect="always"
       xlink:href="#linearGradient943"
       id="radialGradient953"
       gradientUnits="userSpaceOnUse"
       gradientTransform="matrix(2.4674713,0,0,2.4674669,-9.027479,-418.36044)"
       cx="6.1517248"
       cy="285.09021"
       fx="6.1517248"
       fy="285.09021"
       r="1.3844374" />
    <radialGradient
       inkscape:collect="always"
       xlink:href="#linearGradient963"
       id="radialGradient965"
       cx="6.1523438"
       cy="285.08984"
       fx="6.1523438"
       fy="285.08984"
       r="1.6679688"
       gradientTransform="matrix(1,0,0,0.99999775,0,6.4095141e-4)"
       gradientUnits="userSpaceOnUse" />
    <radialGradient
       inkscape:collect="always"
       xlink:href="#linearGradient971"
       id="radialGradient973"
       cx="4.6300988"
       cy="285.0715"
       fx="4.6300988"
       fy="285.0715"
       r="0.88396439"
       gradientTransform="matrix(3.5121044,0,0,4.5073949,-11.771195,-1000.0836)"
       gradientUnits="userSpaceOnUse" />
    <linearGradient
       inkscape:collect="always"
       xlink:href="#linearGradient979"
       id="linearGradient981"
       x1="6.3269596"
       y1="286.08289"
       x2="6.3263793"
       y2="288.44873"
       gradientUnits="userSpaceOnUse" />
    <linearGradient
       inkscape:collect="always"
       xlink:href="#linearGradient1019"
       id="linearGradient1009"
       gradientUnits="userSpaceOnUse"
       x1="6.3269596"
       y1="286.08289"
       x2="6.3263793"
       y2="288.44873" />
    <radialGradient
       inkscape:collect="always"
       xlink:href="#linearGradient1034"
       id="radialGradient1028"
       cx="7.8964839"
       cy="10.825195"
       fx="7.8964839"
       fy="10.825195"
       r="6.1388507"
       gradientTransform="matrix(1.5553588,0,0,2.1211746,-4.385382,-12.136934)"
       gradientUnits="userSpaceOnUse" />
    <radialGradient
       inkscape:collect="always"
       xlink:href="#linearGradient1034"
       id="radialGradient1038"
       gradientUnits="userSpaceOnUse"
       gradientTransform="matrix(-1.5553588,0,0,-1.3840186,20.178349,25.807467)"
       cx="7.8964839"
       cy="10.825195"
       fx="7.8964839"
       fy="10.825195"
       r="6.1388507" />
    <radialGradient
       inkscape:collect="always"
       xlink:href="#linearGradient1054"
       id="radialGradient1048"
       gradientUnits="userSpaceOnUse"
       gradientTransform="matrix(2.4674713,0,0,2.4674669,-5.8821075,-417.49152)"
       cx="6.1517248"
       cy="285.09021"
       fx="6.1517248"
       fy="285.09021"
       r="1.3844374" />
    <radialGradient
       inkscape:collect="always"
       xlink:href="#linearGradient1589"
       id="radialGradient1149"
       gradientUnits="userSpaceOnUse"
       gradientTransform="matrix(-1.5553588,0,0,-1.3840186,20.178349,25.807467)"
       cx="7.8964839"
       cy="10.825195"
       fx="7.8964839"
       fy="10.825195"
       r="6.1388507" />
    <linearGradient
       inkscape:collect="always"
       xlink:href="#linearGradient1597"
       id="linearGradient1151"
       gradientUnits="userSpaceOnUse"
       x1="6.3269596"
       y1="286.08289"
       x2="6.3263793"
       y2="288.44873" />
    <radialGradient
       inkscape:collect="always"
       xlink:href="#linearGradient1603"
       id="radialGradient1153"
       gradientUnits="userSpaceOnUse"
       gradientTransform="matrix(3.5121044,0,0,4.5073949,-11.771195,-1000.0836)"
       cx="4.6300988"
       cy="285.0715"
       fx="4.6300988"
       fy="285.0715"
       r="0.88396439" />
    <radialGradient
       inkscape:collect="always"
       xlink:href="#linearGradient1005"
       id="radialGradient1142-3"
       gradientUnits="userSpaceOnUse"
       gradientTransform="matrix(7.7743272,0,0,7.7743124,-43.463536,-2212.0183)"
       cx="6.1517248"
       cy="285.09021"
       fx="6.1517248"
       fy="285.09021"
       r="1.3844374" />
    <radialGradient
       inkscape:collect="always"
       xlink:href="#linearGradient1005"
       id="radialGradient1575"
       gradientUnits="userSpaceOnUse"
       gradientTransform="matrix(1.3440509,0,0,1.3440485,-0.7046247,-98.181012)"
       cx="5.6033692"
       cy="284.85327"
       fx="5.6033692"
       fy="284.85327"
       r="1.3844374" />
    <radialGradient
       inkscape:collect="always"
       xlink:href="#linearGradient1609"
       id="radialGradient1579"
       gradientUnits="userSpaceOnUse"
       gradientTransform="matrix(1.3440509,0,0,1.3440485,-7.5617585,-98.181012)"
       cx="5.6033692"
       cy="284.85327"
       fx="5.6033692"
       fy="284.85327"
       r="1.3844374" />
    <radialGradient
       inkscape:collect="always"
       xlink:href="#linearGradient1609"
       id="radialGradient1613"
       gradientUnits="userSpaceOnUse"
       gradientTransform="matrix(1.3440509,0,0,1.3440485,-0.62945515,-98.297942)"
       cx="5.6033692"
       cy="284.85327"
       fx="5.6033692"
       fy="284.85327"
       r="1.3844374" />
    <radialGradient
       inkscape:collect="always"
       xlink:href="#linearGradient1609"
       id="radialGradient1619"
       gradientUnits="userSpaceOnUse"
       gradientTransform="matrix(1.7810629,0,0,1.7810597,-2.9864585,-223.14925)"
       cx="5.3173594"
       cy="285.32516"
       fx="5.3173594"
       fy="285.32516"
       r="1.3844374" />
    <radialGradient
       inkscape:collect="always"
       xlink:href="#linearGradient1647"
       id="radialGradient1639"
       gradientUnits="userSpaceOnUse"
       gradientTransform="matrix(1.7810629,0,0,1.7810597,-2.9864585,-223.14925)"
       cx="5.3173594"
       cy="285.32516"
       fx="5.3173594"
       fy="285.32516"
       r="1.3844374" />
    <radialGradient
       inkscape:collect="always"
       xlink:href="#linearGradient1647"
       id="radialGradient1641"
       gradientUnits="userSpaceOnUse"
       gradientTransform="matrix(1.3440509,0,0,1.3440485,-0.62945515,-98.297942)"
       cx="5.6033692"
       cy="284.85327"
       fx="5.6033692"
       fy="284.85327"
       r="1.3844374" />
    <radialGradient
       inkscape:collect="always"
       xlink:href="#linearGradient1609"
       id="radialGradient1773"
       gradientUnits="userSpaceOnUse"
       gradientTransform="matrix(2.5551461,0,0,2.5551415,-7.1614394,-444.56578)"
       cx="5.3173594"
       cy="285.32516"
       fx="5.3173594"
       fy="285.32516"
       r="1.3844374" />
    <radialGradient
       inkscape:collect="always"
       xlink:href="#linearGradient1609"
       id="radialGradient1775"
       gradientUnits="userSpaceOnUse"
       gradientTransform="matrix(1.3440509,0,0,1.3440485,-0.62945515,-98.297942)"
       cx="5.6033692"
       cy="284.85327"
       fx="5.6033692"
       fy="284.85327"
       r="1.3844374" />
    <radialGradient
       inkscape:collect="always"
       xlink:href="#linearGradient1647"
       id="radialGradient1785"
       gradientUnits="userSpaceOnUse"
       gradientTransform="matrix(1.3440509,0,0,1.3440485,-0.62945515,-98.297942)"
       cx="5.6033692"
       cy="284.85327"
       fx="5.6033692"
       fy="284.85327"
       r="1.3844374" />
    <filter
       inkscape:collect="always"
       style="color-interpolation-filters:sRGB"
       id="filter1811"
       x="-0.10140134"
       width="1.2028027"
       y="-0.055816109"
       height="1.1116322">
      <feGaussianBlur
         inkscape:collect="always"
         stdDeviation="0.066236744"
         id="feGaussianBlur1813" />
    </filter>
    <filter
       inkscape:collect="always"
       style="color-interpolation-filters:sRGB"
       id="filter1889"
       x="-0.42250557"
       width="1.8450111"
       y="-0.23256712"
       height="1.4651342">
      <feGaussianBlur
         inkscape:collect="always"
         stdDeviation="0.27598643"
         id="feGaussianBlur1891" />
    </filter>
    <radialGradient
       inkscape:collect="always"
       xlink:href="#linearGradient1647"
       id="radialGradient1893"
       gradientUnits="userSpaceOnUse"
       gradientTransform="matrix(1.7810629,0,0,1.7810597,-2.9864585,-223.14925)"
       cx="5.3173594"
       cy="285.32516"
       fx="5.3173594"
       fy="285.32516"
       r="1.3844374" />
    <radialGradient
       inkscape:collect="always"
       xlink:href="#linearGradient1647"
       id="radialGradient1895"
       gradientUnits="userSpaceOnUse"
       gradientTransform="matrix(1.3440509,0,0,1.3440485,-0.62945515,-98.297942)"
       cx="5.6033692"
       cy="284.85327"
       fx="5.6033692"
       fy="284.85327"
       r="1.3844374" />
  </defs>
  <sodipodi:namedview
     id="base"
     pagecolor="#181818"
     bordercolor="#666666"
     borderopacity="1.0"
     inkscape:pageopacity="0"
     inkscape:pageshadow="2"
     inkscape:zoom="1.979899"
     inkscape:cx="-51.662731"
     inkscape:cy="-45.562994"
     inkscape:document-units="mm"
     inkscape:current-layer="layer1"
     showgrid="false"
     units="px"
     inkscape:window-width="1920"
     inkscape:window-height="1042"
     inkscape:window-x="0"
     inkscape:window-y="38"
     inkscape:window-maximized="0"
     showguides="true"
     fit-margin-top="0"
     fit-margin-left="0"
     fit-margin-right="0"
     fit-margin-bottom="0" />
  <metadata
     id="metadata5">
    <rdf:RDF>
      <cc:Work
         rdf:about="">
        <dc:format>image/svg+xml</dc:format>
        <dc:type
           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
        <dc:title></dc:title>
      </cc:Work>
    </rdf:RDF>
  </metadata>
  <g
     inkscape:label="Layer 1"
     inkscape:groupmode="layer"
     id="layer1"
     transform="translate(-2.6134661,-283.36966)">
    <g
       id="g1147"
       transform="matrix(0.9120087,0,0,0.9120087,0.33514277,25.128587)"
       style="stroke-width:1.09648085">
      <path
         transform="matrix(0.26458333,0,0,0.26458333,2.6134661,283.36966)"
         d="m 7.9257812,0.8359375 a 1.6178892,1.6178892 0 0 0 -0.2207031,0.009766 C 5.1410214,1.1501128 3.6301435,2.94586 3.1875,4.4550781 2.798777,5.7804512 2.9557893,6.822698 3.0019531,7.09375 c 0.045127,0.9218304 0.3822636,1.5885831 0.7910157,2.2910156 0.3127802,0.5375068 0.7335894,1.0229114 1.15625,1.4980464 0.028572,0.111047 0.050755,0.187185 0.082031,0.326172 0.0073,0.03244 0.00535,0.02259 0.011719,0.05664 -0.3761238,0.02572 -0.8216256,0.06774 -1.2910157,0.138672 -0.4857631,0.07341 -0.9830994,0.168253 -1.4765625,0.332031 -0.493463,0.163778 -1.0998654,0.233591 -1.6191406,1.203125 -0.23350107,0.435868 -0.24245693,0.654459 -0.2890625,0.923828 -0.0466056,0.269369 -0.0772406,0.547444 -0.10351562,0.84961 -0.0525501,0.604332 -0.0820372,1.293158 -0.0996094,1.955078 -0.0351443,1.323838 -0.0214844,2.544922 -0.0214844,2.544922 A 1.6178892,1.6178892 0 0 0 1.7597656,20.814453 H 14.033203 a 1.6178892,1.6178892 0 0 0 1.617188,-1.601562 c 0,0 0.01366,-1.221084 -0.02149,-2.544922 -0.01757,-0.661919 -0.04706,-1.350747 -0.09961,-1.955078 -0.02628,-0.302166 -0.05691,-0.580241 -0.103516,-0.84961 -0.04661,-0.269368 -0.05556,-0.487961 -0.289062,-0.923828 C 14.61759,11.970465 14.010855,11.900125 13.517578,11.736328 13.024302,11.572531 12.526554,11.477735 12.041016,11.404297 11.571088,11.33322 11.126348,11.28939 10.75,11.263672 c 0.0028,-0.01394 8.95e-4,-0.0098 0.0039,-0.02344 0.02862,-0.129185 0.05214,-0.210465 0.08008,-0.320312 0.433111,-0.472266 0.867515,-0.9510681 1.1875,-1.498047 0.424707,-0.7259885 0.791976,-1.4348115 0.777344,-2.4375 l -0.02734,0.3183594 c 0,0 0.276624,-1.338406 -0.166015,-2.8476563 C 12.162829,2.9458278 10.651988,1.1499497 8.0878906,0.84570312 A 1.6178892,1.6178892 0 0 0 7.9257812,0.8359375 Z M 10.701172,11.511719 c -0.0029,-0.0036 0.007,0.0064 0.0039,0.002 -0.04632,-0.06737 -0.0293,-0.353973 -0.0293,0.134766 0,0.04526 0.01972,-0.102408 0.02539,-0.136719 z m -5.6093751,0.002 c 0.00534,0.02911 0.025391,0.180144 0.025391,0.134766 0,-0.486237 0.016732,-0.199942 -0.029297,-0.132813 -0.00293,0.0043 0.00675,-0.0055 0.00391,-0.002 z"
         id="path1021"
         style="fill:url(#radialGradient1149);fill-opacity:1;stroke:none;stroke-width:1.09648073px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
         inkscape:original="M 7.8964844 2.453125 C 3.8572424 2.9326715 4.6113281 7.0078125 4.6113281 7.0078125 C 4.6485531 7.8381593 5.5094605 9.2817797 6.4453125 10.195312 C 6.4986525 10.392157 6.734375 11.271085 6.734375 11.648438 C 6.734375 12.069175 6.1908578 12.91084 5.8691406 12.861328 C 5.5474216 12.811888 2.4532451 13.010035 2.0820312 13.703125 C 1.7108136 14.396064 1.7597656 19.197266 1.7597656 19.197266 L 14.033203 19.197266 C 14.033203 19.197266 14.082152 14.396064 13.710938 13.703125 C 13.339721 13.010224 10.247498 12.811816 9.9257812 12.861328 C 9.6040608 12.910798 9.0585937 12.069174 9.0585938 11.648438 C 9.0585938 11.282797 9.2742792 10.466705 9.3378906 10.230469 C 10.300978 9.3271083 11.194239 7.8711535 11.181641 7.0078125 C 11.181641 7.0078125 11.935727 2.9324069 7.8964844 2.453125 z "
         inkscape:radius="1.6177274"
         sodipodi:type="inkscape:offset" />
      <path
         sodipodi:nodetypes="cccsccscccc"
         inkscape:connector-curvature="0"
         d="m 3.0792354,288.44873 c 0,0 -0.013096,-1.27028 0.085122,-1.45362 0.098217,-0.18338 0.9166976,-0.23571 1.0018191,-0.22263 0.085121,0.0131 0.2291744,-0.20953 0.2291744,-0.32085 0,-0.11131 -0.085123,-0.41905 -0.085123,-0.41905 h 0.7851589 c 0,0 -0.085122,0.30774 -0.085122,0.41905 0,0.11132 0.1440525,0.33394 0.2291744,0.32085 0.085121,-0.0131 0.9036015,0.0393 1.0018191,0.22263 0.098217,0.18334 0.085121,1.45362 0.085121,1.45362 z"
         style="fill:url(#linearGradient1151);fill-opacity:1;stroke:none;stroke-width:0.29011053px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
         id="path893" />
      <path
         sodipodi:nodetypes="cczcc"
         inkscape:connector-curvature="0"
         d="m 4.7028071,284.01884 c -1.0687161,0.12688 -0.8692216,1.20492 -0.8692216,1.20492 0.015116,0.33718 0.5431676,1.06402 0.8692218,1.06402 0.3260542,0 0.8742883,-0.71682 0.8692217,-1.06402 0,0 0.1994946,-1.07811 -0.8692216,-1.20492"
         style="fill:url(#radialGradient1153);fill-opacity:1;stroke:none;stroke-width:0.18091933;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
         id="path888" />
    </g>
    <g
       id="g1138"
       transform="matrix(0.83362971,0,0,0.83362961,-1.263316,47.635689)"
       style="stroke-width:1.1995734" />
    <path
       inkscape:connector-curvature="0"
       d="m -0.41544932,286.24407 -6e-7,0.0467 0.079412,0.0794 0.046709,2e-5 0.079411,-0.0794 -2.1e-5,-0.0467 -0.079412,-0.0794 h -0.046687 z m 0.1890431,-2.42429 c -0.050462,-5.4e-4 -0.099995,0.003 -0.1477672,0.0101 -0.0022,4.1e-4 -0.00479,5.4e-4 -0.00567,10e-4 a 0.03465606,0.03465606 0 0 1 -8.061e-4,0 c -0.1189057,0.0126 -0.2173312,0.0418 -0.2781267,0.085 -0.061644,0.0439 -0.097253,0.0941 -0.1089026,0.21092 -6.4e-6,2e-4 6.3e-6,2.1e-4 0,4.2e-4 -0.00108,0.0333 0.0017,0.0566 0.00486,0.0709 0.016269,-0.003 0.05438,-0.0179 0.097162,-0.0433 0.045262,-0.027 0.095547,-0.0636 0.1372414,-0.10242 a 0.03465606,0.03465606 0 0 1 0.01093,-0.006 c 2.279e-4,-2.1e-4 5.828e-4,-1.9e-4 8.061e-4,-4.2e-4 0.173871,-0.16094 0.4251415,-0.16698 0.6222422,-0.081 0.2022168,0.0882 0.3694383,0.29957 0.3214446,0.55544 -0.02942,0.25302 -0.2068773,0.42314 -0.3667866,0.56314 -0.1610552,0.14099 -0.3114891,0.26796 -0.3623339,0.38864 a 0.03465606,0.03465606 0 0 1 -8.062e-4,0.002 c -1.652e-4,4.1e-4 -7.68e-4,0.002 -0.00123,0.002 -0.011871,0.0255 -0.026525,0.0833 -0.033602,0.14534 -0.00708,0.062 -0.00747,0.12983 0.00243,0.18015 0.00958,0.0487 0.0298,0.0797 0.036841,0.087 2.695e-4,0 9.314e-4,10e-6 0.00123,0 0.0228,-0.0467 0.038064,-0.0971 0.059512,-0.18744 0.028951,-0.1219 0.091926,-0.27814 0.2586942,-0.41253 a 0.03465606,0.03465606 0 0 1 0.00162,-0.001 c 4.352e-4,-4.2e-4 7.68e-4,-10e-4 0.00123,-10e-4 a 0.03465606,0.03465606 0 0 1 4.145e-4,-4.2e-4 c 0.1472617,-0.11555 0.2995168,-0.24699 0.4032226,-0.38905 0.1044145,-0.14303 0.1602547,-0.28747 0.139266,-0.45707 -3.192e-4,-0.002 -5.992e-4,-0.004 -8.171e-4,-0.006 a 0.03465606,0.03465606 0 0 1 0,-0.002 l -0.00321,-0.0474 -0.00607,-0.0401 -4.042e-4,-10e-4 c -0.039415,-0.19025 -0.1654822,-0.32758 -0.3412823,-0.41821 -0.1316913,-0.0679 -0.2898916,-0.1043 -0.441278,-0.10606 z"
       style="opacity:1;vector-effect:none;fill:url(#radialGradient1579);fill-opacity:1;stroke:none;stroke-width:0.16500001;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
       id="path1577" />
    <g
       id="g1783"
       transform="matrix(0.74111183,0.1038833,-0.1038833,0.74111183,31.405072,72.993613)"
       style="stroke-width:1.33626032">
      <g
         transform="translate(-0.33072917,0.20079985)"
         style="stroke-width:1.33626032;filter:url(#filter1811)"
         id="g1633">
        <path
           inkscape:connector-curvature="0"
           id="path1629"
           d="m 6.4834403,286.26988 -8e-7,0.0619 0.1052325,0.10522 0.061896,3e-5 0.1052311,-0.10522 -2.78e-5,-0.0619 -0.1052325,-0.10522 H 6.588672 Z"
           style="opacity:1;vector-effect:none;fill:url(#radialGradient1893);fill-opacity:1;stroke:none;stroke-width:0.22048296;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
        <path
           sodipodi:nodetypes="cccccccccccccccccccccscccccccccccccccccccccc"
           inkscape:connector-curvature="0"
           id="path1631"
           d="m 6.7087489,283.58994 c -0.050882,-5.4e-4 -0.10279,0.003 -0.1523438,0.01 -0.00506,6.7e-4 -0.010589,-7.5e-4 -0.015625,0 -3.981e-4,2e-5 -0.00193,0 -0.00195,0 -1.645e-4,2e-5 -0.038952,0.0117 -0.039063,0.0117 -0.1113928,0.0158 -0.2154498,0.0387 -0.2929688,0.0937 -5.9e-6,6.7e-4 -5.9e-6,0.001 0,0.002 -0.07796,0.0555 -0.1426172,0.15431 -0.15625,0.29102 -9.37e-5,0.003 -9.37e-5,0.005 0,0.008 -0.00142,0.0437 0.00283,0.079 0.00781,0.10156 0.013901,0.0597 0.07263,0.0977 0.1328125,0.0859 0.057973,-0.0107 0.084766,-0.0289 0.1347656,-0.0586 0.059414,-0.0359 0.1122747,-0.0811 0.1679671,-0.12224 0.094598,-0.0883 0.3547641,-0.16032 0.5,-0.0625 0.1617225,0.0705 0.2910221,0.23377 0.2539063,0.43164 -7.463e-4,0.003 -0.0014,0.005 -0.00195,0.008 -0.024015,0.20653 -0.170998,0.35267 -0.328125,0.49023 -0.1602903,0.14032 -0.3218459,0.26181 -0.3925782,0.42969 -0.00426,0.0106 -0.00689,0.0218 -0.00781,0.0332 -0.016089,0.0442 -0.028521,0.0883 -0.035156,0.14649 -0.00805,0.0705 -0.010059,0.14581 0.00391,0.21679 0.0069,0.0351 0.017881,0.0649 0.029297,0.0879 0.011416,0.023 0.017907,0.0348 0.037109,0.0547 0.054282,0.0565 0.1481054,0.0426 0.1835937,-0.0273 0.027599,-0.0565 0.048071,-0.11921 0.070312,-0.21289 0.025238,-0.10627 0.07465,-0.23075 0.2148438,-0.34571 0.00165,-10e-4 0.00223,-0.003 0.00391,-0.004 6.5e-4,10e-6 0.0013,10e-6 0.00195,0 0.1508326,-0.11835 0.3089707,-0.25673 0.4238281,-0.41407 0.1142535,-0.1565 0.1871609,-0.33467 0.1621093,-0.5371 2.34e-5,-10e-4 2.34e-5,-0.003 0,-0.004 -1.9e-6,-1e-5 -0.00195,3e-5 -0.00195,0 4.3e-6,4e-5 3.81e-5,-0.002 0,-0.002 -1.897e-4,-8.3e-4 4.685e-4,-0.004 0,-0.006 l -0.00195,-0.0332 c -1.92e-4,-0.006 -8.44e-4,-0.0118 -0.00195,-0.0176 l -0.00586,-0.0293 c -3.716e-4,-0.005 -0.00102,-0.009 -0.00195,-0.0137 -0.046881,-0.22629 -0.2005557,-0.39409 -0.3984375,-0.4961 -0.1502384,-0.0775 -0.3237824,-0.11523 -0.4921875,-0.11719 z m 0.6757812,0.72266 c -9.37e-5,0.003 -9.37e-5,0.005 0,0.008 -0.00407,-0.14371 -0.00305,0.0432 0,-0.008 z"
           style="opacity:1;vector-effect:none;fill:url(#radialGradient1895);fill-opacity:1;stroke:none;stroke-width:0.22048296;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
      </g>
      <g
         id="g1819"
         style="stroke-width:1.33626032;filter:url(#filter1889)"
         transform="translate(-0.33072917,0.20079985)">
        <path
           style="opacity:1;vector-effect:none;fill:url(#radialGradient1639);fill-opacity:1;stroke:none;stroke-width:0.22048296;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
           d="m 6.4834403,286.26988 -8e-7,0.0619 0.1052325,0.10522 0.061896,3e-5 0.1052311,-0.10522 -2.78e-5,-0.0619 -0.1052325,-0.10522 H 6.588672 Z"
           id="path1815"
           inkscape:connector-curvature="0" />
        <path
           style="opacity:1;vector-effect:none;fill:url(#radialGradient1785);fill-opacity:1;stroke:none;stroke-width:0.22048296;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
           d="m 6.7087489,283.58994 c -0.050882,-5.4e-4 -0.10279,0.003 -0.1523438,0.01 -0.00506,6.7e-4 -0.010589,-7.5e-4 -0.015625,0 -3.981e-4,2e-5 -0.00193,0 -0.00195,0 -1.645e-4,2e-5 -0.038952,0.0117 -0.039063,0.0117 -0.1113928,0.0158 -0.2154498,0.0387 -0.2929688,0.0937 -5.9e-6,6.7e-4 -5.9e-6,0.001 0,0.002 -0.07796,0.0555 -0.1426172,0.15431 -0.15625,0.29102 -9.37e-5,0.003 -9.37e-5,0.005 0,0.008 -0.00142,0.0437 0.00283,0.079 0.00781,0.10156 0.013901,0.0597 0.07263,0.0977 0.1328125,0.0859 0.057973,-0.0107 0.084766,-0.0289 0.1347656,-0.0586 0.059414,-0.0359 0.1122747,-0.0811 0.1679671,-0.12224 0.094598,-0.0883 0.3547641,-0.16032 0.5,-0.0625 0.1617225,0.0705 0.2910221,0.23377 0.2539063,0.43164 -7.463e-4,0.003 -0.0014,0.005 -0.00195,0.008 -0.024015,0.20653 -0.170998,0.35267 -0.328125,0.49023 -0.1602903,0.14032 -0.3218459,0.26181 -0.3925782,0.42969 -0.00426,0.0106 -0.00689,0.0218 -0.00781,0.0332 -0.016089,0.0442 -0.028521,0.0883 -0.035156,0.14649 -0.00805,0.0705 -0.010059,0.14581 0.00391,0.21679 0.0069,0.0351 0.017881,0.0649 0.029297,0.0879 0.011416,0.023 0.017907,0.0348 0.037109,0.0547 0.054282,0.0565 0.1481054,0.0426 0.1835937,-0.0273 0.027599,-0.0565 0.048071,-0.11921 0.070312,-0.21289 0.025238,-0.10627 0.07465,-0.23075 0.2148438,-0.34571 0.00165,-10e-4 0.00223,-0.003 0.00391,-0.004 6.5e-4,10e-6 0.0013,10e-6 0.00195,0 0.1508326,-0.11835 0.3089707,-0.25673 0.4238281,-0.41407 0.1142535,-0.1565 0.1871609,-0.33467 0.1621093,-0.5371 2.34e-5,-10e-4 2.34e-5,-0.003 0,-0.004 -1.9e-6,-1e-5 -0.00195,3e-5 -0.00195,0 4.3e-6,4e-5 3.81e-5,-0.002 0,-0.002 -1.897e-4,-8.3e-4 4.685e-4,-0.004 0,-0.006 l -0.00195,-0.0332 c -1.92e-4,-0.006 -8.44e-4,-0.0118 -0.00195,-0.0176 l -0.00586,-0.0293 c -3.716e-4,-0.005 -0.00102,-0.009 -0.00195,-0.0137 -0.046881,-0.22629 -0.2005557,-0.39409 -0.3984375,-0.4961 -0.1502384,-0.0775 -0.3237824,-0.11523 -0.4921875,-0.11719 z m 0.6757812,0.72266 c -9.37e-5,0.003 -9.37e-5,0.005 0,0.008 -0.00407,-0.14371 -0.00305,0.0432 0,-0.008 z"
           id="path1817"
           inkscape:connector-curvature="0"
           sodipodi:nodetypes="cccccccccccccccccccccscccccccccccccccccccccc" />
      </g>
      <g
         transform="translate(-0.33072916,0.20079985)"
         id="g1627"
         style="stroke-width:1.33626032">
        <path
           style="opacity:1;vector-effect:none;fill:url(#radialGradient1773);fill-opacity:1;stroke:none;stroke-width:0.22048296;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
           d="m 6.4242542,286.25641 -1.2e-6,0.0888 0.1509685,0.15095 0.088797,4e-5 0.1509666,-0.15095 -3.99e-5,-0.0888 -0.1509685,-0.15095 h -0.088755 z"
           id="path1617"
           inkscape:connector-curvature="0" />
        <path
           style="opacity:1;vector-effect:none;fill:url(#radialGradient1775);fill-opacity:1;stroke:none;stroke-width:0.22048296;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
           d="m 6.7087489,283.58994 c -0.050882,-5.4e-4 -0.10279,0.003 -0.1523438,0.01 -0.00506,6.7e-4 -0.010589,-7.5e-4 -0.015625,0 -3.981e-4,2e-5 -0.00193,0 -0.00195,0 -1.645e-4,2e-5 -0.038952,0.0117 -0.039063,0.0117 -0.1113928,0.0158 -0.2154498,0.0387 -0.2929688,0.0937 -5.9e-6,6.7e-4 -5.9e-6,0.001 0,0.002 -0.07796,0.0555 -0.1426172,0.15431 -0.15625,0.29102 -9.37e-5,0.003 -9.37e-5,0.005 0,0.008 -0.00142,0.0437 0.00283,0.079 0.00781,0.10156 0.013901,0.0597 0.07263,0.0977 0.1328125,0.0859 0.057973,-0.0107 0.084766,-0.0289 0.1347656,-0.0586 0.059414,-0.0359 0.1122747,-0.0811 0.1679671,-0.12224 0.094598,-0.0883 0.3547641,-0.16032 0.5,-0.0625 0.1617225,0.0705 0.2910221,0.23377 0.2539063,0.43164 -7.463e-4,0.003 -0.0014,0.005 -0.00195,0.008 -0.024015,0.20653 -0.170998,0.35267 -0.328125,0.49023 -0.1602903,0.14032 -0.3218459,0.26181 -0.3925782,0.42969 -0.00426,0.0106 -0.00689,0.0218 -0.00781,0.0332 -0.016089,0.0442 -0.028521,0.0883 -0.035156,0.14649 -0.00805,0.0705 -0.010059,0.14581 0.00391,0.21679 0.0069,0.0351 0.017881,0.0649 0.029297,0.0879 0.011416,0.023 0.017907,0.0348 0.037109,0.0547 0.054282,0.0565 0.1481054,0.0426 0.1835937,-0.0273 0.027599,-0.0565 0.048071,-0.11921 0.070312,-0.21289 0.025238,-0.10627 0.07465,-0.23075 0.2148438,-0.34571 0.00165,-10e-4 0.00223,-0.003 0.00391,-0.004 6.5e-4,10e-6 0.0013,10e-6 0.00195,0 0.1508326,-0.11835 0.3089707,-0.25673 0.4238281,-0.41407 0.1142535,-0.1565 0.1871609,-0.33467 0.1621093,-0.5371 2.34e-5,-10e-4 2.34e-5,-0.003 0,-0.004 -1.9e-6,-1e-5 -0.00195,3e-5 -0.00195,0 4.3e-6,4e-5 3.81e-5,-0.002 0,-0.002 -1.897e-4,-8.3e-4 4.685e-4,-0.004 0,-0.006 l -0.00195,-0.0332 c -1.92e-4,-0.006 -8.44e-4,-0.0118 -0.00195,-0.0176 l -0.00586,-0.0293 c -3.716e-4,-0.005 -0.00102,-0.009 -0.00195,-0.0137 -0.046881,-0.22629 -0.2005557,-0.39409 -0.3984375,-0.4961 -0.1502384,-0.0775 -0.3237824,-0.11523 -0.4921875,-0.11719 z m 0.6757812,0.72266 c -9.37e-5,0.003 -9.37e-5,0.005 0,0.008 -0.00407,-0.14371 -0.00305,0.0432 0,-0.008 z"
           id="path1611"
           inkscape:connector-curvature="0"
           sodipodi:nodetypes="cccccccccccccccccccccscccccccccccccccccccccc" />
      </g>
    </g>
  </g>
</svg>

Modified static/style.scss from [de5cdf2da4] to [d08e77f56c].

268
269
270
271
272
273
274






























275
276
277
278
279
280
281
...
290
291
292
293
294
295
296

297
298
299
300
301
302
303
...
411
412
413
414
415
416
417


418




419
420
421
422
423
424
425
...
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
	}
	> .xid {
		color: tone(20%,-0.1);
		font-size: 80%;
		vertical-align: text-top;
	}
}































div.profile {
	padding: 0.1in;
	position: relative;
	display: grid;
	margin-bottom: 0.4in;
	grid-template-columns: 2fr 1fr;
................................................................................
		> .avatar {
			display: block;
			width: 1in; height: 1in;
			object-fit: cover;
			grid-column: 1 / 2;
			grid-row: 1 / 3;
			border: 1px solid black;

		}
		> .id {
			grid-column: 2 / 3;
			grid-row: 1 / 2;
		}
		> .bio {
			grid-column: 2 / 3;
................................................................................
		> .msg:first-child { padding-top: 0; }
		> .user {
			width: max-content; margin: auto;
			background: tone(-20%,-0.3);
			border: 1px solid black;
			color: tone(-50%);
			padding: 0.1in;


			> img { display: block; width: 1in; height: 1in; margin: auto; border: 1px solid black; }




			> .name { @extend %serif; text-align: center; font-size: 130%; font-weight: bold; margin-top: 0.08in; }
		}
		>form {
			display: grid;
			grid-template-columns: 1fr 1fr;
			grid-template-rows: 1.2em max-content max-content;
			grid-gap: 5px;
................................................................................
		background: url(/s/padlock.svg) no-repeat, $grad-ui-focus;
		background-size: 20pt;
		background-position: 0.05in 50%;
	};
	padding-left: 0.40in;
}

div.modal {
	@extend %box;
	position: fixed;
	display: none;
	left: 0; right: 0; bottom: 0; top: 0;
	max-width: 7in;
	margin: 1in auto;
	padding: 0.2in 0.3in;







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







 







>







 







>
>
|
>
>
>
>







 







|







268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
...
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
...
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
...
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
	}
	> .xid {
		color: tone(20%,-0.1);
		font-size: 80%;
		vertical-align: text-top;
	}
}

body.profile {
	#rel {
		menu {
			display: grid;
			grid-template-columns: 1fr 1fr;
			grid-template-rows: repeat(max-content);
			grid-gap: 0.1in;
			> .opt {
				padding: 0.1in;
				border-radius: 5px;
				border: 1px solid transparent;
				&.on {
					background-color: tone(-30%, -0.7);
					box-shadow: 0 0 10px tone(-30%);
					border-color: tone(-20%);
				}
				> button, > p { display: block; }
				> p { text-align: center; font-size: 80%; margin: 0; margin-top: 0.1in; }
				> button {
					margin: auto;
				}
				&:last-child:nth-child(2n-1) {
					grid-column: 1/3;
				}
			}

		}
	}
}

div.profile {
	padding: 0.1in;
	position: relative;
	display: grid;
	margin-bottom: 0.4in;
	grid-template-columns: 2fr 1fr;
................................................................................
		> .avatar {
			display: block;
			width: 1in; height: 1in;
			object-fit: cover;
			grid-column: 1 / 2;
			grid-row: 1 / 3;
			border: 1px solid black;
			background-color: tone(-57%);
		}
		> .id {
			grid-column: 2 / 3;
			grid-row: 1 / 2;
		}
		> .bio {
			grid-column: 2 / 3;
................................................................................
		> .msg:first-child { padding-top: 0; }
		> .user {
			width: max-content; margin: auto;
			background: tone(-20%,-0.3);
			border: 1px solid black;
			color: tone(-50%);
			padding: 0.1in;
			> img {
				display: block;
				width: 1in; height: 1in;
				margin: auto;
				border: 1px solid black;
				background-color: tone(-50%);
			}
			> .name { @extend %serif; text-align: center; font-size: 130%; font-weight: bold; margin-top: 0.08in; }
		}
		>form {
			display: grid;
			grid-template-columns: 1fr 1fr;
			grid-template-rows: 1.2em max-content max-content;
			grid-gap: 5px;
................................................................................
		background: url(/s/padlock.svg) no-repeat, $grad-ui-focus;
		background-size: 20pt;
		background-position: 0.05in 50%;
	};
	padding-left: 0.40in;
}

.modal {
	@extend %box;
	position: fixed;
	display: none;
	left: 0; right: 0; bottom: 0; top: 0;
	max-width: 7in;
	margin: 1in auto;
	padding: 0.2in 0.3in;

Added static/sub.svg version [ed11a76c38].









































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->

<svg
   xmlns:dc="http://purl.org/dc/elements/1.1/"
   xmlns:cc="http://creativecommons.org/ns#"
   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
   xmlns:svg="http://www.w3.org/2000/svg"
   xmlns="http://www.w3.org/2000/svg"
   xmlns:xlink="http://www.w3.org/1999/xlink"
   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
   width="20"
   height="20"
   viewBox="0 0 5.2916664 5.2916665"
   version="1.1"
   id="svg8"
   inkscape:version="0.92.4 (5da689c313, 2019-01-14)"
   sodipodi:docname="sub.svg">
  <defs
     id="defs2">
    <linearGradient
       inkscape:collect="always"
       id="linearGradient1080">
      <stop
         style="stop-color:#c2ff3c;stop-opacity:0.81568629"
         offset="0"
         id="stop1076" />
      <stop
         style="stop-color:#b6ff0d;stop-opacity:0.02173913"
         offset="1"
         id="stop1078" />
    </linearGradient>
    <linearGradient
       id="linearGradient1070"
       inkscape:collect="always">
      <stop
         id="stop1066"
         offset="0"
         style="stop-color:#f6ffe2;stop-opacity:0.81568629" />
      <stop
         id="stop1068"
         offset="1"
         style="stop-color:#b6ff0d;stop-opacity:1" />
    </linearGradient>
    <linearGradient
       inkscape:collect="always"
       id="linearGradient1054">
      <stop
         style="stop-color:#ff73e0;stop-opacity:1"
         offset="0"
         id="stop1050" />
      <stop
         style="stop-color:#ff00cb;stop-opacity:0.98431373"
         offset="1"
         id="stop1052" />
    </linearGradient>
    <linearGradient
       id="linearGradient1034"
       inkscape:collect="always">
      <stop
         id="stop1030"
         offset="0"
         style="stop-color:#ff55dd;stop-opacity:0.50869566" />
      <stop
         id="stop1032"
         offset="1"
         style="stop-color:#ff55dd;stop-opacity:0.02173913" />
    </linearGradient>
    <linearGradient
       id="linearGradient1019"
       inkscape:collect="always">
      <stop
         id="stop1013"
         offset="0"
         style="stop-color:#680054;stop-opacity:0.01304348" />
      <stop
         style="stop-color:#ff40d9;stop-opacity:0.23913044"
         offset="0.30000001"
         id="stop1015" />
      <stop
         id="stop1017"
         offset="1"
         style="stop-color:#d400aa;stop-opacity:0;" />
    </linearGradient>
    <linearGradient
       id="linearGradient1005"
       inkscape:collect="always">
      <stop
         id="stop1001"
         offset="0"
         style="stop-color:#fff6fd;stop-opacity:1" />
      <stop
         id="stop1003"
         offset="1"
         style="stop-color:#ff3cd7;stop-opacity:0.98260868" />
    </linearGradient>
    <linearGradient
       inkscape:collect="always"
       id="linearGradient979">
      <stop
         style="stop-color:#680054;stop-opacity:1"
         offset="0"
         id="stop975" />
      <stop
         id="stop983"
         offset="0.30000001"
         style="stop-color:#d400aa;stop-opacity:0.77254902;" />
      <stop
         style="stop-color:#d400aa;stop-opacity:0;"
         offset="1"
         id="stop977" />
    </linearGradient>
    <linearGradient
       inkscape:collect="always"
       id="linearGradient971">
      <stop
         style="stop-color:#ff55dd;stop-opacity:1;"
         offset="0"
         id="stop967" />
      <stop
         style="stop-color:#ff55dd;stop-opacity:0.97826087"
         offset="1"
         id="stop969" />
    </linearGradient>
    <linearGradient
       inkscape:collect="always"
       id="linearGradient963">
      <stop
         style="stop-color:#c839ac;stop-opacity:0.00392157"
         offset="0"
         id="stop959" />
      <stop
         style="stop-color:#852572;stop-opacity:1"
         offset="1"
         id="stop961" />
    </linearGradient>
    <linearGradient
       inkscape:collect="always"
       id="linearGradient943">
      <stop
         style="stop-color:#f4d7ee;stop-opacity:1;"
         offset="0"
         id="stop939" />
      <stop
         style="stop-color:#f4d7ee;stop-opacity:0;"
         offset="1"
         id="stop941" />
    </linearGradient>
    <linearGradient
       inkscape:collect="always"
       id="linearGradient954">
      <stop
         style="stop-color:#ffffff;stop-opacity:1;"
         offset="0"
         id="stop950" />
      <stop
         style="stop-color:#ffffff;stop-opacity:0;"
         offset="1"
         id="stop952" />
    </linearGradient>
    <linearGradient
       inkscape:collect="always"
       id="linearGradient938">
      <stop
         style="stop-color:#d9fff6;stop-opacity:1;"
         offset="0"
         id="stop934" />
      <stop
         style="stop-color:#d9fff6;stop-opacity:0;"
         offset="1"
         id="stop936" />
    </linearGradient>
    <linearGradient
       inkscape:collect="always"
       id="linearGradient1403">
      <stop
         style="stop-color:#ccaaff;stop-opacity:1;"
         offset="0"
         id="stop1399" />
      <stop
         style="stop-color:#ccaaff;stop-opacity:0;"
         offset="1"
         id="stop1401" />
    </linearGradient>
    <linearGradient
       id="linearGradient1395"
       inkscape:collect="always">
      <stop
         id="stop1391"
         offset="0"
         style="stop-color:#ff1616;stop-opacity:1" />
      <stop
         id="stop1393"
         offset="1"
         style="stop-color:#ff1d1d;stop-opacity:0" />
    </linearGradient>
    <linearGradient
       inkscape:collect="always"
       id="linearGradient1383">
      <stop
         style="stop-color:#980000;stop-opacity:1;"
         offset="0"
         id="stop1379" />
      <stop
         style="stop-color:#980000;stop-opacity:0;"
         offset="1"
         id="stop1381" />
    </linearGradient>
    <linearGradient
       inkscape:collect="always"
       id="linearGradient832">
      <stop
         style="stop-color:#ffcfcf;stop-opacity:1;"
         offset="0"
         id="stop828" />
      <stop
         style="stop-color:#ffcfcf;stop-opacity:0;"
         offset="1"
         id="stop830" />
    </linearGradient>
    <radialGradient
       inkscape:collect="always"
       xlink:href="#linearGradient832"
       id="radialGradient834"
       cx="3.2286437"
       cy="286.62921"
       fx="3.2286437"
       fy="286.62921"
       r="1.0866126"
       gradientTransform="matrix(1.8608797,0.8147617,-0.38242057,0.87343168,106.71446,33.692223)"
       gradientUnits="userSpaceOnUse" />
    <radialGradient
       inkscape:collect="always"
       xlink:href="#linearGradient1383"
       id="radialGradient1385"
       cx="4.1787109"
       cy="286.89261"
       fx="4.1787109"
       fy="286.89261"
       r="1.2260786"
       gradientTransform="matrix(1.7016464,0,0,1.6348586,-2.9319775,-182.10895)"
       gradientUnits="userSpaceOnUse" />
    <radialGradient
       inkscape:collect="always"
       xlink:href="#linearGradient1395"
       id="radialGradient1389"
       gradientUnits="userSpaceOnUse"
       gradientTransform="matrix(0.66230313,-1.6430738,1.0154487,0.40931507,-290.06307,177.39489)"
       cx="4.02069"
       cy="287.79269"
       fx="4.02069"
       fy="287.79269"
       r="1.0866126" />
    <linearGradient
       inkscape:collect="always"
       xlink:href="#linearGradient1403"
       id="linearGradient1405"
       x1="8.3939333"
       y1="288.1091"
       x2="7.0158253"
       y2="287.32819"
       gradientUnits="userSpaceOnUse" />
    <linearGradient
       inkscape:collect="always"
       xlink:href="#linearGradient938"
       id="linearGradient940"
       x1="7.609839"
       y1="288.73215"
       x2="7.609839"
       y2="283.78305"
       gradientUnits="userSpaceOnUse" />
    <linearGradient
       inkscape:collect="always"
       xlink:href="#linearGradient954"
       id="linearGradient956"
       x1="3.0150654"
       y1="285.94464"
       x2="3.0150654"
       y2="282.40109"
       gradientUnits="userSpaceOnUse" />
    <linearGradient
       inkscape:collect="always"
       xlink:href="#linearGradient954"
       id="linearGradient1138"
       gradientUnits="userSpaceOnUse"
       x1="3.0150654"
       y1="285.94464"
       x2="3.0150654"
       y2="284.62277" />
    <radialGradient
       inkscape:collect="always"
       xlink:href="#linearGradient1005"
       id="radialGradient945"
       cx="6.1517248"
       cy="285.09021"
       fx="6.1517248"
       fy="285.09021"
       r="1.3844374"
       gradientTransform="matrix(2.4674713,0,0,2.4674669,-5.8821073,-417.49152)"
       gradientUnits="userSpaceOnUse" />
    <radialGradient
       inkscape:collect="always"
       xlink:href="#linearGradient943"
       id="radialGradient953"
       gradientUnits="userSpaceOnUse"
       gradientTransform="matrix(2.4674713,0,0,2.4674669,-9.027479,-418.36044)"
       cx="6.1517248"
       cy="285.09021"
       fx="6.1517248"
       fy="285.09021"
       r="1.3844374" />
    <radialGradient
       inkscape:collect="always"
       xlink:href="#linearGradient963"
       id="radialGradient965"
       cx="6.1523438"
       cy="285.08984"
       fx="6.1523438"
       fy="285.08984"
       r="1.6679688"
       gradientTransform="matrix(1,0,0,0.99999775,0,6.4095141e-4)"
       gradientUnits="userSpaceOnUse" />
    <radialGradient
       inkscape:collect="always"
       xlink:href="#linearGradient971"
       id="radialGradient973"
       cx="4.6300988"
       cy="285.0715"
       fx="4.6300988"
       fy="285.0715"
       r="0.88396439"
       gradientTransform="matrix(3.5121044,0,0,4.5073949,-11.771195,-1000.0836)"
       gradientUnits="userSpaceOnUse" />
    <linearGradient
       inkscape:collect="always"
       xlink:href="#linearGradient979"
       id="linearGradient981"
       x1="6.3269596"
       y1="286.08289"
       x2="6.3263793"
       y2="288.44873"
       gradientUnits="userSpaceOnUse" />
    <linearGradient
       inkscape:collect="always"
       xlink:href="#linearGradient1019"
       id="linearGradient1009"
       gradientUnits="userSpaceOnUse"
       x1="6.3269596"
       y1="286.08289"
       x2="6.3263793"
       y2="288.44873" />
    <radialGradient
       inkscape:collect="always"
       xlink:href="#linearGradient1034"
       id="radialGradient1028"
       cx="7.8964839"
       cy="10.825195"
       fx="7.8964839"
       fy="10.825195"
       r="6.1388507"
       gradientTransform="matrix(1.5553588,0,0,2.1211746,-4.385382,-12.136934)"
       gradientUnits="userSpaceOnUse" />
    <radialGradient
       inkscape:collect="always"
       xlink:href="#linearGradient1034"
       id="radialGradient1038"
       gradientUnits="userSpaceOnUse"
       gradientTransform="matrix(-1.5553588,0,0,-1.3840186,20.178349,25.807467)"
       cx="7.8964839"
       cy="10.825195"
       fx="7.8964839"
       fy="10.825195"
       r="6.1388507" />
    <radialGradient
       inkscape:collect="always"
       xlink:href="#linearGradient1054"
       id="radialGradient1048"
       gradientUnits="userSpaceOnUse"
       gradientTransform="matrix(2.4674713,0,0,2.4674669,-5.8821075,-417.49152)"
       cx="6.1517248"
       cy="285.09021"
       fx="6.1517248"
       fy="285.09021"
       r="1.3844374" />
    <radialGradient
       inkscape:collect="always"
       xlink:href="#linearGradient863"
       id="radialGradient865"
       cx="2.5399754"
       cy="295.73944"
       fx="2.5399754"
       fy="295.91135"
       r="1.6620824"
       gradientTransform="matrix(2.9231275,0,0,2.7982783,-2.1662153,-540.00013)"
       gradientUnits="userSpaceOnUse" />
    <linearGradient
       inkscape:collect="always"
       id="linearGradient863">
      <stop
         style="stop-color:#b5ff0b;stop-opacity:0.81739128"
         offset="0"
         id="stop859" />
      <stop
         style="stop-color:#f5ffde;stop-opacity:0.88695651"
         offset="1"
         id="stop861" />
    </linearGradient>
    <radialGradient
       inkscape:collect="always"
       xlink:href="#linearGradient863"
       id="radialGradient865-3"
       cx="2.5399754"
       cy="295.73944"
       fx="2.5399754"
       fy="295.91135"
       r="1.6620824"
       gradientTransform="matrix(11.048041,0,0,10.57617,-21.779867,-3114.7234)"
       gradientUnits="userSpaceOnUse" />
    <radialGradient
       inkscape:collect="always"
       xlink:href="#linearGradient863"
       id="radialGradient1051"
       gradientUnits="userSpaceOnUse"
       gradientTransform="matrix(0.43493944,0,0,0.41636282,4.1537212,164.42622)"
       cx="1.4165958"
       cy="296.81403"
       fx="1.4165958"
       fy="296.81403"
       r="1.6620824" />
    <radialGradient
       inkscape:collect="always"
       xlink:href="#linearGradient1070"
       id="radialGradient1063"
       gradientUnits="userSpaceOnUse"
       gradientTransform="matrix(1.9675506,-0.52720423,0.47654978,1.778508,-133.90186,-239.39035)"
       cx="1.7406042"
       cy="296.20987"
       fx="1.7406042"
       fy="296.20987"
       r="1.6620824" />
    <radialGradient
       inkscape:collect="always"
       xlink:href="#linearGradient1080"
       id="radialGradient1074"
       gradientUnits="userSpaceOnUse"
       gradientTransform="matrix(1.9675506,-0.52720423,0.47654978,1.778508,-133.90186,-239.39035)"
       cx="1.7406042"
       cy="296.20987"
       fx="1.7406042"
       fy="296.20987"
       r="1.6620824" />
  </defs>
  <sodipodi:namedview
     id="base"
     pagecolor="#181818"
     bordercolor="#666666"
     borderopacity="1.0"
     inkscape:pageopacity="0"
     inkscape:pageshadow="2"
     inkscape:zoom="1.979899"
     inkscape:cx="106.19422"
     inkscape:cy="36.397286"
     inkscape:document-units="mm"
     inkscape:current-layer="layer1"
     showgrid="false"
     units="px"
     inkscape:window-width="1920"
     inkscape:window-height="1042"
     inkscape:window-x="0"
     inkscape:window-y="38"
     inkscape:window-maximized="0"
     showguides="true"
     fit-margin-top="0"
     fit-margin-left="0"
     fit-margin-right="0"
     fit-margin-bottom="0" />
  <metadata
     id="metadata5">
    <rdf:RDF>
      <cc:Work
         rdf:about="">
        <dc:format>image/svg+xml</dc:format>
        <dc:type
           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
        <dc:title></dc:title>
      </cc:Work>
    </rdf:RDF>
  </metadata>
  <g
     inkscape:label="Layer 1"
     inkscape:groupmode="layer"
     id="layer1"
     transform="translate(-2.6134661,-283.36966)">
    <path
       sodipodi:type="inkscape:offset"
       inkscape:radius="0.54500842"
       inkscape:original="M 10.669922 284.26953 C 10.335835 284.27753 10.318359 284.625 10.318359 284.625 C 9.3265928 284.99453 9.4746094 286.52344 9.4746094 286.52344 C 9.0235918 286.69884 9.0078125 287.27539 9.0078125 287.27539 C 10.093208 287.51331 11.202093 287.50516 12.332031 287.27539 C 12.332031 287.27539 12.314303 286.69884 11.863281 286.52344 C 11.863281 286.52344 12.011298 284.99453 11.019531 284.625 C 11.019531 284.625 11.004009 284.27789 10.669922 284.26953 z M 10.34375 287.5957 C 10.34375 287.5957 10.304813 288.0715 10.669922 288.0918 C 11.035034 288.0715 10.994141 287.5957 10.994141 287.5957 C 10.828305 287.6047 10.667343 287.6136 10.34375 287.5957 z "
       style="opacity:1;vector-effect:none;fill:url(#radialGradient1074);fill-opacity:1;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
       id="path1055"
       d="m 10.65625,283.72461 c -0.355601,0.009 -0.661267,0.26644 -0.7695312,0.47266 -0.040068,0.0763 -0.024426,0.0846 -0.041016,0.14843 -0.4844992,0.3329 -0.7685327,0.82955 -0.8554687,1.25782 -0.069301,0.34139 -0.044874,0.46468 -0.039063,0.63867 -0.4655292,0.3998 -0.4882813,1.01758 -0.4882813,1.01758 a 0.54506292,0.54506292 0 0 0 0.4277344,0.54882 c 0.3131979,0.0687 0.632618,0.0707 0.9492188,0.10157 0.00732,0.04 -0.00509,0.0364 0.00781,0.0801 0.035449,0.11995 0.097474,0.26806 0.2324218,0.40235 0.134948,0.13429 0.350907,0.23248 0.560547,0.24414 a 0.54506292,0.54506292 0 0 0 0.05859,0 c 0.209666,-0.0117 0.425592,-0.11128 0.560547,-0.2461 0.134954,-0.13481 0.197395,-0.28219 0.232422,-0.40234 0.01276,-0.0438 6.38e-4,-0.04 0.0078,-0.0801 0.3138,-0.0311 0.626443,-0.0356 0.941406,-0.0996 a 0.54506292,0.54506292 0 0 0 0.435547,-0.55078 c 0,0 -0.02416,-0.61828 -0.490234,-1.01758 0.0057,-0.17429 0.03003,-0.29636 -0.03906,-0.63671 -0.08686,-0.42789 -0.369921,-0.92484 -0.853515,-1.25782 -0.01631,-0.0633 -0.0018,-0.0711 -0.04102,-0.14648 -0.107556,-0.20696 -0.414125,-0.46572 -0.769531,-0.47461 a 0.54506292,0.54506292 0 0 0 -0.02734,0 z"
       transform="translate(-5.4106269,-0.16547988)" />
    <path
       id="path836"
       style="fill:url(#radialGradient865);fill-opacity:1;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
       d="m 6.9205392,287.11054 c 0,0 -0.016703,-0.57629 -0.4677217,-0.75169 0,0 0.1485135,-1.53033 -0.8432532,-1.89986 0,0 -0.017021,-0.34687 -0.3511075,-0.35523 -0.3340865,0.008 -0.3511075,0.35523 -0.3511075,0.35523 -0.9917668,0.36953 -0.8432532,1.89986 -0.8432532,1.89986 -0.4510176,0.1754 -0.4677216,0.75169 -0.4677216,0.75169 1.0853958,0.23792 2.1942267,0.22977 3.3241647,0 z"
       inkscape:connector-curvature="0"
       sodipodi:nodetypes="cccccccc" />
    <path
       id="path841"
       style="opacity:1;vector-effect:none;fill:url(#radialGradient1051);fill-opacity:1;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
       d="m 5.5830229,287.43002 c 0,0 0.040568,0.47666 -0.3245413,0.49696 -0.3651092,-0.0203 -0.3245412,-0.49696 -0.3245412,-0.49696 0.3235932,0.0179 0.4832471,0.009 0.6490825,0 z"
       inkscape:connector-curvature="0"
       sodipodi:nodetypes="cccc" />
  </g>
</svg>

Modified store.t from [bb9260aa8d] to [131fc4292b].

2
3
4
5
6
7
8


9
10
11
12
13
14
15
16
17
18
19
20
21
..
98
99
100
101
102
103
104







105
106
107
108
109
110
111
...
115
116
117
118
119
120
121

122
123
124
125
126
127
128
...
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
local m = {
	timepoint = lib.osclock.time_t;
	scope = lib.enum {
		'public', 'private', 'local';
		'personal', 'direct', 'circle';
	};
	noticetype = lib.enum {


		'none', 'mention', 'reply', 'like', 'rt', 'react', 'follow'
	};

	relation = lib.set {
		'follow',
		'subscribe', -- get a notification for every post
		'mute', -- posts will be completely hidden at all times
		'block', -- no interactions will be permitted, but posts will remain visible
		'silence', -- messages will not be accepted
		'collapse', -- posts will be collapsed by default
		'disemvowel', -- posts will be ritually humiliated, but shown
		'avoid', -- posts will be kept out of the timeline but will show on users' posts and in conversations
		'exclude', -- own posts will not be visible to this user
................................................................................
	(pow.propagate << true)
	(pow.artifact  << true)
	(pow.account   << true)
	(pow.edit      << true)
	(pow.snitch    << true)
	return m.rights { rank = 0, quota = 1000, invites = 0, powers = pow; }
end








struct m.actor {
	id: uint64
	nym: str
	handle: str
	origin: uint64
	bio: str
................................................................................
	knownsince: m.timepoint
	rights: m.rights
	key: lib.mem.ptr(uint8)

-- ephemera
	xid: str
	source: &m.source

}

terra m.actor:outranks(other: &m.actor)
 -- this predicate determines where two users stand relative to
 -- each other in the formal staff hierarchy. it is used in
 -- authority calculations, but this function should only be
 -- used directly in rendering code and by other predicates.
................................................................................
	aname: str
	comment: str
	netmask: m.inet
	privs: m.privset
	blacklist: bool
}

struct m.relationship {
	agent: uint64
	patient: uint64
	rel: m.relation -- agent โ†’ patient
	recip: m.relation -- patient โ†’ agent
}

-- backends only handle content on the local server
struct m.backend { id: rawstring
	open: &m.source -> &opaque
	close: &m.source -> {}
	dbsetup: &m.source -> bool -- creates the schema needed to call conprep (called only once per database e.g. with `parsav db init`)
	conprep: {&m.source, m.prepmode.t} -> {} -- prepares queries and similar tasks that require the schema to already be in place
	obliterate_everything: &m.source -> bool -- wipes everything parsav-related out of the database







>
>
|




|







 







>
>
>
>
>
>
>







 







>







 







<
<
<
<
<
<
<







2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
...
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
...
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
...
340
341
342
343
344
345
346







347
348
349
350
351
352
353
local m = {
	timepoint = lib.osclock.time_t;
	scope = lib.enum {
		'public', 'private', 'local';
		'personal', 'direct', 'circle';
	};
	noticetype = lib.enum {
	 -- only add new values to the end of this list! the numerical value
	 -- is stored in the database and must be kept synchronized across versions
		'none', 'mention', 'reply', 'like', 'rt', 'react', 'follow', 'followreq'
	};

	relation = lib.set {
		'follow',
		'sub', -- get a notification for every post
		'mute', -- posts will be completely hidden at all times
		'block', -- no interactions will be permitted, but posts will remain visible
		'silence', -- messages will not be accepted
		'collapse', -- posts will be collapsed by default
		'disemvowel', -- posts will be ritually humiliated, but shown
		'avoid', -- posts will be kept out of the timeline but will show on users' posts and in conversations
		'exclude', -- own posts will not be visible to this user
................................................................................
	(pow.propagate << true)
	(pow.artifact  << true)
	(pow.account   << true)
	(pow.edit      << true)
	(pow.snitch    << true)
	return m.rights { rank = 0, quota = 1000, invites = 0, powers = pow; }
end

struct m.relationship {
	agent: uint64
	patient: uint64
	rel: m.relation -- agent โ†’ patient
	recip: m.relation -- patient โ†’ agent
}

struct m.actor {
	id: uint64
	nym: str
	handle: str
	origin: uint64
	bio: str
................................................................................
	knownsince: m.timepoint
	rights: m.rights
	key: lib.mem.ptr(uint8)

-- ephemera
	xid: str
	source: &m.source
	relationship: m.relationship -- relationship to the logged-in user, if any
}

terra m.actor:outranks(other: &m.actor)
 -- this predicate determines where two users stand relative to
 -- each other in the formal staff hierarchy. it is used in
 -- authority calculations, but this function should only be
 -- used directly in rendering code and by other predicates.
................................................................................
	aname: str
	comment: str
	netmask: m.inet
	privs: m.privset
	blacklist: bool
}








-- backends only handle content on the local server
struct m.backend { id: rawstring
	open: &m.source -> &opaque
	close: &m.source -> {}
	dbsetup: &m.source -> bool -- creates the schema needed to call conprep (called only once per database e.g. with `parsav db init`)
	conprep: {&m.source, m.prepmode.t} -> {} -- prepares queries and similar tasks that require the schema to already be in place
	obliterate_everything: &m.source -> bool -- wipes everything parsav-related out of the database

Modified view/media-gallery.tpl from [18862037ee] to [898f5a4a48].

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<menu>@menu
	<a href="@pfx/media">new uploads</a>
	<a href="@pfx/media/unfiled">unfiled</a>
	<hr>
	@folders
	<a href="@pfx/media/all">all uploads</a>
	<a href="@pfx/media/kind/img">all images</a>
	<a href="@pfx/media/kind/vid">all videos</a>
	<a href="@pfx/media/kind/txt">all text files</a>
	<a href="@pfx/media/kind/misc">all others</a>
</menu>

<div class="dir">
	@directory
</div>

<div class="gallery">
	@images
</div>

|
|


|
|
|
|
|









1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<menu>@menu
	<a class="button" href="@pfx/media">new uploads</a>
	<a class="button" href="@pfx/media/unfiled">unfiled</a>
	<hr>
	@folders
	<a class="button" href="@pfx/media/all">all uploads</a>
	<a class="button" href="@pfx/media/kind/img">all images</a>
	<a class="button" href="@pfx/media/kind/vid">all videos</a>
	<a class="button" href="@pfx/media/kind/txt">all text files</a>
	<a class="button" href="@pfx/media/kind/misc">all others</a>
</menu>

<div class="dir">
	@directory
</div>

<div class="gallery">
	@images
</div>

Modified view/profile.tpl from [1a1f71cc57] to [b073906391].

21
22
23
24
25
26
27

28
29
30
31



















		<div>
			<a class="button" href="/@:xid">posts</a>
			<a class="button" href="/@:xid/arc">archive</a>
			<a class="button" href="/@:xid/media">media</a>
			<a class="button" href="/@:xid/social">associates</a>
		</div>
		<div>

			@auxbtn
		</div>
	</form>
</div>


























>




>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
		<div>
			<a class="button" href="/@:xid">posts</a>
			<a class="button" href="/@:xid/arc">archive</a>
			<a class="button" href="/@:xid/media">media</a>
			<a class="button" href="/@:xid/social">associates</a>
		</div>
		<div>
			<a class="button" href="#rel">options</a>
			@auxbtn
		</div>
	</form>
</div>

<form id="rel" class="modal" method="post">
<a href="#0" class="button">close</a><div>
	<menu>@relations</menu>
	<details>
		<summary>circles</summary>
		<button name="act" value="encircle">commit</button>
	</details>
	<details>
		<summary>sanctions</summary>
		<menu>
			@sanctions
			<div class="opt">
				<button class="neg" name="act" value="report">report</button>
				<p>if this user is violating instance rules, you can report this behavior to moderation staff and ask them to take action. please do not report users simply because you dislike them; this is what the above options are for.</p>
			</div>
		</menu>
	</details>
</div></form>