Warning: session_start(): open(/var/lib/php/sessions/sess_2ol3vvc0a9vf67geinl6e64ej1, O_RDWR) failed: No space left on device (28) in /var/www/tb-usercake/models/config.php on line 51
Warning: session_start(): Failed to read session data: files (path: /var/lib/php/sessions) in /var/www/tb-usercake/models/config.php on line 51
sclass HCRUD extends HAbstractRenderable {
HCRUD_Data data;
int defaultTextFieldCols = 80;
// yeah these fields are a mess...
bool mutationRights = true;
bool allowCreateOrDelete = true;
bool allowCreate = true;
bool allowEdit = true;
bool singleton;
bool cmdsLeft; // put commands on the left instead of the right
S tableClass; // CSS class for table
S formTableClass; // CSS class for form
Set unshownFields; // not shown in table or form
Set uneditableFields; // not shown in form
Set unlistedFields; // not shown in table
bool showCheckBoxes;
bool haveJQuery;
bool needsJQuery; // unused
*() {}
*(HCRUD_Data *data) {}
*(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); }
S duplicateLink(O id) { ret appendQueryToURL(baseLink, duplicate := id); }
// also handles commands if withCmds=true
// you probably want to call renderPage() instead to handle all commands
S render(bool withCmds, SS params) {
if (!withCmds) ret renderTable(false);
try answer handleCommands(params);
ret renderMsgs(params)
+ pUnlessEmpty(nav())
+ renderTable(withCmds);
}
swappable S nav() {
ret !actuallyAllowCreate() ? "" : ahref(newLink(), "New " + itemName());
}
S handleCommands(SS params) {
new LS msgs;
if (eqGet(params, "action", "create")) {
if (!actuallyAllowCreate()) fail("Creating objects not allowed");
O id = data.createObject(subMapStartingWith_dropPrefix(params, "f_"));
msgs.add(itemName() + " created (ID: " + id + ")");
}
if (eqGet(params, "action", "update")) {
if (!actuallyAllowEdit()) fail("Editing objects not allowed");
msgs.add(data.updateObject(params.get("id"),
subMapStartingWith_dropPrefix(params, "f_")));
}
LS toDeleteList = keysDeprefixNemptyValue(params, "delete_");
if (eq(params.get("bulkAction"), "deleteSelected"))
toDeleteList.addAll(keysDeprefixNemptyValue(params, "obj_"));
for (S toDelete : toDeleteList) {
if (!actuallyAllowDelete()) fail("Deleting objects not allowed");
msgs.add(data.deleteObject(toDelete));
}
ret nempty(msgs) ? refreshWithMsgs(msgs) : "";
}
S encodeField(S s) {
ret or(data.fieldNameToHTML(s), s);
}
// in table
swappable S renderValue(S field, O value) {
if (value cast HTML) ret value.html;
value = deref(value);
if (value cast SecretValue)
ret hhiddenStuff(renderValue_inner(value!));
ret renderValue_inner(value);
}
swappable S renderValue_inner(O value) {
if (value cast Bool)
ret yesNo_short(value);
ret htmlEncode_nlToBr_withIndents(strOrEmpty(value));
}
S renderTable(bool withCmds) {
ret renderTable(withCmds, data.list());
}
S renderTable(bool withCmds, L l) {
if (empty(l)) ret p("No entries");
//LS fields = data.fields();
//if (fields == null) fields = allKeysFromList_inOrder();
l = map(l, map -> {
O id = itemID(map);
map = mapMinusKeys(map, joinSets(unshownFields, unlistedFields));
MapSO map2 = postProcessTableRow(map, mapToMap(
(key, value) -> pair(encodeField(key), renderValue(key, value)),
map));
if (singleton)
map2.remove(data.fieldNameToHTML(data.idField())); // don't show ID in table in singleton mode
if (withCmds)
map2 = addCmdsToTableRow(map, map2);
// add anchor to row
map2.put(firstKey(map2), aname("obj" + id, firstValue(map2)));
ret map2;
});
ret hpostform(
htmlTable2_noHtmlEncode(l, tableParams())
+ (!withCmds || !showCheckBoxes ? "" : "\n" + pUnlessEmpty(renderBulkCmds())),
action := baseLink);
}
swappable S renderBulkCmds() {
ret "Bulk action: " + hselect("bulkAction", litorderedmap("" := "", "deleteSelected" := "Delete selected"))
+ " " + hsubmit("OK", onclick := "return confirm('Are you sure?')");
}
MapSO addCmdsToTableRow(MapSO map, MapSO map2) {
if (showCheckBoxes) {
O id = itemID(map);
map2.put(checkBoxKey(), hcheckbox("obj_" + id, false, title := "Select this object for a bulk action"));
map2 = putKeysFirst(map2, checkBoxKey());
}
map2.put(cmdsKey(), renderCmds(map));
if (cmdsLeft)
map2 = putKeysFirst(map2, cmdsKey());
ret map2;
}
/*swappable*/ O[] tableParams() {
ret litparams(
tdParams := litparams(valign := "top"),
tableParams := litparams(class := tableClass));
}
S renderForm(MapSO map) {
map = mapMinusKeys(map, joinSets(unshownFields, uneditableFields));
LLS matrix = map(map, (field, value) -> {
S help = data.fieldHelp(field);
ret ll(
encodeField(field),
renderInput(field, value)
+ (empty(help) ? "" : p(small(help), style := "text-align: right"))
);
});
massageFormMatrix(map, matrix);
ret htableRaw_valignTop(matrix
, empty(formTableClass) ? litparams(border := 1, cellpadding := 4) : litparams(class := formTableClass));
}
S renderInput(S field, O value) {
S name = "f_" + field;
ret renderInput(name, data.getRenderer(field), value);
}
S renderInput(S name, HCRUD_Data.Renderer r, O value) {
//print("Renderer for " + name + ": " + r);
if (r != null) value = r.preprocessValue(value);
// switch by renderer type
if (r cast HCRUD_Data.TextArea)
ret htextarea(strOrEmpty(value), +name, cols := r.cols, rows := r.rows);
if (r cast HCRUD_Data.TextField)
ret htextfield(name, strOrEmpty(value), size := r.cols, style := "font-family: monospace");
if (r cast HCRUD_Data.ComboBox)
ret renderComboBox(name, r.valueToEntry(value), r.entries);
if (r cast HCRUD_Data.CheckBox)
//ret hcheckbox(name, isTrue(value));
ret htrickcheckboxWithText(name, "", isTrue(value));
if (r cast HCRUD_Data.FlexibleLengthList) {
L list = cast value;
new LS rows;
int leeway = 5, n = l(list)+leeway;
for i to n: {
O item = _get(list, i);
print("Item: " + item);
rows.add(tr(td(i+1 + ".", align := "right")
+ td(renderInput(name + "_" + i, r.itemRenderer, item))));
}
ret htag table(lines(rows));
}
ret renderInput_default(name, value);
}
S renderInput_default(S name, O value) {
ret htextfield(name, strOrEmpty(value), size := defaultTextFieldCols);
}
S renderNewForm() {
ret renderNewForm(data.emptyObject());
}
S renderNewForm(MapSO map1) {
//printStruct("renderNewForm", map1);
Map map = mapWithoutKey(map1, data.idField());
//printStruct("renderNewForm", map);
ret hpostform(
hhidden("action", "create") +
renderForm(map)
+ p(hsubmit("Create")),
paramsPlus(formParameters(), action := baseLink));
}
swappable O[] formParameters() { null; }
S renderEditForm(S id) {
Map map = mapWithoutKey(data.getObject(id), data.idField());
if (map == null) ret htmlEncode2("Entry " + id + " not found");
ret hpostform(
hhidden("action", "update") +
hhidden(+id) +
p("Object ID: " + htmlEncode2(id)) +
renderForm(map)
+ p(hsubmit("Save changes")),
paramsPlus(formParameters(), action := baseLink));
}
S renderPage(SS params) {
if (eqGet(params, "cmd", "new"))
ret frame("New " + itemName(), renderNewForm());
if (nempty(params.get("edit")))
ret frame("Edit " + itemName(), renderEditForm(params.get("edit")));
if (nempty(params.get("duplicate")))
ret frame("New " + itemName(), renderNewForm(data.getObject(params.get("duplicate"))));
ret frame(ahref(baseLink, firstToUpper(singleton ? data.itemName() : data.itemNamePlural())), render(mutationRights, params));
}
HCRUD makeFrame(MakeFrame makeFrame) { super.makeFrame(makeFrame); this; }
S cmdsKey() { ret ""; }
S checkBoxKey() { ret ""; }
S itemName() { ret data.itemName(); }
swappable MapSO postProcessTableRow(MapSO data, MapSO rendered) { ret rendered; }
O itemID(MapSO item) { ret mapGet(item, data.idField()); }
swappable S renderCmds(MapSO item) {
O id = itemID(item);
ret joinNemptiesWithVBar(
!actuallyAllowEdit() ? null : ahref(editLink(id), "EDIT"),
!actuallyAllowDelete() ? null :
!data.objectCanBeDeleted(id) ?
span_title("Object can't be deleted, please delete references first", htmlEncode2(unicode_DEL()))
: ahrefWithConfirm(
"Really delete item " + id + "?", deleteLink(id), htmlEncode2(unicode_DEL()), title := "delete"),
!actuallyAllowCreate() ? null : ahref(duplicateLink(id), "dup", title := "duplicate")
);
}
bool actuallyAllowCreate() {
ret !singleton && allowCreateOrDelete && allowCreate;
}
bool actuallyAllowEdit() {
ret allowCreateOrDelete && allowEdit;
}
bool actuallyAllowDelete() {
ret !singleton && allowCreateOrDelete;
}
// e.g. for adding rows to edit/create form
swappable void massageFormMatrix(MapSO map, LLS matrix) {
}
swappable S renderComboBox(S name, S value, LS entries) {
if (haveJQuery) {
// make searchable list if JQuery is available
S id = aGlobalID();
ret tag datalist(+id, lmap hoption(entries))
+ tag input("", +name, list := id);
}
// standard non-searchable list
ret hselect_list(entries, value, +name);
}
}