diff options
Diffstat (limited to 'html.c')
| -rw-r--r-- | html.c | 454 |
1 files changed, 355 insertions, 99 deletions
| @@ -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 | { |
| @@ -181,6 +210,41 @@ xs_str *format_text_with_emoji(snac *user, const char *text, int ems, const char | |||
| 181 | } | 210 | } |
| 182 | 211 | ||
| 183 | 212 | ||
| 213 | xs_str *html_date_label(snac *user, const char *date) | ||
| 214 | { | ||
| 215 | time_t t; | ||
| 216 | |||
| 217 | /* check if a user has actually set a timezone */ | ||
| 218 | if (user != NULL && xs_dict_get(user->config, "tz") != NULL && | ||
| 219 | (t = xs_parse_iso_date(date, 0)) != 0) { | ||
| 220 | t += xs_tz_offset(user->tz); | ||
| 221 | |||
| 222 | time_t today = time(NULL); | ||
| 223 | if (today - t >= 3600 * 24 * 30 * 6) { | ||
| 224 | /* more than half a year ago */ | ||
| 225 | return xs_str_utctime(t, "%Y-%m-%d %H:%M"); | ||
| 226 | } | ||
| 227 | |||
| 228 | xs_str *date = xs_str_utctime(t, "%b%d %H:%M"); | ||
| 229 | |||
| 230 | struct tm tm; | ||
| 231 | localtime_r(&t, &tm); | ||
| 232 | |||
| 233 | if (tm.tm_mon >= 0 && tm.tm_mon <= 11) { | ||
| 234 | /* copy the 3letter translated month name */ | ||
| 235 | const char *m = L(months[tm.tm_mon]); | ||
| 236 | |||
| 237 | if (xs_is_string(m)) | ||
| 238 | date = xs_replace_i(date, months[tm.tm_mon], m); | ||
| 239 | } | ||
| 240 | |||
| 241 | return date; | ||
| 242 | } | ||
| 243 | |||
| 244 | return xs_crop_i(xs_dup(date), 0, 10); | ||
| 245 | } | ||
| 246 | |||
| 247 | |||
| 184 | xs_html *html_actor_icon(snac *user, xs_dict *actor, const char *date, | 248 | xs_html *html_actor_icon(snac *user, xs_dict *actor, const char *date, |
| 185 | const char *udate, const char *url, int scope, | 249 | const char *udate, const char *url, int scope, |
| 186 | int in_people, const char *proxy, const char *lang, | 250 | int in_people, const char *proxy, const char *lang, |
| @@ -194,6 +258,7 @@ xs_html *html_actor_icon(snac *user, xs_dict *actor, const char *date, | |||
| 194 | int fwer = 0; | 258 | int fwer = 0; |
| 195 | 259 | ||
| 196 | xs *name = actor_name(actor, proxy); | 260 | xs *name = actor_name(actor, proxy); |
| 261 | xs *pronouns = actor_pronouns(actor); | ||
| 197 | 262 | ||
| 198 | /* get the avatar */ | 263 | /* get the avatar */ |
| 199 | if ((v = xs_dict_get(actor, "icon")) != NULL) { | 264 | if ((v = xs_dict_get(actor, "icon")) != NULL) { |
| @@ -209,6 +274,14 @@ xs_html *html_actor_icon(snac *user, xs_dict *actor, const char *date, | |||
| 209 | avatar = xs_fmt("data:image/png;base64, %s", default_avatar_base64()); | 274 | avatar = xs_fmt("data:image/png;base64, %s", default_avatar_base64()); |
| 210 | 275 | ||
| 211 | const char *actor_id = xs_dict_get(actor, "id"); | 276 | const char *actor_id = xs_dict_get(actor, "id"); |
| 277 | const char *html_url = xs_dict_get_def(actor, "url", actor_id); | ||
| 278 | |||
| 279 | if (xs_is_list(html_url)) | ||
| 280 | html_url = xs_list_get(html_url, 0); | ||
| 281 | |||
| 282 | if (!xs_is_string(html_url)) | ||
| 283 | html_url = actor_id; | ||
| 284 | |||
| 212 | xs *href = NULL; | 285 | xs *href = NULL; |
| 213 | 286 | ||
| 214 | if (user) { | 287 | if (user) { |
| @@ -217,16 +290,27 @@ xs_html *html_actor_icon(snac *user, xs_dict *actor, const char *date, | |||
| 217 | } | 290 | } |
| 218 | 291 | ||
| 219 | if (user && !in_people) { | 292 | if (user && !in_people) { |
| 220 | /* if this actor is a follower or being followed, create an | 293 | xs *md5 = xs_md5_hex(actor_id, strlen(actor_id)); |
| 221 | anchored link to the people page instead of the actor url */ | 294 | href = xs_fmt("%s/people/%s", user->actor, md5); |
| 222 | if (fwer || fwing) { | ||
| 223 | xs *md5 = xs_md5_hex(actor_id, strlen(actor_id)); | ||
| 224 | href = xs_fmt("%s/people/%s", user->actor, md5); | ||
| 225 | } | ||
| 226 | } | 295 | } |
| 227 | 296 | ||
| 228 | if (href == NULL) | 297 | if (href == NULL) |
| 229 | href = xs_dup(actor_id); | 298 | href = xs_dup(html_url); |
| 299 | |||
| 300 | xs_html *name_link = xs_html_tag("a", | ||
| 301 | xs_html_attr("href", href), | ||
| 302 | xs_html_attr("class", "p-author h-card snac-author"), | ||
| 303 | xs_html_raw(name)); /* name is already html-escaped */ | ||
| 304 | |||
| 305 | if (*pronouns) { | ||
| 306 | xs_html_add(name_link, | ||
| 307 | xs_html_text(" ["), | ||
| 308 | xs_html_tag("span", | ||
| 309 | xs_html_attr("class", "snac-pronouns"), | ||
| 310 | xs_html_attr("title", "user's pronouns"), | ||
| 311 | xs_html_raw(pronouns)), | ||
| 312 | xs_html_text("]")); | ||
| 313 | } | ||
| 230 | 314 | ||
| 231 | xs_html_add(actor_icon, | 315 | xs_html_add(actor_icon, |
| 232 | xs_html_sctag("img", | 316 | xs_html_sctag("img", |
| @@ -234,10 +318,7 @@ xs_html *html_actor_icon(snac *user, xs_dict *actor, const char *date, | |||
| 234 | xs_html_attr("class", "snac-avatar"), | 318 | xs_html_attr("class", "snac-avatar"), |
| 235 | xs_html_attr("src", avatar), | 319 | xs_html_attr("src", avatar), |
| 236 | xs_html_attr("alt", "[?]")), | 320 | xs_html_attr("alt", "[?]")), |
| 237 | xs_html_tag("a", | 321 | 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 | 322 | ||
| 242 | if (!xs_is_null(url)) { | 323 | if (!xs_is_null(url)) { |
| 243 | xs *md5 = xs_md5_hex(url, strlen(url)); | 324 | xs *md5 = xs_md5_hex(url, strlen(url)); |
| @@ -250,6 +331,7 @@ xs_html *html_actor_icon(snac *user, xs_dict *actor, const char *date, | |||
| 250 | xs_html_text("»"))); | 331 | xs_html_text("»"))); |
| 251 | } | 332 | } |
| 252 | 333 | ||
| 334 | |||
| 253 | if (strcmp(xs_dict_get(actor, "type"), "Service") == 0) { | 335 | if (strcmp(xs_dict_get(actor, "type"), "Service") == 0) { |
| 254 | xs_html_add(actor_icon, | 336 | xs_html_add(actor_icon, |
| 255 | xs_html_text(" "), | 337 | xs_html_text(" "), |
| @@ -318,11 +400,11 @@ xs_html *html_actor_icon(snac *user, xs_dict *actor, const char *date, | |||
| 318 | xs_html_raw(" ")); | 400 | xs_html_raw(" ")); |
| 319 | } | 401 | } |
| 320 | else { | 402 | else { |
| 321 | xs *date_label = xs_crop_i(xs_dup(date), 0, 10); | 403 | xs *date_label = html_date_label(user, date); |
| 322 | xs *date_title = xs_dup(date); | 404 | xs *date_title = xs_dup(date); |
| 323 | 405 | ||
| 324 | if (!xs_is_null(udate)) { | 406 | if (!xs_is_null(udate)) { |
| 325 | xs *sd = xs_crop_i(xs_dup(udate), 0, 10); | 407 | xs *sd = html_date_label(user, udate); |
| 326 | 408 | ||
| 327 | date_label = xs_str_cat(date_label, " / ", sd); | 409 | date_label = xs_str_cat(date_label, " / ", sd); |
| 328 | 410 | ||
| @@ -379,7 +461,7 @@ xs_html *html_actor_icon(snac *user, xs_dict *actor, const char *date, | |||
| 379 | xs_html_add(actor_icon, | 461 | xs_html_add(actor_icon, |
| 380 | xs_html_sctag("br", NULL), | 462 | xs_html_sctag("br", NULL), |
| 381 | xs_html_tag("a", | 463 | xs_html_tag("a", |
| 382 | xs_html_attr("href", xs_dict_get(actor, "id")), | 464 | xs_html_attr("href", html_url), |
| 383 | xs_html_attr("class", "p-author-tag h-card snac-author-tag"), | 465 | xs_html_attr("class", "p-author-tag h-card snac-author-tag"), |
| 384 | xs_html_text(user))); | 466 | xs_html_text(user))); |
| 385 | } | 467 | } |
| @@ -401,9 +483,14 @@ xs_html *html_msg_icon(snac *user, const char *actor_id, const xs_dict *msg, | |||
| 401 | const char *type = xs_dict_get(msg, "type"); | 483 | const char *type = xs_dict_get(msg, "type"); |
| 402 | const int scope = get_msg_visibility(msg); | 484 | const int scope = get_msg_visibility(msg); |
| 403 | 485 | ||
| 404 | if (xs_match(type, POSTLIKE_OBJECT_TYPE)) | 486 | if (xs_match(type, POSTLIKE_OBJECT_TYPE)) { |
| 405 | url = xs_dict_get(msg, "id"); | 487 | url = xs_dict_get(msg, "url"); |
| 488 | if (xs_is_list(url)) | ||
| 489 | url = xs_list_get(url, 0); | ||
| 406 | 490 | ||
| 491 | if (!xs_is_string(url)) | ||
| 492 | url = xs_dict_get(msg, "id"); | ||
| 493 | } | ||
| 407 | 494 | ||
| 408 | date = xs_dict_get(msg, "published"); | 495 | date = xs_dict_get(msg, "published"); |
| 409 | udate = xs_dict_get(msg, "updated"); | 496 | udate = xs_dict_get(msg, "updated"); |
| @@ -427,7 +514,7 @@ void html_note_render_visibility(snac* user, xs_html *form, const int scope) | |||
| 427 | 514 | ||
| 428 | xs_html *paragraph = xs_html_tag("p", xs_html_text(L("Visibility: "))); | 515 | xs_html *paragraph = xs_html_tag("p", xs_html_text(L("Visibility: "))); |
| 429 | const int* to_render = scopes[scope]; | 516 | const int* to_render = scopes[scope]; |
| 430 | for( int i = 0; to_render[i] != -1; i++ ){ | 517 | for( int i = 0; to_render[i] != -1; i++ ) { |
| 431 | const int scope_i = to_render[i]; | 518 | const int scope_i = to_render[i]; |
| 432 | const char* value = scopes_tags[scope_i]; | 519 | const char* value = scopes_tags[scope_i]; |
| 433 | const char* name = scopes_names[scope_i]; | 520 | const char* name = scopes_names[scope_i]; |
| @@ -735,13 +822,18 @@ xs_html *html_note(snac *user, const char *summary, | |||
| 735 | 822 | ||
| 736 | /* add poll controls */ | 823 | /* add poll controls */ |
| 737 | if (poll) { | 824 | if (poll) { |
| 825 | const xs_number *max_options = xs_dict_get(srv_config, "max_poll_options"); | ||
| 826 | xs *poll_limit_str = xs_dup(L("Poll options (one per line, up to 8):")); | ||
| 827 | if (max_options != NULL) | ||
| 828 | poll_limit_str = xs_replace_i(poll_limit_str, "8", xs_number_str(max_options)); | ||
| 829 | |||
| 738 | xs_html_add(form, | 830 | xs_html_add(form, |
| 739 | xs_html_tag("p", NULL), | 831 | xs_html_tag("p", NULL), |
| 740 | xs_html_tag("details", | 832 | xs_html_tag("details", |
| 741 | xs_html_tag("summary", | 833 | xs_html_tag("summary", |
| 742 | xs_html_text(L("Poll..."))), | 834 | xs_html_text(L("Poll..."))), |
| 743 | xs_html_tag("p", | 835 | xs_html_tag("p", |
| 744 | xs_html_text(L("Poll options (one per line, up to 8):")), | 836 | xs_html_text(poll_limit_str), |
| 745 | xs_html_sctag("br", NULL), | 837 | xs_html_sctag("br", NULL), |
| 746 | xs_html_tag("textarea", | 838 | xs_html_tag("textarea", |
| 747 | xs_html_attr("class", "snac-textarea"), | 839 | xs_html_attr("class", "snac-textarea"), |
| @@ -769,7 +861,13 @@ xs_html *html_note(snac *user, const char *summary, | |||
| 769 | xs_html_text(L("End in 1 hour"))), | 861 | xs_html_text(L("End in 1 hour"))), |
| 770 | xs_html_tag("option", | 862 | xs_html_tag("option", |
| 771 | xs_html_attr("value", "86400"), | 863 | xs_html_attr("value", "86400"), |
| 772 | xs_html_text(L("End in 1 day")))))); | 864 | xs_html_text(L("End in 1 day"))), |
| 865 | xs_html_tag("option", | ||
| 866 | xs_html_attr("value", "259200"), | ||
| 867 | xs_html_text(L("End in 3 days"))), | ||
| 868 | xs_html_tag("option", | ||
| 869 | xs_html_attr("value", "31536000"), | ||
| 870 | xs_html_text(L("End in 1 year")))))); | ||
| 773 | } | 871 | } |
| 774 | 872 | ||
| 775 | xs_html_add(form, | 873 | xs_html_add(form, |
| @@ -1346,6 +1444,20 @@ static xs_html *html_user_body(snac *user, int read_only) | |||
| 1346 | } | 1444 | } |
| 1347 | 1445 | ||
| 1348 | 1446 | ||
| 1447 | xs_html *html_checkbox(const char *form_name, const char *label, int flag) | ||
| 1448 | /* helper for checkbox rendering */ | ||
| 1449 | { | ||
| 1450 | return xs_html_tag("p", | ||
| 1451 | xs_html_sctag("input", | ||
| 1452 | xs_html_attr("type", "checkbox"), | ||
| 1453 | xs_html_attr("name", form_name), | ||
| 1454 | xs_html_attr(flag ? "checked" : "", NULL)), | ||
| 1455 | xs_html_tag("label", | ||
| 1456 | xs_html_attr("for", form_name), | ||
| 1457 | xs_html_text(label))); | ||
| 1458 | } | ||
| 1459 | |||
| 1460 | |||
| 1349 | xs_html *html_top_controls(snac *user) | 1461 | xs_html *html_top_controls(snac *user) |
| 1350 | /* generates the top controls */ | 1462 | /* generates the top controls */ |
| 1351 | { | 1463 | { |
| @@ -1669,70 +1781,21 @@ xs_html *html_top_controls(snac *user) | |||
| 1669 | xs_html_attr("type", "number"), | 1781 | xs_html_attr("type", "number"), |
| 1670 | xs_html_attr("name", "purge_days"), | 1782 | xs_html_attr("name", "purge_days"), |
| 1671 | xs_html_attr("value", purge_days))), | 1783 | xs_html_attr("value", purge_days))), |
| 1672 | xs_html_tag("p", | 1784 | html_checkbox("drop_dm_from_unknown", L("Drop direct messages from people you don't follow"), |
| 1673 | xs_html_sctag("input", | 1785 | xs_is_true(d_dm_f_u)), |
| 1674 | xs_html_attr("type", "checkbox"), | 1786 | html_checkbox("bot", L("This account is a bot"), |
| 1675 | xs_html_attr("name", "drop_dm_from_unknown"), | 1787 | xs_is_true(bot)), |
| 1676 | xs_html_attr("id", "drop_dm_from_unknown"), | 1788 | html_checkbox("auto_boost", L("Auto-boost all mentions to this account"), |
| 1677 | xs_html_attr(xs_type(d_dm_f_u) == XSTYPE_TRUE ? "checked" : "", NULL)), | 1789 | xs_is_true(auto_boost)), |
| 1678 | xs_html_tag("label", | 1790 | html_checkbox("private", L("This account is private " |
| 1679 | xs_html_attr("for", "drop_dm_from_unknown"), | 1791 | "(posts are not shown through the web)"), |
| 1680 | xs_html_text(L("Drop direct messages from people you don't follow")))), | 1792 | xs_is_true(a_private)), |
| 1681 | xs_html_tag("p", | 1793 | html_checkbox("collapse_threads", L("Collapse top threads by default"), |
| 1682 | xs_html_sctag("input", | 1794 | xs_is_true(coll_thrds)), |
| 1683 | xs_html_attr("type", "checkbox"), | 1795 | html_checkbox("approve_followers", L("Follow requests must be approved"), |
| 1684 | xs_html_attr("name", "bot"), | 1796 | xs_is_true(pending)), |
| 1685 | xs_html_attr("id", "bot"), | 1797 | html_checkbox("show_contact_metrics", L("Publish follower and following metrics"), |
| 1686 | xs_html_attr(xs_type(bot) == XSTYPE_TRUE ? "checked" : "", NULL)), | 1798 | xs_is_true(show_foll)), |
| 1687 | xs_html_tag("label", | ||
| 1688 | xs_html_attr("for", "bot"), | ||
| 1689 | xs_html_text(L("This account is a bot")))), | ||
| 1690 | xs_html_tag("p", | ||
| 1691 | xs_html_sctag("input", | ||
| 1692 | xs_html_attr("type", "checkbox"), | ||
| 1693 | xs_html_attr("name", "auto_boost"), | ||
| 1694 | xs_html_attr("id", "auto_boost"), | ||
| 1695 | xs_html_attr(xs_is_true(auto_boost) ? "checked" : "", NULL)), | ||
| 1696 | xs_html_tag("label", | ||
| 1697 | xs_html_attr("for", "auto_boost"), | ||
| 1698 | xs_html_text(L("Auto-boost all mentions to this account")))), | ||
| 1699 | xs_html_tag("p", | ||
| 1700 | xs_html_sctag("input", | ||
| 1701 | xs_html_attr("type", "checkbox"), | ||
| 1702 | xs_html_attr("name", "private"), | ||
| 1703 | xs_html_attr("id", "private"), | ||
| 1704 | xs_html_attr(xs_type(a_private) == XSTYPE_TRUE ? "checked" : "", NULL)), | ||
| 1705 | xs_html_tag("label", | ||
| 1706 | xs_html_attr("for", "private"), | ||
| 1707 | xs_html_text(L("This account is private " | ||
| 1708 | "(posts are not shown through the web)")))), | ||
| 1709 | xs_html_tag("p", | ||
| 1710 | xs_html_sctag("input", | ||
| 1711 | xs_html_attr("type", "checkbox"), | ||
| 1712 | xs_html_attr("name", "collapse_threads"), | ||
| 1713 | xs_html_attr("id", "collapse_threads"), | ||
| 1714 | xs_html_attr(xs_is_true(coll_thrds) ? "checked" : "", NULL)), | ||
| 1715 | xs_html_tag("label", | ||
| 1716 | xs_html_attr("for", "collapse_threads"), | ||
| 1717 | xs_html_text(L("Collapse top threads by default")))), | ||
| 1718 | xs_html_tag("p", | ||
| 1719 | xs_html_sctag("input", | ||
| 1720 | xs_html_attr("type", "checkbox"), | ||
| 1721 | xs_html_attr("name", "approve_followers"), | ||
| 1722 | xs_html_attr("id", "approve_followers"), | ||
| 1723 | xs_html_attr(xs_is_true(pending) ? "checked" : "", NULL)), | ||
| 1724 | xs_html_tag("label", | ||
| 1725 | xs_html_attr("for", "approve_followers"), | ||
| 1726 | xs_html_text(L("Follow requests must be approved")))), | ||
| 1727 | xs_html_tag("p", | ||
| 1728 | xs_html_sctag("input", | ||
| 1729 | xs_html_attr("type", "checkbox"), | ||
| 1730 | xs_html_attr("name", "show_contact_metrics"), | ||
| 1731 | xs_html_attr("id", "show_contact_metrics"), | ||
| 1732 | xs_html_attr(xs_is_true(show_foll) ? "checked" : "", NULL)), | ||
| 1733 | xs_html_tag("label", | ||
| 1734 | xs_html_attr("for", "show_contact_metrics"), | ||
| 1735 | xs_html_text(L("Publish follower and following metrics")))), | ||
| 1736 | xs_html_tag("p", | 1799 | xs_html_tag("p", |
| 1737 | xs_html_text(L("Current location:")), | 1800 | xs_html_text(L("Current location:")), |
| 1738 | xs_html_sctag("br", NULL), | 1801 | xs_html_sctag("br", NULL), |
| @@ -1864,6 +1927,38 @@ xs_html *html_top_controls(snac *user) | |||
| 1864 | xs_html_attr("class", "button"), | 1927 | xs_html_attr("class", "button"), |
| 1865 | xs_html_attr("value", L("Update hashtags"))))))); | 1928 | xs_html_attr("value", L("Update hashtags"))))))); |
| 1866 | 1929 | ||
| 1930 | xs *muted_words_action = xs_fmt("%s/admin/muted-words", user->actor); | ||
| 1931 | xs *muted_words = xs_join(xs_dict_get_def(user->config, | ||
| 1932 | "muted_words", xs_stock(XSTYPE_LIST)), "\n"); | ||
| 1933 | |||
| 1934 | xs_html_add(top_controls, | ||
| 1935 | xs_html_tag("details", | ||
| 1936 | xs_html_tag("summary", | ||
| 1937 | xs_html_text(L("Muted words..."))), | ||
| 1938 | xs_html_tag("p", | ||
| 1939 | xs_html_text(L("One word per line, partial matches count"))), | ||
| 1940 | xs_html_tag("div", | ||
| 1941 | xs_html_attr("class", "snac-muted-words"), | ||
| 1942 | xs_html_tag("form", | ||
| 1943 | xs_html_attr("autocomplete", "off"), | ||
| 1944 | xs_html_attr("method", "post"), | ||
| 1945 | xs_html_attr("action", muted_words_action), | ||
| 1946 | xs_html_attr("enctype", "multipart/form-data"), | ||
| 1947 | |||
| 1948 | xs_html_tag("textarea", | ||
| 1949 | xs_html_attr("name", "muted_words"), | ||
| 1950 | xs_html_attr("cols", "40"), | ||
| 1951 | xs_html_attr("rows", "4"), | ||
| 1952 | xs_html_attr("placeholder", "nascar\nsuperbowl\nFIFA"), | ||
| 1953 | xs_html_text(muted_words)), | ||
| 1954 | |||
| 1955 | xs_html_tag("br", NULL), | ||
| 1956 | |||
| 1957 | xs_html_sctag("input", | ||
| 1958 | xs_html_attr("type", "submit"), | ||
| 1959 | xs_html_attr("class", "button"), | ||
| 1960 | xs_html_attr("value", L("Update muted words"))))))); | ||
| 1961 | |||
| 1867 | return top_controls; | 1962 | return top_controls; |
| 1868 | } | 1963 | } |
| 1869 | 1964 | ||
| @@ -2144,6 +2239,29 @@ xs_html *html_entry_controls(snac *user, const char *actor, | |||
| 2144 | } | 2239 | } |
| 2145 | 2240 | ||
| 2146 | 2241 | ||
| 2242 | static const xs_str* words_in_content(const xs_list *words, const xs_val *content) | ||
| 2243 | /* returns a word that matches any of the words in content */ | ||
| 2244 | { | ||
| 2245 | if (!xs_is_list(words) || !xs_is_string(content)) { | ||
| 2246 | return NULL; | ||
| 2247 | } | ||
| 2248 | xs *c = xs_split(content, " "); | ||
| 2249 | xs *sc = xs_list_sort(c, NULL); | ||
| 2250 | |||
| 2251 | const xs_str *wv; | ||
| 2252 | const xs_str *cv; | ||
| 2253 | xs_list_foreach(words, wv) { | ||
| 2254 | xs_list_foreach(sc, cv) { | ||
| 2255 | xs_tolower_i((xs_str*)cv); | ||
| 2256 | if(xs_str_in(cv, wv) != -1) | ||
| 2257 | return wv; | ||
| 2258 | } | ||
| 2259 | } | ||
| 2260 | |||
| 2261 | return NULL; | ||
| 2262 | } | ||
| 2263 | |||
| 2264 | |||
| 2147 | xs_html *html_entry(snac *user, xs_dict *msg, int read_only, | 2265 | xs_html *html_entry(snac *user, xs_dict *msg, int read_only, |
| 2148 | int level, const char *md5, int hide_children) | 2266 | int level, const char *md5, int hide_children) |
| 2149 | { | 2267 | { |
| @@ -2438,6 +2556,17 @@ xs_html *html_entry(snac *user, xs_dict *msg, int read_only, | |||
| 2438 | xs_html_text(v), | 2556 | xs_html_text(v), |
| 2439 | xs_html_text(L(" [SENSITIVE CONTENT]")))); | 2557 | xs_html_text(L(" [SENSITIVE CONTENT]")))); |
| 2440 | } | 2558 | } |
| 2559 | else | ||
| 2560 | if (user && | ||
| 2561 | /* muted_words is all lowercase and sorted for performance */ | ||
| 2562 | (v = words_in_content(xs_dict_get(user->config, "muted_words"), | ||
| 2563 | xs_dict_get(msg, "content"))) != NULL) { | ||
| 2564 | snac_debug(user, 1, xs_fmt("word %s muted by user preferences: %s", v, id)); | ||
| 2565 | snac_content = xs_html_tag("details", | ||
| 2566 | xs_html_tag("summary", | ||
| 2567 | xs_html_text(L("Muted: ")), | ||
| 2568 | xs_html_text(v))); | ||
| 2569 | } | ||
| 2441 | else { | 2570 | else { |
| 2442 | snac_content = xs_html_tag("div", NULL); | 2571 | snac_content = xs_html_tag("div", NULL); |
| 2443 | } | 2572 | } |
| @@ -2675,7 +2804,7 @@ xs_html *html_entry(snac *user, xs_dict *msg, int read_only, | |||
| 2675 | xs_html_add(snac_content, | 2804 | xs_html_add(snac_content, |
| 2676 | xs_html_tag("blockquote", | 2805 | xs_html_tag("blockquote", |
| 2677 | xs_html_attr("class", "snac-quoted-post"), | 2806 | xs_html_attr("class", "snac-quoted-post"), |
| 2678 | html_entry(user, quoted_post, 1, 1, NULL, 1))); | 2807 | html_entry(user, quoted_post, 1, level + 1, NULL, 1))); |
| 2679 | } | 2808 | } |
| 2680 | else | 2809 | else |
| 2681 | if (user) | 2810 | if (user) |
| @@ -3491,7 +3620,7 @@ xs_str *html_timeline(snac *user, const xs_list *list, int read_only, | |||
| 3491 | continue; | 3620 | continue; |
| 3492 | 3621 | ||
| 3493 | const int scope = get_msg_visibility(msg); | 3622 | const int scope = get_msg_visibility(msg); |
| 3494 | if (user != NULL && scope != SCOPE_PUBLIC){ | 3623 | if (user != NULL && scope != SCOPE_PUBLIC && !is_msg_mine(user, xs_dict_get(msg, "id"))) { |
| 3495 | /* is this message a non-public reply? */ | 3624 | /* is this message a non-public reply? */ |
| 3496 | const char *irt = get_in_reply_to(msg); | 3625 | const char *irt = get_in_reply_to(msg); |
| 3497 | 3626 | ||
| @@ -3508,13 +3637,11 @@ xs_str *html_timeline(snac *user, const xs_list *list, int read_only, | |||
| 3508 | } | 3637 | } |
| 3509 | } | 3638 | } |
| 3510 | /* hide non-public posts from /instance view */ | 3639 | /* hide non-public posts from /instance view */ |
| 3511 | if (page != NULL && strcmp(page, "/instance") == 0 && scope != SCOPE_PUBLIC){ | 3640 | if (page != NULL && strcmp(page, "/instance") == 0 && scope != SCOPE_PUBLIC) |
| 3512 | continue; | 3641 | continue; |
| 3513 | } | ||
| 3514 | /* hide non-public posts viewed from outside */ | 3642 | /* hide non-public posts viewed from outside */ |
| 3515 | if (read_only && (scope != SCOPE_PUBLIC && scope != SCOPE_UNLISTED)) { | 3643 | if (read_only && (scope != SCOPE_PUBLIC && scope != SCOPE_UNLISTED)) |
| 3516 | continue; | 3644 | continue; |
| 3517 | } | ||
| 3518 | 3645 | ||
| 3519 | xs_html *entry = html_entry(user, msg, read_only, 0, v, (user && !hide_children) ? 0 : 1); | 3646 | xs_html *entry = html_entry(user, msg, read_only, 0, v, (user && !hide_children) ? 0 : 1); |
| 3520 | 3647 | ||
| @@ -3597,13 +3724,19 @@ xs_str *html_timeline(snac *user, const xs_list *list, int read_only, | |||
| 3597 | } | 3724 | } |
| 3598 | 3725 | ||
| 3599 | 3726 | ||
| 3600 | xs_html *html_people_list(snac *user, xs_list *list, const char *header, const char *t, const char *proxy) | 3727 | xs_html *html_people_list(snac *user, xs_list *list, const char *header, const char *t, const char *proxy, int count) |
| 3601 | { | 3728 | { |
| 3602 | xs_html *snac_posts; | 3729 | xs_html *snac_posts; |
| 3730 | xs *header_cnt; | ||
| 3731 | if (count > -1) | ||
| 3732 | header_cnt = xs_fmt("%s - %d\n", header, count); | ||
| 3733 | else | ||
| 3734 | header_cnt = xs_fmt("%s\n", header); | ||
| 3735 | |||
| 3603 | xs_html *people = xs_html_tag("div", | 3736 | xs_html *people = xs_html_tag("div", |
| 3604 | xs_html_tag("h2", | 3737 | xs_html_tag("h2", |
| 3605 | xs_html_attr("class", "snac-header"), | 3738 | xs_html_attr("class", "snac-header"), |
| 3606 | xs_html_text(header)), | 3739 | xs_html_text(header_cnt)), |
| 3607 | snac_posts = xs_html_tag("details", | 3740 | snac_posts = xs_html_tag("details", |
| 3608 | xs_html_attr("open", NULL), | 3741 | xs_html_attr("open", NULL), |
| 3609 | xs_html_tag("summary", | 3742 | xs_html_tag("summary", |
| @@ -3844,12 +3977,12 @@ xs_str *html_people(snac *user) | |||
| 3844 | 3977 | ||
| 3845 | if (xs_list_len(pending) || xs_is_true(xs_dict_get(user->config, "approve_followers"))) { | 3978 | if (xs_list_len(pending) || xs_is_true(xs_dict_get(user->config, "approve_followers"))) { |
| 3846 | xs_html_add(lists, | 3979 | xs_html_add(lists, |
| 3847 | html_people_list(user, pending, L("Pending follow confirmations"), "p", proxy)); | 3980 | html_people_list(user, pending, L("Pending follow confirmations"), "p", proxy, xs_list_len(pending))); |
| 3848 | } | 3981 | } |
| 3849 | 3982 | ||
| 3850 | xs_html_add(lists, | 3983 | xs_html_add(lists, |
| 3851 | html_people_list(user, wing, L("People you follow"), "i", proxy), | 3984 | html_people_list(user, wing, L("People you follow"), "i", proxy, following_list_len(user)), |
| 3852 | html_people_list(user, wers, L("People that follow you"), "e", proxy)); | 3985 | html_people_list(user, wers, L("People that follow you"), "e", proxy, xs_list_len(wers))); |
| 3853 | 3986 | ||
| 3854 | xs_html *html = xs_html_tag("html", | 3987 | xs_html *html = xs_html_tag("html", |
| 3855 | html_user_head(user, NULL, NULL), | 3988 | html_user_head(user, NULL, NULL), |
| @@ -3869,6 +4002,8 @@ xs_str *html_people_one(snac *user, const char *actor, const xs_list *list, | |||
| 3869 | xs_list *p = (xs_list *)list; | 4002 | xs_list *p = (xs_list *)list; |
| 3870 | const char *v; | 4003 | const char *v; |
| 3871 | 4004 | ||
| 4005 | enqueue_actor_refresh(user, actor, 0); | ||
| 4006 | |||
| 3872 | if (xs_is_true(xs_dict_get(srv_config, "proxy_media"))) | 4007 | if (xs_is_true(xs_dict_get(srv_config, "proxy_media"))) |
| 3873 | proxy = user->actor; | 4008 | proxy = user->actor; |
| 3874 | 4009 | ||
| @@ -3880,7 +4015,7 @@ xs_str *html_people_one(snac *user, const char *actor, const xs_list *list, | |||
| 3880 | xs *foll = xs_list_append(xs_list_new(), actor); | 4015 | xs *foll = xs_list_append(xs_list_new(), actor); |
| 3881 | 4016 | ||
| 3882 | xs_html_add(lists, | 4017 | xs_html_add(lists, |
| 3883 | html_people_list(user, foll, L("Contact's posts"), "p", proxy)); | 4018 | html_people_list(user, foll, L("Contact's posts"), "p", proxy, -1)); |
| 3884 | 4019 | ||
| 3885 | xs_html_add(body, lists); | 4020 | xs_html_add(body, lists); |
| 3886 | 4021 | ||
| @@ -3963,6 +4098,32 @@ xs_str *html_people_one(snac *user, const char *actor, const xs_list *list, | |||
| 3963 | return xs_html_render_s(html, "<!DOCTYPE html>\n"); | 4098 | return xs_html_render_s(html, "<!DOCTYPE html>\n"); |
| 3964 | } | 4099 | } |
| 3965 | 4100 | ||
| 4101 | void notify_filter(snac *user, const xs_dict *p_vars) | ||
| 4102 | /* sets filter for notifications */ | ||
| 4103 | { | ||
| 4104 | const char *v; | ||
| 4105 | int likes_on = (v = xs_dict_get(p_vars, "likes_on")) ? strcmp(v, "on") == 0 : 0; | ||
| 4106 | int reacts_on = (v = xs_dict_get(p_vars, "reacts_on")) ? strcmp(v, "on") == 0 : 0; | ||
| 4107 | int ments_on = (v = xs_dict_get(p_vars, "mentions_on")) ? strcmp(v, "on") == 0 : 0; | ||
| 4108 | int ann_on = (v = xs_dict_get(p_vars, "announces_on")) ? strcmp(v, "on") == 0 : 0; | ||
| 4109 | int foll_on = (v = xs_dict_get(p_vars, "follows_on")) ? strcmp(v, "on") == 0 : 0; | ||
| 4110 | int unfoll_on = (v = xs_dict_get(p_vars, "unfollows_on")) ? strcmp(v, "on") == 0 : 0; | ||
| 4111 | int folreq_on = (v = xs_dict_get(p_vars, "folreqs_on")) ? strcmp(v, "on") == 0 : 0; | ||
| 4112 | int blocks_on = (v = xs_dict_get(p_vars, "blocks_on")) ? strcmp(v, "on") == 0 : 0; | ||
| 4113 | int polls_on = (v = xs_dict_get(p_vars, "polls_on")) ? strcmp(v, "on") == 0 : 0; | ||
| 4114 | xs_dict *filter = xs_dict_new(); | ||
| 4115 | filter = xs_dict_set(filter, "likes", xs_stock(likes_on ? XSTYPE_TRUE : XSTYPE_FALSE)); | ||
| 4116 | filter = xs_dict_set(filter, "reacts", xs_stock(reacts_on ? XSTYPE_TRUE : XSTYPE_FALSE)); | ||
| 4117 | filter = xs_dict_set(filter, "mentions", xs_stock(ments_on ? XSTYPE_TRUE : XSTYPE_FALSE)); | ||
| 4118 | filter = xs_dict_set(filter, "announces", xs_stock(ann_on ? XSTYPE_TRUE : XSTYPE_FALSE)); | ||
| 4119 | filter = xs_dict_set(filter, "follows", xs_stock(foll_on ? XSTYPE_TRUE : XSTYPE_FALSE)); | ||
| 4120 | filter = xs_dict_set(filter, "unfollows", xs_stock(unfoll_on ? XSTYPE_TRUE : XSTYPE_FALSE)); | ||
| 4121 | filter = xs_dict_set(filter, "folreqs", xs_stock(folreq_on ? XSTYPE_TRUE : XSTYPE_FALSE)); | ||
| 4122 | filter = xs_dict_set(filter, "blocks", xs_stock(blocks_on ? XSTYPE_TRUE : XSTYPE_FALSE)); | ||
| 4123 | filter = xs_dict_set(filter, "polls", xs_stock(polls_on ? XSTYPE_TRUE : XSTYPE_FALSE)); | ||
| 4124 | user->config = xs_dict_set(user->config, "notify_filter", filter); | ||
| 4125 | } | ||
| 4126 | |||
| 3966 | xs_str *html_notifications(snac *user, int skip, int show) | 4127 | xs_str *html_notifications(snac *user, int skip, int show) |
| 3967 | { | 4128 | { |
| 3968 | const char *proxy = NULL; | 4129 | const char *proxy = NULL; |
| @@ -3970,15 +4131,66 @@ xs_str *html_notifications(snac *user, int skip, int show) | |||
| 3970 | if (xs_is_true(xs_dict_get(srv_config, "proxy_media"))) | 4131 | if (xs_is_true(xs_dict_get(srv_config, "proxy_media"))) |
| 3971 | proxy = user->actor; | 4132 | proxy = user->actor; |
| 3972 | 4133 | ||
| 3973 | xs *n_list = notify_list(user, skip, show); | 4134 | xs *n_list_unfilt = notify_list(user, skip, show); |
| 3974 | xs *n_time = notify_check_time(user, 0); | 4135 | xs *n_time = notify_check_time(user, 0); |
| 3975 | 4136 | ||
| 3976 | xs_html *body = html_user_body(user, 0); | 4137 | xs_html *body = html_user_body(user, 0); |
| 4138 | const xs_dict *n_filter = xs_dict_get(user->config, "notify_filter"); | ||
| 4139 | if (!n_filter) { | ||
| 4140 | user->config = xs_dict_set(user->config, "notify_filter", xs_dict_new()); | ||
| 4141 | n_filter = xs_dict_get(user->config, "notify_filter"); | ||
| 4142 | } | ||
| 4143 | xs *n_list = notify_filter_list(user, n_list_unfilt); | ||
| 4144 | /* all filters are true by default */ | ||
| 4145 | const xs_val *n_def = xs_stock( XSTYPE_TRUE ); | ||
| 4146 | int n_likes_on = xs_is_true(xs_dict_get_def(n_filter, "likes", n_def)); | ||
| 4147 | int n_reacts_on = xs_is_true(xs_dict_get_def(n_filter, "reacts", n_def)); | ||
| 4148 | int n_ments_on = xs_is_true(xs_dict_get_def(n_filter, "mentions", n_def)); | ||
| 4149 | int n_ann_on = xs_is_true(xs_dict_get_def(n_filter, "announces", n_def)); | ||
| 4150 | int n_fol_on = xs_is_true(xs_dict_get_def(n_filter, "follows", n_def)); | ||
| 4151 | int n_unfol_on = xs_is_true(xs_dict_get_def(n_filter, "unfollows", n_def)); | ||
| 4152 | int n_folreq_on = xs_is_true(xs_dict_get_def(n_filter, "folreqs", n_def)); | ||
| 4153 | int n_blocks_on = xs_is_true(xs_dict_get_def(n_filter, "blocks", n_def)); | ||
| 4154 | int n_polls_on = xs_is_true(xs_dict_get_def(n_filter, "polls", n_def)); | ||
| 3977 | 4155 | ||
| 3978 | xs_html *html = xs_html_tag("html", | 4156 | xs_html *html = xs_html_tag("html", |
| 3979 | html_user_head(user, NULL, NULL), | 4157 | html_user_head(user, NULL, NULL), |
| 3980 | body); | 4158 | body); |
| 3981 | 4159 | ||
| 4160 | xs *filter_notifs_action = xs_fmt("%s/admin/filter-notifications", user->actor); | ||
| 4161 | xs_html *notifs_form = xs_html_tag("form", | ||
| 4162 | xs_html_attr("autocomplete", "off"), | ||
| 4163 | xs_html_attr("method", "post"), | ||
| 4164 | xs_html_attr("action", filter_notifs_action), | ||
| 4165 | xs_html_attr("enctype", "multipart/form-data"), | ||
| 4166 | xs_html_attr("id", "filter"), | ||
| 4167 | xs_html_sctag("input", | ||
| 4168 | xs_html_attr("type", "hidden"), | ||
| 4169 | xs_html_attr("name", "hard-redir"), | ||
| 4170 | xs_html_attr("value", xs_fmt("%s/notifications", user->actor))), | ||
| 4171 | html_checkbox("likes_on", L("Likes"), n_likes_on), | ||
| 4172 | html_checkbox("reacts_on", L("Emoji reacts"), n_reacts_on), | ||
| 4173 | html_checkbox("mentions_on", L("Mentions"), n_ments_on), | ||
| 4174 | html_checkbox("announces_on", L("Announces"), n_ann_on), | ||
| 4175 | html_checkbox("follows_on", L("Follows"), n_fol_on), | ||
| 4176 | html_checkbox("unfollows_on", L("Unfollows"), n_unfol_on), | ||
| 4177 | html_checkbox("folreqs_on", L("Follow requests"), n_folreq_on), | ||
| 4178 | html_checkbox("blocks_on", L("Blocks"), n_blocks_on), | ||
| 4179 | html_checkbox("polls_on", L("Polls"), n_polls_on), | ||
| 4180 | xs_html_sctag("input", | ||
| 4181 | xs_html_attr("type", "submit"), | ||
| 4182 | xs_html_attr("class", "button"), | ||
| 4183 | xs_html_attr("value", L("Save")))); | ||
| 4184 | |||
| 4185 | xs_html_add(body, | ||
| 4186 | xs_html_tag("p", | ||
| 4187 | xs_html_tag("div", | ||
| 4188 | xs_html_attr("class", "snac-notify-filter"), | ||
| 4189 | xs_html_tag("details", | ||
| 4190 | xs_html_tag("summary", | ||
| 4191 | xs_html_text(L("Notifications filter..."))), | ||
| 4192 | notifs_form)))); | ||
| 4193 | |||
| 3982 | xs *clear_all_action = xs_fmt("%s/admin/clear-notifications", user->actor); | 4194 | xs *clear_all_action = xs_fmt("%s/admin/clear-notifications", user->actor); |
| 3983 | 4195 | ||
| 3984 | xs_html_add(body, | 4196 | xs_html_add(body, |
| @@ -4053,6 +4265,14 @@ xs_str *html_notifications(snac *user, int skip, int show) | |||
| 4053 | enqueue_actor_refresh(user, actor_id, 0); | 4265 | enqueue_actor_refresh(user, actor_id, 0); |
| 4054 | } | 4266 | } |
| 4055 | 4267 | ||
| 4268 | const char *html_url = xs_dict_get_def(actor, "url", actor_id); | ||
| 4269 | |||
| 4270 | if (xs_is_list(html_url)) | ||
| 4271 | html_url = xs_list_get(html_url, 0); | ||
| 4272 | |||
| 4273 | if (!xs_is_string(html_url)) | ||
| 4274 | html_url = actor_id; | ||
| 4275 | |||
| 4056 | xs *label_sanitized = sanitize(type); | 4276 | xs *label_sanitized = sanitize(type); |
| 4057 | const char *label = label_sanitized; | 4277 | const char *label = label_sanitized; |
| 4058 | 4278 | ||
| @@ -4078,6 +4298,8 @@ xs_str *html_notifications(snac *user, int skip, int show) | |||
| 4078 | isEmoji = 1; | 4298 | isEmoji = 1; |
| 4079 | 4299 | ||
| 4080 | if (xs_type(content) == XSTYPE_STRING) { | 4300 | if (xs_type(content) == XSTYPE_STRING) { |
| 4301 | if (!isEmoji && !n_likes_on) | ||
| 4302 | continue; | ||
| 4081 | xs *emoji = replace_shortnames(xs_dup(content), xs_dict_get_path(noti, "msg.tag"), 1, proxy); | 4303 | xs *emoji = replace_shortnames(xs_dup(content), xs_dict_get_path(noti, "msg.tag"), 1, proxy); |
| 4082 | wrk = xs_fmt("%s (%s️)", isEmoji ? "EmojiReact" : "Like", emoji); | 4304 | wrk = xs_fmt("%s (%s️)", isEmoji ? "EmojiReact" : "Like", emoji); |
| 4083 | label = wrk; | 4305 | label = wrk; |
| @@ -4087,14 +4309,14 @@ xs_str *html_notifications(snac *user, int skip, int show) | |||
| 4087 | if (strcmp(type, "Follow") == 0 && pending_check(user, actor_id)) | 4309 | if (strcmp(type, "Follow") == 0 && pending_check(user, actor_id)) |
| 4088 | label = L("Follow Request"); | 4310 | label = L("Follow Request"); |
| 4089 | 4311 | ||
| 4090 | xs *s_date = xs_crop_i(xs_dup(date), 0, 10); | 4312 | xs *s_date = html_date_label(user, date); |
| 4091 | 4313 | ||
| 4092 | xs_html *this_html_label = xs_html_container( | 4314 | xs_html *this_html_label = xs_html_container( |
| 4093 | xs_html_tag("b", | 4315 | xs_html_tag("b", |
| 4094 | xs_html_raw(label), | 4316 | xs_html_raw(label), |
| 4095 | xs_html_text(" by "), | 4317 | xs_html_text(" by "), |
| 4096 | xs_html_tag("a", | 4318 | xs_html_tag("a", |
| 4097 | xs_html_attr("href", actor_id), | 4319 | xs_html_attr("href", html_url), |
| 4098 | xs_html_raw(a_name))), /* a_name is already sanitized */ | 4320 | xs_html_raw(a_name))), /* a_name is already sanitized */ |
| 4099 | xs_html_text(" "), | 4321 | xs_html_text(" "), |
| 4100 | xs_html_tag("time", | 4322 | xs_html_tag("time", |
| @@ -4534,7 +4756,7 @@ int html_get_handler(const xs_dict *req, const char *q_path, | |||
| 4534 | 4756 | ||
| 4535 | xs *title = xs_fmt(L("Search results for account %s"), q); | 4757 | xs *title = xs_fmt(L("Search results for account %s"), q); |
| 4536 | 4758 | ||
| 4537 | page = html_people_list(&snac, l, title, "wf", NULL); | 4759 | page = html_people_list(&snac, l, title, "wf", NULL, 1); |
| 4538 | } | 4760 | } |
| 4539 | } | 4761 | } |
| 4540 | 4762 | ||
| @@ -5243,6 +5465,7 @@ int html_post_handler(const xs_dict *req, const char *q_path, | |||
| 5243 | if (valid_status(object_get(edit_id, &p_msg))) { | 5465 | if (valid_status(object_get(edit_id, &p_msg))) { |
| 5244 | /* copy relevant fields from previous version */ | 5466 | /* copy relevant fields from previous version */ |
| 5245 | char *fields[] = { "id", "context", "url", | 5467 | char *fields[] = { "id", "context", "url", |
| 5468 | "cc", "attributedTo", | ||
| 5246 | "to", "inReplyTo", NULL }; | 5469 | "to", "inReplyTo", NULL }; |
| 5247 | int n; | 5470 | int n; |
| 5248 | 5471 | ||
| @@ -5685,6 +5908,12 @@ int html_post_handler(const xs_dict *req, const char *q_path, | |||
| 5685 | status = HTTP_STATUS_SEE_OTHER; | 5908 | status = HTTP_STATUS_SEE_OTHER; |
| 5686 | } | 5909 | } |
| 5687 | else | 5910 | else |
| 5911 | if (p_path && strcmp(p_path, "admin/filter-notifications") == 0) { /** **/ | ||
| 5912 | notify_filter(&snac, p_vars); | ||
| 5913 | user_persist(&snac, 0); | ||
| 5914 | status = HTTP_STATUS_SEE_OTHER; | ||
| 5915 | } | ||
| 5916 | else | ||
| 5688 | if (p_path && strcmp(p_path, "admin/clear-notifications") == 0) { /** **/ | 5917 | if (p_path && strcmp(p_path, "admin/clear-notifications") == 0) { /** **/ |
| 5689 | notify_clear(&snac); | 5918 | notify_clear(&snac); |
| 5690 | timeline_touch(&snac); | 5919 | timeline_touch(&snac); |
| @@ -5813,6 +6042,33 @@ int html_post_handler(const xs_dict *req, const char *q_path, | |||
| 5813 | 6042 | ||
| 5814 | status = HTTP_STATUS_SEE_OTHER; | 6043 | status = HTTP_STATUS_SEE_OTHER; |
| 5815 | } | 6044 | } |
| 6045 | else | ||
| 6046 | if (p_path && strcmp(p_path, "admin/muted-words") == 0) { | ||
| 6047 | const char *words = xs_dict_get(p_vars, "muted_words"); | ||
| 6048 | |||
| 6049 | if (xs_is_string(words)) { | ||
| 6050 | xs *new_words = xs_list_new(); | ||
| 6051 | xs *l = xs_split(words, "\n"); | ||
| 6052 | const char *v; | ||
| 6053 | |||
| 6054 | xs_list_foreach(l, v) { | ||
| 6055 | xs *s1 = xs_strip_i(xs_dup(v)); | ||
| 6056 | s1 = xs_replace_i(s1, " ", ""); | ||
| 6057 | |||
| 6058 | if (*s1 == '\0') | ||
| 6059 | continue; | ||
| 6060 | |||
| 6061 | xs *s2 = xs_utf8_to_lower(s1); | ||
| 6062 | |||
| 6063 | new_words = xs_list_insert_sorted(new_words, s2); | ||
| 6064 | } | ||
| 6065 | |||
| 6066 | snac.config = xs_dict_set(snac.config, "muted_words", new_words); | ||
| 6067 | user_persist(&snac, 0); | ||
| 6068 | } | ||
| 6069 | |||
| 6070 | status = HTTP_STATUS_SEE_OTHER; | ||
| 6071 | } | ||
| 5816 | 6072 | ||
| 5817 | if (status == HTTP_STATUS_SEE_OTHER) { | 6073 | if (status == HTTP_STATUS_SEE_OTHER) { |
| 5818 | const char *hard_redir = xs_dict_get(p_vars, "hard-redir"); | 6074 | const char *hard_redir = xs_dict_get(p_vars, "hard-redir"); |