parsav  Diff

Differences From Artifact [0f343f8c98]:

To Artifact [41f55e0682]:


103
104
105
106
107
108
109





110
111
112
113
114
115
116
...
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
		if punct ~= nil then a:push(punct, 1) end
		a:ipush(rnd(uint16,0,65535))
	end

	if xXx then a:lpush('_xXx') end

end






local push_num_field = macro(function(acc,name,lbl,min,max,value,disable)
	name = name:asvalue()
	lbl = lbl:asvalue()
	local start = '<div class="elem small">'
	local enabled = start .. string.format('<label for="%s">%s</label><input type="number" id="%s" name="%s" min="', name, lbl, name, name)
	local disabled = start .. string.format('<label>%s</label><div class="txtbox">', lbl)
................................................................................
local push_checkbox = input_pusher('checkbox',true,false)
local push_pickbox = input_pusher('checkbox',false,false)
local push_radio = input_pusher('radio',false,true)

local mode_local, mode_remote, mode_staff, mode_peers, mode_peons, mode_all = 0,1,2,3,4,5
local terra 
render_conf_users(co: &lib.srv.convo, path: lib.mem.ptr(pref)): pstr
	if path.ct == 3 then
		var uid, ok = lib.math.shorthand.parse(path(2).ptr,path(2).ct)
		if not ok then goto e404 end
		var user = co.srv:actor_fetch_uid(uid)
		-- FIXME allow xids as well, for manual queries
		if not user then goto e404 end
		defer user:free()
		if not co.who:overpowers(user.ptr) then goto e403 end











		var cinp: lib.str.acc cinp:init(256)
		var clnk: lib.str.acc clnk:init(512)
		cinp:lpush('<div class="elem-group">')
		if user.ptr.rights.rank > 0 and (co.who.rights.powers.elevate() or co.who.rights.powers.demote()) then
			var max = co.who.rights.rank
			if not co.who.rights.powers.elevate() then max = user.ptr.rights.rank end
			var min = co.srv.cfg.nranks
			if not co.who.rights.powers.demote() then min = user.ptr.rights.rank end

			push_num_field(cinp, 'rank', 'rank', max, min, user.ptr.rights.rank, user.ptr.id == co.who.id)
		end
		if co.who.rights.powers.herald() then
			var sanitized: pstr
			if user.ptr.epithet == nil
				then sanitized = pstr {ptr='', ct=0}
				else sanitized = lib.html.sanitize(cs(user.ptr.epithet),true)
			end
			cinp:lpush('<div class="elem"><label for="epithet">epithet</label><input type="text" id="epithet" name="epithet" value="'):ppush(sanitized):lpush('"></div>')
			if user.ptr.epithet ~= nil then sanitized:free() end
		end
		if co.who.rights.powers.invite() or co.who.rights.powers.discipline() then
			var min: uint32 = 0
			if not (co.who.rights.powers.discipline() or
				co.who.rights.powers.demote() and co.who.rights.powers.invite())
					then min = user.ptr.rights.invites end
			var max: uint32 = co.srv.cfg.maxinvites
			if not co.who.rights.powers.invite() then max = user.ptr.rights.invites end

			push_num_field(cinp, 'invites', 'invites', min, max, user.ptr.rights.invites, false)
		end
		if co.who.rights.powers.elevate() or co.who.rights.powers.demote() then
			var max: uint32 = 5000
			if not co.who.rights.powers.elevate() then max = user.ptr.rights.quota end
			var min: uint32 = 0
			if not co.who.rights.powers.demote() then min = user.ptr.rights.quota end

			push_num_field(cinp, 'quota', 'quota', min, max, user.ptr.rights.quota, user.ptr.id == co.who.id and co.who.rights.rank ~= 1)
		end
		cinp:lpush('</div><div class="elem"><div class="check-panel">')

		if user.ptr.id ~= co.who.id and
		   ((user.ptr.rights.rank == 0 and co.who.rights.powers.elevate()) or
		    (user.ptr.rights.rank >  0 and co.who.rights.powers.demote())) then
			push_checkbox(&cinp, 'staff', pstr.null(), 'site staff member', user.ptr.rights.rank > 0, true, pstr.null())
		end

		cinp:lpush('</div></div>')

		if (co.who.rights.powers.elevate() or
		   co.who.rights.powers.demote()) and user.ptr.id ~= co.who.id then
			var map = array([lib.store.privmap])
			cinp:lpush('<details><summary>powers</summary><div class="pick-list">')
				for i=0, [map.type.N] do
					if (co.who.rights.powers and map[i].priv):sz() > 0 then
						var on = (user.ptr.rights.powers and map[i].priv):sz() > 0
						var enabled = (     on  and co.who.rights.powers.demote() ) or
									  ((not on) and co.who.rights.powers.elevate())
						var namea: lib.str.acc namea:compose('power-', map[i].name)
						var name = namea:finalize()
						push_pickbox(&cinp, name, pstr.null(), map[i].name, on, enabled, pstr.null())
						name:free()
					end
				end
			cinp:lpush('</div></details>')
		end
















		-- TODO black mark system? e.g. resolution option for badthink reports
		-- adds a black mark to the offending user; they can be automatically banned
		-- or brought up for review after a certain number of offenses; possibly lower
		-- set of default privs for marked users

		var cinpp = cinp:finalize() defer cinpp:free()
		var clnkp: pstr
		if clnk.sz > 0 then clnkp = clnk:finalize() else
			clnk:free()
			clnkp = pstr { ptr='', ct=0 }
		end
		var unym: lib.str.acc unym:init(64)
		unym:lpush('<a href="/')
		if user(0).origin ~= 0 then unym:lpush('@') end
		do var sanxid = lib.html.sanitize(user(0).xid, true)
			unym:ppush(sanxid)
			sanxid:free() end
		unym:lpush('" class="id">')
		lib.render.nym(user.ptr,0,&unym,false)
		unym:lpush('</a>')
		var pg = data.view.conf_user_ctl {
			name = unym:finalize();
			inputcontent = cinpp;
			linkcontent = clnkp;

		}
		var ret = pg:tostr()





		pg.name:free()
		if clnkp.ct > 0 then clnkp:free() end


		return ret

	else
		var modes = array(P'local', P'remote', P'staff', P'titled', P'peons', P'all')
		var idbuf: int8[lib.math.shorthand.maxlen]
		var ulst: lib.str.acc ulst:init(256)
		var mode: uint8 = mode_local
		var modestr = co:pgetv('show')
		ulst:lpush('<div style="text-align: right"><em>showing ')







>
>
>
>
>







 







|








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

|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|

|
|
|
|
|
|
|

|
|
|

|
|
|
|
|

|

|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|

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

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







103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
...
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
		if punct ~= nil then a:push(punct, 1) end
		a:ipush(rnd(uint16,0,65535))
	end

	if xXx then a:lpush('_xXx') end

end

local terra 
suggest_domain(a: &lib.str.acc)
	var tlds = array('tld','club','town','space','xxx')
end

local push_num_field = macro(function(acc,name,lbl,min,max,value,disable)
	name = name:asvalue()
	lbl = lbl:asvalue()
	local start = '<div class="elem small">'
	local enabled = start .. string.format('<label for="%s">%s</label><input type="number" id="%s" name="%s" min="', name, lbl, name, name)
	local disabled = start .. string.format('<label>%s</label><div class="txtbox">', lbl)
................................................................................
local push_checkbox = input_pusher('checkbox',true,false)
local push_pickbox = input_pusher('checkbox',false,false)
local push_radio = input_pusher('radio',false,true)

local mode_local, mode_remote, mode_staff, mode_peers, mode_peons, mode_all = 0,1,2,3,4,5
local terra 
render_conf_users(co: &lib.srv.convo, path: lib.mem.ptr(pref)): pstr
	if path.ct >= 3 then
		var uid, ok = lib.math.shorthand.parse(path(2).ptr,path(2).ct)
		if not ok then goto e404 end
		var user = co.srv:actor_fetch_uid(uid)
		-- FIXME allow xids as well, for manual queries
		if not user then goto e404 end
		defer user:free()
		if not co.who:overpowers(user.ptr) then goto e403 end

		if path.ct == 4 then
			if path(3):cmp(lib.str.lit'cred') then
				var pg: lib.str.acc pg:init(1024)
				pg:lpush('<div class="context">editing credentials for user <a href="/conf/users/'):rpush(path(2)):lpush('">'):push(user(0).xid,0):lpush('</a></div>')
				var credmgr = lib.render.conf.sec(co, uid)
				pg:ppush(credmgr)
				credmgr:free()
				return pg:finalize()
			else goto e404 end
		elseif path.ct == 3 then
			var cinp: lib.str.acc cinp:init(256)

			cinp:lpush('<div class="elem-group">')
			if user.ptr.rights.rank > 0 and (co.who.rights.powers.elevate() or co.who.rights.powers.demote()) then
				var max = co.who.rights.rank
				if not co.who.rights.powers.elevate() then max = user.ptr.rights.rank end
				var min = co.srv.cfg.nranks
				if not co.who.rights.powers.demote() then min = user.ptr.rights.rank end

				push_num_field(cinp, 'rank', 'rank', max, min, user.ptr.rights.rank, user.ptr.id == co.who.id)
			end
			if co.who.rights.powers.herald() then
				var sanitized: pstr
				if user.ptr.epithet == nil
					then sanitized = pstr {ptr='', ct=0}
					else sanitized = lib.html.sanitize(cs(user.ptr.epithet),true)
				end
				cinp:lpush('<div class="elem"><label for="epithet">epithet</label><input type="text" id="epithet" name="epithet" value="'):ppush(sanitized):lpush('"></div>')
				if user.ptr.epithet ~= nil then sanitized:free() end
			end
			if co.who.rights.powers.invite() or co.who.rights.powers.discipline() then
				var min: uint32 = 0
				if not (co.who.rights.powers.discipline() or
					co.who.rights.powers.demote() and co.who.rights.powers.invite())
						then min = user.ptr.rights.invites end
				var max: uint32 = co.srv.cfg.maxinvites
				if not co.who.rights.powers.invite() then max = user.ptr.rights.invites end

				push_num_field(cinp, 'invites', 'invites', min, max, user.ptr.rights.invites, false)
			end
			if co.who.rights.powers.elevate() or co.who.rights.powers.demote() then
				var max: uint32 = 5000
				if not co.who.rights.powers.elevate() then max = user.ptr.rights.quota end
				var min: uint32 = 0
				if not co.who.rights.powers.demote() then min = user.ptr.rights.quota end

				push_num_field(cinp, 'quota', 'quota', min, max, user.ptr.rights.quota, user.ptr.id == co.who.id and co.who.rights.rank ~= 1)
			end
			cinp:lpush('</div><div class="elem"><div class="check-panel">')

			if user.ptr.id ~= co.who.id and
			   ((user.ptr.rights.rank == 0 and co.who.rights.powers.elevate()) or
				(user.ptr.rights.rank >  0 and co.who.rights.powers.demote())) then
				push_checkbox(&cinp, 'staff', pstr.null(), 'site staff member', user.ptr.rights.rank > 0, true, pstr.null())
			end

			cinp:lpush('</div></div>')

			if (co.who.rights.powers.elevate() or
			   co.who.rights.powers.demote()) and user.ptr.id ~= co.who.id then
				var map = array([lib.store.privmap])
				cinp:lpush('<details><summary>powers</summary><div class="pick-list">')
					for i=0, [map.type.N] do
						if (co.who.rights.powers and map[i].priv):sz() > 0 then
							var on = (user.ptr.rights.powers and map[i].priv):sz() > 0
							var enabled = (     on  and co.who.rights.powers.demote() ) or
										  ((not on) and co.who.rights.powers.elevate())
							var namea: lib.str.acc namea:compose('power-', map[i].name)
							var name = namea:finalize()
							push_pickbox(&cinp, name, pstr.null(), map[i].name, on, enabled, pstr.null())
							name:free()
						end
					end
				cinp:lpush('</div></details>')
			end

			if co.who.id ~= uid and co.who.rights.powers.purge() then
				var purgeconf: lib.str.acc purgeconf:init(48)
				var purgestrs = array(
					'alpha', 'beta', 'gamma', 'delta', 'epsilon', 'eta', 'nu', 'kappa',
					'emerald', 'carnelian', 'sapphire', 'ruby', 'amethyst', 'glory',
					'hope', 'grace', 'pearl', 'carnation', 'rose', 'peony', 'poppy'
				)
				for i=0,3 do
					purgeconf:push(purgestrs[lib.crypt.random(intptr,0,[purgestrs.type.N])],0)
					if i ~= 2 then purgeconf:lpush('-') end
				end
				cinp:lpush('<details><summary>purge account</summary><p>you have the authority to destroy this account and all its associated content irreversibly and irretrievably. if you really wish to apply such an extreme sanction, enter the confirmation string <strong style="user-select:none">'):push(purgeconf.buf,purgeconf.sz):lpush('</strong> below and press the “alter” button to begin the process.</p><div class="elem"><label for="purge">purge confirmation string</label><input type="text" id="purge" name="purgekey"></div><input type="hidden" name="purgestr" value="'):push(purgeconf.buf,purgeconf.sz):lpush('"></details>')
				purgeconf:free()
			end

			-- TODO black mark system? e.g. resolution option for badthink reports
			-- adds a black mark to the offending user; they can be automatically banned
			-- or brought up for review after a certain number of offenses; possibly lower
			-- set of default privs for marked users

			var cinpp = cinp:finalize() defer cinpp:free()





			var unym: lib.str.acc unym:init(64)
			unym:lpush('<a href="/')
			if user(0).origin ~= 0 then unym:lpush('@') end
			do var sanxid = lib.html.sanitize(user(0).xid, true)
				unym:ppush(sanxid)
				sanxid:free() end
			unym:lpush('" class="id">')
			lib.render.nym(user.ptr,0,&unym,false)
			unym:lpush('</a>')
			var ctlbox = data.view.conf_user_ctl {
				name = unym:finalize();
				inputcontent = cinpp;

				btns = pstr{'',0};
			}

			if co.who.id ~= uid and co.who.rights.powers.cred() then
				ctlbox.btns = lib.str.acc{}:compose('<a class="button" href="/conf/users/',path(2),'/cred">security &amp; credentials</a>'):finalize()
			end
			var pg: lib.str.acc pg:init(512)
			ctlbox:append(&pg)
			ctlbox.name:free()

			if ctlbox.btns.ct > 0 then ctlbox.btns:free() end

			return pg:finalize()
		end
	else
		var modes = array(P'local', P'remote', P'staff', P'titled', P'peons', P'all')
		var idbuf: int8[lib.math.shorthand.maxlen]
		var ulst: lib.str.acc ulst:init(256)
		var mode: uint8 = mode_local
		var modestr = co:pgetv('show')
		ulst:lpush('<div style="text-align: right"><em>showing ')