╭ changed ──╮ │src/web.zig│ ╰───────────╯ ╭───────────╮ │← all files│ ╰───────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────╮ │482 }); │ │483 } │ │484 │ │485 -// the actor for a write to the repo `repo_base` names with at least `min_role`,│ │486 -// or null once the refusal has been sent. local mode always writes. │ │485 +// the actor for a write to the repo `repo_base` names with at least `min_role` │ │486 +// and in the `thread_kind` tab when there is one, or null once the refusal has │ │487 +// been sent. local mode always writes. │ │488 fn authorizeWrite( │ │489 io: std.Io, │ │490 allocator: std.mem.Allocator, │ ╰─────────────────────────────────────────────────────────────────────────────────────╯ ╭────────────────────────────────────────╮ │493 host: Host, │ │494 repo_base: []const u8, │ │495 min_role: evt.Repo.Role, │ │496 + thread_kind: ?evt.EventKind, │ │497 ) !?ui.Actor { │ │498 const server = switch (host) { │ │499 .server => |server| server,│ ╰────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │507 var admin_repo = try rp.Repo(.xit, evt.admin_repo_opts).open(io, allocator, .{ .path = server.admin_repo_path });│ │508 defer admin_repo.deinit(io, allocator); │ │509 const moment = try evt.currentMoment(evt.admin_repo_opts, &admin_repo); │ │508 - break :blk try ui.authorizeUser(io, moment, arena, server.users_dir, user_id, identity, min_role); │ │510 + break :blk try ui.authorizeUser(io, moment, arena, server.users_dir, user_id, identity, min_role, thread_kind); │ │511 }; │ │512 switch (authorization) { │ │513 .actor => |actor| return actor, │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │677 }; │ │678 var arena = std.heap.ArenaAllocator.init(allocator); │ │679 defer arena.deinit(); │ │678 - const actor = (try authorizeWrite(io, allocator, &arena, request, host, repo_base, .owner)) orelse return; │ │680 + const actor = (try authorizeWrite(io, allocator, &arena, request, host, repo_base, .owner, null)) orelse return;│ │681 │ │682 const body = try readFormBody(request, allocator); │ │683 defer allocator.free(body); │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │688 const access_value = (try parseFormField(allocator, body, "access")) orelse try allocator.dupe(u8, ""); │ │689 defer allocator.free(access_value); │ │690 const access = std.meta.stringToEnum(evt.Repo.Access, access_value) orelse return request.respond("invalid access", .{ .status = .bad_request, .extra_headers = &.{.{ .name = "content-type", .value = "text/plain" }} });│ │691 + var roles: [3]?evt.Repo.Role = undefined; │ │692 + for (&roles, [_][]const u8{ "discuss_role", "issue_role", "patch_role" }) |*role, field| { │ │693 + const role_value = (try parseFormField(allocator, body, field)) orelse try allocator.dupe(u8, ""); │ │694 + defer allocator.free(role_value); │ │695 + role.* = ui.Repo.Settings.labelRole(role_value) catch return request.respond("invalid role", .{ .status = .bad_request, .extra_headers = &.{.{ .name = "content-type", .value = "text/plain" }} }); │ │696 + } │ │697 + const discuss_role, const issue_role, const patch_role = roles; │ │698 │ │699 // authorizeWrite refused any other base │ │700 const identity = repoBaseIdentity(repo_base) orelse unreachable; │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │692 - const route = ui.Repo.Settings.update(io, allocator, server.users_dir, actor, identity, name, description, access) catch |err| switch (err) { │ │701 + const route = ui.Repo.Settings.update(io, allocator, server.users_dir, actor, identity, name, description, access, discuss_role, issue_role, patch_role) catch |err| switch (err) {│ │702 error.NotFound => return respondRepoNotFound(request), │ │703 else => { │ │704 const failure = ui.Session.FormFeedback.RepoFailure.fromError(err) orelse return err; │ ╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────╮ │706 .name = name, │ │707 .description = description, │ │708 .access = access, │ │709 + .discuss_role = discuss_role,│ │710 + .issue_role = issue_role, │ │711 + .patch_role = patch_role, │ │712 } } }); │ │713 }, │ │714 }; │ ╰──────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │783 ) !void { │ │784 var author_arena = std.heap.ArenaAllocator.init(allocator); │ │785 defer author_arena.deinit(); │ │774 - const actor = (try authorizeWrite(io, allocator, &author_arena, request, host, base, .read)) orelse return; │ │786 + const actor = (try authorizeWrite(io, allocator, &author_arena, request, host, base, .read, kind)) orelse return;│ │787 const author = actor.author; │ │788 const body = try readFormBody(request, allocator); │ │789 defer allocator.free(body); │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │950 var author_arena = std.heap.ArenaAllocator.init(allocator); │ │951 defer author_arena.deinit(); │ │952 // only the draft's author may publish it, which publish checks │ │941 - const actor = (try authorizeWrite(io, allocator, &author_arena, request, host, parts.repo_base, .read)) orelse return; │ │953 + const actor = (try authorizeWrite(io, allocator, &author_arena, request, host, parts.repo_base, .read, .patch)) orelse return;│ │954 const user_id = actor.user_id orelse return respondLoginRequired(request); │ │955 const author = actor.author; │ │956 │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │993 }; │ │994 var author_arena = std.heap.ArenaAllocator.init(allocator); │ │995 defer author_arena.deinit(); │ │984 - const author = ((try authorizeWrite(io, allocator, &author_arena, request, host, parts.repo_base, .write)) orelse return).author; │ │996 + const author = ((try authorizeWrite(io, allocator, &author_arena, request, host, parts.repo_base, .write, .patch)) orelse return).author;│ │997 │ │998 const request_repo = (try requestRepoSource(io, allocator, host, parts.repo_base)) orelse return respondRemoveNotFound(request); │ │999 defer request_repo.deinit(allocator); │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────╮ │next →│ ╰──────╯