sclass HCRUD { HCRUD_Data data; S baseLink; *() {} *(S *baseLink, HCRUD_Data *data) {} S newLink() { ret appendQueryToURL(baseLink, cmd := "new"); } S deleteLink(O id) { ret appendQueryToURL(baseLink, "delete_" + id, 1); } S editLink(O id) { ret appendQueryToURL(baseLink, edit := id); } // also handles commands if withCmds=true S render(bool withCmds, SS params) { if (!withCmds) ret renderTable(false); try answer handleCommands(params); ret pUnlessEmpty(params.get("msg")) + p(ahref(newLink(), "New entry")) + renderTable(withCmds); } S refreshWithMsgs(LS msgs) { ret hrefresh(appendQueryToURL(baseLink, msg := htmlEncode_nlToBr(lines_rtrim(msgs)))); } S handleCommands(SS params) { new LS msgs; if (eqGet(params, "action", "create")) { O id = data.createObject(subMapStartingWith_dropPrefix(params, "f_")); msgs.add("Object created (ID: " + id + ")"); } if (eqGet(params, "action", "update")) msgs.add(data.updateObject(params.get("id"), subMapStartingWith_dropPrefix(params, "f_"))); for (S toDelete : keysDeprefixNemptyValue(params, "delete_")) msgs.add(data.deleteObject(toDelete)); ret nempty(msgs) ? refreshWithMsgs(msgs) : ""; } S encodeField(S s) { ret or(data.fieldNameToHTML(s), s); } S renderTable(bool withCmds) { L> l = data.list(); if (empty(l)) ret p("No entries"); //LS fields = data.fields(); //if (fields == null) fields = allKeysFromList_inOrder(); l = map(l, map -> { Map map2 = mapKeysAndValues( s -> encodeField(s), value -> htmlEncode2(strOrEmpty(value)), map); if (withCmds) { O id = map.get(data.idField()); map2.put("", ahref(editLink(id), "edit") + " | " + ahrefWithConfirm( "Really delete item " + id + "?", deleteLink(id), htmlEncode2(unicode_DEL())/*"delete"*/)); } ret map2; }); ret htmlTable2_noHtmlEncode(l); } S renderForm(Map map) { ret htableRaw_valignTop( map(map, (field, value) -> ll( encodeField(field), renderInput(field, value) )) , border := 1, cellpadding := 4); } S renderInput(S field, O value) { S name = "f_" + field; Renderer r = data.getRenderer(field); if (r cast TextArea) ret htextarea(strOrEmpty(value), +name, cols := r.cols, rows := r.rows); ret htextfield(name, strOrEmpty(value)); } S renderNewForm() { Map map1 = data.emptyObject(); //printStruct("renderNewForm", map1); Map map = mapWithoutKey(map1, data.idField()); //printStruct("renderNewForm", map); ret hpostform( hhidden("action", "create") + renderForm(map) + p(hsubmit("Create")), action := baseLink); } S renderEditForm(S id) { Map map = mapWithoutKey(data.getObject(id), data.idField()); if (map == null) ret htmlEncode2("Object " + id + " not found"); ret hpostform( hhidden("action", "update") + hhidden(+id) + renderForm(map) + p(hsubmit("Save changes")), action := baseLink); } S renderPage(SS params) { if (eqGet(params, "cmd", "new")) ret h1_title("New " + data.itemName()) + renderNewForm(); if (nempty(params.get("edit"))) ret h1_title("Edit " + data.itemName()) + renderEditForm(params.get("edit")); ret h1_title(ahref(baseLink, firstToUpper(data.itemNamePlural()))) + render(true, params); } }