File a document into a folder named by one of its fields
What it does. A document lands in an inbox folder. Once the Customer field is filled in, the script copies the document into a folder named after the customer, and creates that folder the first time it’s needed. Running it twice for the same document does nothing the second time.
What you need.
- The MaSH feature.
- A share with a Customer custom field. Captur or a person fills it in.
- Two triggers on Settings > MaSH > Events: Captur Extracted (if Captur fills the field) and Fields Changed (if a person does). Filter both to the inbox’s Path prefix, so the script doesn’t see its own filed copies. See Events.
- Two script settings (config parameters):
PARENT(the folder that holds the customer folders) and, optionally,FIELD(the field to read, defaultCustomer).
The script
Section titled “The script”set errorblock to "onError"block onError printline "Error on line {{mash.error.line}}: {{mash.error.message}}"end
# Make a safe folder name out of a field value.block folderNameFor raw set name to mash.str(raw ?? "").trim().toString() set name to mash.str(name).replaceMatches('#[\\/:*?"<>|]#', "-").toString() return mash.str(name).trim().toString()end
set fieldName to mash.config.get("FIELD") ?? "Customer"set parentPath to mash.config.get("PARENT")
if not parentPath fail "PARENT is not set on this automation."end
set doc to mash.event.fileif not doc returnendif doc.is_dir returnend
# The field is empty until Captur or a person fills it in. Wait quietly.set folderName to folderNameFor(doc.fields[fieldName] ?? "")if folderName is "" returnend
set parent to mash.files.get(doc.share, parentPath)set target to parent.make(folderName, true)
# Already filed? Then this is a repeat trigger. Stop.if mash.files.exists(doc.share, target.path ~ "/" ~ doc.name) returnend
set copy to doc.duplicate(target)if not copy fail "Could not copy {{doc.name}} into {{target.name}}"end
printline "Filed {{doc.name}} into {{target.name}}"errorblock = "onError"block onError() { printline("Error on line {{mash.error.line}}: {{mash.error.message}}")}
# Make a safe folder name out of a field value.block folderNameFor(raw) { name = mash.str(raw ?? "").trim().toString() name = mash.str(name).replaceMatches('#[\\/:*?"<>|]#', "-").toString() return mash.str(name).trim().toString()}
fieldName = mash.config.get("FIELD") ?? "Customer"parentPath = mash.config.get("PARENT")
if (not parentPath) { fail "PARENT is not set on this automation."}
doc = mash.event.fileif (not doc) { return}if (doc.is_dir) { return}
# The field is empty until Captur or a person fills it in. Wait quietly.folderName = folderNameFor(doc.fields[fieldName] ?? "")if (folderName == "") { return}
parent = mash.files.get(doc.share, parentPath)target = parent.make(folderName, true)
# Already filed? Then this is a repeat trigger. Stop.if (mash.files.exists(doc.share, target.path ~ "/" ~ doc.name)) { return}
copy = doc.duplicate(target)if (not copy) { fail "Could not copy {{doc.name}} into {{target.name}}"}
printline("Filed {{doc.name}} into {{target.name}}")How it works
Section titled “How it works”- The error block prints the line and message of anything that goes wrong, so the run log says what happened. The Errors page explains why a run doesn’t stop by itself.
folderNameForturns a field value into a folder name. A scanned value can hold a slash or a colon, which a folder name can’t. Swapping those for-keeps the script from failing on one odd document.- The guards return quietly for a run with no file, a folder, or an empty field. Both triggers fire for every document, and a run can arrive before the field is filled in. Captur Extracted or Fields Changed brings the script back when it is.
parent.make(folderName, true)creates the folder, or gives you the existing one. It’s safe to run every time.- The
existscheck is what keeps the script from looping. The copy it makes is itself a new file, and a new file can fire events again. The second time round, the copy is already there, so the script stops. Filtering the trigger to the inbox does the same job a second way. duplicate(target)takes the folder object, not a path in text. Text would be read as a path on the document’s own share.
Variations
Section titled “Variations”- Move instead of copy. Use
doc.move(target). Then the inbox empties itself. Theexistscheck is still worth keeping. - A second level. Add the year:
target = target.make(mash.date.now().format("Y"), true). The dates section of the tutorial showsformat. - A different share. Pass the other share to
mash.files.get(otherShare, parentPath). Becauseduplicatetakes a folder object, a copy across shares works. Check that the script’s owner can write there. See Who a script runs as. - Several customers’ documents in one order. The tutorial’s Putting it together gathers every document with the same order number.
- No match, say so. Instead of returning on an empty field, print a line, so the run log shows which documents were left in the inbox.
Library pages used: Files, Fields, Core MaSH (for mash.config).