id: UserId,
name: Username,
display_name: DisplayName,
+ description: UserDescription,
password_hash: PasswordHash,
+
posts: PostList,
+
following: UserList,
followers: UserList,
const Timestamp = i64;
const Username = std.BoundedArray(u8, 32);
const DisplayName = std.BoundedArray(u8, 64);
+const UserDescription = std.BoundedArray(u8, 1024);
const PasswordHash = std.BoundedArray(u8, 128);
const SessionToken = u64;
const CookieValue = std.BoundedArray(u8, 128);
const c = text[idx];
if (c == '+') {
try result.append(' ');
- } else if (c == '%') {
- // special case of &#...
- // assume only &#, no &#x
- if (idx + 6 < text.len and std.mem.eql(u8, text[idx .. idx + 6], "%26%23")) {
- const num_start = idx + 6;
- var num_end = num_start;
- while (num_end < text.len and std.ascii.isDigit(text[num_end])) {
- num_end += 1;
- }
+ } else if (c == '%' and idx + 2 < text.len) {
+ const allow = &[_]u8{ 0x26, 0x23, 0x3b, 0x0a };
- if (num_end + 2 < text.len and
- text[num_end] == '%' and
- text[num_end + 1] == '3' and
- std.ascii.toLower(text[num_end + 2]) == 'b')
- {
- try std.fmt.format(result.writer(), "&#{s};", .{text[num_start..num_end]});
- idx = num_end + 2;
- continue;
- }
+ const escaped_value = std.fmt.parseUnsigned(u8, text[idx + 1 .. idx + 3], 16) catch continue;
+
+ if (escaped_value == 0x0d) {
+ try std.fmt.format(result.writer(), "<br />", .{});
+ } else if (std.mem.indexOfScalar(u8, allow, escaped_value) != null) {
+ try std.fmt.format(result.writer(), "{c}", .{escaped_value});
+ } else {
+ try std.fmt.format(result.writer(), "&#x{x};", .{escaped_value});
}
- try std.fmt.format(result.writer(), "&#x{s};", .{text[idx + 1 .. idx + 3]});
idx += 2;
} else {
try result.append(c);
.id = user_id,
.name = username_array,
.display_name = display_name,
+ .description = try UserDescription.init(0),
.password_hash = try hash_password(password),
.posts = try PostList.init(txn),
.following = try UserList.init(txn),
inline for (inputs) |input| {
switch (@typeInfo(@TypeOf(input))) {
- .Struct => {
- try res.write("<input ", .{});
- try res.write(input[0], input[1]);
- try res.write(" />", .{});
+ .Struct => |s| {
+ if (s.fields.len == 3) {
+ try res.write("<{s} ", .{input[0]});
+ try res.write(input[1], input[2]);
+ try res.write("></{s}>", .{input[0]});
+ } else {
+ try res.write("<input ", .{});
+ try res.write(input[0], input[1]);
+ try res.write(" />", .{});
+ }
},
else => {
try res.write("<input ", .{});
try res.write("<option value=\"{x}\">{s}{s}</option>", .{ id, name.constSlice(), if (list_view.has(post_id) catch false) " *" else "" });
}
try res.write("</select>", .{});
- try res.write("<input type=\"hidden\" name=\"post_id\" value=\"{x}\"></input>", .{@intFromEnum(post_id)});
- try res.write("<input type=\"submit\" value=\"Save\"></input>", .{});
+ try res.write("<input type=\"hidden\" name=\"post_id\" value=\"{x}\" />", .{@intFromEnum(post_id)});
+ try res.write("<input type=\"submit\" value=\"Save\" />", .{});
try res.write("</form>", .{});
}
try res.write("<option value=\"{x}\">{s}{s}</option>", .{ id, name.constSlice(), if (list_view.has(user.id) catch false) " *" else "" });
}
try res.write("</select>", .{});
- try res.write("<input type=\"hidden\" name=\"user_id\" value=\"{x}\"></input>", .{@intFromEnum(user.id)});
- try res.write("<input type=\"submit\" value=\"Add to feed\"></input>", .{});
+ try res.write("<input type=\"hidden\" name=\"user_id\" value=\"{x}\" />", .{@intFromEnum(user.id)});
+ try res.write("<input type=\"submit\" value=\"Add to feed\" />", .{});
try res.write("</form>", .{});
}
try res.write(
, .{});
}
+ if (user.description.len > 0) {
+ try res.write(
+ \\<div style="padding-left: 5px; border-left: 1px solid grey;">
+ // \\« {s} »
+ \\<i>{s}</i>
+ \\</div>
+ , .{user.description.constSlice()});
+ }
+
try res.write("<br />", .{});
}
fn write_posts(res: *http.Response, txn: lmdb.Txn, logged_in: ?Login, post_list: PostList, options: struct {
.{ "type=\"text\" name=\"display_name\" placeholder=\"{s}\"", .{login.user.display_name.constSlice()} },
"type=\"submit\" value=\"Change\"",
});
+ try self.res.write("<br />Description: ", .{});
+ try html_form(self.res, "/set_description", .{
+ .{ "textarea", "type=\"text\" name=\"description\" placeholder=\"{s}\"", .{login.user.description.constSlice()} },
+ "type=\"submit\" value=\"Change\"",
+ });
try self.res.write("<br />Password: ", .{});
try html_form(self.res, "/set_password", .{
"type=\"text\" name=\"password\"",
user.display_name = display_name;
try users.put(login.user.id, user);
}
+ pub fn @"/set_description"(self: Self, args: struct { description: []const u8 }) !void {
+ const login = self.logged_in orelse return error.NotLoggedIn;
+ const description = try reencode(UserDescription, args.description);
+
+ const txn = try self.env.txn();
+ defer txn.commit() catch {};
+
+ const users = try Db.users(txn);
+ var user = login.user;
+ user.description = description;
+ try users.put(login.user.id, user);
+ }
pub fn @"/set_password"(self: Self, args: struct { password: []const u8 }) !void {
const login = self.logged_in orelse return error.NotLoggedIn;
server.wait();
while (true) {
const req = (server.next_request(&req_buffer) catch break) orelse break;
- handle_request(env, req) catch {
- try handle_error(env, req);
- };
+ // handle_request(env, req) catch {
+ // try handle_error(env, req);
+ // };
+ try handle_request(env, req);
}
}
// const ThreadCount = 1;