╭──────────╮ │← previous│ ╰──────────╯ ╭ changed ───────╮ │src/test/net.zig│ ╰────────────────╯ ╭─────────────────────────────────────────────╮ │828 .user_id = &user_id,│ │829 .name = "target", │ │830 .description = "", │ │831 + .patch_role = .read,│ │832 } }, │ │833 }, │ │834 }); │ ╰─────────────────────────────────────────────╯ ╭ changed ──╮ │src/try.zig│ ╰───────────╯ ╭──────────────────────────────────────────────────────────╮ │248 .name = r.name, │ │249 .description = r.description,│ │250 .read_access = r.read_access,│ │251 + .discuss_role = .read, │ │252 + .issue_role = .read, │ │253 + .patch_role = .read, │ │254 }, │ │255 }, │ │256 }; │ ╰──────────────────────────────────────────────────────────╯ ╭ changed ─╮ │src/ui.zig│ ╰──────────╯ ╭──────────────────────────────────────────────────────────────────────────────╮ │2061 } │ │2062 │ │2063 // whether the user may write to the repo `identity` names with at least│ │2064 -// `min_role` │ │2064 +// `min_role`, and to the `thread_kind` tab when there is one │ │2065 pub fn authorizeUser( │ │2066 io: std.Io, │ │2067 moment: evt.AdminDB.HashMap(.read_only), │ ╰──────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────╮ │2070 user_id: [evt.event_id_size]u8, │ │2071 identity: []const u8, │ │2072 min_role: evt.Repo.Role, │ │2073 + thread_kind: ?evt.EventKind, │ │2074 ) !Authorization { │ │2075 const user = (try activeUser(moment, arena, user_id)) orelse return .login_required;│ │2076 │ ╰──────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────╮ │2079 // a repo the user can't read doesn't exist to them │ │2080 const role = repo.role orelse return .repo_not_found; │ │2081 if (!role.atLeast(min_role)) return .forbidden; │ │2082 + if (thread_kind) |kind| { │ │2083 + // a tab that is off doesn't exist │ │2084 + const tab_role = repo.repo.event.threadRole(kind) orelse return .repo_not_found;│ │2085 + if (!role.atLeast(tab_role)) return .forbidden; │ │2086 + } │ │2087 return .{ .actor = .{ │ │2088 .author = .{ .name = user.event.name, .email = user.event.email }, │ │2089 .user_id = user_id, │ ╰──────────────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────╮ │2261 name: []const u8, │ │2262 description: []const u8, │ │2263 access: evt.Repo.Access, │ │2264 + discuss_role: ?evt.Repo.Role,│ │2265 + issue_role: ?evt.Repo.Role, │ │2266 + patch_role: ?evt.Repo.Role, │ │2267 }, │ │2268 }, │ │2269 issue: struct { │ ╰───────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │2376 } │ │2377 │ │2378 // the actor for a write to the repo `identity` names, or null when the │ │2370 - // session may not write there with at least `min_role`. local mode always │ │2371 - // writes. │ │2372 - pub fn authorize(self: *Self, identity: []const u8, min_role: evt.Repo.Role) !?Actor { │ │2379 + // session may not write there with at least `min_role` or in the │ │2380 + // `thread_kind` tab. local mode always writes. │ │2381 + pub fn authorize(self: *Self, identity: []const u8, min_role: evt.Repo.Role, thread_kind: ?evt.EventKind) !?Actor {│ │2382 if (self.data.host_kind == .local) { │ │2383 const author = blk: { │ │2384 const src = self.local orelse break :blk local_author_fallback; │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │2393 const io = self.io orelse return null; │ │2394 const users_dir = self.users_dir orelse return null; │ │2395 const moment = try evt.currentMoment(evt.admin_repo_opts, admin_repo); │ │2387 - return switch (try authorizeUser(io, moment, self.page_arena, users_dir, user_id, identity, min_role)) { │ │2396 + return switch (try authorizeUser(io, moment, self.page_arena, users_dir, user_id, identity, min_role, thread_kind)) {│ │2397 .actor => |actor| actor, │ │2398 .login_required, .repo_not_found, .forbidden => null, │ │2399 }; │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭ changed ──────╮ │src/ui/Fork.zig│ ╰───────────────╯ ╭────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │49 // a draft is as readable as the repo it targets │ │50 const target_role = target.role orelse return error.NotFound; │ │51 const target_record = target.repo; │ │52 + // patches that are off take their drafts with them │ │53 + if (target_record.event.patch_role == null) return error.NotFound; │ │54 const owner = (try evt.User.readById(evt.AdminDB, evt.admin_repo_opts.hash, haxy_moment, arena, owner_id)) orelse return error.NotFound;│ │55 if (!std.mem.eql(u8, target_record.event.name, identity.name)) return error.NotFound; │ │56 │ ╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭ changed ─────────╮ │src/ui/NewRepo.zig│ ╰──────────────────╯ ╭────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │46 } │ │47 │ │48 { │ │49 - var access_radio = try ui.widget.Radio.init(allocator, session, "access", &.{ "private", "public" }, if (saved_fields) |saved| @tagName(saved.access) else "private"); │ │49 + var access_radio = try ui.widget.Radio.init(allocator, session, "access", &.{ "private", "public" }, if (saved_fields) |saved| @tagName(saved.access) else "private", null);│ │50 errdefer access_radio.deinit(allocator); │ │51 try box.children.put(allocator, access_radio.getFocus().id, .{ .widget = .{ .radio = access_radio }, .rect = null, .min_size = .{ .width = null, .height = 3 } }); │ │52 } │ ╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────╮ │next →│ ╰──────╯