diff options
Diffstat (limited to 'html.c')
| -rw-r--r-- | html.c | 360 |
1 files changed, 289 insertions, 71 deletions
| @@ -14,6 +14,7 @@ | |||
| 14 | #include "xs_curl.h" | 14 | #include "xs_curl.h" |
| 15 | #include "xs_unicode.h" | 15 | #include "xs_unicode.h" |
| 16 | #include "xs_url.h" | 16 | #include "xs_url.h" |
| 17 | #include "xs_random.h" | ||
| 17 | 18 | ||
| 18 | #include "snac.h" | 19 | #include "snac.h" |
| 19 | 20 | ||
| @@ -68,10 +69,14 @@ xs_str *replace_shortnames(xs_str *s, const xs_list *tag, int ems, const char *p | |||
| 68 | } | 69 | } |
| 69 | 70 | ||
| 70 | xs *style = xs_fmt("height: %dem; width: %dem; vertical-align: middle;", ems, ems); | 71 | xs *style = xs_fmt("height: %dem; width: %dem; vertical-align: middle;", ems, ems); |
| 72 | xs *class = xs_fmt("snac-emoji snac-emoji-%d-em", ems); | ||
| 71 | 73 | ||
| 72 | const xs_dict *v; | 74 | const xs_dict *v; |
| 73 | int c = 0; | 75 | int c = 0; |
| 74 | 76 | ||
| 77 | xs_set rep_emoji; | ||
| 78 | xs_set_init(&rep_emoji); | ||
| 79 | |||
| 75 | while (xs_list_next(tag_list, &v, &c)) { | 80 | while (xs_list_next(tag_list, &v, &c)) { |
| 76 | const char *t = xs_dict_get(v, "type"); | 81 | const char *t = xs_dict_get(v, "type"); |
| 77 | 82 | ||
| @@ -79,11 +84,21 @@ xs_str *replace_shortnames(xs_str *s, const xs_list *tag, int ems, const char *p | |||
| 79 | const char *n = xs_dict_get(v, "name"); | 84 | const char *n = xs_dict_get(v, "name"); |
| 80 | const xs_dict *i = xs_dict_get(v, "icon"); | 85 | const xs_dict *i = xs_dict_get(v, "icon"); |
| 81 | 86 | ||
| 87 | /* avoid repeated emojis (Misskey seems to return this) */ | ||
| 88 | if (xs_set_add(&rep_emoji, n) == 0) | ||
| 89 | continue; | ||
| 90 | |||
| 82 | if (xs_is_string(n) && xs_is_dict(i)) { | 91 | if (xs_is_string(n) && xs_is_dict(i)) { |
| 83 | const char *u = xs_dict_get(i, "url"); | 92 | const char *u = xs_dict_get(i, "url"); |
| 84 | const char *mt = xs_dict_get(i, "mediaType"); | 93 | const char *mt = xs_dict_get(i, "mediaType"); |
| 85 | 94 | ||
| 86 | if (xs_is_string(u) && xs_is_string(mt)) { | 95 | if (xs_is_string(u)) { |
| 96 | // on akkoma instances mediaType is not present. | ||
| 97 | // but we need to to know if the image is an svg or not. | ||
| 98 | // for now, i just use the file extention, which may not be the most reliable... | ||
| 99 | if (!xs_is_string(mt)) | ||
| 100 | mt = xs_mime_by_ext(u); | ||
| 101 | |||
| 87 | if (strcmp(mt, "image/svg+xml") == 0 && !xs_is_true(xs_dict_get(srv_config, "enable_svg"))) | 102 | if (strcmp(mt, "image/svg+xml") == 0 && !xs_is_true(xs_dict_get(srv_config, "enable_svg"))) |
| 88 | s = xs_replace_i(s, n, ""); | 103 | s = xs_replace_i(s, n, ""); |
| 89 | else { | 104 | else { |
| @@ -93,6 +108,8 @@ xs_str *replace_shortnames(xs_str *s, const xs_list *tag, int ems, const char *p | |||
| 93 | xs_html_attr("loading", "lazy"), | 108 | xs_html_attr("loading", "lazy"), |
| 94 | xs_html_attr("src", url), | 109 | xs_html_attr("src", url), |
| 95 | xs_html_attr("alt", n), | 110 | xs_html_attr("alt", n), |
| 111 | xs_html_attr("title", n), | ||
| 112 | xs_html_attr("class", class), | ||
| 96 | xs_html_attr("style", style)); | 113 | xs_html_attr("style", style)); |
| 97 | 114 | ||
| 98 | xs *s1 = xs_html_render(img); | 115 | xs *s1 = xs_html_render(img); |
| @@ -104,6 +121,8 @@ xs_str *replace_shortnames(xs_str *s, const xs_list *tag, int ems, const char *p | |||
| 104 | } | 121 | } |
| 105 | } | 122 | } |
| 106 | } | 123 | } |
| 124 | |||
| 125 | xs_set_free(&rep_emoji); | ||
| 107 | } | 126 | } |
| 108 | 127 | ||
| 109 | return s; | 128 | return s; |
| @@ -125,6 +144,26 @@ xs_str *actor_name(xs_dict *actor, const char *proxy) | |||
| 125 | } | 144 | } |
| 126 | 145 | ||
| 127 | 146 | ||
| 147 | xs_str *format_text_with_emoji(snac *user, const char *text, int ems, const char *proxy) | ||
| 148 | /* needed when we have local text with no tags attached */ | ||
| 149 | { | ||
| 150 | xs *tags = xs_list_new(); | ||
| 151 | xs *name1 = not_really_markdown(text, NULL, &tags); | ||
| 152 | |||
| 153 | xs_str *name3; | ||
| 154 | if (user) { | ||
| 155 | xs *name2 = process_tags(user, name1, &tags); | ||
| 156 | name3 = sanitize(name2); | ||
| 157 | } | ||
| 158 | else { | ||
| 159 | name3 = sanitize(name1); | ||
| 160 | name3 = xs_replace_i(name3, "<br>", ""); | ||
| 161 | } | ||
| 162 | |||
| 163 | return replace_shortnames(name3, tags, ems, proxy); | ||
| 164 | } | ||
| 165 | |||
| 166 | |||
| 128 | xs_html *html_actor_icon(snac *user, xs_dict *actor, const char *date, | 167 | xs_html *html_actor_icon(snac *user, xs_dict *actor, const char *date, |
| 129 | const char *udate, const char *url, int priv, | 168 | const char *udate, const char *url, int priv, |
| 130 | int in_people, const char *proxy, const char *lang, | 169 | int in_people, const char *proxy, const char *lang, |
| @@ -177,7 +216,7 @@ xs_html *html_actor_icon(snac *user, xs_dict *actor, const char *date, | |||
| 177 | xs_html_attr("loading", "lazy"), | 216 | xs_html_attr("loading", "lazy"), |
| 178 | xs_html_attr("class", "snac-avatar"), | 217 | xs_html_attr("class", "snac-avatar"), |
| 179 | xs_html_attr("src", avatar), | 218 | xs_html_attr("src", avatar), |
| 180 | xs_html_attr("alt", "")), | 219 | xs_html_attr("alt", "[?]")), |
| 181 | xs_html_tag("a", | 220 | xs_html_tag("a", |
| 182 | xs_html_attr("href", href), | 221 | xs_html_attr("href", href), |
| 183 | xs_html_attr("class", "p-author h-card snac-author"), | 222 | xs_html_attr("class", "p-author h-card snac-author"), |
| @@ -293,7 +332,8 @@ xs_html *html_actor_icon(snac *user, xs_dict *actor, const char *date, | |||
| 293 | } | 332 | } |
| 294 | 333 | ||
| 295 | 334 | ||
| 296 | xs_html *html_msg_icon(snac *user, const char *actor_id, const xs_dict *msg, const char *proxy, const char *md5) | 335 | xs_html *html_msg_icon(snac *user, const char *actor_id, const xs_dict *msg, |
| 336 | const char *proxy, const char *md5, const char *lang) | ||
| 297 | { | 337 | { |
| 298 | xs *actor = NULL; | 338 | xs *actor = NULL; |
| 299 | xs_html *actor_icon = NULL; | 339 | xs_html *actor_icon = NULL; |
| @@ -302,7 +342,6 @@ xs_html *html_msg_icon(snac *user, const char *actor_id, const xs_dict *msg, con | |||
| 302 | const char *date = NULL; | 342 | const char *date = NULL; |
| 303 | const char *udate = NULL; | 343 | const char *udate = NULL; |
| 304 | const char *url = NULL; | 344 | const char *url = NULL; |
| 305 | const char *lang = NULL; | ||
| 306 | int priv = 0; | 345 | int priv = 0; |
| 307 | const char *type = xs_dict_get(msg, "type"); | 346 | const char *type = xs_dict_get(msg, "type"); |
| 308 | 347 | ||
| @@ -314,16 +353,6 @@ xs_html *html_msg_icon(snac *user, const char *actor_id, const xs_dict *msg, con | |||
| 314 | date = xs_dict_get(msg, "published"); | 353 | date = xs_dict_get(msg, "published"); |
| 315 | udate = xs_dict_get(msg, "updated"); | 354 | udate = xs_dict_get(msg, "updated"); |
| 316 | 355 | ||
| 317 | lang = xs_dict_get(msg, "contentMap"); | ||
| 318 | if (xs_is_dict(lang)) { | ||
| 319 | const char *v; | ||
| 320 | int c = 0; | ||
| 321 | |||
| 322 | xs_dict_next(lang, &lang, &v, &c); | ||
| 323 | } | ||
| 324 | else | ||
| 325 | lang = NULL; | ||
| 326 | |||
| 327 | actor_icon = html_actor_icon(user, actor, date, udate, url, priv, 0, proxy, lang, md5); | 356 | actor_icon = html_actor_icon(user, actor, date, udate, url, priv, 0, proxy, lang, md5); |
| 328 | } | 357 | } |
| 329 | 358 | ||
| @@ -339,7 +368,7 @@ xs_html *html_note(snac *user, const char *summary, | |||
| 339 | const xs_val *mnt_only, const char *redir, | 368 | const xs_val *mnt_only, const char *redir, |
| 340 | const char *in_reply_to, int poll, | 369 | const char *in_reply_to, int poll, |
| 341 | const xs_list *att_files, const xs_list *att_alt_texts, | 370 | const xs_list *att_files, const xs_list *att_alt_texts, |
| 342 | int is_draft) | 371 | int is_draft, const char *published) |
| 343 | /* Yes, this is a FUCKTON of arguments and I'm a bit embarrased */ | 372 | /* Yes, this is a FUCKTON of arguments and I'm a bit embarrased */ |
| 344 | { | 373 | { |
| 345 | xs *action = xs_fmt("%s/admin/note", user->actor); | 374 | xs *action = xs_fmt("%s/admin/note", user->actor); |
| @@ -429,6 +458,42 @@ xs_html *html_note(snac *user, const char *summary, | |||
| 429 | xs_html_attr("name", "is_draft"), | 458 | xs_html_attr("name", "is_draft"), |
| 430 | xs_html_attr(is_draft ? "checked" : "", NULL)))); | 459 | xs_html_attr(is_draft ? "checked" : "", NULL)))); |
| 431 | 460 | ||
| 461 | /* post date and time */ | ||
| 462 | xs *post_date = NULL; | ||
| 463 | xs *post_time = NULL; | ||
| 464 | |||
| 465 | if (xs_is_string(published)) { | ||
| 466 | time_t t = xs_parse_iso_date(published, 0); | ||
| 467 | |||
| 468 | if (t > 0) { | ||
| 469 | post_date = xs_str_time(t, "%Y-%m-%d", 1); | ||
| 470 | post_time = xs_str_time(t, "%H:%M:%S", 1); | ||
| 471 | } | ||
| 472 | } | ||
| 473 | |||
| 474 | if (edit_id == NULL || is_draft || is_scheduled(user, edit_id)) { | ||
| 475 | xs *pdat = xs_fmt(L("Post date and time (timezone: %s):"), user->tz); | ||
| 476 | |||
| 477 | xs_html_add(form, | ||
| 478 | xs_html_tag("p", | ||
| 479 | xs_html_tag("details", | ||
| 480 | xs_html_tag("summary", | ||
| 481 | xs_html_text(L("Scheduled post..."))), | ||
| 482 | xs_html_tag("p", | ||
| 483 | xs_html_text(pdat), | ||
| 484 | xs_html_sctag("br", NULL), | ||
| 485 | xs_html_sctag("input", | ||
| 486 | xs_html_attr("type", "date"), | ||
| 487 | xs_html_attr("value", post_date ? post_date : ""), | ||
| 488 | xs_html_attr("name", "post_date")), | ||
| 489 | xs_html_text(" "), | ||
| 490 | xs_html_sctag("input", | ||
| 491 | xs_html_attr("type", "time"), | ||
| 492 | xs_html_attr("value", post_time ? post_time : ""), | ||
| 493 | xs_html_attr("step", "1"), | ||
| 494 | xs_html_attr("name", "post_time")))))); | ||
| 495 | } | ||
| 496 | |||
| 432 | if (edit_id) | 497 | if (edit_id) |
| 433 | xs_html_add(form, | 498 | xs_html_add(form, |
| 434 | xs_html_sctag("input", | 499 | xs_html_sctag("input", |
| @@ -746,11 +811,7 @@ xs_html *html_user_head(snac *user, const char *desc, const char *url) | |||
| 746 | 811 | ||
| 747 | /* show metrics in og:description? */ | 812 | /* show metrics in og:description? */ |
| 748 | if (xs_is_true(xs_dict_get(user->config, "show_contact_metrics"))) { | 813 | if (xs_is_true(xs_dict_get(user->config, "show_contact_metrics"))) { |
| 749 | xs *fwers = follower_list(user); | 814 | xs *s1 = xs_fmt(L("%d following, %d followers"), following_list_len(user), follower_list_len(user)); |
| 750 | xs *fwing = following_list(user); | ||
| 751 | |||
| 752 | xs *s1 = xs_fmt(L("%d following, %d followers"), | ||
| 753 | xs_list_len(fwing), xs_list_len(fwers)); | ||
| 754 | 815 | ||
| 755 | s1 = xs_str_cat(s1, " · "); | 816 | s1 = xs_str_cat(s1, " · "); |
| 756 | 817 | ||
| @@ -848,7 +909,9 @@ static xs_html *html_user_body(snac *user, int read_only) | |||
| 848 | } | 909 | } |
| 849 | else { | 910 | else { |
| 850 | int n_len = notify_new_num(user); | 911 | int n_len = notify_new_num(user); |
| 912 | int p_len = pending_count(user); | ||
| 851 | xs_html *notify_count = NULL; | 913 | xs_html *notify_count = NULL; |
| 914 | xs_html *pending_follow_count = NULL; | ||
| 852 | 915 | ||
| 853 | /* show the number of new notifications, if there are any */ | 916 | /* show the number of new notifications, if there are any */ |
| 854 | if (n_len) { | 917 | if (n_len) { |
| @@ -860,6 +923,15 @@ static xs_html *html_user_body(snac *user, int read_only) | |||
| 860 | else | 923 | else |
| 861 | notify_count = xs_html_text(""); | 924 | notify_count = xs_html_text(""); |
| 862 | 925 | ||
| 926 | if (p_len) { | ||
| 927 | xs *s = xs_fmt(" %d ", p_len); | ||
| 928 | pending_follow_count = xs_html_tag("sup", | ||
| 929 | xs_html_attr("style", "background-color: red; color: white;"), | ||
| 930 | xs_html_text(s)); | ||
| 931 | } | ||
| 932 | else | ||
| 933 | pending_follow_count = xs_html_text(""); | ||
| 934 | |||
| 863 | xs *admin_url = xs_fmt("%s/admin", user->actor); | 935 | xs *admin_url = xs_fmt("%s/admin", user->actor); |
| 864 | xs *notify_url = xs_fmt("%s/notifications", user->actor); | 936 | xs *notify_url = xs_fmt("%s/notifications", user->actor); |
| 865 | xs *people_url = xs_fmt("%s/people", user->actor); | 937 | xs *people_url = xs_fmt("%s/people", user->actor); |
| @@ -882,6 +954,7 @@ static xs_html *html_user_body(snac *user, int read_only) | |||
| 882 | xs_html_tag("a", | 954 | xs_html_tag("a", |
| 883 | xs_html_attr("href", people_url), | 955 | xs_html_attr("href", people_url), |
| 884 | xs_html_text(L("people"))), | 956 | xs_html_text(L("people"))), |
| 957 | pending_follow_count, | ||
| 885 | xs_html_text(" - "), | 958 | xs_html_text(" - "), |
| 886 | xs_html_tag("a", | 959 | xs_html_tag("a", |
| 887 | xs_html_attr("href", instance_url), | 960 | xs_html_attr("href", instance_url), |
| @@ -922,10 +995,12 @@ static xs_html *html_user_body(snac *user, int read_only) | |||
| 922 | xs_dict_get(user->config, "uid"), | 995 | xs_dict_get(user->config, "uid"), |
| 923 | xs_dict_get(srv_config, "host")); | 996 | xs_dict_get(srv_config, "host")); |
| 924 | 997 | ||
| 998 | xs *display_name = format_text_with_emoji(NULL, xs_dict_get(user->config, "name"), 1, proxy); | ||
| 999 | |||
| 925 | xs_html_add(top_user, | 1000 | xs_html_add(top_user, |
| 926 | xs_html_tag("p", | 1001 | xs_html_tag("p", |
| 927 | xs_html_attr("class", "p-name snac-top-user-name"), | 1002 | xs_html_attr("class", "p-name snac-top-user-name"), |
| 928 | xs_html_text(xs_dict_get(user->config, "name"))), | 1003 | xs_html_raw(display_name)), |
| 929 | xs_html_tag("p", | 1004 | xs_html_tag("p", |
| 930 | xs_html_attr("class", "snac-top-user-id"), | 1005 | xs_html_attr("class", "snac-top-user-id"), |
| 931 | xs_html_text(handle))); | 1006 | xs_html_text(handle))); |
| @@ -953,16 +1028,11 @@ static xs_html *html_user_body(snac *user, int read_only) | |||
| 953 | } | 1028 | } |
| 954 | 1029 | ||
| 955 | if (read_only) { | 1030 | if (read_only) { |
| 956 | xs *tags = xs_list_new(); | 1031 | xs *bio = format_text_with_emoji(user, xs_dict_get(user->config, "bio"), 2, proxy); |
| 957 | xs *bio1 = not_really_markdown(xs_dict_get(user->config, "bio"), NULL, &tags); | ||
| 958 | xs *bio2 = process_tags(user, bio1, &tags); | ||
| 959 | xs *bio3 = sanitize(bio2); | ||
| 960 | |||
| 961 | bio3 = replace_shortnames(bio3, tags, 2, proxy); | ||
| 962 | 1032 | ||
| 963 | xs_html *top_user_bio = xs_html_tag("div", | 1033 | xs_html *top_user_bio = xs_html_tag("div", |
| 964 | xs_html_attr("class", "p-note snac-top-user-bio"), | 1034 | xs_html_attr("class", "p-note snac-top-user-bio"), |
| 965 | xs_html_raw(bio3)); /* already sanitized */ | 1035 | xs_html_raw(bio)); /* already sanitized */ |
| 966 | 1036 | ||
| 967 | xs_html_add(top_user, | 1037 | xs_html_add(top_user, |
| 968 | top_user_bio); | 1038 | top_user_bio); |
| @@ -1082,11 +1152,7 @@ static xs_html *html_user_body(snac *user, int read_only) | |||
| 1082 | } | 1152 | } |
| 1083 | 1153 | ||
| 1084 | if (xs_is_true(xs_dict_get(user->config, "show_contact_metrics"))) { | 1154 | if (xs_is_true(xs_dict_get(user->config, "show_contact_metrics"))) { |
| 1085 | xs *fwers = follower_list(user); | 1155 | xs *s1 = xs_fmt(L("%d following, %d followers"), following_list_len(user), follower_list_len(user)); |
| 1086 | xs *fwing = following_list(user); | ||
| 1087 | |||
| 1088 | xs *s1 = xs_fmt(L("%d following, %d followers"), | ||
| 1089 | xs_list_len(fwing), xs_list_len(fwers)); | ||
| 1090 | 1156 | ||
| 1091 | xs_html_add(top_user, | 1157 | xs_html_add(top_user, |
| 1092 | xs_html_tag("p", | 1158 | xs_html_tag("p", |
| @@ -1116,7 +1182,7 @@ xs_html *html_top_controls(snac *user) | |||
| 1116 | NULL, NULL, | 1182 | NULL, NULL, |
| 1117 | xs_stock(XSTYPE_FALSE), "", | 1183 | xs_stock(XSTYPE_FALSE), "", |
| 1118 | xs_stock(XSTYPE_FALSE), NULL, | 1184 | xs_stock(XSTYPE_FALSE), NULL, |
| 1119 | NULL, 1, NULL, NULL, 0), | 1185 | NULL, 1, NULL, NULL, 0, NULL), |
| 1120 | 1186 | ||
| 1121 | /** operations **/ | 1187 | /** operations **/ |
| 1122 | xs_html_tag("details", | 1188 | xs_html_tag("details", |
| @@ -1270,6 +1336,27 @@ xs_html *html_top_controls(snac *user) | |||
| 1270 | xs_html_attr("value", lang))); | 1336 | xs_html_attr("value", lang))); |
| 1271 | } | 1337 | } |
| 1272 | 1338 | ||
| 1339 | /* timezone */ | ||
| 1340 | xs_html *tz_select = xs_html_tag("select", | ||
| 1341 | xs_html_attr("name", "tz")); | ||
| 1342 | |||
| 1343 | xs *tzs = xs_tz_list(); | ||
| 1344 | const char *tz; | ||
| 1345 | |||
| 1346 | xs_list_foreach(tzs, tz) { | ||
| 1347 | if (strcmp(tz, user->tz) == 0) | ||
| 1348 | xs_html_add(tz_select, | ||
| 1349 | xs_html_tag("option", | ||
| 1350 | xs_html_text(tz), | ||
| 1351 | xs_html_attr("value", tz), | ||
| 1352 | xs_html_attr("selected", "selected"))); | ||
| 1353 | else | ||
| 1354 | xs_html_add(tz_select, | ||
| 1355 | xs_html_tag("option", | ||
| 1356 | xs_html_text(tz), | ||
| 1357 | xs_html_attr("value", tz))); | ||
| 1358 | } | ||
| 1359 | |||
| 1273 | xs *user_setup_action = xs_fmt("%s/admin/user-setup", user->actor); | 1360 | xs *user_setup_action = xs_fmt("%s/admin/user-setup", user->actor); |
| 1274 | 1361 | ||
| 1275 | xs_html_add(top_controls, | 1362 | xs_html_add(top_controls, |
| @@ -1466,6 +1553,11 @@ xs_html *html_top_controls(snac *user) | |||
| 1466 | lang_select), | 1553 | lang_select), |
| 1467 | 1554 | ||
| 1468 | xs_html_tag("p", | 1555 | xs_html_tag("p", |
| 1556 | xs_html_text(L("Time zone:")), | ||
| 1557 | xs_html_sctag("br", NULL), | ||
| 1558 | tz_select), | ||
| 1559 | |||
| 1560 | xs_html_tag("p", | ||
| 1469 | xs_html_text(L("New password:")), | 1561 | xs_html_text(L("New password:")), |
| 1470 | xs_html_sctag("br", NULL), | 1562 | xs_html_sctag("br", NULL), |
| 1471 | xs_html_sctag("input", | 1563 | xs_html_sctag("input", |
| @@ -1774,7 +1866,8 @@ xs_html *html_entry_controls(snac *user, const char *actor, | |||
| 1774 | id, NULL, | 1866 | id, NULL, |
| 1775 | xs_dict_get(msg, "sensitive"), xs_dict_get(msg, "summary"), | 1867 | xs_dict_get(msg, "sensitive"), xs_dict_get(msg, "summary"), |
| 1776 | xs_stock(is_msg_public(msg) ? XSTYPE_FALSE : XSTYPE_TRUE), redir, | 1868 | xs_stock(is_msg_public(msg) ? XSTYPE_FALSE : XSTYPE_TRUE), redir, |
| 1777 | NULL, 0, att_files, att_alt_texts, is_draft(user, id))), | 1869 | NULL, 0, att_files, att_alt_texts, is_draft(user, id), |
| 1870 | xs_dict_get(msg, "published"))), | ||
| 1778 | xs_html_tag("p", NULL)); | 1871 | xs_html_tag("p", NULL)); |
| 1779 | } | 1872 | } |
| 1780 | 1873 | ||
| @@ -1793,7 +1886,7 @@ xs_html *html_entry_controls(snac *user, const char *actor, | |||
| 1793 | NULL, NULL, | 1886 | NULL, NULL, |
| 1794 | xs_dict_get(msg, "sensitive"), xs_dict_get(msg, "summary"), | 1887 | xs_dict_get(msg, "sensitive"), xs_dict_get(msg, "summary"), |
| 1795 | xs_stock(is_msg_public(msg) ? XSTYPE_FALSE : XSTYPE_TRUE), redir, | 1888 | xs_stock(is_msg_public(msg) ? XSTYPE_FALSE : XSTYPE_TRUE), redir, |
| 1796 | id, 0, NULL, NULL, 0)), | 1889 | id, 0, NULL, NULL, 0, NULL)), |
| 1797 | xs_html_tag("p", NULL)); | 1890 | xs_html_tag("p", NULL)); |
| 1798 | } | 1891 | } |
| 1799 | 1892 | ||
| @@ -1840,6 +1933,15 @@ xs_html *html_entry(snac *user, xs_dict *msg, int read_only, | |||
| 1840 | return xs_html_tag("mark", | 1933 | return xs_html_tag("mark", |
| 1841 | xs_html_text(L("Truncated (too deep)"))); | 1934 | xs_html_text(L("Truncated (too deep)"))); |
| 1842 | 1935 | ||
| 1936 | const char *lang = NULL; | ||
| 1937 | const xs_dict *cmap = xs_dict_get(msg, "contentMap"); | ||
| 1938 | if (xs_is_dict(cmap)) { | ||
| 1939 | const char *dummy; | ||
| 1940 | int c = 0; | ||
| 1941 | |||
| 1942 | xs_dict_next(cmap, &lang, &dummy, &c); | ||
| 1943 | } | ||
| 1944 | |||
| 1843 | if (strcmp(type, "Follow") == 0) { | 1945 | if (strcmp(type, "Follow") == 0) { |
| 1844 | return xs_html_tag("div", | 1946 | return xs_html_tag("div", |
| 1845 | xs_html_attr("class", "snac-post"), | 1947 | xs_html_attr("class", "snac-post"), |
| @@ -1848,7 +1950,7 @@ xs_html *html_entry(snac *user, xs_dict *msg, int read_only, | |||
| 1848 | xs_html_tag("div", | 1950 | xs_html_tag("div", |
| 1849 | xs_html_attr("class", "snac-origin"), | 1951 | xs_html_attr("class", "snac-origin"), |
| 1850 | xs_html_text(L("follows you"))), | 1952 | xs_html_text(L("follows you"))), |
| 1851 | html_msg_icon(read_only ? NULL : user, xs_dict_get(msg, "actor"), msg, proxy, NULL))); | 1953 | html_msg_icon(read_only ? NULL : user, xs_dict_get(msg, "actor"), msg, proxy, NULL, lang))); |
| 1852 | } | 1954 | } |
| 1853 | else | 1955 | else |
| 1854 | if (!xs_match(type, POSTLIKE_OBJECT_TYPE)) { | 1956 | if (!xs_match(type, POSTLIKE_OBJECT_TYPE)) { |
| @@ -2029,13 +2131,17 @@ xs_html *html_entry(snac *user, xs_dict *msg, int read_only, | |||
| 2029 | } | 2131 | } |
| 2030 | 2132 | ||
| 2031 | xs_html_add(post_header, | 2133 | xs_html_add(post_header, |
| 2032 | html_msg_icon(read_only ? NULL : user, actor, msg, proxy, md5)); | 2134 | html_msg_icon(read_only ? NULL : user, actor, msg, proxy, md5, lang)); |
| 2033 | 2135 | ||
| 2034 | /** post content **/ | 2136 | /** post content **/ |
| 2035 | 2137 | ||
| 2036 | xs_html *snac_content_wrap = xs_html_tag("div", | 2138 | xs_html *snac_content_wrap = xs_html_tag("div", |
| 2037 | xs_html_attr("class", "e-content snac-content")); | 2139 | xs_html_attr("class", "e-content snac-content")); |
| 2038 | 2140 | ||
| 2141 | if (xs_is_string(lang)) | ||
| 2142 | xs_html_add(snac_content_wrap, | ||
| 2143 | xs_html_attr("lang", lang)); | ||
| 2144 | |||
| 2039 | xs_html_add(entry, | 2145 | xs_html_add(entry, |
| 2040 | snac_content_wrap); | 2146 | snac_content_wrap); |
| 2041 | 2147 | ||
| @@ -2313,14 +2419,10 @@ xs_html *html_entry(snac *user, xs_dict *msg, int read_only, | |||
| 2313 | const char *o_href = xs_dict_get(a, "href"); | 2419 | const char *o_href = xs_dict_get(a, "href"); |
| 2314 | const char *name = xs_dict_get(a, "name"); | 2420 | const char *name = xs_dict_get(a, "name"); |
| 2315 | 2421 | ||
| 2316 | /* if this image is already in the post content, skip */ | 2422 | /* if this URL is already in the post content, skip */ |
| 2317 | if (content && xs_str_in(content, o_href) != -1) | 2423 | if (content && xs_str_in(content, o_href) != -1) |
| 2318 | continue; | 2424 | continue; |
| 2319 | 2425 | ||
| 2320 | /* drop silently any attachment that may include JavaScript */ | ||
| 2321 | if (strcmp(type, "text/html") == 0) | ||
| 2322 | continue; | ||
| 2323 | |||
| 2324 | if (strcmp(type, "image/svg+xml") == 0 && !xs_is_true(xs_dict_get(srv_config, "enable_svg"))) | 2426 | if (strcmp(type, "image/svg+xml") == 0 && !xs_is_true(xs_dict_get(srv_config, "enable_svg"))) |
| 2325 | continue; | 2427 | continue; |
| 2326 | 2428 | ||
| @@ -2396,13 +2498,19 @@ xs_html *html_entry(snac *user, xs_dict *msg, int read_only, | |||
| 2396 | name = NULL; | 2498 | name = NULL; |
| 2397 | } | 2499 | } |
| 2398 | else { | 2500 | else { |
| 2501 | xs *d_href = xs_dup(o_href); | ||
| 2502 | if (strlen(d_href) > 64) { | ||
| 2503 | d_href[64] = '\0'; | ||
| 2504 | d_href = xs_str_cat(d_href, "..."); | ||
| 2505 | } | ||
| 2506 | |||
| 2399 | xs_html_add(content_attachments, | 2507 | xs_html_add(content_attachments, |
| 2400 | xs_html_tag("p", | 2508 | xs_html_tag("p", |
| 2401 | xs_html_tag("a", | 2509 | xs_html_tag("a", |
| 2402 | xs_html_attr("href", o_href), | 2510 | xs_html_attr("href", o_href), |
| 2403 | xs_html_text(L("Attachment")), | 2511 | xs_html_text(L("Attachment")), |
| 2404 | xs_html_text(": "), | 2512 | xs_html_text(": "), |
| 2405 | xs_html_text(o_href)))); | 2513 | xs_html_text(d_href)))); |
| 2406 | 2514 | ||
| 2407 | /* do not generate an Alt... */ | 2515 | /* do not generate an Alt... */ |
| 2408 | name = NULL; | 2516 | name = NULL; |
| @@ -2689,6 +2797,11 @@ xs_html *html_entry(snac *user, xs_dict *msg, int read_only, | |||
| 2689 | } | 2797 | } |
| 2690 | } | 2798 | } |
| 2691 | 2799 | ||
| 2800 | /* add an invisible hr, to help differentiate between posts in text browsers */ | ||
| 2801 | xs_html_add(entry_top, | ||
| 2802 | xs_html_sctag("hr", | ||
| 2803 | xs_html_attr("hidden", NULL))); | ||
| 2804 | |||
| 2692 | return entry_top; | 2805 | return entry_top; |
| 2693 | } | 2806 | } |
| 2694 | 2807 | ||
| @@ -2830,6 +2943,18 @@ xs_str *html_timeline(snac *user, const xs_list *list, int read_only, | |||
| 2830 | xs_html_text(L("drafts"))))); | 2943 | xs_html_text(L("drafts"))))); |
| 2831 | } | 2944 | } |
| 2832 | 2945 | ||
| 2946 | { | ||
| 2947 | /* show the list of scheduled posts */ | ||
| 2948 | xs *url = xs_fmt("%s/sched", user->actor); | ||
| 2949 | xs_html_add(lol, | ||
| 2950 | xs_html_tag("li", | ||
| 2951 | xs_html_tag("a", | ||
| 2952 | xs_html_attr("href", url), | ||
| 2953 | xs_html_attr("class", "snac-list-link"), | ||
| 2954 | xs_html_attr("title", L("Scheduled posts")), | ||
| 2955 | xs_html_text(L("scheduled posts"))))); | ||
| 2956 | } | ||
| 2957 | |||
| 2833 | /* the list of followed hashtags */ | 2958 | /* the list of followed hashtags */ |
| 2834 | const char *followed_hashtags = xs_dict_get(user->config, "followed_hashtags"); | 2959 | const char *followed_hashtags = xs_dict_get(user->config, "followed_hashtags"); |
| 2835 | 2960 | ||
| @@ -3019,6 +3144,8 @@ xs_html *html_people_list(snac *user, xs_list *list, const char *header, const c | |||
| 3019 | xs_html_tag("summary", | 3144 | xs_html_tag("summary", |
| 3020 | xs_html_text("...")))); | 3145 | xs_html_text("...")))); |
| 3021 | 3146 | ||
| 3147 | xs *redir = xs_fmt("%s/people", user->actor); | ||
| 3148 | |||
| 3022 | const char *actor_id; | 3149 | const char *actor_id; |
| 3023 | 3150 | ||
| 3024 | xs_list_foreach(list, actor_id) { | 3151 | xs_list_foreach(list, actor_id) { |
| @@ -3040,6 +3167,7 @@ xs_html *html_people_list(snac *user, xs_list *list, const char *header, const c | |||
| 3040 | 3167 | ||
| 3041 | if (!xs_is_null(c)) { | 3168 | if (!xs_is_null(c)) { |
| 3042 | xs *sc = sanitize(c); | 3169 | xs *sc = sanitize(c); |
| 3170 | sc = replace_shortnames(sc, xs_dict_get(actor, "tag"), 2, proxy); | ||
| 3043 | 3171 | ||
| 3044 | xs_html *snac_content = xs_html_tag("div", | 3172 | xs_html *snac_content = xs_html_tag("div", |
| 3045 | xs_html_attr("class", "snac-content")); | 3173 | xs_html_attr("class", "snac-content")); |
| @@ -3071,6 +3199,10 @@ xs_html *html_people_list(snac *user, xs_list *list, const char *header, const c | |||
| 3071 | xs_html_attr("value", actor_id)), | 3199 | xs_html_attr("value", actor_id)), |
| 3072 | xs_html_sctag("input", | 3200 | xs_html_sctag("input", |
| 3073 | xs_html_attr("type", "hidden"), | 3201 | xs_html_attr("type", "hidden"), |
| 3202 | xs_html_attr("name", "hard-redir"), | ||
| 3203 | xs_html_attr("value", redir)), | ||
| 3204 | xs_html_sctag("input", | ||
| 3205 | xs_html_attr("type", "hidden"), | ||
| 3074 | xs_html_attr("name", "actor-form"), | 3206 | xs_html_attr("name", "actor-form"), |
| 3075 | xs_html_attr("value", "yes"))); | 3207 | xs_html_attr("value", "yes"))); |
| 3076 | 3208 | ||
| @@ -3130,7 +3262,7 @@ xs_html *html_people_list(snac *user, xs_list *list, const char *header, const c | |||
| 3130 | NULL, actor_id, | 3262 | NULL, actor_id, |
| 3131 | xs_stock(XSTYPE_FALSE), "", | 3263 | xs_stock(XSTYPE_FALSE), "", |
| 3132 | xs_stock(XSTYPE_FALSE), NULL, | 3264 | xs_stock(XSTYPE_FALSE), NULL, |
| 3133 | NULL, 0, NULL, NULL, 0), | 3265 | NULL, 0, NULL, NULL, 0, NULL), |
| 3134 | xs_html_tag("p", NULL)); | 3266 | xs_html_tag("p", NULL)); |
| 3135 | 3267 | ||
| 3136 | xs_html_add(snac_post, snac_controls); | 3268 | xs_html_add(snac_post, snac_controls); |
| @@ -3257,7 +3389,8 @@ xs_str *html_notifications(snac *user, int skip, int show) | |||
| 3257 | continue; | 3389 | continue; |
| 3258 | 3390 | ||
| 3259 | xs *a_name = actor_name(actor, proxy); | 3391 | xs *a_name = actor_name(actor, proxy); |
| 3260 | const char *label = type; | 3392 | xs *label_sanatized = sanitize(type); |
| 3393 | const char *label = label_sanatized; | ||
| 3261 | 3394 | ||
| 3262 | if (strcmp(type, "Create") == 0) | 3395 | if (strcmp(type, "Create") == 0) |
| 3263 | label = L("Mention"); | 3396 | label = L("Mention"); |
| @@ -3268,11 +3401,12 @@ xs_str *html_notifications(snac *user, int skip, int show) | |||
| 3268 | if (strcmp(type, "Undo") == 0 && strcmp(utype, "Follow") == 0) | 3401 | if (strcmp(type, "Undo") == 0 && strcmp(utype, "Follow") == 0) |
| 3269 | label = L("Unfollow"); | 3402 | label = L("Unfollow"); |
| 3270 | else | 3403 | else |
| 3271 | if (strcmp(type, "EmojiReact") == 0) { | 3404 | if (strcmp(type, "EmojiReact") == 0 || strcmp(type, "Like") == 0) { |
| 3272 | const char *content = xs_dict_get_path(noti, "msg.content"); | 3405 | const char *content = xs_dict_get_path(noti, "msg.content"); |
| 3273 | 3406 | ||
| 3274 | if (xs_type(content) == XSTYPE_STRING) { | 3407 | if (xs_type(content) == XSTYPE_STRING) { |
| 3275 | wrk = xs_fmt("%s (%s)", type, content); | 3408 | xs *emoji = replace_shortnames(xs_dup(content), xs_dict_get_path(noti, "msg.tag"), 1, proxy); |
| 3409 | wrk = xs_fmt("%s (%s️)", type, emoji); | ||
| 3276 | label = wrk; | 3410 | label = wrk; |
| 3277 | } | 3411 | } |
| 3278 | } | 3412 | } |
| @@ -3284,7 +3418,7 @@ xs_str *html_notifications(snac *user, int skip, int show) | |||
| 3284 | 3418 | ||
| 3285 | xs_html *this_html_label = xs_html_container( | 3419 | xs_html *this_html_label = xs_html_container( |
| 3286 | xs_html_tag("b", | 3420 | xs_html_tag("b", |
| 3287 | xs_html_text(label), | 3421 | xs_html_raw(label), |
| 3288 | xs_html_text(" by "), | 3422 | xs_html_text(" by "), |
| 3289 | xs_html_tag("a", | 3423 | xs_html_tag("a", |
| 3290 | xs_html_attr("href", actor_id), | 3424 | xs_html_attr("href", actor_id), |
| @@ -3584,7 +3718,7 @@ int html_get_handler(const xs_dict *req, const char *q_path, | |||
| 3584 | 3718 | ||
| 3585 | if (xs_is_true(xs_dict_get(srv_config, "strict_public_timelines"))) | 3719 | if (xs_is_true(xs_dict_get(srv_config, "strict_public_timelines"))) |
| 3586 | list = timeline_simple_list(&snac, "public", skip, show, &more); | 3720 | list = timeline_simple_list(&snac, "public", skip, show, &more); |
| 3587 | else | 3721 | else |
| 3588 | list = timeline_list(&snac, "public", skip, show, &more); | 3722 | list = timeline_list(&snac, "public", skip, show, &more); |
| 3589 | 3723 | ||
| 3590 | xs *pins = pinned_list(&snac); | 3724 | xs *pins = pinned_list(&snac); |
| @@ -3879,6 +4013,21 @@ int html_get_handler(const xs_dict *req, const char *q_path, | |||
| 3879 | } | 4013 | } |
| 3880 | } | 4014 | } |
| 3881 | else | 4015 | else |
| 4016 | if (strcmp(p_path, "sched") == 0) { /** list of scheduled posts **/ | ||
| 4017 | if (!login(&snac, req)) { | ||
| 4018 | *body = xs_dup(uid); | ||
| 4019 | status = HTTP_STATUS_UNAUTHORIZED; | ||
| 4020 | } | ||
| 4021 | else { | ||
| 4022 | xs *list = scheduled_list(&snac); | ||
| 4023 | |||
| 4024 | *body = html_timeline(&snac, list, 0, skip, show, | ||
| 4025 | 0, L("Scheduled posts"), "", 0, error); | ||
| 4026 | *b_size = strlen(*body); | ||
| 4027 | status = HTTP_STATUS_OK; | ||
| 4028 | } | ||
| 4029 | } | ||
| 4030 | else | ||
| 3882 | if (xs_startswith(p_path, "list/")) { /** list timelines **/ | 4031 | if (xs_startswith(p_path, "list/")) { /** list timelines **/ |
| 3883 | if (!login(&snac, req)) { | 4032 | if (!login(&snac, req)) { |
| 3884 | *body = xs_dup(uid); | 4033 | *body = xs_dup(uid); |
| @@ -4175,12 +4324,14 @@ int html_post_handler(const xs_dict *req, const char *q_path, | |||
| 4175 | snac_debug(&snac, 1, xs_fmt("web action '%s' received", p_path)); | 4324 | snac_debug(&snac, 1, xs_fmt("web action '%s' received", p_path)); |
| 4176 | 4325 | ||
| 4177 | /* post note */ | 4326 | /* post note */ |
| 4178 | const xs_str *content = xs_dict_get(p_vars, "content"); | 4327 | const char *content = xs_dict_get(p_vars, "content"); |
| 4179 | const xs_str *in_reply_to = xs_dict_get(p_vars, "in_reply_to"); | 4328 | const char *in_reply_to = xs_dict_get(p_vars, "in_reply_to"); |
| 4180 | const xs_str *to = xs_dict_get(p_vars, "to"); | 4329 | const char *to = xs_dict_get(p_vars, "to"); |
| 4181 | const xs_str *sensitive = xs_dict_get(p_vars, "sensitive"); | 4330 | const char *sensitive = xs_dict_get(p_vars, "sensitive"); |
| 4182 | const xs_str *summary = xs_dict_get(p_vars, "summary"); | 4331 | const char *summary = xs_dict_get(p_vars, "summary"); |
| 4183 | const xs_str *edit_id = xs_dict_get(p_vars, "edit_id"); | 4332 | const char *edit_id = xs_dict_get(p_vars, "edit_id"); |
| 4333 | const char *post_date = xs_dict_get_def(p_vars, "post_date", ""); | ||
| 4334 | const char *post_time = xs_dict_get_def(p_vars, "post_time", ""); | ||
| 4184 | int priv = !xs_is_null(xs_dict_get(p_vars, "mentioned_only")); | 4335 | int priv = !xs_is_null(xs_dict_get(p_vars, "mentioned_only")); |
| 4185 | int store_as_draft = !xs_is_null(xs_dict_get(p_vars, "is_draft")); | 4336 | int store_as_draft = !xs_is_null(xs_dict_get(p_vars, "is_draft")); |
| 4186 | xs *attach_list = xs_list_new(); | 4337 | xs *attach_list = xs_list_new(); |
| @@ -4210,9 +4361,12 @@ int html_post_handler(const xs_dict *req, const char *q_path, | |||
| 4210 | const char *fn = xs_list_get(attach_file, 0); | 4361 | const char *fn = xs_list_get(attach_file, 0); |
| 4211 | 4362 | ||
| 4212 | if (xs_is_string(fn) && *fn != '\0') { | 4363 | if (xs_is_string(fn) && *fn != '\0') { |
| 4213 | char *ext = strrchr(fn, '.'); | 4364 | char rnd[32]; |
| 4214 | xs *hash = xs_md5_hex(fn, strlen(fn)); | 4365 | xs_rnd_buf(rnd, sizeof(rnd)); |
| 4215 | xs *id = xs_fmt("%s%s", hash, ext); | 4366 | |
| 4367 | const char *ext = strrchr(fn, '.'); | ||
| 4368 | xs *hash = xs_md5_hex(rnd, strlen(rnd)); | ||
| 4369 | xs *id = xs_fmt("post-%s%s", hash, ext ? ext : ""); | ||
| 4216 | xs *url = xs_fmt("%s/s/%s", snac.actor, id); | 4370 | xs *url = xs_fmt("%s/s/%s", snac.actor, id); |
| 4217 | int fo = xs_number_get(xs_list_get(attach_file, 1)); | 4371 | int fo = xs_number_get(xs_list_get(attach_file, 1)); |
| 4218 | int fs = xs_number_get(xs_list_get(attach_file, 2)); | 4372 | int fs = xs_number_get(xs_list_get(attach_file, 2)); |
| @@ -4268,6 +4422,31 @@ int html_post_handler(const xs_dict *req, const char *q_path, | |||
| 4268 | msg = xs_dict_set(msg, "summary", xs_is_null(summary) ? "..." : summary); | 4422 | msg = xs_dict_set(msg, "summary", xs_is_null(summary) ? "..." : summary); |
| 4269 | } | 4423 | } |
| 4270 | 4424 | ||
| 4425 | if (xs_is_string(post_date) && *post_date) { | ||
| 4426 | xs *post_pubdate = xs_fmt("%sT%s", post_date, | ||
| 4427 | xs_is_string(post_time) && *post_time ? post_time : "00:00:00"); | ||
| 4428 | |||
| 4429 | time_t t = xs_parse_iso_date(post_pubdate, 0); | ||
| 4430 | |||
| 4431 | if (t != 0) { | ||
| 4432 | t -= xs_tz_offset(snac.tz); | ||
| 4433 | |||
| 4434 | xs *iso_date = xs_str_iso_date(t); | ||
| 4435 | msg = xs_dict_set(msg, "published", iso_date); | ||
| 4436 | |||
| 4437 | snac_debug(&snac, 1, xs_fmt("Published date: [%s]", iso_date)); | ||
| 4438 | } | ||
| 4439 | else | ||
| 4440 | snac_log(&snac, xs_fmt("Invalid post date: [%s]", post_pubdate)); | ||
| 4441 | } | ||
| 4442 | |||
| 4443 | /* is the published date from the future? */ | ||
| 4444 | int future_post = 0; | ||
| 4445 | xs *right_now = xs_str_utctime(0, ISO_DATE_SPEC); | ||
| 4446 | |||
| 4447 | if (strcmp(xs_dict_get(msg, "published"), right_now) > 0) | ||
| 4448 | future_post = 1; | ||
| 4449 | |||
| 4271 | if (xs_is_null(edit_id)) { | 4450 | if (xs_is_null(edit_id)) { |
| 4272 | /* new message */ | 4451 | /* new message */ |
| 4273 | const char *id = xs_dict_get(msg, "id"); | 4452 | const char *id = xs_dict_get(msg, "id"); |
| @@ -4275,6 +4454,10 @@ int html_post_handler(const xs_dict *req, const char *q_path, | |||
| 4275 | if (store_as_draft) { | 4454 | if (store_as_draft) { |
| 4276 | draft_add(&snac, id, msg); | 4455 | draft_add(&snac, id, msg); |
| 4277 | } | 4456 | } |
| 4457 | else | ||
| 4458 | if (future_post) { | ||
| 4459 | schedule_add(&snac, id, msg); | ||
| 4460 | } | ||
| 4278 | else { | 4461 | else { |
| 4279 | c_msg = msg_create(&snac, msg); | 4462 | c_msg = msg_create(&snac, msg); |
| 4280 | timeline_add(&snac, id, msg); | 4463 | timeline_add(&snac, id, msg); |
| @@ -4286,7 +4469,7 @@ int html_post_handler(const xs_dict *req, const char *q_path, | |||
| 4286 | 4469 | ||
| 4287 | if (valid_status(object_get(edit_id, &p_msg))) { | 4470 | if (valid_status(object_get(edit_id, &p_msg))) { |
| 4288 | /* copy relevant fields from previous version */ | 4471 | /* copy relevant fields from previous version */ |
| 4289 | char *fields[] = { "id", "context", "url", "published", | 4472 | char *fields[] = { "id", "context", "url", |
| 4290 | "to", "inReplyTo", NULL }; | 4473 | "to", "inReplyTo", NULL }; |
| 4291 | int n; | 4474 | int n; |
| 4292 | 4475 | ||
| @@ -4302,18 +4485,34 @@ int html_post_handler(const xs_dict *req, const char *q_path, | |||
| 4302 | if (is_draft(&snac, edit_id)) { | 4485 | if (is_draft(&snac, edit_id)) { |
| 4303 | /* message was previously a draft; it's a create activity */ | 4486 | /* message was previously a draft; it's a create activity */ |
| 4304 | 4487 | ||
| 4305 | /* set the published field to now */ | 4488 | /* if the date is from the past, overwrite it with right_now */ |
| 4306 | xs *published = xs_str_utctime(0, ISO_DATE_SPEC); | 4489 | if (strcmp(xs_dict_get(msg, "published"), right_now) < 0) { |
| 4307 | msg = xs_dict_set(msg, "published", published); | 4490 | snac_debug(&snac, 1, xs_fmt("setting draft ancient date to %s", right_now)); |
| 4491 | msg = xs_dict_set(msg, "published", right_now); | ||
| 4492 | } | ||
| 4308 | 4493 | ||
| 4309 | /* overwrite object */ | 4494 | /* overwrite object */ |
| 4310 | object_add_ow(edit_id, msg); | 4495 | object_add_ow(edit_id, msg); |
| 4311 | 4496 | ||
| 4312 | c_msg = msg_create(&snac, msg); | 4497 | if (future_post) { |
| 4313 | timeline_add(&snac, edit_id, msg); | 4498 | schedule_add(&snac, edit_id, msg); |
| 4499 | } | ||
| 4500 | else { | ||
| 4501 | c_msg = msg_create(&snac, msg); | ||
| 4502 | timeline_add(&snac, edit_id, msg); | ||
| 4503 | } | ||
| 4504 | |||
| 4314 | draft_del(&snac, edit_id); | 4505 | draft_del(&snac, edit_id); |
| 4315 | } | 4506 | } |
| 4507 | else | ||
| 4508 | if (is_scheduled(&snac, edit_id)) { | ||
| 4509 | /* editing an scheduled post; just update it */ | ||
| 4510 | schedule_add(&snac, edit_id, msg); | ||
| 4511 | } | ||
| 4316 | else { | 4512 | else { |
| 4513 | /* ignore the (possibly changed) published date */ | ||
| 4514 | msg = xs_dict_set(msg, "published", xs_dict_get(p_msg, "published")); | ||
| 4515 | |||
| 4317 | /* set the updated field */ | 4516 | /* set the updated field */ |
| 4318 | xs *updated = xs_str_utctime(0, ISO_DATE_SPEC); | 4517 | xs *updated = xs_str_utctime(0, ISO_DATE_SPEC); |
| 4319 | msg = xs_dict_set(msg, "updated", updated); | 4518 | msg = xs_dict_set(msg, "updated", updated); |
| @@ -4329,8 +4528,10 @@ int html_post_handler(const xs_dict *req, const char *q_path, | |||
| 4329 | snac_log(&snac, xs_fmt("cannot get object '%s' for editing", edit_id)); | 4528 | snac_log(&snac, xs_fmt("cannot get object '%s' for editing", edit_id)); |
| 4330 | } | 4529 | } |
| 4331 | 4530 | ||
| 4332 | if (c_msg != NULL) | 4531 | if (c_msg != NULL) { |
| 4333 | enqueue_message(&snac, c_msg); | 4532 | enqueue_message(&snac, c_msg); |
| 4533 | enqueue_webmention(msg); | ||
| 4534 | } | ||
| 4334 | 4535 | ||
| 4335 | history_del(&snac, "timeline.html_"); | 4536 | history_del(&snac, "timeline.html_"); |
| 4336 | } | 4537 | } |
| @@ -4398,6 +4599,9 @@ int html_post_handler(const xs_dict *req, const char *q_path, | |||
| 4398 | if (is_draft(&snac, id)) | 4599 | if (is_draft(&snac, id)) |
| 4399 | draft_del(&snac, id); | 4600 | draft_del(&snac, id); |
| 4400 | else | 4601 | else |
| 4602 | if (is_scheduled(&snac, id)) | ||
| 4603 | schedule_del(&snac, id); | ||
| 4604 | else | ||
| 4401 | hide(&snac, id); | 4605 | hide(&snac, id); |
| 4402 | } | 4606 | } |
| 4403 | else | 4607 | else |
| @@ -4493,6 +4697,8 @@ int html_post_handler(const xs_dict *req, const char *q_path, | |||
| 4493 | 4697 | ||
| 4494 | draft_del(&snac, id); | 4698 | draft_del(&snac, id); |
| 4495 | 4699 | ||
| 4700 | schedule_del(&snac, id); | ||
| 4701 | |||
| 4496 | snac_log(&snac, xs_fmt("deleted entry %s", id)); | 4702 | snac_log(&snac, xs_fmt("deleted entry %s", id)); |
| 4497 | } | 4703 | } |
| 4498 | } | 4704 | } |
| @@ -4613,6 +4819,8 @@ int html_post_handler(const xs_dict *req, const char *q_path, | |||
| 4613 | snac.config = xs_dict_set(snac.config, "show_contact_metrics", xs_stock(XSTYPE_FALSE)); | 4819 | snac.config = xs_dict_set(snac.config, "show_contact_metrics", xs_stock(XSTYPE_FALSE)); |
| 4614 | if ((v = xs_dict_get(p_vars, "web_ui_lang")) != NULL) | 4820 | if ((v = xs_dict_get(p_vars, "web_ui_lang")) != NULL) |
| 4615 | snac.config = xs_dict_set(snac.config, "lang", v); | 4821 | snac.config = xs_dict_set(snac.config, "lang", v); |
| 4822 | if ((v = xs_dict_get(p_vars, "tz")) != NULL) | ||
| 4823 | snac.config = xs_dict_set(snac.config, "tz", v); | ||
| 4616 | 4824 | ||
| 4617 | snac.config = xs_dict_set(snac.config, "latitude", xs_dict_get_def(p_vars, "latitude", "")); | 4825 | snac.config = xs_dict_set(snac.config, "latitude", xs_dict_get_def(p_vars, "latitude", "")); |
| 4618 | snac.config = xs_dict_set(snac.config, "longitude", xs_dict_get_def(p_vars, "longitude", "")); | 4826 | snac.config = xs_dict_set(snac.config, "longitude", xs_dict_get_def(p_vars, "longitude", "")); |
| @@ -4636,7 +4844,7 @@ int html_post_handler(const xs_dict *req, const char *q_path, | |||
| 4636 | if (xs_startswith(mimetype, "image/")) { | 4844 | if (xs_startswith(mimetype, "image/")) { |
| 4637 | const char *ext = strrchr(fn, '.'); | 4845 | const char *ext = strrchr(fn, '.'); |
| 4638 | xs *hash = xs_md5_hex(fn, strlen(fn)); | 4846 | xs *hash = xs_md5_hex(fn, strlen(fn)); |
| 4639 | xs *id = xs_fmt("%s%s", hash, ext); | 4847 | xs *id = xs_fmt("%s-%s%s", uploads[n], hash, ext ? ext : ""); |
| 4640 | xs *url = xs_fmt("%s/s/%s", snac.actor, id); | 4848 | xs *url = xs_fmt("%s/s/%s", snac.actor, id); |
| 4641 | int fo = xs_number_get(xs_list_get(uploaded_file, 1)); | 4849 | int fo = xs_number_get(xs_list_get(uploaded_file, 1)); |
| 4642 | int fs = xs_number_get(xs_list_get(uploaded_file, 2)); | 4850 | int fs = xs_number_get(xs_list_get(uploaded_file, 2)); |
| @@ -4705,6 +4913,9 @@ int html_post_handler(const xs_dict *req, const char *q_path, | |||
| 4705 | /* set the option */ | 4913 | /* set the option */ |
| 4706 | msg = xs_dict_append(msg, "name", v); | 4914 | msg = xs_dict_append(msg, "name", v); |
| 4707 | 4915 | ||
| 4916 | /* delete the content */ | ||
| 4917 | msg = xs_dict_del(msg, "content"); | ||
| 4918 | |||
| 4708 | xs *c_msg = msg_create(&snac, msg); | 4919 | xs *c_msg = msg_create(&snac, msg); |
| 4709 | 4920 | ||
| 4710 | enqueue_message(&snac, c_msg); | 4921 | enqueue_message(&snac, c_msg); |
| @@ -4793,12 +5004,19 @@ int html_post_handler(const xs_dict *req, const char *q_path, | |||
| 4793 | } | 5004 | } |
| 4794 | 5005 | ||
| 4795 | if (status == HTTP_STATUS_SEE_OTHER) { | 5006 | if (status == HTTP_STATUS_SEE_OTHER) { |
| 4796 | const char *redir = xs_dict_get(p_vars, "redir"); | 5007 | const char *hard_redir = xs_dict_get(p_vars, "hard-redir"); |
| 4797 | 5008 | ||
| 4798 | if (xs_is_null(redir)) | 5009 | if (xs_is_string(hard_redir)) |
| 4799 | redir = "top"; | 5010 | *body = xs_dup(hard_redir); |
| 5011 | else { | ||
| 5012 | const char *redir = xs_dict_get(p_vars, "redir"); | ||
| 5013 | |||
| 5014 | if (xs_is_null(redir)) | ||
| 5015 | redir = "top"; | ||
| 5016 | |||
| 5017 | *body = xs_fmt("%s/admin#%s", snac.actor, redir); | ||
| 5018 | } | ||
| 4800 | 5019 | ||
| 4801 | *body = xs_fmt("%s/admin#%s", snac.actor, redir); | ||
| 4802 | *b_size = strlen(*body); | 5020 | *b_size = strlen(*body); |
| 4803 | } | 5021 | } |
| 4804 | 5022 | ||