Rename files from their fields
What it does. When a document’s fields are filled in, the script renames it to <Customer> - <Order No> - <Type>.<ext>. Odd characters are cleaned out, empty parts are skipped, and a file that already has the right name is left alone.
What you need.
- The MaSH feature.
- Fields on the share, here Customer, Order No and Type.
- Triggers: Fields Changed (a person or script edits the fields), and Captur Extracted if Captur fills them. Filter to the share’s path and, if you like, to an extension. See Events. Don’t rely on Indexed: the fields are often empty then.
- A script setting
PATTERN_FIELDS: the field names in order, separated by commas. The default isCustomer,Order No,Type.
The logic, on its own
Section titled “The logic, on its own”The part worth testing is the name. This block builds it, and you can run it without a share:
block cleanPart raw set part to mash.str(raw ?? "").trim().toString() set part to mash.str(part).replaceMatches('#[\\/:*?"<>|]#', "-").toString() return mash.str(part).trim().toString()end
block buildName values extension set parts to [] each values as v set clean to cleanPart(v) if clean is not "" parts[] = clean end end
if count(parts) is 0 return "" end
return mash.collect(parts).join(" - ") ~ "." ~ extensionend
printline buildName(["Acme Ltd", "SO-20417", "Despatch Note"], "pdf")printline buildName(["A/B Traders", null, "Invoice"], "pdf")printline buildName([null, "", " "], "pdf")Acme Ltd - SO-20417 - Despatch Note.pdfA-B Traders - Invoice.pdfblock cleanPart(raw) { part = mash.str(raw ?? "").trim().toString() part = mash.str(part).replaceMatches('#[\\/:*?"<>|]#', "-").toString() return mash.str(part).trim().toString()}
block buildName(values, extension) { parts = [] each (values) as v { clean = cleanPart(v) if (clean != "") { parts[] = clean } }
if (count(parts) == 0) { return "" }
return mash.collect(parts).join(" - ") ~ "." ~ extension}
printline(buildName(["Acme Ltd", "SO-20417", "Despatch Note"], "pdf"))printline(buildName(["A/B Traders", null, "Invoice"], "pdf"))printline(buildName([null, "", " "], "pdf"))Acme Ltd - SO-20417 - Despatch Note.pdfA-B Traders - Invoice.pdfThe last call returns an empty name, so it prints an empty line. The script must treat an empty name as “do nothing”.
The script
Section titled “The script”set errorblock to "onError"block onError printline "Error on line {{mash.error.line}}: {{mash.error.message}}"end
block cleanPart raw set part to mash.str(raw ?? "").trim().toString() set part to mash.str(part).replaceMatches('#[\\/:*?"<>|]#', "-").toString() return mash.str(part).trim().toString()end
set doc to mash.event.fileif not doc returnendif doc.is_dir returnend
set fieldList to mash.config.get("PATTERN_FIELDS") ?? "Customer,Order No,Type"
set parts to []each fieldList.split(",") as fieldName set fieldName to mash.str(fieldName).trim().toString() set value to "" if doc.fields.has(fieldName) set value to cleanPart(doc.fields[fieldName] ?? "") end if value is not "" parts[] = value endend
# Wait until at least two parts are known. One part is not a name yet.if count(parts) is less than 2 returnend
set newName to mash.collect(parts).join(" - ") ~ "." ~ doc.extension
if doc.name is newName returnend
# Never overwrite a different file with the same name.if mash.files.exists(doc.share, doc.parent.path ~ "/" ~ newName) printline "{{newName}} already exists, leaving {{doc.name}} as it is." returnend
set oldName to doc.namedoc.rename(newName)printline "Renamed {{oldName}} to {{newName}}"errorblock = "onError"block onError() { printline("Error on line {{mash.error.line}}: {{mash.error.message}}")}
block cleanPart(raw) { part = mash.str(raw ?? "").trim().toString() part = mash.str(part).replaceMatches('#[\\/:*?"<>|]#', "-").toString() return mash.str(part).trim().toString()}
doc = mash.event.fileif (not doc) { return}if (doc.is_dir) { return}
fieldList = mash.config.get("PATTERN_FIELDS") ?? "Customer,Order No,Type"
parts = []each (fieldList.split(",")) as fieldName { fieldName = mash.str(fieldName).trim().toString() value = "" if (doc.fields.has(fieldName)) { value = cleanPart(doc.fields[fieldName] ?? "") } if (value != "") { parts[] = value }}
# Wait until at least two parts are known. One part is not a name yet.if (count(parts) < 2) { return}
newName = mash.collect(parts).join(" - ") ~ "." ~ doc.extension
if (doc.name == newName) { return}
# Never overwrite a different file with the same name.if (mash.files.exists(doc.share, doc.parent.path ~ "/" ~ newName)) { printline("{{newName}} already exists, leaving {{doc.name}} as it is.") return}
oldName = doc.namedoc.rename(newName)printline("Renamed {{oldName}} to {{newName}}")How it works
Section titled “How it works”doc.fields.has(name)is checked first, because asking for a field the share doesn’t have is an error, not null. This lets one script serve several shares that carry different fields.- Skipping empty parts stops names like
Acme Ltd - - pdffrom appearing while Captur is still working. - The two-part minimum is a policy choice. Pick whatever number means “enough to be a useful name” for your documents.
doc.name == newNameis what makes the script safe to run twice. A rename can fire further events for the same file, and the script must settle instead of renaming again.oldNameis copied before the rename so the message can name both.- The
existscheck refuses to overwrite another file.
Variations
Section titled “Variations”- Keep the original name. Add it at the end:
parts[] = doc.display_name. - Add a date. Use
mash.date.now().format("Y-m-d")as the first part for sortable names. See Date. - A counter for duplicates. When the name exists, try
newNamewith(2),(3)added in awhileloop. Cap the loop, since the run has a time limit. - Write the new name back to a field.
doc.fields.set("Filed name", newName)records what the script did.