Skip to content

Instantly share code, notes, and snippets.

@JamoCA
Last active November 11, 2024 02:22
Show Gist options
  • Save JamoCA/bc5c58e829e191947f2e34289fd98a5a to your computer and use it in GitHub Desktop.
Save JamoCA/bc5c58e829e191947f2e34289fd98a5a to your computer and use it in GitHub Desktop.
ColdFusion UDF that accept a struct with simple key-value pairs to generate legacy-valid, email-friendly html table and text-only key-value output
<cfscript>
/**
* structToTable UDF
* @displayname structToTable
* @author James Moberg http://sunstarmedia.com, @sunstarmedia
* @version 1
* @lastUpdate 10/28/2024 15:58
* @gist https://gist.github.com/JamoCA/bc5c58e829e191947f2e34289fd98a5a
* @blog https://dev.to/gamesover/structtotable-generate-htmltext-output-for-displayemail-1mk4
* @twitter https://x.com/gamesover/status/1851046259387469955
* @LinkedIn https://www.linkedin.com/posts/jamesmoberg_coldfusion-activity-7256824771630243841-gYX7
* @param String data A struct with key - simple value pairs. Required.
* @param String tableId Table ID to use. Optional.
* @param String tableClass Table class name(s) to use. Optional.
* @param Any keysToRedact Array to string list of keys to redact. Optional.
* @param Any keysToRemove Array to string list of keys to remove. Optional.
* @param Any sortOrder Array to string list of keys to prioritize in order (if they exist). Optional.
* @param Boolean niceVars An option to humanize variable names. Optional. Default = true
*/
public struct function structToTable(
required struct data,
string tableId="",
string tableClass="",
any keysToRedact="",
any keysToRemove="",
any sortOrder="",
boolean niceVars=true
) output=false hint="I accept a struct with key-value pairs to generate legacy-valid, email-friendly html table and text-only key-value output" {
local.data = duplicate(arguments.data);
local.html = ["<table"];
local.text = [];
local.td = "<td style=""word-break:break-word;"">";
local.row = 0;
local.sortedKeys = [];
string function humanizeHeader(string string) hint="Convert a camelized/dasherized/underscored string into a humanized one" {
arguments.string = rereplace(arguments.string, "([^[:alnum:]_-]+)", " ", "ALL").replaceAll("_", " ");
arguments.string = rereplace(arguments.string, "([A-Z]+)([A-Z][a-z])", "\1 \2", "ALL");
return rereplace(arguments.string, "([a-z\d])([A-Z])", "\1 \2", "ALL").replaceAll("\s+", " ");
}
if (len(trim(arguments.tableId))) arrayappend(local.html, " id=""#trim(arguments.tableId)#""");
if (len(trim(arguments.tableClass))) arrayappend(local.html, " tableClass=""#trim(arguments.tableClass)#""");
// update Sort Order
local.sortOrder = (isarray(arguments.sortOrder)) ? arguments.sortOrder : (issimplevalue(arguments.sortOrder)) ? listtoarray(arguments.sortOrder) : [];
// Get initial keys with original key case, append to sort order if they don't exist in user-defined order
local.initialKeys = listtoarray(structkeylist(local.data));
for(local.key in local.sortOrder){
if (arrayfindnocase(local.initialKeys, local.key)){
arrayappend(local.sortedKeys, local.initialKeys[arrayfindnocase(local.initialKeys, local.key)]);
}
}
for(local.key in local.data){
if (!arrayfindnocase(local.sortedKeys, local.key)){
arrayappend(local.sortedKeys, local.key);
}
}
// remove keys
local.keys = (isarray(arguments.keysToRemove)) ? arguments.keysToRemove : (issimplevalue(arguments.keysToRemove)) ? listtoarray(arguments.keysToRemove) : [];
for(local.key in local.keys) {
if (arrayfindnocase(local.sortedKeys, local.key)){
arraydeleteat(local.sortedKeys, arrayfindnocase(local.sortedKeys, local.key));
}
}
arrayappend(local.html, " border=""1"" cellspacing=""0"" cellpadding=""2"">");
for(local.key in local.sortedKeys){
local.row += 1;
local.rowAttributes = !(local.row mod 2) ? " style=""background-color:##e0e0e0;"" class=""odd""" : "";
local.keyText = (arguments.niceVars) ? humanizeHeader(local.key) : local.key;
arrayappend(local.html, "<tr#local.rowAttributes#><td style=""font-weight:bold; vertical-align:top;"">#local.keyText#:</td>");
local.val = local.data[local.key];
if (listfindnocase(arguments.keysToRedact, local.key)){
arrayappend(local.html, "#local.td#REDACTED</td>");
arrayappend(local.text, "#local.keyText#: REDACTED");
} else if (isnull(local.data[local.key])){
arrayappend(local.html, "#local.td#NULL</td>");
arrayappend(local.text, "#local.keyText#: NULL");
} else if (!issimplevalue(local.data[local.key])){
arrayappend(local.html, "#local.td#[Complex Object]</td>");
arrayappend(local.text, "#local.keyText#: [Complex Object]");
} else if (isvalid("url", local.data[local.key])){
arrayappend(local.html, "#local.td#<a href=""#local.data[local.key]#"" target=""_blank"" rel=""nofollow noreferrer"">#encodeforhtml(local.data[local.key])#</a></td>");
arrayappend(local.text, "#local.keyText#: #local.val#");
} else {
arrayappend(local.html, local.td & encodeforhtml(local.val) & "</td>");
arrayappend(local.text, "#local.keyText#: #local.val#");
}
arrayappend(local.html, "</tr>");
}
arrayappend(local.html, "</table>");
return [
"html": arraytolist(local.html, " ")
,"text": arraytolist(local.text, chr(13) & chr(10))
];
}
</cfscript>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment