diff options
Diffstat (limited to 'html.c')
| -rw-r--r-- | html.c | 333 |
1 files changed, 312 insertions, 21 deletions
| @@ -1,5 +1,5 @@ | |||
| 1 | /* snac - A simple, minimalistic ActivityPub instance */ | 1 | /* snac - A simple, minimalistic ActivityPub instance */ |
| 2 | /* copyright (c) 2022 - 2025 grunfink et al. / MIT license */ | 2 | /* copyright (c) 2022 - 2026 grunfink et al. / MIT license */ |
| 3 | 3 | ||
| 4 | #include "xs.h" | 4 | #include "xs.h" |
| 5 | #include "xs_io.h" | 5 | #include "xs_io.h" |
| @@ -16,6 +16,7 @@ | |||
| 16 | #include "xs_url.h" | 16 | #include "xs_url.h" |
| 17 | #include "xs_random.h" | 17 | #include "xs_random.h" |
| 18 | #include "xs_http.h" | 18 | #include "xs_http.h" |
| 19 | #include "xs_list_tools.h" | ||
| 19 | 20 | ||
| 20 | #include "snac.h" | 21 | #include "snac.h" |
| 21 | 22 | ||
| @@ -161,6 +162,34 @@ xs_str *actor_name(xs_dict *actor, const char *proxy) | |||
| 161 | } | 162 | } |
| 162 | 163 | ||
| 163 | 164 | ||
| 165 | xs_str *actor_pronouns(xs_dict *actor) | ||
| 166 | /* gets the actor name */ | ||
| 167 | { | ||
| 168 | const xs_list *attachment; | ||
| 169 | const xs_dict *d; | ||
| 170 | const char *v; | ||
| 171 | const char *pronouns = ""; | ||
| 172 | xs_str *ret; | ||
| 173 | |||
| 174 | if (xs_is_list((attachment = xs_dict_get(actor, "attachment")))) { | ||
| 175 | xs_list_foreach(attachment, d) { | ||
| 176 | xs *prop = xs_utf8_to_lower(xs_dict_get(d, "name")); | ||
| 177 | /* make sure that we are reading the correct metadata */ | ||
| 178 | if (strlen(prop) == 8 && strcmp(prop, "pronouns") == 0) { | ||
| 179 | /* safeguard from NULL values */ | ||
| 180 | v = xs_dict_get(d, "value"); | ||
| 181 | pronouns = v ? v : pronouns; | ||
| 182 | break; | ||
| 183 | } | ||
| 184 | } | ||
| 185 | } | ||
| 186 | |||
| 187 | /* strip all HTML tags */ | ||
| 188 | ret = xs_regex_replace(pronouns, "</?[^>]+>", ""); | ||
| 189 | return ret; | ||
| 190 | } | ||
| 191 | |||
| 192 | |||
| 164 | xs_str *format_text_with_emoji(snac *user, const char *text, int ems, const char *proxy) | 193 | xs_str *format_text_with_emoji(snac *user, const char *text, int ems, const char *proxy) |
| 165 | /* needed when we have local text with no tags attached */ | 194 | /* needed when we have local text with no tags attached */ |
| 166 | { | 195 | { |
| @@ -194,6 +223,7 @@ xs_html *html_actor_icon(snac *user, xs_dict *actor, const char *date, | |||
| 194 | int fwer = 0; | 223 | int fwer = 0; |
| 195 | 224 | ||
| 196 | xs *name = actor_name(actor, proxy); | 225 | xs *name = actor_name(actor, proxy); |
| 226 | xs *pronouns = actor_pronouns(actor); | ||
| 197 | 227 | ||
| 198 | /* get the avatar */ | 228 | /* get the avatar */ |
| 199 | if ((v = xs_dict_get(actor, "icon")) != NULL) { | 229 | if ((v = xs_dict_get(actor, "icon")) != NULL) { |
| @@ -221,23 +251,35 @@ xs_html *html_actor_icon(snac *user, xs_dict *actor, const char *date, | |||
| 221 | anchored link to the people page instead of the actor url */ | 251 | anchored link to the people page instead of the actor url */ |
| 222 | if (fwer || fwing) { | 252 | if (fwer || fwing) { |
| 223 | xs *md5 = xs_md5_hex(actor_id, strlen(actor_id)); | 253 | xs *md5 = xs_md5_hex(actor_id, strlen(actor_id)); |
| 224 | href = xs_fmt("%s/people#%s", user->actor, md5); | 254 | href = xs_fmt("%s/people/%s", user->actor, md5); |
| 225 | } | 255 | } |
| 226 | } | 256 | } |
| 227 | 257 | ||
| 228 | if (href == NULL) | 258 | if (href == NULL) |
| 229 | href = xs_dup(actor_id); | 259 | href = xs_dup(actor_id); |
| 230 | 260 | ||
| 261 | xs_html *name_link = xs_html_tag("a", | ||
| 262 | xs_html_attr("href", href), | ||
| 263 | xs_html_attr("class", "p-author h-card snac-author"), | ||
| 264 | xs_html_raw(name)); /* name is already html-escaped */ | ||
| 265 | |||
| 266 | if (*pronouns) { | ||
| 267 | xs_html_add(name_link, | ||
| 268 | xs_html_text(" ["), | ||
| 269 | xs_html_tag("span", | ||
| 270 | xs_html_attr("class", "snac-pronouns"), | ||
| 271 | xs_html_attr("title", "user's pronouns"), | ||
| 272 | xs_html_raw(pronouns)), | ||
| 273 | xs_html_text("]")); | ||
| 274 | } | ||
| 275 | |||
| 231 | xs_html_add(actor_icon, | 276 | xs_html_add(actor_icon, |
| 232 | xs_html_sctag("img", | 277 | xs_html_sctag("img", |
| 233 | xs_html_attr("loading", "lazy"), | 278 | xs_html_attr("loading", "lazy"), |
| 234 | xs_html_attr("class", "snac-avatar"), | 279 | xs_html_attr("class", "snac-avatar"), |
| 235 | xs_html_attr("src", avatar), | 280 | xs_html_attr("src", avatar), |
| 236 | xs_html_attr("alt", "[?]")), | 281 | xs_html_attr("alt", "[?]")), |
| 237 | xs_html_tag("a", | 282 | name_link); |
| 238 | xs_html_attr("href", href), | ||
| 239 | xs_html_attr("class", "p-author h-card snac-author"), | ||
| 240 | xs_html_raw(name))); /* name is already html-escaped */ | ||
| 241 | 283 | ||
| 242 | if (!xs_is_null(url)) { | 284 | if (!xs_is_null(url)) { |
| 243 | xs *md5 = xs_md5_hex(url, strlen(url)); | 285 | xs *md5 = xs_md5_hex(url, strlen(url)); |
| @@ -250,6 +292,7 @@ xs_html *html_actor_icon(snac *user, xs_dict *actor, const char *date, | |||
| 250 | xs_html_text("»"))); | 292 | xs_html_text("»"))); |
| 251 | } | 293 | } |
| 252 | 294 | ||
| 295 | |||
| 253 | if (strcmp(xs_dict_get(actor, "type"), "Service") == 0) { | 296 | if (strcmp(xs_dict_get(actor, "type"), "Service") == 0) { |
| 254 | xs_html_add(actor_icon, | 297 | xs_html_add(actor_icon, |
| 255 | xs_html_text(" "), | 298 | xs_html_text(" "), |
| @@ -1864,6 +1907,38 @@ xs_html *html_top_controls(snac *user) | |||
| 1864 | xs_html_attr("class", "button"), | 1907 | xs_html_attr("class", "button"), |
| 1865 | xs_html_attr("value", L("Update hashtags"))))))); | 1908 | xs_html_attr("value", L("Update hashtags"))))))); |
| 1866 | 1909 | ||
| 1910 | xs *muted_words_action = xs_fmt("%s/admin/muted-words", user->actor); | ||
| 1911 | xs *muted_words = xs_join(xs_dict_get_def(user->config, | ||
| 1912 | "muted_words", xs_stock(XSTYPE_LIST)), "\n"); | ||
| 1913 | |||
| 1914 | xs_html_add(top_controls, | ||
| 1915 | xs_html_tag("details", | ||
| 1916 | xs_html_tag("summary", | ||
| 1917 | xs_html_text(L("Muted words..."))), | ||
| 1918 | xs_html_tag("p", | ||
| 1919 | xs_html_text(L("One word per line, partial matches count"))), | ||
| 1920 | xs_html_tag("div", | ||
| 1921 | xs_html_attr("class", "snac-muted-words"), | ||
| 1922 | xs_html_tag("form", | ||
| 1923 | xs_html_attr("autocomplete", "off"), | ||
| 1924 | xs_html_attr("method", "post"), | ||
| 1925 | xs_html_attr("action", muted_words_action), | ||
| 1926 | xs_html_attr("enctype", "multipart/form-data"), | ||
| 1927 | |||
| 1928 | xs_html_tag("textarea", | ||
| 1929 | xs_html_attr("name", "muted_words"), | ||
| 1930 | xs_html_attr("cols", "40"), | ||
| 1931 | xs_html_attr("rows", "4"), | ||
| 1932 | xs_html_attr("placeholder", "nascar\nsuperbowl\nFIFA"), | ||
| 1933 | xs_html_text(muted_words)), | ||
| 1934 | |||
| 1935 | xs_html_tag("br", NULL), | ||
| 1936 | |||
| 1937 | xs_html_sctag("input", | ||
| 1938 | xs_html_attr("type", "submit"), | ||
| 1939 | xs_html_attr("class", "button"), | ||
| 1940 | xs_html_attr("value", L("Update muted words"))))))); | ||
| 1941 | |||
| 1867 | return top_controls; | 1942 | return top_controls; |
| 1868 | } | 1943 | } |
| 1869 | 1944 | ||
| @@ -2103,7 +2178,7 @@ xs_html *html_entry_controls(snac *user, const char *actor, | |||
| 2103 | xs_html_tag("p", NULL)); | 2178 | xs_html_tag("p", NULL)); |
| 2104 | } | 2179 | } |
| 2105 | 2180 | ||
| 2106 | { /** emoji react **/ | 2181 | if (!xs_is_true(xs_dict_get(srv_config, "disable_emojireact"))) { /** emoji react **/ |
| 2107 | /* the post textarea */ | 2182 | /* the post textarea */ |
| 2108 | xs *div_id = xs_fmt("%s_reply", md5); | 2183 | xs *div_id = xs_fmt("%s_reply", md5); |
| 2109 | xs *form_id = xs_fmt("%s_reply_form", md5); | 2184 | xs *form_id = xs_fmt("%s_reply_form", md5); |
| @@ -2144,6 +2219,30 @@ xs_html *html_entry_controls(snac *user, const char *actor, | |||
| 2144 | } | 2219 | } |
| 2145 | 2220 | ||
| 2146 | 2221 | ||
| 2222 | static const xs_str* words_in_content(const xs_list *words, const xs_val *content) | ||
| 2223 | /* returns a word that matches any of the words in content */ | ||
| 2224 | { | ||
| 2225 | if (!xs_is_list(words) || !xs_is_string(content)) { | ||
| 2226 | return NULL; | ||
| 2227 | } | ||
| 2228 | xs *c = xs_split(content, " "); | ||
| 2229 | xs *sc = xs_list_sort(c, NULL); | ||
| 2230 | |||
| 2231 | const xs_str *wv; | ||
| 2232 | const xs_str *cv; | ||
| 2233 | xs_list_foreach(words, wv) { | ||
| 2234 | xs_list_foreach(sc, cv) { | ||
| 2235 | xs_tolower_i((xs_str*)cv); | ||
| 2236 | if(xs_str_in(cv, wv) != -1){ | ||
| 2237 | return wv; | ||
| 2238 | } | ||
| 2239 | } | ||
| 2240 | } | ||
| 2241 | |||
| 2242 | return NULL; | ||
| 2243 | } | ||
| 2244 | |||
| 2245 | |||
| 2147 | xs_html *html_entry(snac *user, xs_dict *msg, int read_only, | 2246 | xs_html *html_entry(snac *user, xs_dict *msg, int read_only, |
| 2148 | int level, const char *md5, int hide_children) | 2247 | int level, const char *md5, int hide_children) |
| 2149 | { | 2248 | { |
| @@ -2355,7 +2454,7 @@ xs_html *html_entry(snac *user, xs_dict *msg, int read_only, | |||
| 2355 | } | 2454 | } |
| 2356 | 2455 | ||
| 2357 | if (!read_only && (fwers || fwing)) | 2456 | if (!read_only && (fwers || fwing)) |
| 2358 | href = xs_fmt("%s/people#%s", user->actor, p); | 2457 | href = xs_fmt("%s/people/%s", user->actor, p); |
| 2359 | else | 2458 | else |
| 2360 | href = xs_dup(id); | 2459 | href = xs_dup(id); |
| 2361 | 2460 | ||
| @@ -2438,6 +2537,17 @@ xs_html *html_entry(snac *user, xs_dict *msg, int read_only, | |||
| 2438 | xs_html_text(v), | 2537 | xs_html_text(v), |
| 2439 | xs_html_text(L(" [SENSITIVE CONTENT]")))); | 2538 | xs_html_text(L(" [SENSITIVE CONTENT]")))); |
| 2440 | } | 2539 | } |
| 2540 | else | ||
| 2541 | if (user && | ||
| 2542 | /* muted_words is all lowercase and sorted for performance */ | ||
| 2543 | (v = words_in_content(xs_dict_get(user->config, "muted_words"), | ||
| 2544 | xs_dict_get(msg, "content"))) != NULL) { | ||
| 2545 | snac_debug(user, 1, xs_fmt("word %s muted by user preferences: %s", v, id)); | ||
| 2546 | snac_content = xs_html_tag("details", | ||
| 2547 | xs_html_tag("summary", | ||
| 2548 | xs_html_text(L("Muted: ")), | ||
| 2549 | xs_html_text(v))); | ||
| 2550 | } | ||
| 2441 | else { | 2551 | else { |
| 2442 | snac_content = xs_html_tag("div", NULL); | 2552 | snac_content = xs_html_tag("div", NULL); |
| 2443 | } | 2553 | } |
| @@ -2447,7 +2557,7 @@ xs_html *html_entry(snac *user, xs_dict *msg, int read_only, | |||
| 2447 | 2557 | ||
| 2448 | /* add all emoji reacts */ | 2558 | /* add all emoji reacts */ |
| 2449 | int is_emoji = 0; | 2559 | int is_emoji = 0; |
| 2450 | { | 2560 | if (!xs_is_true(xs_dict_get(srv_config, "disable_emojireact"))) { |
| 2451 | int c = 0; | 2561 | int c = 0; |
| 2452 | const xs_dict *k; | 2562 | const xs_dict *k; |
| 2453 | xs *ls = xs_list_new(); | 2563 | xs *ls = xs_list_new(); |
| @@ -2460,6 +2570,10 @@ xs_html *html_entry(snac *user, xs_dict *msg, int read_only, | |||
| 2460 | const char *content = xs_dict_get(m, "content"); | 2570 | const char *content = xs_dict_get(m, "content"); |
| 2461 | const char *actor = xs_dict_get(m, "actor"); | 2571 | const char *actor = xs_dict_get(m, "actor"); |
| 2462 | const xs_list *contentl = xs_dict_get(sfrl, content); | 2572 | const xs_list *contentl = xs_dict_get(sfrl, content); |
| 2573 | |||
| 2574 | if ((user && is_muted(user, actor)) || is_instance_blocked(actor)) | ||
| 2575 | continue; | ||
| 2576 | |||
| 2463 | xs *actors = xs_list_new(); | 2577 | xs *actors = xs_list_new(); |
| 2464 | actors = xs_list_append(actors, actor); | 2578 | actors = xs_list_append(actors, actor); |
| 2465 | char me = actor && user && strcmp(actor, user->actor) == 0; | 2579 | char me = actor && user && strcmp(actor, user->actor) == 0; |
| @@ -3593,13 +3707,19 @@ xs_str *html_timeline(snac *user, const xs_list *list, int read_only, | |||
| 3593 | } | 3707 | } |
| 3594 | 3708 | ||
| 3595 | 3709 | ||
| 3596 | xs_html *html_people_list(snac *user, xs_list *list, const char *header, const char *t, const char *proxy) | 3710 | xs_html *html_people_list(snac *user, xs_list *list, const char *header, const char *t, const char *proxy, int do_count) |
| 3597 | { | 3711 | { |
| 3598 | xs_html *snac_posts; | 3712 | xs_html *snac_posts; |
| 3713 | xs *header_cnt; | ||
| 3714 | if (do_count) | ||
| 3715 | header_cnt = xs_fmt("%s - %d\n", header, xs_list_len(list)); | ||
| 3716 | else | ||
| 3717 | header_cnt = xs_fmt("%s\n", header); | ||
| 3718 | |||
| 3599 | xs_html *people = xs_html_tag("div", | 3719 | xs_html *people = xs_html_tag("div", |
| 3600 | xs_html_tag("h2", | 3720 | xs_html_tag("h2", |
| 3601 | xs_html_attr("class", "snac-header"), | 3721 | xs_html_attr("class", "snac-header"), |
| 3602 | xs_html_text(header)), | 3722 | xs_html_text(header_cnt)), |
| 3603 | snac_posts = xs_html_tag("details", | 3723 | snac_posts = xs_html_tag("details", |
| 3604 | xs_html_attr("open", NULL), | 3724 | xs_html_attr("open", NULL), |
| 3605 | xs_html_tag("summary", | 3725 | xs_html_tag("summary", |
| @@ -3840,12 +3960,12 @@ xs_str *html_people(snac *user) | |||
| 3840 | 3960 | ||
| 3841 | if (xs_list_len(pending) || xs_is_true(xs_dict_get(user->config, "approve_followers"))) { | 3961 | if (xs_list_len(pending) || xs_is_true(xs_dict_get(user->config, "approve_followers"))) { |
| 3842 | xs_html_add(lists, | 3962 | xs_html_add(lists, |
| 3843 | html_people_list(user, pending, L("Pending follow confirmations"), "p", proxy)); | 3963 | html_people_list(user, pending, L("Pending follow confirmations"), "p", proxy, 1)); |
| 3844 | } | 3964 | } |
| 3845 | 3965 | ||
| 3846 | xs_html_add(lists, | 3966 | xs_html_add(lists, |
| 3847 | html_people_list(user, wing, L("People you follow"), "i", proxy), | 3967 | html_people_list(user, wing, L("People you follow"), "i", proxy, 1), |
| 3848 | html_people_list(user, wers, L("People that follow you"), "e", proxy)); | 3968 | html_people_list(user, wers, L("People that follow you"), "e", proxy, 1)); |
| 3849 | 3969 | ||
| 3850 | xs_html *html = xs_html_tag("html", | 3970 | xs_html *html = xs_html_tag("html", |
| 3851 | html_user_head(user, NULL, NULL), | 3971 | html_user_head(user, NULL, NULL), |
| @@ -3856,6 +3976,108 @@ xs_str *html_people(snac *user) | |||
| 3856 | return xs_html_render_s(html, "<!DOCTYPE html>\n"); | 3976 | return xs_html_render_s(html, "<!DOCTYPE html>\n"); |
| 3857 | } | 3977 | } |
| 3858 | 3978 | ||
| 3979 | /* Filter list to display only posts by actor. We'll probably show | ||
| 3980 | fewer than show posts. Should we try harder to find some? */ | ||
| 3981 | xs_str *html_people_one(snac *user, const char *actor, const xs_list *list, | ||
| 3982 | int skip, int show, int show_more, const char *page) | ||
| 3983 | { | ||
| 3984 | const char *proxy = NULL; | ||
| 3985 | xs_list *p = (xs_list *)list; | ||
| 3986 | const char *v; | ||
| 3987 | |||
| 3988 | if (xs_is_true(xs_dict_get(srv_config, "proxy_media"))) | ||
| 3989 | proxy = user->actor; | ||
| 3990 | |||
| 3991 | xs_html *body = html_user_body(user, 0); | ||
| 3992 | |||
| 3993 | xs_html *lists = xs_html_tag("div", | ||
| 3994 | xs_html_attr("class", "snac-posts")); | ||
| 3995 | |||
| 3996 | xs *foll = xs_list_append(xs_list_new(), actor); | ||
| 3997 | |||
| 3998 | xs_html_add(lists, | ||
| 3999 | html_people_list(user, foll, L("Contact's posts"), "p", proxy, 0)); | ||
| 4000 | |||
| 4001 | xs_html_add(body, lists); | ||
| 4002 | |||
| 4003 | while (xs_list_iter(&p, &v)) { | ||
| 4004 | xs *msg = NULL; | ||
| 4005 | int status; | ||
| 4006 | |||
| 4007 | status = timeline_get_by_md5(user, v, &msg); | ||
| 4008 | |||
| 4009 | if (!valid_status(status)) | ||
| 4010 | continue; | ||
| 4011 | |||
| 4012 | const char *id = xs_dict_get(msg, "id"); | ||
| 4013 | const char *by = get_atto(msg); | ||
| 4014 | xs *actor_md5 = NULL; | ||
| 4015 | xs_list *boosts = NULL; | ||
| 4016 | xs_list *likes = NULL; | ||
| 4017 | xs_list *reacts = NULL; | ||
| 4018 | /* Besides actor's posts, also show actor's boosts, and also | ||
| 4019 | posts by user with likes or reacts by actor. I.e., any | ||
| 4020 | actor's actions that user could have seen in the timeline | ||
| 4021 | or in notifications. */ | ||
| 4022 | if (!(by && strcmp(actor, by) == 0) && | ||
| 4023 | xs_list_in((boosts = object_announces(id)), | ||
| 4024 | (actor_md5 = xs_md5_hex(actor, strlen(actor)))) == -1 && | ||
| 4025 | (!(by && strcmp(user->actor, by) == 0) || | ||
| 4026 | (xs_list_in((likes = object_likes(id)), actor_md5) == -1 && | ||
| 4027 | xs_list_in((reacts = object_get_emoji_reacts(id)), actor_md5) == -1))) | ||
| 4028 | continue; | ||
| 4029 | |||
| 4030 | xs_html *entry = html_entry(user, msg, 0, 0, v, 1); | ||
| 4031 | |||
| 4032 | if (entry != NULL) | ||
| 4033 | xs_html_add(lists, | ||
| 4034 | entry); | ||
| 4035 | } | ||
| 4036 | |||
| 4037 | if (show_more) { | ||
| 4038 | xs *m = NULL; | ||
| 4039 | xs *m10 = NULL; | ||
| 4040 | xs *ss = xs_fmt("skip=%d&show=%d", skip + show, show); | ||
| 4041 | |||
| 4042 | xs *url = xs_dup(user == NULL ? srv_baseurl : user->actor); | ||
| 4043 | |||
| 4044 | if (page != NULL) | ||
| 4045 | url = xs_str_cat(url, page); | ||
| 4046 | |||
| 4047 | if (xs_str_in(url, "?") != -1) | ||
| 4048 | m = xs_fmt("%s&%s", url, ss); | ||
| 4049 | else | ||
| 4050 | m = xs_fmt("%s?%s", url, ss); | ||
| 4051 | |||
| 4052 | m10 = xs_fmt("%s0", m); | ||
| 4053 | |||
| 4054 | xs_html *more_links = xs_html_tag("p", | ||
| 4055 | xs_html_tag("a", | ||
| 4056 | xs_html_attr("href", url), | ||
| 4057 | xs_html_attr("name", "snac-more"), | ||
| 4058 | xs_html_text(L("Back to top"))), | ||
| 4059 | xs_html_text(" - "), | ||
| 4060 | xs_html_tag("a", | ||
| 4061 | xs_html_attr("href", m), | ||
| 4062 | xs_html_attr("name", "snac-more"), | ||
| 4063 | xs_html_text(L("More..."))), | ||
| 4064 | xs_html_text(" - "), | ||
| 4065 | xs_html_tag("a", | ||
| 4066 | xs_html_attr("href", m10), | ||
| 4067 | xs_html_attr("name", "snac-more"), | ||
| 4068 | xs_html_text(L("More (x 10)...")))); | ||
| 4069 | |||
| 4070 | xs_html_add(body, | ||
| 4071 | more_links); | ||
| 4072 | } | ||
| 4073 | |||
| 4074 | xs_html *html = xs_html_tag("html", | ||
| 4075 | html_user_head(user, NULL, NULL), | ||
| 4076 | xs_html_add(body, | ||
| 4077 | html_footer(user))); | ||
| 4078 | |||
| 4079 | return xs_html_render_s(html, "<!DOCTYPE html>\n"); | ||
| 4080 | } | ||
| 3859 | 4081 | ||
| 3860 | xs_str *html_notifications(snac *user, int skip, int show) | 4082 | xs_str *html_notifications(snac *user, int skip, int show) |
| 3861 | { | 4083 | { |
| @@ -3924,6 +4146,9 @@ xs_str *html_notifications(snac *user, int skip, int show) | |||
| 3924 | if (xs_is_string(id2) && xs_set_add(&rep, id2) != 1) | 4146 | if (xs_is_string(id2) && xs_set_add(&rep, id2) != 1) |
| 3925 | continue; | 4147 | continue; |
| 3926 | 4148 | ||
| 4149 | if (strcmp(type, "EmojiReact") == 0 && xs_is_true(xs_dict_get(srv_config, "disable_emojireact"))) | ||
| 4150 | continue; | ||
| 4151 | |||
| 3927 | object_get(id, &obj); | 4152 | object_get(id, &obj); |
| 3928 | 4153 | ||
| 3929 | const char *msg_id = NULL; | 4154 | const char *msg_id = NULL; |
| @@ -4271,8 +4496,12 @@ int html_get_handler(const xs_dict *req, const char *q_path, | |||
| 4271 | cache = 0; | 4496 | cache = 0; |
| 4272 | 4497 | ||
| 4273 | int skip = 0; | 4498 | int skip = 0; |
| 4499 | const char *max_show_default = "50"; | ||
| 4500 | int max_show = xs_number_get(xs_dict_get_def(srv_config, "max_timeline_entries", | ||
| 4501 | max_show_default)); | ||
| 4274 | int def_show = xs_number_get(xs_dict_get_def(srv_config, "def_timeline_entries", | 4502 | int def_show = xs_number_get(xs_dict_get_def(srv_config, "def_timeline_entries", |
| 4275 | xs_dict_get_def(srv_config, "max_timeline_entries", "50"))); | 4503 | xs_dict_get_def(srv_config, "max_timeline_entries", |
| 4504 | max_show_default))); | ||
| 4276 | int show = def_show; | 4505 | int show = def_show; |
| 4277 | 4506 | ||
| 4278 | if ((v = xs_dict_get(q_vars, "skip")) != NULL) | 4507 | if ((v = xs_dict_get(q_vars, "skip")) != NULL) |
| @@ -4298,6 +4527,8 @@ int html_get_handler(const xs_dict *req, const char *q_path, | |||
| 4298 | /* a show of 0 has no sense */ | 4527 | /* a show of 0 has no sense */ |
| 4299 | if (show == 0) | 4528 | if (show == 0) |
| 4300 | show = def_show; | 4529 | show = def_show; |
| 4530 | if (show > max_show) | ||
| 4531 | show = max_show; | ||
| 4301 | 4532 | ||
| 4302 | if (p_path == NULL) { /** public timeline **/ | 4533 | if (p_path == NULL) { /** public timeline **/ |
| 4303 | xs *h = xs_str_localtime(0, "%Y-%m.html"); | 4534 | xs *h = xs_str_localtime(0, "%Y-%m.html"); |
| @@ -4343,12 +4574,14 @@ int html_get_handler(const xs_dict *req, const char *q_path, | |||
| 4343 | status = HTTP_STATUS_UNAUTHORIZED; | 4574 | status = HTTP_STATUS_UNAUTHORIZED; |
| 4344 | } | 4575 | } |
| 4345 | else { | 4576 | else { |
| 4346 | xs *q = NULL; | 4577 | const char *q = NULL; |
| 4347 | const char *q1 = xs_dict_get(q_vars, "q"); | 4578 | xs *cq = xs_dup(xs_dict_get(q_vars, "q")); |
| 4348 | xs *url_acct = NULL; | 4579 | xs *url_acct = NULL; |
| 4349 | 4580 | ||
| 4350 | if (xs_is_string(q1)) | 4581 | if (xs_is_string(cq)) { |
| 4351 | q = xs_strip_i(xs_dup(q1)); | 4582 | cq = xs_strip_i(cq); |
| 4583 | q = cq; | ||
| 4584 | } | ||
| 4352 | 4585 | ||
| 4353 | /* searching for an URL? */ | 4586 | /* searching for an URL? */ |
| 4354 | if (q && xs_match(q, "https://*|http://*")) { | 4587 | if (q && xs_match(q, "https://*|http://*")) { |
| @@ -4413,11 +4646,11 @@ int html_get_handler(const xs_dict *req, const char *q_path, | |||
| 4413 | actor_add(actor, actor_obj); | 4646 | actor_add(actor, actor_obj); |
| 4414 | 4647 | ||
| 4415 | /* create a people list with only one element */ | 4648 | /* create a people list with only one element */ |
| 4416 | l = xs_list_append(xs_list_new(), actor); | 4649 | l = xs_list_append(l, actor); |
| 4417 | 4650 | ||
| 4418 | xs *title = xs_fmt(L("Search results for account %s"), q); | 4651 | xs *title = xs_fmt(L("Search results for account %s"), q); |
| 4419 | 4652 | ||
| 4420 | page = html_people_list(&snac, l, title, "wf", NULL); | 4653 | page = html_people_list(&snac, l, title, "wf", NULL, 1); |
| 4421 | } | 4654 | } |
| 4422 | } | 4655 | } |
| 4423 | 4656 | ||
| @@ -4553,6 +4786,37 @@ int html_get_handler(const xs_dict *req, const char *q_path, | |||
| 4553 | } | 4786 | } |
| 4554 | } | 4787 | } |
| 4555 | else | 4788 | else |
| 4789 | if (xs_startswith(p_path, "people/")) { /** a single actor **/ | ||
| 4790 | if (!login(&snac, req)) { | ||
| 4791 | *body = xs_dup(uid); | ||
| 4792 | status = HTTP_STATUS_UNAUTHORIZED; | ||
| 4793 | } | ||
| 4794 | else { | ||
| 4795 | xs *actor_dict = NULL; | ||
| 4796 | const char *actor_id = NULL; | ||
| 4797 | xs *actor = NULL; | ||
| 4798 | xs_list *page_lst = xs_split_n(p_path, "?", 2); | ||
| 4799 | xs *page = xs_str_cat(xs_str_new("/"), xs_list_get(page_lst, 0)); | ||
| 4800 | xs_list *l = xs_split_n(page, "/", 3); | ||
| 4801 | const char *actor_md5 = xs_list_get(l, 2); | ||
| 4802 | |||
| 4803 | if (valid_status(object_get_by_md5(actor_md5, &actor_dict)) && | ||
| 4804 | (actor_id = xs_dict_get(actor_dict, "id")) != NULL && | ||
| 4805 | valid_status(actor_get(actor_id, &actor))) { | ||
| 4806 | int more = 0; | ||
| 4807 | xs *list = timeline_simple_list(&snac, "private", skip, show, &more); | ||
| 4808 | |||
| 4809 | *body = html_people_one(&snac, actor_id, list, skip, show, more, page); | ||
| 4810 | *b_size = strlen(*body); | ||
| 4811 | status = HTTP_STATUS_OK; | ||
| 4812 | } | ||
| 4813 | else { | ||
| 4814 | *body = xs_dup(uid); | ||
| 4815 | status = HTTP_STATUS_NOT_FOUND; | ||
| 4816 | } | ||
| 4817 | } | ||
| 4818 | } | ||
| 4819 | else | ||
| 4556 | if (strcmp(p_path, "notifications") == 0) { /** the list of notifications **/ | 4820 | if (strcmp(p_path, "notifications") == 0) { /** the list of notifications **/ |
| 4557 | if (!login(&snac, req)) { | 4821 | if (!login(&snac, req)) { |
| 4558 | *body = xs_dup(uid); | 4822 | *body = xs_dup(uid); |
| @@ -5665,6 +5929,33 @@ int html_post_handler(const xs_dict *req, const char *q_path, | |||
| 5665 | 5929 | ||
| 5666 | status = HTTP_STATUS_SEE_OTHER; | 5930 | status = HTTP_STATUS_SEE_OTHER; |
| 5667 | } | 5931 | } |
| 5932 | else | ||
| 5933 | if (p_path && strcmp(p_path, "admin/muted-words") == 0) { | ||
| 5934 | const char *words = xs_dict_get(p_vars, "muted_words"); | ||
| 5935 | |||
| 5936 | if (xs_is_string(words)) { | ||
| 5937 | xs *new_words = xs_list_new(); | ||
| 5938 | xs *l = xs_split(words, "\n"); | ||
| 5939 | const char *v; | ||
| 5940 | |||
| 5941 | xs_list_foreach(l, v) { | ||
| 5942 | xs *s1 = xs_strip_i(xs_dup(v)); | ||
| 5943 | s1 = xs_replace_i(s1, " ", ""); | ||
| 5944 | |||
| 5945 | if (*s1 == '\0') | ||
| 5946 | continue; | ||
| 5947 | |||
| 5948 | xs *s2 = xs_utf8_to_lower(s1); | ||
| 5949 | |||
| 5950 | new_words = xs_list_insert_sorted(new_words, s2); | ||
| 5951 | } | ||
| 5952 | |||
| 5953 | snac.config = xs_dict_set(snac.config, "muted_words", new_words); | ||
| 5954 | user_persist(&snac, 0); | ||
| 5955 | } | ||
| 5956 | |||
| 5957 | status = HTTP_STATUS_SEE_OTHER; | ||
| 5958 | } | ||
| 5668 | 5959 | ||
| 5669 | if (status == HTTP_STATUS_SEE_OTHER) { | 5960 | if (status == HTTP_STATUS_SEE_OTHER) { |
| 5670 | const char *hard_redir = xs_dict_get(p_vars, "hard-redir"); | 5961 | const char *hard_redir = xs_dict_get(p_vars, "hard-redir"); |