Differences From
Artifact [0f343f8c98]:
103 103 if punct ~= nil then a:push(punct, 1) end
104 104 a:ipush(rnd(uint16,0,65535))
105 105 end
106 106
107 107 if xXx then a:lpush('_xXx') end
108 108
109 109 end
110 +
111 +local terra
112 +suggest_domain(a: &lib.str.acc)
113 + var tlds = array('tld','club','town','space','xxx')
114 +end
110 115
111 116 local push_num_field = macro(function(acc,name,lbl,min,max,value,disable)
112 117 name = name:asvalue()
113 118 lbl = lbl:asvalue()
114 119 local start = '<div class="elem small">'
115 120 local enabled = start .. string.format('<label for="%s">%s</label><input type="number" id="%s" name="%s" min="', name, lbl, name, name)
116 121 local disabled = start .. string.format('<label>%s</label><div class="txtbox">', lbl)
................................................................................
154 159 local push_checkbox = input_pusher('checkbox',true,false)
155 160 local push_pickbox = input_pusher('checkbox',false,false)
156 161 local push_radio = input_pusher('radio',false,true)
157 162
158 163 local mode_local, mode_remote, mode_staff, mode_peers, mode_peons, mode_all = 0,1,2,3,4,5
159 164 local terra
160 165 render_conf_users(co: &lib.srv.convo, path: lib.mem.ptr(pref)): pstr
161 - if path.ct == 3 then
166 + if path.ct >= 3 then
162 167 var uid, ok = lib.math.shorthand.parse(path(2).ptr,path(2).ct)
163 168 if not ok then goto e404 end
164 169 var user = co.srv:actor_fetch_uid(uid)
165 170 -- FIXME allow xids as well, for manual queries
166 171 if not user then goto e404 end
167 172 defer user:free()
168 173 if not co.who:overpowers(user.ptr) then goto e403 end
169 174
170 - var cinp: lib.str.acc cinp:init(256)
171 - var clnk: lib.str.acc clnk:init(512)
172 - cinp:lpush('<div class="elem-group">')
173 - if user.ptr.rights.rank > 0 and (co.who.rights.powers.elevate() or co.who.rights.powers.demote()) then
174 - var max = co.who.rights.rank
175 - if not co.who.rights.powers.elevate() then max = user.ptr.rights.rank end
176 - var min = co.srv.cfg.nranks
177 - if not co.who.rights.powers.demote() then min = user.ptr.rights.rank end
178 -
179 - push_num_field(cinp, 'rank', 'rank', max, min, user.ptr.rights.rank, user.ptr.id == co.who.id)
180 - end
181 - if co.who.rights.powers.herald() then
182 - var sanitized: pstr
183 - if user.ptr.epithet == nil
184 - then sanitized = pstr {ptr='', ct=0}
185 - else sanitized = lib.html.sanitize(cs(user.ptr.epithet),true)
175 + if path.ct == 4 then
176 + if path(3):cmp(lib.str.lit'cred') then
177 + var pg: lib.str.acc pg:init(1024)
178 + 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>')
179 + var credmgr = lib.render.conf.sec(co, uid)
180 + pg:ppush(credmgr)
181 + credmgr:free()
182 + return pg:finalize()
183 + else goto e404 end
184 + elseif path.ct == 3 then
185 + var cinp: lib.str.acc cinp:init(256)
186 + cinp:lpush('<div class="elem-group">')
187 + if user.ptr.rights.rank > 0 and (co.who.rights.powers.elevate() or co.who.rights.powers.demote()) then
188 + var max = co.who.rights.rank
189 + if not co.who.rights.powers.elevate() then max = user.ptr.rights.rank end
190 + var min = co.srv.cfg.nranks
191 + if not co.who.rights.powers.demote() then min = user.ptr.rights.rank end
192 +
193 + push_num_field(cinp, 'rank', 'rank', max, min, user.ptr.rights.rank, user.ptr.id == co.who.id)
194 + end
195 + if co.who.rights.powers.herald() then
196 + var sanitized: pstr
197 + if user.ptr.epithet == nil
198 + then sanitized = pstr {ptr='', ct=0}
199 + else sanitized = lib.html.sanitize(cs(user.ptr.epithet),true)
200 + end
201 + cinp:lpush('<div class="elem"><label for="epithet">epithet</label><input type="text" id="epithet" name="epithet" value="'):ppush(sanitized):lpush('"></div>')
202 + if user.ptr.epithet ~= nil then sanitized:free() end
203 + end
204 + if co.who.rights.powers.invite() or co.who.rights.powers.discipline() then
205 + var min: uint32 = 0
206 + if not (co.who.rights.powers.discipline() or
207 + co.who.rights.powers.demote() and co.who.rights.powers.invite())
208 + then min = user.ptr.rights.invites end
209 + var max: uint32 = co.srv.cfg.maxinvites
210 + if not co.who.rights.powers.invite() then max = user.ptr.rights.invites end
211 +
212 + push_num_field(cinp, 'invites', 'invites', min, max, user.ptr.rights.invites, false)
213 + end
214 + if co.who.rights.powers.elevate() or co.who.rights.powers.demote() then
215 + var max: uint32 = 5000
216 + if not co.who.rights.powers.elevate() then max = user.ptr.rights.quota end
217 + var min: uint32 = 0
218 + if not co.who.rights.powers.demote() then min = user.ptr.rights.quota end
219 +
220 + push_num_field(cinp, 'quota', 'quota', min, max, user.ptr.rights.quota, user.ptr.id == co.who.id and co.who.rights.rank ~= 1)
186 221 end
187 - cinp:lpush('<div class="elem"><label for="epithet">epithet</label><input type="text" id="epithet" name="epithet" value="'):ppush(sanitized):lpush('"></div>')
188 - if user.ptr.epithet ~= nil then sanitized:free() end
189 - end
190 - if co.who.rights.powers.invite() or co.who.rights.powers.discipline() then
191 - var min: uint32 = 0
192 - if not (co.who.rights.powers.discipline() or
193 - co.who.rights.powers.demote() and co.who.rights.powers.invite())
194 - then min = user.ptr.rights.invites end
195 - var max: uint32 = co.srv.cfg.maxinvites
196 - if not co.who.rights.powers.invite() then max = user.ptr.rights.invites end
222 + cinp:lpush('</div><div class="elem"><div class="check-panel">')
223 +
224 + if user.ptr.id ~= co.who.id and
225 + ((user.ptr.rights.rank == 0 and co.who.rights.powers.elevate()) or
226 + (user.ptr.rights.rank > 0 and co.who.rights.powers.demote())) then
227 + push_checkbox(&cinp, 'staff', pstr.null(), 'site staff member', user.ptr.rights.rank > 0, true, pstr.null())
228 + end
197 229
198 - push_num_field(cinp, 'invites', 'invites', min, max, user.ptr.rights.invites, false)
199 - end
200 - if co.who.rights.powers.elevate() or co.who.rights.powers.demote() then
201 - var max: uint32 = 5000
202 - if not co.who.rights.powers.elevate() then max = user.ptr.rights.quota end
203 - var min: uint32 = 0
204 - if not co.who.rights.powers.demote() then min = user.ptr.rights.quota end
230 + cinp:lpush('</div></div>')
205 231
206 - push_num_field(cinp, 'quota', 'quota', min, max, user.ptr.rights.quota, user.ptr.id == co.who.id and co.who.rights.rank ~= 1)
207 - end
208 - cinp:lpush('</div><div class="elem"><div class="check-panel">')
209 -
210 - if user.ptr.id ~= co.who.id and
211 - ((user.ptr.rights.rank == 0 and co.who.rights.powers.elevate()) or
212 - (user.ptr.rights.rank > 0 and co.who.rights.powers.demote())) then
213 - push_checkbox(&cinp, 'staff', pstr.null(), 'site staff member', user.ptr.rights.rank > 0, true, pstr.null())
214 - end
215 -
216 - cinp:lpush('</div></div>')
217 -
218 - if (co.who.rights.powers.elevate() or
219 - co.who.rights.powers.demote()) and user.ptr.id ~= co.who.id then
220 - var map = array([lib.store.privmap])
221 - cinp:lpush('<details><summary>powers</summary><div class="pick-list">')
222 - for i=0, [map.type.N] do
223 - if (co.who.rights.powers and map[i].priv):sz() > 0 then
224 - var on = (user.ptr.rights.powers and map[i].priv):sz() > 0
225 - var enabled = ( on and co.who.rights.powers.demote() ) or
226 - ((not on) and co.who.rights.powers.elevate())
227 - var namea: lib.str.acc namea:compose('power-', map[i].name)
228 - var name = namea:finalize()
229 - push_pickbox(&cinp, name, pstr.null(), map[i].name, on, enabled, pstr.null())
230 - name:free()
232 + if (co.who.rights.powers.elevate() or
233 + co.who.rights.powers.demote()) and user.ptr.id ~= co.who.id then
234 + var map = array([lib.store.privmap])
235 + cinp:lpush('<details><summary>powers</summary><div class="pick-list">')
236 + for i=0, [map.type.N] do
237 + if (co.who.rights.powers and map[i].priv):sz() > 0 then
238 + var on = (user.ptr.rights.powers and map[i].priv):sz() > 0
239 + var enabled = ( on and co.who.rights.powers.demote() ) or
240 + ((not on) and co.who.rights.powers.elevate())
241 + var namea: lib.str.acc namea:compose('power-', map[i].name)
242 + var name = namea:finalize()
243 + push_pickbox(&cinp, name, pstr.null(), map[i].name, on, enabled, pstr.null())
244 + name:free()
245 + end
231 246 end
247 + cinp:lpush('</div></details>')
248 + end
249 +
250 + if co.who.id ~= uid and co.who.rights.powers.purge() then
251 + var purgeconf: lib.str.acc purgeconf:init(48)
252 + var purgestrs = array(
253 + 'alpha', 'beta', 'gamma', 'delta', 'epsilon', 'eta', 'nu', 'kappa',
254 + 'emerald', 'carnelian', 'sapphire', 'ruby', 'amethyst', 'glory',
255 + 'hope', 'grace', 'pearl', 'carnation', 'rose', 'peony', 'poppy'
256 + )
257 + for i=0,3 do
258 + purgeconf:push(purgestrs[lib.crypt.random(intptr,0,[purgestrs.type.N])],0)
259 + if i ~= 2 then purgeconf:lpush('-') end
232 260 end
233 - cinp:lpush('</div></details>')
234 - end
261 + 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>')
262 + purgeconf:free()
263 + end
264 +
265 + -- TODO black mark system? e.g. resolution option for badthink reports
266 + -- adds a black mark to the offending user; they can be automatically banned
267 + -- or brought up for review after a certain number of offenses; possibly lower
268 + -- set of default privs for marked users
235 269
236 - -- TODO black mark system? e.g. resolution option for badthink reports
237 - -- adds a black mark to the offending user; they can be automatically banned
238 - -- or brought up for review after a certain number of offenses; possibly lower
239 - -- set of default privs for marked users
270 + var cinpp = cinp:finalize() defer cinpp:free()
271 + var unym: lib.str.acc unym:init(64)
272 + unym:lpush('<a href="/')
273 + if user(0).origin ~= 0 then unym:lpush('@') end
274 + do var sanxid = lib.html.sanitize(user(0).xid, true)
275 + unym:ppush(sanxid)
276 + sanxid:free() end
277 + unym:lpush('" class="id">')
278 + lib.render.nym(user.ptr,0,&unym,false)
279 + unym:lpush('</a>')
280 + var ctlbox = data.view.conf_user_ctl {
281 + name = unym:finalize();
282 + inputcontent = cinpp;
283 + btns = pstr{'',0};
284 + }
285 + if co.who.id ~= uid and co.who.rights.powers.cred() then
286 + ctlbox.btns = lib.str.acc{}:compose('<a class="button" href="/conf/users/',path(2),'/cred">security & credentials</a>'):finalize()
287 + end
288 + var pg: lib.str.acc pg:init(512)
289 + ctlbox:append(&pg)
290 + ctlbox.name:free()
291 + if ctlbox.btns.ct > 0 then ctlbox.btns:free() end
240 292
241 - var cinpp = cinp:finalize() defer cinpp:free()
242 - var clnkp: pstr
243 - if clnk.sz > 0 then clnkp = clnk:finalize() else
244 - clnk:free()
245 - clnkp = pstr { ptr='', ct=0 }
293 + return pg:finalize()
246 294 end
247 - var unym: lib.str.acc unym:init(64)
248 - unym:lpush('<a href="/')
249 - if user(0).origin ~= 0 then unym:lpush('@') end
250 - do var sanxid = lib.html.sanitize(user(0).xid, true)
251 - unym:ppush(sanxid)
252 - sanxid:free() end
253 - unym:lpush('" class="id">')
254 - lib.render.nym(user.ptr,0,&unym,false)
255 - unym:lpush('</a>')
256 - var pg = data.view.conf_user_ctl {
257 - name = unym:finalize();
258 - inputcontent = cinpp;
259 - linkcontent = clnkp;
260 - }
261 - var ret = pg:tostr()
262 - pg.name:free()
263 - if clnkp.ct > 0 then clnkp:free() end
264 - return ret
265 295 else
266 296 var modes = array(P'local', P'remote', P'staff', P'titled', P'peons', P'all')
267 297 var idbuf: int8[lib.math.shorthand.maxlen]
268 298 var ulst: lib.str.acc ulst:init(256)
269 299 var mode: uint8 = mode_local
270 300 var modestr = co:pgetv('show')
271 301 ulst:lpush('<div style="text-align: right"><em>showing ')