summaryrefslogtreecommitdiff
path: root/html.c
diff options
context:
space:
mode:
Diffstat (limited to 'html.c')
-rw-r--r--html.c230
1 files changed, 190 insertions, 40 deletions
diff --git a/html.c b/html.c
index 74de47d..edb7e1e 100644
--- a/html.c
+++ b/html.c
@@ -770,7 +770,7 @@ static xs_html *html_user_body(snac *user, int read_only)
770 xs_html_sctag("input", 770 xs_html_sctag("input",
771 xs_html_attr("type", "text"), 771 xs_html_attr("type", "text"),
772 xs_html_attr("name", "q"), 772 xs_html_attr("name", "q"),
773 xs_html_attr("title", L("Search posts by content (regular expression) or #tag")), 773 xs_html_attr("title", L("Search posts by content (regular expression), @user@host accounts, or #tag")),
774 xs_html_attr("placeholder", L("Content search"))))); 774 xs_html_attr("placeholder", L("Content search")))));
775 } 775 }
776 776
@@ -829,21 +829,45 @@ static xs_html *html_user_body(snac *user, int read_only)
829 } 829 }
830 830
831 if (read_only) { 831 if (read_only) {
832 xs *es1 = encode_html(xs_dict_get(user->config, "bio"));
833 xs *tags = xs_list_new(); 832 xs *tags = xs_list_new();
834 xs *bio1 = not_really_markdown(es1, NULL, &tags); 833 xs *bio1 = not_really_markdown(xs_dict_get(user->config, "bio"), NULL, &tags);
835 xs *bio2 = process_tags(user, bio1, &tags); 834 xs *bio2 = process_tags(user, bio1, &tags);
835 xs *bio3 = sanitize(bio2);
836 836
837 bio2 = replace_shortnames(bio2, tags, 2, proxy); 837 bio3 = replace_shortnames(bio3, tags, 2, proxy);
838 838
839 xs_html *top_user_bio = xs_html_tag("div", 839 xs_html *top_user_bio = xs_html_tag("div",
840 xs_html_attr("class", "p-note snac-top-user-bio"), 840 xs_html_attr("class", "p-note snac-top-user-bio"),
841 xs_html_raw(bio2)); /* already sanitized */ 841 xs_html_raw(bio3)); /* already sanitized */
842 842
843 xs_html_add(top_user, 843 xs_html_add(top_user,
844 top_user_bio); 844 top_user_bio);
845 845
846 const xs_dict *metadata = xs_dict_get(user->config, "metadata"); 846 xs *metadata = NULL;
847 const xs_dict *md = xs_dict_get(user->config, "metadata");
848
849 if (xs_type(md) == XSTYPE_DICT)
850 metadata = xs_dup(md);
851 else
852 if (xs_type(md) == XSTYPE_STRING) {
853 /* convert to dict for easier iteration */
854 metadata = xs_dict_new();
855 xs *l = xs_split(md, "\n");
856 const char *ll;
857
858 xs_list_foreach(l, ll) {
859 xs *kv = xs_split_n(ll, "=", 1);
860 const char *k = xs_list_get(kv, 0);
861 const char *v = xs_list_get(kv, 1);
862
863 if (k && v) {
864 xs *kk = xs_strip_i(xs_dup(k));
865 xs *vv = xs_strip_i(xs_dup(v));
866 metadata = xs_dict_set(metadata, kk, vv);
867 }
868 }
869 }
870
847 if (xs_type(metadata) == XSTYPE_DICT) { 871 if (xs_type(metadata) == XSTYPE_DICT) {
848 const xs_str *k; 872 const xs_str *k;
849 const xs_str *v; 873 const xs_str *v;
@@ -914,6 +938,18 @@ static xs_html *html_user_body(snac *user, int read_only)
914 xs_html_add(top_user, 938 xs_html_add(top_user,
915 snac_metadata); 939 snac_metadata);
916 } 940 }
941
942 if (xs_is_true(xs_dict_get(user->config, "show_contact_metrics"))) {
943 xs *fwers = follower_list(user);
944 xs *fwing = following_list(user);
945
946 xs *s1 = xs_fmt(L("%d following %d followers"),
947 xs_list_len(fwing), xs_list_len(fwers));
948
949 xs_html_add(top_user,
950 xs_html_tag("p",
951 xs_html_text(s1)));
952 }
917 } 953 }
918 954
919 xs_html_add(body, 955 xs_html_add(body,
@@ -1025,20 +1061,31 @@ xs_html *html_top_controls(snac *snac)
1025 const xs_val *a_private = xs_dict_get(snac->config, "private"); 1061 const xs_val *a_private = xs_dict_get(snac->config, "private");
1026 const xs_val *auto_boost = xs_dict_get(snac->config, "auto_boost"); 1062 const xs_val *auto_boost = xs_dict_get(snac->config, "auto_boost");
1027 const xs_val *coll_thrds = xs_dict_get(snac->config, "collapse_threads"); 1063 const xs_val *coll_thrds = xs_dict_get(snac->config, "collapse_threads");
1064 const xs_val *pending = xs_dict_get(snac->config, "approve_followers");
1065 const xs_val *show_foll = xs_dict_get(snac->config, "show_contact_metrics");
1028 1066
1029 xs *metadata = xs_str_new(NULL); 1067 xs *metadata = NULL;
1030 const xs_dict *md = xs_dict_get(snac->config, "metadata"); 1068 const xs_dict *md = xs_dict_get(snac->config, "metadata");
1031 const xs_str *k;
1032 const xs_str *v;
1033 1069
1034 int c = 0; 1070 if (xs_type(md) == XSTYPE_DICT) {
1035 while (xs_dict_next(md, &k, &v, &c)) { 1071 const xs_str *k;
1036 xs *kp = xs_fmt("%s=%s", k, v); 1072 const xs_str *v;
1037 1073
1038 if (*metadata) 1074 metadata = xs_str_new(NULL);
1039 metadata = xs_str_cat(metadata, "\n"); 1075
1040 metadata = xs_str_cat(metadata, kp); 1076 xs_dict_foreach(md, k, v) {
1077 xs *kp = xs_fmt("%s=%s", k, v);
1078
1079 if (*metadata)
1080 metadata = xs_str_cat(metadata, "\n");
1081 metadata = xs_str_cat(metadata, kp);
1082 }
1041 } 1083 }
1084 else
1085 if (xs_type(md) == XSTYPE_STRING)
1086 metadata = xs_dup(md);
1087 else
1088 metadata = xs_str_new(NULL);
1042 1089
1043 xs *user_setup_action = xs_fmt("%s/admin/user-setup", snac->actor); 1090 xs *user_setup_action = xs_fmt("%s/admin/user-setup", snac->actor);
1044 1091
@@ -1188,6 +1235,24 @@ xs_html *html_top_controls(snac *snac)
1188 xs_html_attr("for", "collapse_threads"), 1235 xs_html_attr("for", "collapse_threads"),
1189 xs_html_text(L("Collapse top threads by default")))), 1236 xs_html_text(L("Collapse top threads by default")))),
1190 xs_html_tag("p", 1237 xs_html_tag("p",
1238 xs_html_sctag("input",
1239 xs_html_attr("type", "checkbox"),
1240 xs_html_attr("name", "approve_followers"),
1241 xs_html_attr("id", "approve_followers"),
1242 xs_html_attr(xs_is_true(pending) ? "checked" : "", NULL)),
1243 xs_html_tag("label",
1244 xs_html_attr("for", "approve_followers"),
1245 xs_html_text(L("Follow requests must be approved")))),
1246 xs_html_tag("p",
1247 xs_html_sctag("input",
1248 xs_html_attr("type", "checkbox"),
1249 xs_html_attr("name", "show_contact_metrics"),
1250 xs_html_attr("id", "show_contact_metrics"),
1251 xs_html_attr(xs_is_true(show_foll) ? "checked" : "", NULL)),
1252 xs_html_tag("label",
1253 xs_html_attr("for", "show_contact_metrics"),
1254 xs_html_text(L("Publish follower and following metrics")))),
1255 xs_html_tag("p",
1191 xs_html_text(L("Profile metadata (key=value pairs in each line):")), 1256 xs_html_text(L("Profile metadata (key=value pairs in each line):")),
1192 xs_html_sctag("br", NULL), 1257 xs_html_sctag("br", NULL),
1193 xs_html_tag("textarea", 1258 xs_html_tag("textarea",
@@ -1481,6 +1546,9 @@ xs_html *html_entry(snac *user, xs_dict *msg, int read_only,
1481 if ((read_only || !user) && !is_msg_public(msg)) 1546 if ((read_only || !user) && !is_msg_public(msg))
1482 return NULL; 1547 return NULL;
1483 1548
1549 if (id && is_instance_blocked(id))
1550 return NULL;
1551
1484 if (user && level == 0 && xs_is_true(xs_dict_get(user->config, "collapse_threads"))) 1552 if (user && level == 0 && xs_is_true(xs_dict_get(user->config, "collapse_threads")))
1485 collapse_threads = 1; 1553 collapse_threads = 1;
1486 1554
@@ -2437,10 +2505,9 @@ xs_html *html_people_list(snac *snac, xs_list *list, char *header, char *t, cons
2437 xs_html_tag("summary", 2505 xs_html_tag("summary",
2438 xs_html_text("...")))); 2506 xs_html_text("..."))));
2439 2507
2440 xs_list *p = list;
2441 const char *actor_id; 2508 const char *actor_id;
2442 2509
2443 while (xs_list_iter(&p, &actor_id)) { 2510 xs_list_foreach(list, actor_id) {
2444 xs *md5 = xs_md5_hex(actor_id, strlen(actor_id)); 2511 xs *md5 = xs_md5_hex(actor_id, strlen(actor_id));
2445 xs *actor = NULL; 2512 xs *actor = NULL;
2446 2513
@@ -2509,6 +2576,15 @@ xs_html *html_people_list(snac *snac, xs_list *list, char *header, char *t, cons
2509 html_button("limit", L("Limit"), 2576 html_button("limit", L("Limit"),
2510 L("Block announces (boosts) from this user"))); 2577 L("Block announces (boosts) from this user")));
2511 } 2578 }
2579 else
2580 if (pending_check(snac, actor_id)) {
2581 xs_html_add(form,
2582 html_button("approve", L("Approve"),
2583 L("Approve this follow request")));
2584
2585 xs_html_add(form,
2586 html_button("discard", L("Discard"), L("Discard this follow request")));
2587 }
2512 else { 2588 else {
2513 xs_html_add(form, 2589 xs_html_add(form,
2514 html_button("follow", L("Follow"), 2590 html_button("follow", L("Follow"),
@@ -2563,13 +2639,23 @@ xs_str *html_people(snac *user)
2563 xs *wing = following_list(user); 2639 xs *wing = following_list(user);
2564 xs *wers = follower_list(user); 2640 xs *wers = follower_list(user);
2565 2641
2642 xs_html *lists = xs_html_tag("div",
2643 xs_html_attr("class", "snac-posts"));
2644
2645 if (xs_is_true(xs_dict_get(user->config, "approve_followers"))) {
2646 xs *pending = pending_list(user);
2647 xs_html_add(lists,
2648 html_people_list(user, pending, L("Pending follow confirmations"), "p", proxy));
2649 }
2650
2651 xs_html_add(lists,
2652 html_people_list(user, wing, L("People you follow"), "i", proxy),
2653 html_people_list(user, wers, L("People that follow you"), "e", proxy));
2654
2566 xs_html *html = xs_html_tag("html", 2655 xs_html *html = xs_html_tag("html",
2567 html_user_head(user, NULL, NULL), 2656 html_user_head(user, NULL, NULL),
2568 xs_html_add(html_user_body(user, 0), 2657 xs_html_add(html_user_body(user, 0),
2569 xs_html_tag("div", 2658 lists,
2570 xs_html_attr("class", "snac-posts"),
2571 html_people_list(user, wing, L("People you follow"), "i", proxy),
2572 html_people_list(user, wers, L("People that follow you"), "e", proxy)),
2573 html_footer())); 2659 html_footer()));
2574 2660
2575 return xs_html_render_s(html, "<!DOCTYPE html>\n"); 2661 return xs_html_render_s(html, "<!DOCTYPE html>\n");
@@ -2661,6 +2747,9 @@ xs_str *html_notifications(snac *user, int skip, int show)
2661 label = wrk; 2747 label = wrk;
2662 } 2748 }
2663 } 2749 }
2750 else
2751 if (strcmp(type, "Follow") == 0 && pending_check(user, actor_id))
2752 label = L("Follow Request");
2664 2753
2665 xs *s_date = xs_crop_i(xs_dup(date), 0, 10); 2754 xs *s_date = xs_crop_i(xs_dup(date), 0, 10);
2666 2755
@@ -2909,6 +2998,48 @@ int html_get_handler(const xs_dict *req, const char *q_path,
2909 const char *q = xs_dict_get(q_vars, "q"); 2998 const char *q = xs_dict_get(q_vars, "q");
2910 2999
2911 if (q && *q) { 3000 if (q && *q) {
3001 if (xs_regex_match(q, "^@?[a-zA-Z0-9_]+@[a-zA-Z0-9-]+\\.")) {
3002 /** search account **/
3003 xs *actor = NULL;
3004 xs *acct = NULL;
3005 xs *l = xs_list_new();
3006 xs_html *page = NULL;
3007
3008 if (valid_status(webfinger_request(q, &actor, &acct))) {
3009 xs *actor_obj = NULL;
3010
3011 if (valid_status(actor_request(&snac, actor, &actor_obj))) {
3012 actor_add(actor, actor_obj);
3013
3014 /* create a people list with only one element */
3015 l = xs_list_append(xs_list_new(), actor);
3016
3017 xs *title = xs_fmt(L("Search results for account %s"), q);
3018
3019 page = html_people_list(&snac, l, title, "wf", NULL);
3020 }
3021 }
3022
3023 if (page == NULL) {
3024 xs *title = xs_fmt(L("Account %s not found"), q);
3025
3026 page = xs_html_tag("div",
3027 xs_html_tag("h2",
3028 xs_html_attr("class", "snac-header"),
3029 xs_html_text(title)));
3030 }
3031
3032 xs_html *html = xs_html_tag("html",
3033 html_user_head(&snac, NULL, NULL),
3034 xs_html_add(html_user_body(&snac, 0),
3035 page,
3036 html_footer()));
3037
3038 *body = xs_html_render_s(html, "<!DOCTYPE html>\n");
3039 *b_size = strlen(*body);
3040 status = HTTP_STATUS_OK;
3041 }
3042 else
2912 if (*q == '#') { 3043 if (*q == '#') {
2913 /** search by tag **/ 3044 /** search by tag **/
2914 xs *tl = tag_search(q, skip, show + 1); 3045 xs *tl = tag_search(q, skip, show + 1);
@@ -3647,6 +3778,34 @@ int html_post_handler(const xs_dict *req, const char *q_path,
3647 timeline_touch(&snac); 3778 timeline_touch(&snac);
3648 } 3779 }
3649 else 3780 else
3781 if (strcmp(action, L("Approve")) == 0) { /** **/
3782 xs *fwreq = pending_get(&snac, actor);
3783
3784 if (fwreq != NULL) {
3785 xs *reply = msg_accept(&snac, fwreq, actor);
3786
3787 enqueue_message(&snac, reply);
3788
3789 if (xs_is_null(xs_dict_get(fwreq, "published"))) {
3790 /* add a date if it doesn't include one (Mastodon) */
3791 xs *date = xs_str_utctime(0, ISO_DATE_SPEC);
3792 fwreq = xs_dict_set(fwreq, "published", date);
3793 }
3794
3795 timeline_add(&snac, xs_dict_get(fwreq, "id"), fwreq);
3796
3797 follower_add(&snac, actor);
3798
3799 pending_del(&snac, actor);
3800
3801 snac_log(&snac, xs_fmt("new follower %s", actor));
3802 }
3803 }
3804 else
3805 if (strcmp(action, L("Discard")) == 0) { /** **/
3806 pending_del(&snac, actor);
3807 }
3808 else
3650 status = HTTP_STATUS_NOT_FOUND; 3809 status = HTTP_STATUS_NOT_FOUND;
3651 3810
3652 /* delete the cached timeline */ 3811 /* delete the cached timeline */
@@ -3705,26 +3864,17 @@ int html_post_handler(const xs_dict *req, const char *q_path,
3705 snac.config = xs_dict_set(snac.config, "collapse_threads", xs_stock(XSTYPE_TRUE)); 3864 snac.config = xs_dict_set(snac.config, "collapse_threads", xs_stock(XSTYPE_TRUE));
3706 else 3865 else
3707 snac.config = xs_dict_set(snac.config, "collapse_threads", xs_stock(XSTYPE_FALSE)); 3866 snac.config = xs_dict_set(snac.config, "collapse_threads", xs_stock(XSTYPE_FALSE));
3867 if ((v = xs_dict_get(p_vars, "approve_followers")) != NULL && strcmp(v, "on") == 0)
3868 snac.config = xs_dict_set(snac.config, "approve_followers", xs_stock(XSTYPE_TRUE));
3869 else
3870 snac.config = xs_dict_set(snac.config, "approve_followers", xs_stock(XSTYPE_FALSE));
3871 if ((v = xs_dict_get(p_vars, "show_contact_metrics")) != NULL && strcmp(v, "on") == 0)
3872 snac.config = xs_dict_set(snac.config, "show_contact_metrics", xs_stock(XSTYPE_TRUE));
3873 else
3874 snac.config = xs_dict_set(snac.config, "show_contact_metrics", xs_stock(XSTYPE_FALSE));
3708 3875
3709 if ((v = xs_dict_get(p_vars, "metadata")) != NULL) { 3876 if ((v = xs_dict_get(p_vars, "metadata")) != NULL)
3710 /* split the metadata and store it as a dict */ 3877 snac.config = xs_dict_set(snac.config, "metadata", v);
3711 xs_dict *md = xs_dict_new();
3712 xs *l = xs_split(v, "\n");
3713 xs_list *p = l;
3714 const xs_str *kp;
3715
3716 while (xs_list_iter(&p, &kp)) {
3717 xs *kpl = xs_split_n(kp, "=", 1);
3718 if (xs_list_len(kpl) == 2) {
3719 xs *k2 = xs_strip_i(xs_dup(xs_list_get(kpl, 0)));
3720 xs *v2 = xs_strip_i(xs_dup(xs_list_get(kpl, 1)));
3721
3722 md = xs_dict_set(md, k2, v2);
3723 }
3724 }
3725
3726 snac.config = xs_dict_set(snac.config, "metadata", md);
3727 }
3728 3878
3729 /* uploads */ 3879 /* uploads */
3730 const char *uploads[] = { "avatar", "header", NULL }; 3880 const char *uploads[] = { "avatar", "header", NULL };