From e2e7c9b71a254eeab5b2511245aaca0ac303e405 Mon Sep 17 00:00:00 2001 From: Chris Angelico Date: Sun, 3 Nov 2024 08:10:01 +1100 Subject: [PATCH] Ensure that required fields exist --- modules/http/chan_form.pike | 69 ++++++++++++++++++++++++------------- 1 file changed, 46 insertions(+), 23 deletions(-) diff --git a/modules/http/chan_form.pike b/modules/http/chan_form.pike index 41777382..769b3714 100644 --- a/modules/http/chan_form.pike +++ b/modules/http/chan_form.pike @@ -137,30 +137,47 @@ __async__ mapping(string:mixed) http_request(Protocols.HTTP.Server.Request req) //TODO: Return a nicer page saying that the form is closed. return 0; } + multiset missing = (<>); //If anything is missing, we'll rerender the form if (req->request_type == "POST") { werror("Variables: %O\n", req->variables); mapping fields = ([]); foreach (form->elements, mapping el) { - string|zero val = req->variables["field-" + el->name]; - //TODO: If field is required and absent, reject - //TODO: Reformat as required, eg address input - fields[el->name] = val; + switch (el->type) { //_element_types + case "checkbox": { + if (el->label) foreach (el->label; int i; string l) { + string field = "field-" + el->name + (-i || ""); //Must match the field generation below + if (el->required && !req->variables[field]) missing[field] = 1; + else if (req->variables[field]) fields[field] = 1; + } + break; + } + //case "address": //Reformat into a single string + default: { + string|zero val = req->variables["field-" + el->name]; + if (el->required && (!val || val == "")) missing[el->name] = 1; + else fields[el->name] = val; + } + } + } + if (!sizeof(missing)) { + mapping response = ([ + "timestamp": time(), + "fields": fields, + "ip": req->get_ip(), + ]); + if (nonce) response->nonce = nonce; + await(G->G->DB->mutate_config(req->misc->channel->userid, "formresponses") {mapping resp = __ARGS__[0]; + if (!resp[formid]) resp[formid] = ([]); + resp[formid]->responses += ({response}); + if (resp[formid]->nonces) m_delete(resp[formid]->nonces, nonce); + }); + return render_template(formcloser, ([ + "formtitle": form->formtitle, + ]) | req->misc->chaninfo); } - mapping response = ([ - "timestamp": time(), - "fields": fields, - "ip": req->get_ip(), - ]); - if (nonce) response->nonce = nonce; - await(G->G->DB->mutate_config(req->misc->channel->userid, "formresponses") {mapping resp = __ARGS__[0]; - if (!resp[formid]) resp[formid] = ([]); - resp[formid]->responses += ({response}); - if (resp[formid]->nonces) m_delete(resp[formid]->nonces, nonce); - }); - return render_template(formcloser, ([ - "formtitle": form->formtitle, - ]) | req->misc->chaninfo); + //Else we fall through and rerender the form. } + //TODO: Prepopulate the form with req->variables on rerender string formdata = ""; foreach (form->elements, mapping el) { string|zero elem = 0; @@ -174,25 +191,31 @@ __async__ mapping(string:mixed) http_request(Protocols.HTTP.Server.Request req) elem = sprintf("", el->label, "field-" + el->name, el->required ? " required" : "", - el->required ? " \\*" : "", + missing[el->name] ? " \\* Please enter something" : + el->required ? " \\*" : "", ); break; case "paragraph": elem = sprintf("", el->label, - el->required ? " \\*" : "", + missing[el->name] ? " \\* Please enter something" : + el->required ? " \\*" : "", "field-" + el->name, el->required ? " required" : "", ); break; case "checkbox": { elem = ""; break; }