aboutsummaryrefslogtreecommitdiff
path: root/html.c
diff options
context:
space:
mode:
Diffstat (limited to 'html.c')
-rw-r--r--html.c234
1 files changed, 192 insertions, 42 deletions
diff --git a/html.c b/html.c
index 3a2b14f..edb7e1e 100644
--- a/html.c
+++ b/html.c
@@ -770,7 +770,7 @@ static xs_html *html_user_body(snac *user, int read_only)
xs_html_sctag("input",
xs_html_attr("type", "text"),
xs_html_attr("name", "q"),
- xs_html_attr("title", L("Search posts by content (regular expression) or #tag")),
+ xs_html_attr("title", L("Search posts by content (regular expression), @user@host accounts, or #tag")),
xs_html_attr("placeholder", L("Content search")))));
}
@@ -829,21 +829,45 @@ static xs_html *html_user_body(snac *user, int read_only)
}
if (read_only) {
- xs *es1 = encode_html(xs_dict_get(user->config, "bio"));
xs *tags = xs_list_new();
- xs *bio1 = not_really_markdown(es1, NULL, &tags);
+ xs *bio1 = not_really_markdown(xs_dict_get(user->config, "bio"), NULL, &tags);
xs *bio2 = process_tags(user, bio1, &tags);
+ xs *bio3 = sanitize(bio2);
- bio2 = replace_shortnames(bio2, tags, 2, proxy);
+ bio3 = replace_shortnames(bio3, tags, 2, proxy);
xs_html *top_user_bio = xs_html_tag("div",
xs_html_attr("class", "p-note snac-top-user-bio"),
- xs_html_raw(bio2)); /* already sanitized */
+ xs_html_raw(bio3)); /* already sanitized */
xs_html_add(top_user,
top_user_bio);
- const xs_dict *metadata = xs_dict_get(user->config, "metadata");
+ xs *metadata = NULL;
+ const xs_dict *md = xs_dict_get(user->config, "metadata");
+
+ if (xs_type(md) == XSTYPE_DICT)
+ metadata = xs_dup(md);
+ else
+ if (xs_type(md) == XSTYPE_STRING) {
+ /* convert to dict for easier iteration */
+ metadata = xs_dict_new();
+ xs *l = xs_split(md, "\n");
+ const char *ll;
+
+ xs_list_foreach(l, ll) {
+ xs *kv = xs_split_n(ll, "=", 1);
+ const char *k = xs_list_get(kv, 0);
+ const char *v = xs_list_get(kv, 1);
+
+ if (k && v) {
+ xs *kk = xs_strip_i(xs_dup(k));
+ xs *vv = xs_strip_i(xs_dup(v));
+ metadata = xs_dict_set(metadata, kk, vv);
+ }
+ }
+ }
+
if (xs_type(metadata) == XSTYPE_DICT) {
const xs_str *k;
const xs_str *v;
@@ -914,6 +938,18 @@ static xs_html *html_user_body(snac *user, int read_only)
xs_html_add(top_user,
snac_metadata);
}
+
+ if (xs_is_true(xs_dict_get(user->config, "show_contact_metrics"))) {
+ xs *fwers = follower_list(user);
+ xs *fwing = following_list(user);
+
+ xs *s1 = xs_fmt(L("%d following %d followers"),
+ xs_list_len(fwing), xs_list_len(fwers));
+
+ xs_html_add(top_user,
+ xs_html_tag("p",
+ xs_html_text(s1)));
+ }
}
xs_html_add(body,
@@ -1025,20 +1061,31 @@ xs_html *html_top_controls(snac *snac)
const xs_val *a_private = xs_dict_get(snac->config, "private");
const xs_val *auto_boost = xs_dict_get(snac->config, "auto_boost");
const xs_val *coll_thrds = xs_dict_get(snac->config, "collapse_threads");
+ const xs_val *pending = xs_dict_get(snac->config, "approve_followers");
+ const xs_val *show_foll = xs_dict_get(snac->config, "show_contact_metrics");
- xs *metadata = xs_str_new(NULL);
+ xs *metadata = NULL;
const xs_dict *md = xs_dict_get(snac->config, "metadata");
- const xs_str *k;
- const xs_str *v;
- int c = 0;
- while (xs_dict_next(md, &k, &v, &c)) {
- xs *kp = xs_fmt("%s=%s", k, v);
+ if (xs_type(md) == XSTYPE_DICT) {
+ const xs_str *k;
+ const xs_str *v;
+
+ metadata = xs_str_new(NULL);
- if (*metadata)
- metadata = xs_str_cat(metadata, "\n");
- metadata = xs_str_cat(metadata, kp);
+ xs_dict_foreach(md, k, v) {
+ xs *kp = xs_fmt("%s=%s", k, v);
+
+ if (*metadata)
+ metadata = xs_str_cat(metadata, "\n");
+ metadata = xs_str_cat(metadata, kp);
+ }
}
+ else
+ if (xs_type(md) == XSTYPE_STRING)
+ metadata = xs_dup(md);
+ else
+ metadata = xs_str_new(NULL);
xs *user_setup_action = xs_fmt("%s/admin/user-setup", snac->actor);
@@ -1188,6 +1235,24 @@ xs_html *html_top_controls(snac *snac)
xs_html_attr("for", "collapse_threads"),
xs_html_text(L("Collapse top threads by default")))),
xs_html_tag("p",
+ xs_html_sctag("input",
+ xs_html_attr("type", "checkbox"),
+ xs_html_attr("name", "approve_followers"),
+ xs_html_attr("id", "approve_followers"),
+ xs_html_attr(xs_is_true(pending) ? "checked" : "", NULL)),
+ xs_html_tag("label",
+ xs_html_attr("for", "approve_followers"),
+ xs_html_text(L("Follow requests must be approved")))),
+ xs_html_tag("p",
+ xs_html_sctag("input",
+ xs_html_attr("type", "checkbox"),
+ xs_html_attr("name", "show_contact_metrics"),
+ xs_html_attr("id", "show_contact_metrics"),
+ xs_html_attr(xs_is_true(show_foll) ? "checked" : "", NULL)),
+ xs_html_tag("label",
+ xs_html_attr("for", "show_contact_metrics"),
+ xs_html_text(L("Publish follower and following metrics")))),
+ xs_html_tag("p",
xs_html_text(L("Profile metadata (key=value pairs in each line):")),
xs_html_sctag("br", NULL),
xs_html_tag("textarea",
@@ -1481,6 +1546,9 @@ xs_html *html_entry(snac *user, xs_dict *msg, int read_only,
if ((read_only || !user) && !is_msg_public(msg))
return NULL;
+ if (id && is_instance_blocked(id))
+ return NULL;
+
if (user && level == 0 && xs_is_true(xs_dict_get(user->config, "collapse_threads")))
collapse_threads = 1;
@@ -1670,7 +1738,7 @@ xs_html *html_entry(snac *user, xs_dict *msg, int read_only,
if (strcmp(type, "Note") == 0) {
if (level == 0) {
/* is the parent not here? */
- const char *parent = xs_dict_get(msg, "inReplyTo");
+ const char *parent = get_in_reply_to(msg);
if (user && !xs_is_null(parent) && *parent && !timeline_here(user, parent)) {
xs_html_add(post_header,
@@ -2329,7 +2397,7 @@ xs_str *html_timeline(snac *user, const xs_list *list, int read_only,
/* is this message a non-public reply? */
if (user != NULL && !is_msg_public(msg)) {
- const char *irt = xs_dict_get(msg, "inReplyTo");
+ const char *irt = get_in_reply_to(msg);
/* is it a reply to something not in the storage? */
if (!xs_is_null(irt) && !object_here(irt)) {
@@ -2437,10 +2505,9 @@ xs_html *html_people_list(snac *snac, xs_list *list, char *header, char *t, cons
xs_html_tag("summary",
xs_html_text("..."))));
- xs_list *p = list;
const char *actor_id;
- while (xs_list_iter(&p, &actor_id)) {
+ xs_list_foreach(list, actor_id) {
xs *md5 = xs_md5_hex(actor_id, strlen(actor_id));
xs *actor = NULL;
@@ -2509,6 +2576,15 @@ xs_html *html_people_list(snac *snac, xs_list *list, char *header, char *t, cons
html_button("limit", L("Limit"),
L("Block announces (boosts) from this user")));
}
+ else
+ if (pending_check(snac, actor_id)) {
+ xs_html_add(form,
+ html_button("approve", L("Approve"),
+ L("Approve this follow request")));
+
+ xs_html_add(form,
+ html_button("discard", L("Discard"), L("Discard this follow request")));
+ }
else {
xs_html_add(form,
html_button("follow", L("Follow"),
@@ -2563,13 +2639,23 @@ xs_str *html_people(snac *user)
xs *wing = following_list(user);
xs *wers = follower_list(user);
+ xs_html *lists = xs_html_tag("div",
+ xs_html_attr("class", "snac-posts"));
+
+ if (xs_is_true(xs_dict_get(user->config, "approve_followers"))) {
+ xs *pending = pending_list(user);
+ xs_html_add(lists,
+ html_people_list(user, pending, L("Pending follow confirmations"), "p", proxy));
+ }
+
+ xs_html_add(lists,
+ html_people_list(user, wing, L("People you follow"), "i", proxy),
+ html_people_list(user, wers, L("People that follow you"), "e", proxy));
+
xs_html *html = xs_html_tag("html",
html_user_head(user, NULL, NULL),
xs_html_add(html_user_body(user, 0),
- xs_html_tag("div",
- xs_html_attr("class", "snac-posts"),
- html_people_list(user, wing, L("People you follow"), "i", proxy),
- html_people_list(user, wers, L("People that follow you"), "e", proxy)),
+ lists,
html_footer()));
return xs_html_render_s(html, "<!DOCTYPE html>\n");
@@ -2661,6 +2747,9 @@ xs_str *html_notifications(snac *user, int skip, int show)
label = wrk;
}
}
+ else
+ if (strcmp(type, "Follow") == 0 && pending_check(user, actor_id))
+ label = L("Follow Request");
xs *s_date = xs_crop_i(xs_dup(date), 0, 10);
@@ -2909,6 +2998,48 @@ int html_get_handler(const xs_dict *req, const char *q_path,
const char *q = xs_dict_get(q_vars, "q");
if (q && *q) {
+ if (xs_regex_match(q, "^@?[a-zA-Z0-9_]+@[a-zA-Z0-9-]+\\.")) {
+ /** search account **/
+ xs *actor = NULL;
+ xs *acct = NULL;
+ xs *l = xs_list_new();
+ xs_html *page = NULL;
+
+ if (valid_status(webfinger_request(q, &actor, &acct))) {
+ xs *actor_obj = NULL;
+
+ if (valid_status(actor_request(&snac, actor, &actor_obj))) {
+ actor_add(actor, actor_obj);
+
+ /* create a people list with only one element */
+ l = xs_list_append(xs_list_new(), actor);
+
+ xs *title = xs_fmt(L("Search results for account %s"), q);
+
+ page = html_people_list(&snac, l, title, "wf", NULL);
+ }
+ }
+
+ if (page == NULL) {
+ xs *title = xs_fmt(L("Account %s not found"), q);
+
+ page = xs_html_tag("div",
+ xs_html_tag("h2",
+ xs_html_attr("class", "snac-header"),
+ xs_html_text(title)));
+ }
+
+ xs_html *html = xs_html_tag("html",
+ html_user_head(&snac, NULL, NULL),
+ xs_html_add(html_user_body(&snac, 0),
+ page,
+ html_footer()));
+
+ *body = xs_html_render_s(html, "<!DOCTYPE html>\n");
+ *b_size = strlen(*body);
+ status = HTTP_STATUS_OK;
+ }
+ else
if (*q == '#') {
/** search by tag **/
xs *tl = tag_search(q, skip, show + 1);
@@ -3647,6 +3778,34 @@ int html_post_handler(const xs_dict *req, const char *q_path,
timeline_touch(&snac);
}
else
+ if (strcmp(action, L("Approve")) == 0) { /** **/
+ xs *fwreq = pending_get(&snac, actor);
+
+ if (fwreq != NULL) {
+ xs *reply = msg_accept(&snac, fwreq, actor);
+
+ enqueue_message(&snac, reply);
+
+ if (xs_is_null(xs_dict_get(fwreq, "published"))) {
+ /* add a date if it doesn't include one (Mastodon) */
+ xs *date = xs_str_utctime(0, ISO_DATE_SPEC);
+ fwreq = xs_dict_set(fwreq, "published", date);
+ }
+
+ timeline_add(&snac, xs_dict_get(fwreq, "id"), fwreq);
+
+ follower_add(&snac, actor);
+
+ pending_del(&snac, actor);
+
+ snac_log(&snac, xs_fmt("new follower %s", actor));
+ }
+ }
+ else
+ if (strcmp(action, L("Discard")) == 0) { /** **/
+ pending_del(&snac, actor);
+ }
+ else
status = HTTP_STATUS_NOT_FOUND;
/* delete the cached timeline */
@@ -3705,26 +3864,17 @@ int html_post_handler(const xs_dict *req, const char *q_path,
snac.config = xs_dict_set(snac.config, "collapse_threads", xs_stock(XSTYPE_TRUE));
else
snac.config = xs_dict_set(snac.config, "collapse_threads", xs_stock(XSTYPE_FALSE));
+ if ((v = xs_dict_get(p_vars, "approve_followers")) != NULL && strcmp(v, "on") == 0)
+ snac.config = xs_dict_set(snac.config, "approve_followers", xs_stock(XSTYPE_TRUE));
+ else
+ snac.config = xs_dict_set(snac.config, "approve_followers", xs_stock(XSTYPE_FALSE));
+ if ((v = xs_dict_get(p_vars, "show_contact_metrics")) != NULL && strcmp(v, "on") == 0)
+ snac.config = xs_dict_set(snac.config, "show_contact_metrics", xs_stock(XSTYPE_TRUE));
+ else
+ snac.config = xs_dict_set(snac.config, "show_contact_metrics", xs_stock(XSTYPE_FALSE));
- if ((v = xs_dict_get(p_vars, "metadata")) != NULL) {
- /* split the metadata and store it as a dict */
- xs_dict *md = xs_dict_new();
- xs *l = xs_split(v, "\n");
- xs_list *p = l;
- const xs_str *kp;
-
- while (xs_list_iter(&p, &kp)) {
- xs *kpl = xs_split_n(kp, "=", 1);
- if (xs_list_len(kpl) == 2) {
- xs *k2 = xs_strip_i(xs_dup(xs_list_get(kpl, 0)));
- xs *v2 = xs_strip_i(xs_dup(xs_list_get(kpl, 1)));
-
- md = xs_dict_set(md, k2, v2);
- }
- }
-
- snac.config = xs_dict_set(snac.config, "metadata", md);
- }
+ if ((v = xs_dict_get(p_vars, "metadata")) != NULL)
+ snac.config = xs_dict_set(snac.config, "metadata", v);
/* uploads */
const char *uploads[] = { "avatar", "header", NULL };