From 085caa7747a3bbebbd2ec09b3264dc6fcc5a7624 Mon Sep 17 00:00:00 2001 From: default Date: Tue, 19 Nov 2024 06:46:14 +0100 Subject: New function get_in_reply_to(). --- html.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'html.c') diff --git a/html.c b/html.c index 3a2b14f..74de47d 100644 --- a/html.c +++ b/html.c @@ -1670,7 +1670,7 @@ xs_html *html_entry(snac *user, xs_dict *msg, int read_only, if (strcmp(type, "Note") == 0) { if (level == 0) { /* is the parent not here? */ - const char *parent = xs_dict_get(msg, "inReplyTo"); + const char *parent = get_in_reply_to(msg); if (user && !xs_is_null(parent) && *parent && !timeline_here(user, parent)) { xs_html_add(post_header, @@ -2329,7 +2329,7 @@ xs_str *html_timeline(snac *user, const xs_list *list, int read_only, /* is this message a non-public reply? */ if (user != NULL && !is_msg_public(msg)) { - const char *irt = xs_dict_get(msg, "inReplyTo"); + const char *irt = get_in_reply_to(msg); /* is it a reply to something not in the storage? */ if (!xs_is_null(irt) && !object_here(irt)) { -- cgit v1.2.3 From 09d268495c12d3c00a7293fbf445830bcc9d74c3 Mon Sep 17 00:00:00 2001 From: default Date: Sat, 23 Nov 2024 17:08:57 +0100 Subject: The 'metadata' field in user.json is now a string instead of a dict. --- html.c | 74 ++++++++++++++++++++++++++++++++++++++++-------------------------- 1 file changed, 45 insertions(+), 29 deletions(-) (limited to 'html.c') diff --git a/html.c b/html.c index 74de47d..7f7c2c7 100644 --- a/html.c +++ b/html.c @@ -843,7 +843,31 @@ static xs_html *html_user_body(snac *user, int read_only) xs_html_add(top_user, top_user_bio); - const xs_dict *metadata = xs_dict_get(user->config, "metadata"); + xs *metadata = NULL; + const xs_dict *md = xs_dict_get(user->config, "metadata"); + + if (xs_type(md) == XSTYPE_DICT) + metadata = xs_dup(md); + else + if (xs_type(md) == XSTYPE_STRING) { + /* convert to dict for easier iteration */ + metadata = xs_dict_new(); + xs *l = xs_split(md, "\r\n"); + const char *ll; + + xs_list_foreach(l, ll) { + xs *kv = xs_split_n(ll, "=", 1); + const char *k = xs_list_get(kv, 0); + const char *v = xs_list_get(kv, 1); + + if (k && v) { + xs *kk = xs_strip_i(xs_dup(k)); + xs *vv = xs_strip_i(xs_dup(v)); + metadata = xs_dict_set(metadata, kk, vv); + } + } + } + if (xs_type(metadata) == XSTYPE_DICT) { const xs_str *k; const xs_str *v; @@ -1026,19 +1050,28 @@ xs_html *html_top_controls(snac *snac) const xs_val *auto_boost = xs_dict_get(snac->config, "auto_boost"); const xs_val *coll_thrds = xs_dict_get(snac->config, "collapse_threads"); - xs *metadata = xs_str_new(NULL); + xs *metadata = NULL; const xs_dict *md = xs_dict_get(snac->config, "metadata"); - const xs_str *k; - const xs_str *v; - int c = 0; - while (xs_dict_next(md, &k, &v, &c)) { - xs *kp = xs_fmt("%s=%s", k, v); + if (xs_type(md) == XSTYPE_DICT) { + const xs_str *k; + const xs_str *v; + + metadata = xs_str_new(NULL); - if (*metadata) - metadata = xs_str_cat(metadata, "\n"); - metadata = xs_str_cat(metadata, kp); + xs_dict_foreach(md, k, v) { + xs *kp = xs_fmt("%s=%s", k, v); + + if (*metadata) + metadata = xs_str_cat(metadata, "\n"); + metadata = xs_str_cat(metadata, kp); + } } + else + if (xs_type(md) == XSTYPE_STRING) + metadata = xs_dup(md); + else + metadata = xs_str_new(NULL); xs *user_setup_action = xs_fmt("%s/admin/user-setup", snac->actor); @@ -3706,25 +3739,8 @@ int html_post_handler(const xs_dict *req, const char *q_path, else snac.config = xs_dict_set(snac.config, "collapse_threads", xs_stock(XSTYPE_FALSE)); - if ((v = xs_dict_get(p_vars, "metadata")) != NULL) { - /* split the metadata and store it as a dict */ - xs_dict *md = xs_dict_new(); - xs *l = xs_split(v, "\n"); - xs_list *p = l; - const xs_str *kp; - - while (xs_list_iter(&p, &kp)) { - xs *kpl = xs_split_n(kp, "=", 1); - if (xs_list_len(kpl) == 2) { - xs *k2 = xs_strip_i(xs_dup(xs_list_get(kpl, 0))); - xs *v2 = xs_strip_i(xs_dup(xs_list_get(kpl, 1))); - - md = xs_dict_set(md, k2, v2); - } - } - - snac.config = xs_dict_set(snac.config, "metadata", md); - } + if ((v = xs_dict_get(p_vars, "metadata")) != NULL) + snac.config = xs_dict_set(snac.config, "metadata", v); /* uploads */ const char *uploads[] = { "avatar", "header", NULL }; -- cgit v1.2.3 From c88d7e72f0531114e7e000f79ee6ce9a11f23952 Mon Sep 17 00:00:00 2001 From: default Date: Sat, 23 Nov 2024 17:16:31 +0100 Subject: Minor tweak to new metadata. --- html.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'html.c') diff --git a/html.c b/html.c index 7f7c2c7..df9e4e3 100644 --- a/html.c +++ b/html.c @@ -852,7 +852,7 @@ static xs_html *html_user_body(snac *user, int read_only) if (xs_type(md) == XSTYPE_STRING) { /* convert to dict for easier iteration */ metadata = xs_dict_new(); - xs *l = xs_split(md, "\r\n"); + xs *l = xs_split(md, "\n"); const char *ll; xs_list_foreach(l, ll) { -- cgit v1.2.3 From 9fb84bcb3aa4c6a8061e4b892dc5bd1826b44715 Mon Sep 17 00:00:00 2001 From: default Date: Sun, 24 Nov 2024 08:54:43 +0100 Subject: The people page shows 'Approve' and 'Discard' buttons for pending follows. --- html.c | 29 +++++++++++++++++++++++------ 1 file changed, 23 insertions(+), 6 deletions(-) (limited to 'html.c') diff --git a/html.c b/html.c index df9e4e3..2d556e8 100644 --- a/html.c +++ b/html.c @@ -2470,10 +2470,9 @@ xs_html *html_people_list(snac *snac, xs_list *list, char *header, char *t, cons xs_html_tag("summary", xs_html_text("...")))); - xs_list *p = list; const char *actor_id; - while (xs_list_iter(&p, &actor_id)) { + xs_list_foreach(list, actor_id) { xs *md5 = xs_md5_hex(actor_id, strlen(actor_id)); xs *actor = NULL; @@ -2542,6 +2541,14 @@ xs_html *html_people_list(snac *snac, xs_list *list, char *header, char *t, cons html_button("limit", L("Limit"), L("Block announces (boosts) from this user"))); } + if (pending_check(snac, actor_id)) { + xs_html_add(form, + html_button("approve", L("Approve"), + L("Approve this follow request"))); + + xs_html_add(form, + html_button("discard", L("Discard"), L("Discard this follow request"))); + } else { xs_html_add(form, html_button("follow", L("Follow"), @@ -2596,13 +2603,23 @@ xs_str *html_people(snac *user) xs *wing = following_list(user); xs *wers = follower_list(user); + xs_html *lists = xs_html_tag("div", + xs_html_attr("class", "snac-posts")); + + if (xs_is_true(xs_dict_get(user->config, "approve_followers"))) { + xs *pending = pending_list(user); + xs_html_add(lists, + html_people_list(user, pending, L("Pending follow confirmations"), "p", proxy)); + } + + xs_html_add(lists, + html_people_list(user, wing, L("People you follow"), "i", proxy), + html_people_list(user, wers, L("People that follow you"), "e", proxy)); + xs_html *html = xs_html_tag("html", html_user_head(user, NULL, NULL), xs_html_add(html_user_body(user, 0), - xs_html_tag("div", - xs_html_attr("class", "snac-posts"), - html_people_list(user, wing, L("People you follow"), "i", proxy), - html_people_list(user, wers, L("People that follow you"), "e", proxy)), + lists, html_footer())); return xs_html_render_s(html, "\n"); -- cgit v1.2.3 From 0ddbe7e53b8331d2de42ad29ceca35b715f1c121 Mon Sep 17 00:00:00 2001 From: default Date: Sun, 24 Nov 2024 08:58:02 +0100 Subject: Pending follow notifications are shown as "Follow Request". --- html.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'html.c') diff --git a/html.c b/html.c index 2d556e8..990fb94 100644 --- a/html.c +++ b/html.c @@ -2711,6 +2711,9 @@ xs_str *html_notifications(snac *user, int skip, int show) label = wrk; } } + else + if (strcmp(type, "Follow") == 0 && pending_check(user, actor_id)) + label = L("Follow Request"); xs *s_date = xs_crop_i(xs_dup(date), 0, 10); -- cgit v1.2.3 From 0db1cf05d1013f9b1e972ae79822f9eda4dede59 Mon Sep 17 00:00:00 2001 From: default Date: Sun, 24 Nov 2024 09:14:59 +0100 Subject: New web actions "Approve" and "Discard". --- html.c | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) (limited to 'html.c') diff --git a/html.c b/html.c index 990fb94..576d94f 100644 --- a/html.c +++ b/html.c @@ -3699,6 +3699,34 @@ int html_post_handler(const xs_dict *req, const char *q_path, unbookmark(&snac, id); timeline_touch(&snac); } + else + if (strcmp(action, L("Approve")) == 0) { /** **/ + xs *fwreq = pending_get(&snac, actor); + + if (fwreq != NULL) { + xs *reply = msg_accept(&snac, fwreq, actor); + + enqueue_message(&snac, reply); + + if (xs_is_null(xs_dict_get(fwreq, "published"))) { + /* add a date if it doesn't include one (Mastodon) */ + xs *date = xs_str_utctime(0, ISO_DATE_SPEC); + fwreq = xs_dict_set(fwreq, "published", date); + } + + timeline_add(&snac, xs_dict_get(fwreq, "id"), fwreq); + + follower_add(&snac, actor); + + pending_del(&snac, actor); + + snac_log(&snac, xs_fmt("new follower %s", actor)); + } + } + else + if (strcmp(action, L("Discard")) == 0) { /** **/ + pending_del(&snac, actor); + } else status = HTTP_STATUS_NOT_FOUND; -- cgit v1.2.3 From 93f70d6759e0bc67326ca78283cd9627f8ca4580 Mon Sep 17 00:00:00 2001 From: default Date: Sun, 24 Nov 2024 09:25:59 +0100 Subject: Added web UI for the 'approve_followers' checkbox. --- html.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) (limited to 'html.c') diff --git a/html.c b/html.c index 576d94f..9c04b99 100644 --- a/html.c +++ b/html.c @@ -1049,6 +1049,7 @@ xs_html *html_top_controls(snac *snac) const xs_val *a_private = xs_dict_get(snac->config, "private"); const xs_val *auto_boost = xs_dict_get(snac->config, "auto_boost"); const xs_val *coll_thrds = xs_dict_get(snac->config, "collapse_threads"); + const xs_val *pending = xs_dict_get(snac->config, "approve_followers"); xs *metadata = NULL; const xs_dict *md = xs_dict_get(snac->config, "metadata"); @@ -1220,6 +1221,15 @@ xs_html *html_top_controls(snac *snac) xs_html_tag("label", xs_html_attr("for", "collapse_threads"), xs_html_text(L("Collapse top threads by default")))), + xs_html_tag("p", + xs_html_sctag("input", + xs_html_attr("type", "checkbox"), + xs_html_attr("name", "approve_followers"), + xs_html_attr("id", "approve_followers"), + xs_html_attr(xs_is_true(pending) ? "checked" : "", NULL)), + xs_html_tag("label", + xs_html_attr("for", "approve_followers"), + xs_html_text(L("Follow requests must be approved")))), xs_html_tag("p", xs_html_text(L("Profile metadata (key=value pairs in each line):")), xs_html_sctag("br", NULL), @@ -3786,6 +3796,10 @@ int html_post_handler(const xs_dict *req, const char *q_path, snac.config = xs_dict_set(snac.config, "collapse_threads", xs_stock(XSTYPE_TRUE)); else snac.config = xs_dict_set(snac.config, "collapse_threads", xs_stock(XSTYPE_FALSE)); + if ((v = xs_dict_get(p_vars, "approve_followers")) != NULL && strcmp(v, "on") == 0) + snac.config = xs_dict_set(snac.config, "approve_followers", xs_stock(XSTYPE_TRUE)); + else + snac.config = xs_dict_set(snac.config, "approve_followers", xs_stock(XSTYPE_FALSE)); if ((v = xs_dict_get(p_vars, "metadata")) != NULL) snac.config = xs_dict_set(snac.config, "metadata", v); -- cgit v1.2.3 From e0fb6a70648297bc02a64e6d4390874a9c6085db Mon Sep 17 00:00:00 2001 From: default Date: Sun, 24 Nov 2024 18:36:33 +0100 Subject: Fixed bio processing in the public page. --- html.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'html.c') diff --git a/html.c b/html.c index 9c04b99..05fa2cf 100644 --- a/html.c +++ b/html.c @@ -829,16 +829,16 @@ static xs_html *html_user_body(snac *user, int read_only) } if (read_only) { - xs *es1 = encode_html(xs_dict_get(user->config, "bio")); xs *tags = xs_list_new(); - xs *bio1 = not_really_markdown(es1, NULL, &tags); + xs *bio1 = not_really_markdown(xs_dict_get(user->config, "bio"), NULL, &tags); xs *bio2 = process_tags(user, bio1, &tags); + xs *bio3 = sanitize(bio2); - bio2 = replace_shortnames(bio2, tags, 2, proxy); + bio3 = replace_shortnames(bio3, tags, 2, proxy); xs_html *top_user_bio = xs_html_tag("div", xs_html_attr("class", "p-note snac-top-user-bio"), - xs_html_raw(bio2)); /* already sanitized */ + xs_html_raw(bio3)); /* already sanitized */ xs_html_add(top_user, top_user_bio); -- cgit v1.2.3 From 3f8db422ce66912a5414968eeae867f9ac12d946 Mon Sep 17 00:00:00 2001 From: default Date: Sun, 24 Nov 2024 20:21:48 +0100 Subject: The search box also searches accounts (via webfinger). --- html.c | 42 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) (limited to 'html.c') diff --git a/html.c b/html.c index 05fa2cf..5774894 100644 --- a/html.c +++ b/html.c @@ -2972,6 +2972,48 @@ int html_get_handler(const xs_dict *req, const char *q_path, const char *q = xs_dict_get(q_vars, "q"); if (q && *q) { + if (xs_regex_match(q, "^@?[a-zA-Z0-9_]+@[a-zA-Z0-9-]+\\.")) { + /** search account **/ + xs *actor = NULL; + xs *acct = NULL; + xs *l = xs_list_new(); + xs_html *page = NULL; + + if (valid_status(webfinger_request(q, &actor, &acct))) { + xs *actor_obj = NULL; + + if (valid_status(actor_request(&snac, actor, &actor_obj))) { + actor_add(actor, actor_obj); + + /* create a people list with only one element */ + l = xs_list_append(xs_list_new(), actor); + + xs *title = xs_fmt(L("Search results for account %s"), q); + + page = html_people_list(&snac, l, title, "wf", NULL); + } + } + + if (page == NULL) { + xs *title = xs_fmt(L("Account %s not found"), q); + + page = xs_html_tag("div", + xs_html_tag("h2", + xs_html_attr("class", "snac-header"), + xs_html_text(title))); + } + + xs_html *html = xs_html_tag("html", + html_user_head(&snac, NULL, NULL), + xs_html_add(html_user_body(&snac, 0), + page, + html_footer())); + + *body = xs_html_render_s(html, "\n"); + *b_size = strlen(*body); + status = HTTP_STATUS_OK; + } + else if (*q == '#') { /** search by tag **/ xs *tl = tag_search(q, skip, show + 1); -- cgit v1.2.3 From 474a750e692370a3f0adbe71c15d6c7b927b7e4a Mon Sep 17 00:00:00 2001 From: default Date: Sun, 24 Nov 2024 20:25:17 +0100 Subject: Changed help for the search box. --- html.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'html.c') diff --git a/html.c b/html.c index 5774894..b65a170 100644 --- a/html.c +++ b/html.c @@ -770,7 +770,7 @@ static xs_html *html_user_body(snac *user, int read_only) xs_html_sctag("input", xs_html_attr("type", "text"), xs_html_attr("name", "q"), - xs_html_attr("title", L("Search posts by content (regular expression) or #tag")), + xs_html_attr("title", L("Search posts by content (regular expression), @user@host accounts, or #tag")), xs_html_attr("placeholder", L("Content search"))))); } -- cgit v1.2.3 From 13647f790d967a046b9bd2f290541c37a0b7ea6f Mon Sep 17 00:00:00 2001 From: default Date: Sun, 24 Nov 2024 20:33:22 +0100 Subject: Fixed missing else in html people list. --- html.c | 1 + 1 file changed, 1 insertion(+) (limited to 'html.c') diff --git a/html.c b/html.c index b65a170..f8fb876 100644 --- a/html.c +++ b/html.c @@ -2551,6 +2551,7 @@ xs_html *html_people_list(snac *snac, xs_list *list, char *header, char *t, cons html_button("limit", L("Limit"), L("Block announces (boosts) from this user"))); } + else if (pending_check(snac, actor_id)) { xs_html_add(form, html_button("approve", L("Approve"), -- cgit v1.2.3 From 203db3b09e3ebf8677f75e83e5470b99c74de470 Mon Sep 17 00:00:00 2001 From: default Date: Wed, 4 Dec 2024 05:51:17 +0100 Subject: Fixed bug in blocked instance rejection. --- html.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'html.c') diff --git a/html.c b/html.c index f8fb876..26831f5 100644 --- a/html.c +++ b/html.c @@ -1524,6 +1524,9 @@ xs_html *html_entry(snac *user, xs_dict *msg, int read_only, if ((read_only || !user) && !is_msg_public(msg)) return NULL; + if (is_instance_blocked(id)) + return NULL; + if (user && level == 0 && xs_is_true(xs_dict_get(user->config, "collapse_threads"))) collapse_threads = 1; -- cgit v1.2.3 From e0ba66a53e8cd89b900b895675588c29d5bb7a2d Mon Sep 17 00:00:00 2001 From: default Date: Wed, 4 Dec 2024 05:53:12 +0100 Subject: Check for existing id. --- html.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'html.c') diff --git a/html.c b/html.c index 26831f5..9a01bea 100644 --- a/html.c +++ b/html.c @@ -1524,7 +1524,7 @@ xs_html *html_entry(snac *user, xs_dict *msg, int read_only, if ((read_only || !user) && !is_msg_public(msg)) return NULL; - if (is_instance_blocked(id)) + if (id && is_instance_blocked(id)) return NULL; if (user && level == 0 && xs_is_true(xs_dict_get(user->config, "collapse_threads"))) -- cgit v1.2.3 From a45a0d68e3d8a942a2bf516d5086920dbcb3a862 Mon Sep 17 00:00:00 2001 From: default Date: Wed, 4 Dec 2024 18:05:41 +0100 Subject: Also show contact metrics in the public page, if the user wants. --- html.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) (limited to 'html.c') diff --git a/html.c b/html.c index 9a01bea..97405fe 100644 --- a/html.c +++ b/html.c @@ -938,6 +938,18 @@ static xs_html *html_user_body(snac *user, int read_only) xs_html_add(top_user, snac_metadata); } + + if (xs_is_true(xs_dict_get(user->config, "show_contact_metrics"))) { + xs *fwers = follower_list(user); + xs *fwing = following_list(user); + + xs *s1 = xs_fmt(L("%d following %d followers"), + xs_list_len(fwing), xs_list_len(fwers)); + + xs_html_add(top_user, + xs_html_tag("p", + xs_html_text(s1))); + } } xs_html_add(body, -- cgit v1.2.3 From b27031cc46b04224585c34eeb70eda396fbeaca2 Mon Sep 17 00:00:00 2001 From: default Date: Thu, 5 Dec 2024 09:13:14 +0100 Subject: Added web UI for the 'show_contact_metrics' toggle. --- html.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) (limited to 'html.c') diff --git a/html.c b/html.c index 97405fe..edb7e1e 100644 --- a/html.c +++ b/html.c @@ -1062,6 +1062,7 @@ xs_html *html_top_controls(snac *snac) const xs_val *auto_boost = xs_dict_get(snac->config, "auto_boost"); const xs_val *coll_thrds = xs_dict_get(snac->config, "collapse_threads"); const xs_val *pending = xs_dict_get(snac->config, "approve_followers"); + const xs_val *show_foll = xs_dict_get(snac->config, "show_contact_metrics"); xs *metadata = NULL; const xs_dict *md = xs_dict_get(snac->config, "metadata"); @@ -1242,6 +1243,15 @@ xs_html *html_top_controls(snac *snac) xs_html_tag("label", xs_html_attr("for", "approve_followers"), xs_html_text(L("Follow requests must be approved")))), + xs_html_tag("p", + xs_html_sctag("input", + xs_html_attr("type", "checkbox"), + xs_html_attr("name", "show_contact_metrics"), + xs_html_attr("id", "show_contact_metrics"), + xs_html_attr(xs_is_true(show_foll) ? "checked" : "", NULL)), + xs_html_tag("label", + xs_html_attr("for", "show_contact_metrics"), + xs_html_text(L("Publish follower and following metrics")))), xs_html_tag("p", xs_html_text(L("Profile metadata (key=value pairs in each line):")), xs_html_sctag("br", NULL), @@ -3858,6 +3868,10 @@ int html_post_handler(const xs_dict *req, const char *q_path, snac.config = xs_dict_set(snac.config, "approve_followers", xs_stock(XSTYPE_TRUE)); else snac.config = xs_dict_set(snac.config, "approve_followers", xs_stock(XSTYPE_FALSE)); + if ((v = xs_dict_get(p_vars, "show_contact_metrics")) != NULL && strcmp(v, "on") == 0) + snac.config = xs_dict_set(snac.config, "show_contact_metrics", xs_stock(XSTYPE_TRUE)); + else + snac.config = xs_dict_set(snac.config, "show_contact_metrics", xs_stock(XSTYPE_FALSE)); if ((v = xs_dict_get(p_vars, "metadata")) != NULL) snac.config = xs_dict_set(snac.config, "metadata", v); -- cgit v1.2.3