Public Access
1060 lines
74 KiB
AppleScript
1060 lines
74 KiB
AppleScript
(*
|
||
_belegversand.scpt – Version 1.4
|
||
Rolle dieses Scripts
|
||
--------------------
|
||
Dieses Script ist die eigentliche Versandlogik des Systems.
|
||
Es uebernimmt:
|
||
- Auslesen der Konfiguration aus DEVONthink
|
||
- Pruefung der aktuellen Auswahl
|
||
- Herausfiltern bereits markierter Dokumente
|
||
- Export in einen temporaeren Arbeitsordner
|
||
- Erzeugen eines passwortgeschuetzten ZIP-Archivs
|
||
- Erzeugen eines Mail-Entwurfs in Apple Mail
|
||
- optionales Tagging der verarbeiteten Dokumente
|
||
- Protokollierung des Versands in NDJSON und Markdown
|
||
*)
|
||
|
||
property sentTag : "gesendet"
|
||
property defaultMailTo : "belegonline@irgendwer.de"
|
||
property defaultMailSubjectPrefix : "Belege"
|
||
property defaultLogSentGroupName : "Versendet"
|
||
property defaultWriteDebugLog : false
|
||
property defaultMailBodyFolderName : "mail_vorlagen"
|
||
property defaultMailBodyRecordName : "belegversand"
|
||
property defaultDryRunPreviewPath : "/tmp/DT-Belegversand-preview.txt"
|
||
property configGroupName : "_config"
|
||
property configRecordName : "belegversand.conf"
|
||
property debugLogPath : "/tmp/DT-Belegversand-debug.log"
|
||
|
||
property cfgConfigRecordUUID : missing value
|
||
property cfgMailToDefault : missing value
|
||
property cfgMailCcToDefault : missing value
|
||
property cfgMailSubjectPrefix : missing value
|
||
property cfgZipPasswordDefault : missing value
|
||
property cfgLogSentGroupName : missing value
|
||
property cfgBelegeRootUUID : missing value
|
||
property cfgWriteDebugLog : missing value
|
||
property cfgMailBodyFolderName : missing value
|
||
property cfgMailBodyRecordName : missing value
|
||
property cfgSentTagForDocs : missing value
|
||
property cfgDryRunPreviewPath : missing value
|
||
|
||
on writeDebug(msg)
|
||
if my writeDebugLogEnabled() is false then return
|
||
set ts to do shell script "date '+%Y-%m-%d %H:%M:%S'"
|
||
do shell script "printf %s\\\\n " & quoted form of ("[" & ts & "] " & msg) & " >> " & quoted form of my debugLogPath
|
||
end writeDebug
|
||
|
||
on resetDebugLog()
|
||
if my writeDebugLogEnabled() is false then return
|
||
do shell script "rm -f " & quoted form of my debugLogPath
|
||
my writeDebug("=== Debug-Session gestartet ===")
|
||
end resetDebugLog
|
||
|
||
on writeDebugLogEnabled()
|
||
try
|
||
if my cfgWriteDebugLog is missing value then return defaultWriteDebugLog
|
||
return my cfgWriteDebugLog
|
||
on error
|
||
return defaultWriteDebugLog
|
||
end try
|
||
end writeDebugLogEnabled
|
||
|
||
on isoTimestamp()
|
||
return (do shell script "date '+%Y-%m-%dT%H:%M:%S%z' | sed -E 's/([+-][0-9]{2})([0-9]{2})/\\1:\\2/'")
|
||
end isoTimestamp
|
||
|
||
on batchId()
|
||
return (do shell script "date '+%Y%m%d-%H%M%S'")
|
||
end batchId
|
||
|
||
on ensureFolder(posixPath)
|
||
do shell script "mkdir -p " & quoted form of posixPath
|
||
end ensureFolder
|
||
|
||
on jsonEscape(s)
|
||
if s is missing value then set s to ""
|
||
set s to s as text
|
||
set s to my replaceText("\\", "\\\\", s)
|
||
set s to my replaceText("\"", "\\\"", s)
|
||
set s to my replaceText(return, "\\n", s)
|
||
return s
|
||
end jsonEscape
|
||
|
||
on mdEscape(s)
|
||
if s is missing value then set s to ""
|
||
set s to s as text
|
||
set s to my replaceText("|", "\\|", s)
|
||
set s to my replaceText(return, " ", s)
|
||
return s
|
||
end mdEscape
|
||
|
||
on replaceText(find, repl, txt)
|
||
set oldTIDs to AppleScript's text item delimiters
|
||
try
|
||
if txt is missing value then set txt to ""
|
||
set txt to txt as text
|
||
set AppleScript's text item delimiters to find
|
||
set parts to every text item of txt
|
||
set AppleScript's text item delimiters to repl
|
||
set txt2 to parts as text
|
||
set AppleScript's text item delimiters to oldTIDs
|
||
return txt2
|
||
on error errMsg number errNum
|
||
set AppleScript's text item delimiters to oldTIDs
|
||
error errMsg number errNum
|
||
end try
|
||
end replaceText
|
||
|
||
on isGroupByUUID(recordUUID)
|
||
tell application id "DNtp"
|
||
set r to get record with uuid recordUUID
|
||
try
|
||
return ((type of r) is group)
|
||
on error errMsg
|
||
my writeDebug("isGroupByUUID error uuid=" & recordUUID & " msg=" & errMsg)
|
||
return false
|
||
end try
|
||
end tell
|
||
end isGroupByUUID
|
||
|
||
on getDTRecordTextByUUID(recordUUID)
|
||
tell application id "DNtp"
|
||
set theRecord to get record with uuid recordUUID
|
||
try
|
||
set oldText to plain text of theRecord
|
||
if oldText is missing value then set oldText to ""
|
||
return oldText as rich text
|
||
on error
|
||
try
|
||
set oldText to content of theRecord
|
||
if oldText is missing value then set oldText to ""
|
||
return oldText as rich text
|
||
on error
|
||
return ""
|
||
end try
|
||
end try
|
||
end tell
|
||
end getDTRecordTextByUUID
|
||
|
||
on setDTRecordTextByUUID(recordUUID, newText)
|
||
tell application id "DNtp"
|
||
set theRecord to get record with uuid recordUUID
|
||
try
|
||
set content of theRecord to newText
|
||
on error
|
||
try
|
||
set plain text of theRecord to newText
|
||
on error errMsg
|
||
error "setDTRecordTextByUUID failed: " & errMsg
|
||
end try
|
||
end try
|
||
end tell
|
||
end setDTRecordTextByUUID
|
||
|
||
on appendLineToDTRecordByUUID(recordUUID, lineText)
|
||
my writeDebug("appendLineToDTRecordByUUID START recordUUID=" & recordUUID)
|
||
set oldText to my getDTRecordTextByUUID(recordUUID)
|
||
|
||
if oldText is "" then
|
||
set newText to lineText & return
|
||
else if oldText ends with return then
|
||
set newText to oldText & lineText & return
|
||
else
|
||
set newText to oldText & return & lineText & return
|
||
end if
|
||
|
||
my setDTRecordTextByUUID(recordUUID, newText)
|
||
my writeDebug("appendLineToDTRecordByUUID DONE recordUUID=" & recordUUID)
|
||
end appendLineToDTRecordByUUID
|
||
|
||
on getSelectedUUIDs()
|
||
tell application id "DNtp"
|
||
set sel to selection
|
||
if sel is {} then error "Keine Objekte ausgewählt."
|
||
|
||
set selCount to count of sel
|
||
set rawList to {}
|
||
|
||
repeat with i from 1 to selCount
|
||
set anItem to item i of sel
|
||
set end of rawList to (uuid of anItem as rich text)
|
||
end repeat
|
||
end tell
|
||
|
||
set outList to {}
|
||
repeat with itemUUID in rawList
|
||
set uuidText to itemUUID as text
|
||
if my isGroupByUUID(uuidText) then
|
||
my writeDebug("Selection ignored group uuid=" & uuidText)
|
||
else
|
||
set end of outList to uuidText
|
||
end if
|
||
end repeat
|
||
|
||
return {selCount, outList}
|
||
end getSelectedUUIDs
|
||
|
||
on recordHasTagByUUID(recordUUID, tagName)
|
||
tell application id "DNtp"
|
||
set r to get record with uuid recordUUID
|
||
try
|
||
set t to tags of r
|
||
if t is missing value then return false
|
||
return (t contains tagName)
|
||
on error
|
||
return false
|
||
end try
|
||
end tell
|
||
end recordHasTagByUUID
|
||
|
||
on filterUUIDsWithoutTag(uuidList, tagName)
|
||
set outList to {}
|
||
repeat with thisUUID in uuidList
|
||
set uuidText to thisUUID as text
|
||
set hasTag to my recordHasTagByUUID(uuidText, tagName)
|
||
my writeDebug("Tag-Check uuid=" & uuidText & " hasTag=" & (hasTag as text))
|
||
if hasTag is false then
|
||
set end of outList to uuidText
|
||
end if
|
||
end repeat
|
||
return outList
|
||
end filterUUIDsWithoutTag
|
||
|
||
on findChildRecordUUIDByName(parentUUID, childName)
|
||
tell application id "DNtp"
|
||
set parentRecord to get record with uuid parentUUID
|
||
try
|
||
set kids to children of parentRecord
|
||
repeat with i from 1 to count of kids
|
||
set c to item i of kids
|
||
if (name of c as rich text) is childName then return (uuid of c as rich text)
|
||
end repeat
|
||
end try
|
||
end tell
|
||
return missing value
|
||
end findChildRecordUUIDByName
|
||
|
||
on getOrCreateChildGroupUUID(parentUUID, groupName)
|
||
set existingUUID to my findChildRecordUUIDByName(parentUUID, groupName)
|
||
if existingUUID is not missing value then
|
||
my writeDebug("Group exists: " & groupName & " uuid=" & existingUUID)
|
||
return existingUUID
|
||
end if
|
||
|
||
tell application id "DNtp"
|
||
set parentRecord to get record with uuid parentUUID
|
||
set newGroup to create record with {name:groupName, type:group} in parentRecord
|
||
set newUUID to uuid of newGroup as rich text
|
||
my writeDebug("Group created: " & groupName & " uuid=" & newUUID)
|
||
return newUUID
|
||
end tell
|
||
end getOrCreateChildGroupUUID
|
||
|
||
on getOrCreateTextLogUUID(parentUUID, childName, initialText)
|
||
set existingUUID to my findChildRecordUUIDByName(parentUUID, childName)
|
||
if existingUUID is not missing value then
|
||
my writeDebug("Text log exists: " & childName & " uuid=" & existingUUID)
|
||
return existingUUID
|
||
end if
|
||
|
||
tell application id "DNtp"
|
||
set parentRecord to get record with uuid parentUUID
|
||
set newRecord to create record with {name:childName, type:txt, content:initialText} in parentRecord
|
||
set newUUID to uuid of newRecord as rich text
|
||
my writeDebug("Text log created: " & childName & " uuid=" & newUUID)
|
||
return newUUID
|
||
end tell
|
||
end getOrCreateTextLogUUID
|
||
|
||
on getOrCreateMarkdownLogUUID(parentUUID, childName, initialText)
|
||
set existingUUID to my findChildRecordUUIDByName(parentUUID, childName)
|
||
if existingUUID is not missing value then
|
||
my writeDebug("Markdown log exists: " & childName & " uuid=" & existingUUID)
|
||
return existingUUID
|
||
end if
|
||
|
||
tell application id "DNtp"
|
||
set parentRecord to get record with uuid parentUUID
|
||
set newRecord to create record with {name:childName, type:markdown, content:initialText} in parentRecord
|
||
set newUUID to uuid of newRecord as rich text
|
||
my writeDebug("Markdown log created: " & childName & " uuid=" & newUUID)
|
||
return newUUID
|
||
end tell
|
||
end getOrCreateMarkdownLogUUID
|
||
|
||
on getRecordNameByUUID(recordUUID)
|
||
tell application id "DNtp"
|
||
set r to get record with uuid recordUUID
|
||
try
|
||
return name of r as rich text
|
||
on error
|
||
return ""
|
||
end try
|
||
end tell
|
||
end getRecordNameByUUID
|
||
|
||
on getRecordFileNameByUUID(recordUUID)
|
||
tell application id "DNtp"
|
||
set r to get record with uuid recordUUID
|
||
try
|
||
set fn to filename of r
|
||
if fn is not missing value then
|
||
set fnText to fn as rich text
|
||
if fnText is not "" then return fnText
|
||
end if
|
||
end try
|
||
|
||
try
|
||
return name of r as rich text
|
||
on error
|
||
return ""
|
||
end try
|
||
end tell
|
||
end getRecordFileNameByUUID
|
||
|
||
on getReferenceURLByUUID(recordUUID)
|
||
tell application id "DNtp"
|
||
set r to get record with uuid recordUUID
|
||
try
|
||
return reference URL of r as rich text
|
||
on error
|
||
return ""
|
||
end try
|
||
end tell
|
||
end getReferenceURLByUUID
|
||
|
||
on addTagByUUID(recordUUID, tagName)
|
||
my writeDebug("addTag START uuid=" & recordUUID)
|
||
tell application id "DNtp"
|
||
set r to get record with uuid recordUUID
|
||
|
||
set t to {}
|
||
try
|
||
set t to tags of r
|
||
if t is missing value then set t to {}
|
||
on error errMsg
|
||
my writeDebug("addTag tags-read error uuid=" & recordUUID & " msg=" & errMsg)
|
||
set t to {}
|
||
end try
|
||
|
||
if t does not contain tagName then
|
||
set end of t to tagName
|
||
set tags of r to t
|
||
my writeDebug("addTag wrote tag uuid=" & recordUUID)
|
||
else
|
||
my writeDebug("addTag tag already present uuid=" & recordUUID)
|
||
end if
|
||
end tell
|
||
|
||
set verifyTag to my recordHasTagByUUID(recordUUID, tagName)
|
||
my writeDebug("addTag DONE uuid=" & recordUUID & " verify=" & (verifyTag as text))
|
||
end addTagByUUID
|
||
|
||
on exportRecordByUUID(recordUUID, targetFolderAlias)
|
||
my writeDebug("export START uuid=" & recordUUID)
|
||
tell application id "DNtp"
|
||
set r to get record with uuid recordUUID
|
||
if r is missing value then error "Record nicht gefunden für UUID: " & recordUUID
|
||
export record r to targetFolderAlias
|
||
end tell
|
||
my writeDebug("export DONE uuid=" & recordUUID)
|
||
end exportRecordByUUID
|
||
|
||
on findChildRecordByName(parentRecord, childName)
|
||
tell application id "DNtp"
|
||
try
|
||
set kids to children of parentRecord
|
||
repeat with i from 1 to count of kids
|
||
set c to item i of kids
|
||
if (name of c as rich text) is childName then return c
|
||
end repeat
|
||
end try
|
||
end tell
|
||
return missing value
|
||
end findChildRecordByName
|
||
|
||
on getTopParentGroupFromUUID(recordUUID)
|
||
tell application id "DNtp"
|
||
set currentRecord to get record with uuid recordUUID
|
||
|
||
repeat
|
||
try
|
||
set p to parents of currentRecord
|
||
if p is {} then return currentRecord
|
||
set currentRecord to item 1 of p
|
||
on error
|
||
return currentRecord
|
||
end try
|
||
end repeat
|
||
end tell
|
||
end getTopParentGroupFromUUID
|
||
|
||
on findConfigRecordUUIDFromCurrentSelection()
|
||
tell application id "DNtp"
|
||
set sel to selection
|
||
if sel is {} then error "Keine Objekte ausgewählt."
|
||
set selectedRecord to item 1 of sel
|
||
set selectedUUID to uuid of selectedRecord as rich text
|
||
end tell
|
||
|
||
set dbRootRecord to my getTopParentGroupFromUUID(selectedUUID)
|
||
set configGroupRecord to my findChildRecordByName(dbRootRecord, my configGroupName)
|
||
if configGroupRecord is missing value then error "Keine Konfiguration gefunden (_config fehlt). Bitte zuerst das Setup-Script ausführen."
|
||
|
||
set configRecord to my findChildRecordByName(configGroupRecord, my configRecordName)
|
||
if configRecord is missing value then error "Keine Konfiguration gefunden (" & my configRecordName & " fehlt). Bitte zuerst das Setup-Script ausführen."
|
||
|
||
tell application id "DNtp"
|
||
return uuid of configRecord as rich text
|
||
end tell
|
||
end findConfigRecordUUIDFromCurrentSelection
|
||
|
||
on trimText(s)
|
||
set s to s as text
|
||
set s to do shell script "printf %s " & quoted form of s & " | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//'"
|
||
return s
|
||
end trimText
|
||
|
||
on parseCSVEmails(csvText)
|
||
if csvText is "" or csvText is missing value then return {}
|
||
|
||
set csvText to csvText as text
|
||
set emails to {}
|
||
set saveDelimiters to AppleScript's text item delimiters
|
||
try
|
||
set AppleScript's text item delimiters to ","
|
||
set parts to every text item of csvText
|
||
set AppleScript's text item delimiters to saveDelimiters
|
||
|
||
repeat with part in parts
|
||
set trimmedPart to my trimText(part as text)
|
||
if trimmedPart is not "" then
|
||
set end of emails to trimmedPart
|
||
end if
|
||
end repeat
|
||
|
||
return emails
|
||
on error errMsg
|
||
set AppleScript's text item delimiters to saveDelimiters
|
||
my writeDebug("parseCSVEmails error: " & errMsg)
|
||
return {}
|
||
end try
|
||
end parseCSVEmails
|
||
|
||
on configValue(configText, keyName)
|
||
set keyPrefix to keyName & "="
|
||
repeat with oneLine in paragraphs of configText
|
||
set lineText to oneLine as text
|
||
if lineText starts with keyPrefix then
|
||
if (length of lineText) is equal to (length of keyPrefix) then
|
||
return ""
|
||
else
|
||
set val to text ((length of keyPrefix) + 1) thru -1 of lineText
|
||
return my trimText(val)
|
||
end if
|
||
end if
|
||
end repeat
|
||
return missing value
|
||
end configValue
|
||
|
||
on loadConfig()
|
||
set configUUID to my findConfigRecordUUIDFromCurrentSelection()
|
||
set cfgText to my getDTRecordTextByUUID(configUUID)
|
||
if cfgText is "" then error "Konfigurationsdatei ist leer: " & my configRecordName
|
||
|
||
set vMailTo to my configValue(cfgText, "MAIL_TO_DEFAULT")
|
||
if vMailTo is missing value then set vMailTo to defaultMailTo
|
||
|
||
set vMailCcTo to my configValue(cfgText, "MAIL_CC_TO_DEFAULT")
|
||
if vMailCcTo is missing value or vMailCcTo is "" then set vMailCcTo to ""
|
||
|
||
set vSubjectPrefix to my configValue(cfgText, "MAIL_SUBJECT_PREFIX")
|
||
if vSubjectPrefix is missing value then set vSubjectPrefix to defaultMailSubjectPrefix
|
||
|
||
set vZipPasswordDefault to my configValue(cfgText, "ZIP_PASSWORD_DEFAULT")
|
||
if vZipPasswordDefault is missing value then set vZipPasswordDefault to ""
|
||
|
||
set vLogSentGroupName to my configValue(cfgText, "LOG_SENT_GROUP_NAME")
|
||
if vLogSentGroupName is missing value then set vLogSentGroupName to defaultLogSentGroupName
|
||
|
||
set vBelegeRootUUID to my configValue(cfgText, "BELEGE_ROOT_UUID")
|
||
if vBelegeRootUUID is missing value or vBelegeRootUUID is "" then error "BELEGE_ROOT_UUID fehlt in " & my configRecordName
|
||
|
||
set vWriteDebugLog to my configValue(cfgText, "WRITE_DEBUG_LOG")
|
||
if vWriteDebugLog is missing value then
|
||
set vDebugBool to defaultWriteDebugLog
|
||
else
|
||
set vLower to do shell script "printf %s " & quoted form of vWriteDebugLog & " | tr '[:upper:]' '[:lower:]'"
|
||
set vDebugBool to (vLower is "true")
|
||
end if
|
||
|
||
set vMailBodyFolderName to my configValue(cfgText, "MAILBODY_FOLDER_NAME")
|
||
if vMailBodyFolderName is missing value or vMailBodyFolderName is "" then set vMailBodyFolderName to defaultMailBodyFolderName
|
||
|
||
set vMailBodyRecordName to my configValue(cfgText, "MAILBODY_RECORD_NAME")
|
||
if vMailBodyRecordName is missing value or vMailBodyRecordName is "" then set vMailBodyRecordName to defaultMailBodyRecordName
|
||
|
||
set vSentTagForDocs to my configValue(cfgText, "SENT_TAG_FOR_DOCS")
|
||
if vSentTagForDocs is missing value or vSentTagForDocs is "" then set vSentTagForDocs to sentTag
|
||
|
||
set vDryRunPreviewPath to my configValue(cfgText, "PATH_TO_DRYRUN_PREVIEW")
|
||
if vDryRunPreviewPath is missing value or vDryRunPreviewPath is "" then set vDryRunPreviewPath to defaultDryRunPreviewPath
|
||
|
||
return {configUUID:configUUID, mailToDefault:vMailTo, mailCcToDefault:vMailCcTo, subjectPrefix:vSubjectPrefix, zipPasswordDefault:vZipPasswordDefault, logSentGroupName:vLogSentGroupName, belegeRootUUID:vBelegeRootUUID, writeDebugLog:vDebugBool, mailBodyFolderName:vMailBodyFolderName, mailBodyRecordName:vMailBodyRecordName, sentTagForDocs:vSentTagForDocs, dryRunPreviewPath:vDryRunPreviewPath}
|
||
end loadConfig
|
||
|
||
on runTemplateSelector()
|
||
set basePath to (do shell script "echo ~") & "/Library/Application Scripts/com.devon-technologies.think/Menu/Belegversand_1-4"
|
||
set scriptPath to basePath & "/Helpers/show_mail_templates.scpt"
|
||
|
||
my writeDebug("Versuche Template-Script zu laden: " & scriptPath)
|
||
|
||
try
|
||
set loadedScript to load script (POSIX file scriptPath as alias)
|
||
my writeDebug("Template-Script geladen, starte jetzt...")
|
||
set templateResult to run loadedScript
|
||
my writeDebug("Template-Script fertig. Rueckgabe: '" & templateResult & "'")
|
||
return templateResult
|
||
on error errMsg
|
||
my writeDebug("FEHLER in runTemplateSelector: " & errMsg)
|
||
return ""
|
||
end try
|
||
end runTemplateSelector
|
||
|
||
on getMailBodyTextFromCurrentDB()
|
||
tell application id "DNtp"
|
||
set sel to selection
|
||
if sel is {} then return ""
|
||
set selectedRecord to item 1 of sel
|
||
set selectedUUID to uuid of selectedRecord as rich text
|
||
end tell
|
||
|
||
set dbRootRecord to my getTopParentGroupFromUUID(selectedUUID)
|
||
set configGroupRecord to my findChildRecordByName(dbRootRecord, my configGroupName)
|
||
if configGroupRecord is missing value then return ""
|
||
|
||
set mailBodyFolderRecord to my findChildRecordByName(configGroupRecord, my cfgMailBodyFolderName)
|
||
if mailBodyFolderRecord is missing value then return ""
|
||
|
||
set templateName to my runTemplateSelector()
|
||
my writeDebug("runTemplateSelector returned: '" & templateName & "'")
|
||
|
||
if templateName is "" then
|
||
my writeDebug("templateName ist leer, fallback zu default: " & my cfgMailBodyRecordName)
|
||
set templateName to my cfgMailBodyRecordName
|
||
end if
|
||
|
||
my writeDebug("Suche nach Mailbody-Record: '" & templateName & "'")
|
||
set mailBodyRecord to my findChildRecordByName(mailBodyFolderRecord, templateName)
|
||
my writeDebug("mailBodyRecord: " & (mailBodyRecord is missing value))
|
||
|
||
if mailBodyRecord is missing value then
|
||
my writeDebug("ERROR: mailBodyRecord nicht gefunden!")
|
||
return ""
|
||
end if
|
||
|
||
tell application id "DNtp"
|
||
try
|
||
set t to plain text of mailBodyRecord
|
||
if t is missing value then set t to ""
|
||
return t as rich text
|
||
on error
|
||
try
|
||
set t to content of mailBodyRecord
|
||
if t is missing value then set t to ""
|
||
return t as rich text
|
||
on error
|
||
return ""
|
||
end try
|
||
end try
|
||
end tell
|
||
end getMailBodyTextFromCurrentDB
|
||
|
||
on safeDryRunPreviewPath()
|
||
set p to my cfgDryRunPreviewPath
|
||
if p is missing value or p is "" then set p to defaultDryRunPreviewPath
|
||
return p as text
|
||
end safeDryRunPreviewPath
|
||
|
||
on writeDryRunPreviewFile(toSendUUIDs, theTo)
|
||
set previewPath to my safeDryRunPreviewPath()
|
||
|
||
set previewDir to do shell script "dirname " & quoted form of previewPath
|
||
do shell script "mkdir -p " & quoted form of previewDir
|
||
|
||
set previewText to "Belegversand – Vorschau (Dry Run)" & return & return & ¬
|
||
"Empfänger: " & theTo & return & ¬
|
||
"Anzahl Belege: " & (count of toSendUUIDs) & return & return
|
||
|
||
set i to 1
|
||
repeat with thisUUID in toSendUUIDs
|
||
set uuidText to thisUUID as text
|
||
set recFileName to my getRecordFileNameByUUID(uuidText)
|
||
set previewText to previewText & i & ". " & recFileName & return
|
||
set i to i + 1
|
||
end repeat
|
||
|
||
do shell script "cat > " & quoted form of previewPath & " <<'EOF'" & linefeed & previewText & "EOF"
|
||
return previewPath
|
||
end writeDryRunPreviewFile
|
||
|
||
on dryRunSummaryText(toSendUUIDs, previewPath)
|
||
set nItems to count of toSendUUIDs
|
||
set maxPreviewItems to 4
|
||
|
||
set outText to "Insgesamt würden " & nItems & " Belege verarbeitet:" & return & return
|
||
|
||
set shownCount to 0
|
||
repeat with thisUUID in toSendUUIDs
|
||
if shownCount is greater than or equal to maxPreviewItems then exit repeat
|
||
set uuidText to thisUUID as text
|
||
set recFileName to my getRecordFileNameByUUID(uuidText)
|
||
set outText to outText & "- " & recFileName & return
|
||
set shownCount to shownCount + 1
|
||
end repeat
|
||
|
||
if nItems is greater than maxPreviewItems then
|
||
set outText to outText & "[...]" & return
|
||
end if
|
||
|
||
set outText to outText & return & "Die gesamte Liste findest du hier:" & return & previewPath
|
||
return outText
|
||
end dryRunSummaryText
|
||
|
||
on showDryRunPreviewInFinder(previewPath)
|
||
do shell script "open -R " & quoted form of previewPath
|
||
end showDryRunPreviewInFinder
|
||
|
||
on isYearGroupName(groupName)
|
||
try
|
||
if (length of groupName) is not 4 then return false
|
||
do shell script "printf %s " & quoted form of groupName & " | /usr/bin/grep -Eq '^[0-9]{4}$'"
|
||
return true
|
||
on error
|
||
return false
|
||
end try
|
||
end isYearGroupName
|
||
|
||
on getBookingYearInfoFromUUID(recordUUID)
|
||
tell application id "DNtp"
|
||
set currentRecord to get record with uuid recordUUID
|
||
|
||
repeat
|
||
try
|
||
set p to parents of currentRecord
|
||
if p is {} then return missing value
|
||
|
||
set parentRecord to item 1 of p
|
||
set parentName to name of parentRecord as rich text
|
||
|
||
if my isYearGroupName(parentName) then
|
||
return {yearUUID:(uuid of parentRecord as rich text), yearName:parentName}
|
||
end if
|
||
|
||
set currentRecord to parentRecord
|
||
on error errMsg
|
||
my writeDebug("getBookingYearInfoFromUUID error: " & errMsg)
|
||
return missing value
|
||
end try
|
||
end repeat
|
||
end tell
|
||
end getBookingYearInfoFromUUID
|
||
|
||
on getSelectedBookingYearInfo(uuidList)
|
||
set firstYearUUID to missing value
|
||
set firstYearName to missing value
|
||
set hasOutsiders to false
|
||
|
||
repeat with thisUUID in uuidList
|
||
set uuidText to thisUUID as text
|
||
set yearInfo to my getBookingYearInfoFromUUID(uuidText)
|
||
|
||
if yearInfo is missing value then
|
||
set hasOutsiders to true
|
||
my writeDebug("BookingYearCheck uuid=" & uuidText & " -> Außenseiter (kein Jahr gefunden)")
|
||
else
|
||
set thisYearUUID to (yearUUID of yearInfo) as text
|
||
set thisYearName to (yearName of yearInfo) as text
|
||
|
||
my writeDebug("BookingYearCheck uuid=" & uuidText & " yearName=" & thisYearName & " yearUUID=" & thisYearUUID)
|
||
|
||
if firstYearUUID is missing value then
|
||
set firstYearUUID to thisYearUUID
|
||
set firstYearName to thisYearName
|
||
my writeDebug("BookingYearReference yearName=" & firstYearName & " yearUUID=" & firstYearUUID)
|
||
else
|
||
if thisYearUUID is not equal to firstYearUUID then
|
||
error "Es wurden Belege aus mehreren Buchungsjahren ausgewählt." & return & return & "Bitte pro Sendelauf nur Belege aus einem Jahr auswählen."
|
||
end if
|
||
my writeDebug("BookingYearCompare first=" & firstYearName & "/" & firstYearUUID & " current=" & thisYearName & "/" & thisYearUUID)
|
||
end if
|
||
end if
|
||
end repeat
|
||
|
||
if hasOutsiders and firstYearUUID is not missing value then
|
||
error "Es wurden Belege aus Buchungsjahren UND von außerhalb ausgewählt." & return & return & "Bitte nur Belege aus einem Bereich wählen."
|
||
end if
|
||
|
||
return {yearUUID:firstYearUUID, yearName:firstYearName, hasOutsiders:hasOutsiders}
|
||
end getSelectedBookingYearInfo
|
||
|
||
on getOrCreateGlobalSentGroupUUID(referenceUUID)
|
||
set dbRootRecord to my getTopParentGroupFromUUID(referenceUUID)
|
||
tell application id "DNtp"
|
||
set dbRootUUID to uuid of dbRootRecord as rich text
|
||
end tell
|
||
|
||
my writeDebug("getOrCreateGlobalSentGroupUUID dbRootUUID=" & dbRootUUID)
|
||
set globalSentGroupUUID to my getOrCreateChildGroupUUID(dbRootUUID, "Versendet_Outsider")
|
||
my writeDebug("globalSentGroupUUID=" & globalSentGroupUUID)
|
||
return globalSentGroupUUID
|
||
end getOrCreateGlobalSentGroupUUID
|
||
|
||
on writeResultRecordByUUID(recordUUID, ts, bid, toAddrs, ccAddrs, nItems, isOutsider)
|
||
my writeDebug("writeResultRecordByUUID START recordUUID=" & recordUUID)
|
||
|
||
set toDisplay to ""
|
||
repeat with addr in toAddrs
|
||
if toDisplay is "" then
|
||
set toDisplay to (addr as text)
|
||
else
|
||
set toDisplay to toDisplay & ", " & (addr as text)
|
||
end if
|
||
end repeat
|
||
|
||
set ccDisplay to ""
|
||
repeat with addr in ccAddrs
|
||
if ccDisplay is "" then
|
||
set ccDisplay to (addr as text)
|
||
else
|
||
set ccDisplay to ccDisplay & ", " & (addr as text)
|
||
end if
|
||
end repeat
|
||
|
||
if ccDisplay is "" then set ccDisplay to "(keine)"
|
||
|
||
my writeDebug("toDisplay=" & toDisplay & " ccDisplay=" & ccDisplay)
|
||
|
||
set resultText to "# Letzter Sendelauf – Ergebnis" & return & return & ¬
|
||
"**Zeitstempel:** " & ts & return & ¬
|
||
"**Batch-ID:** " & bid & return & ¬
|
||
"**Empfänger (TO):** " & toDisplay & return & ¬
|
||
"**Empfänger (CC):** " & ccDisplay & return & ¬
|
||
"**Anzahl Belege:** " & nItems & return & ¬
|
||
"**Status:** ok Erfolgreich" & return & return & ¬
|
||
"Der Mail-Entwurf wurde in Apple Mail erstellt."
|
||
|
||
if isOutsider then
|
||
set resultText to resultText & return & return & "*Hinweis: Dies waren Außenseiter-Belege (von außerhalb eines Buchungsjahres).*"
|
||
end if
|
||
|
||
my writeDebug("resultText length=" & (length of resultText))
|
||
|
||
try
|
||
my setDTRecordTextByUUID(recordUUID, resultText)
|
||
my writeDebug("writeResultRecordByUUID text set OK")
|
||
on error errMsg
|
||
my writeDebug("writeResultRecordByUUID ERROR: " & errMsg)
|
||
end try
|
||
|
||
my writeDebug("writeResultRecordByUUID DONE")
|
||
end writeResultRecordByUUID
|
||
|
||
on showCompletionNotification(nItems)
|
||
my writeDebug("showCompletionNotification START nItems=" & nItems)
|
||
try
|
||
set msgText to (nItems as text) & " Beleg(e) – Mail-Entwurf erstellt."
|
||
my writeDebug("notification message=" & msgText)
|
||
|
||
tell application id "DNtp"
|
||
display notification msgText with title "Belegversand" subtitle "Mail-Entwurf erstellt."
|
||
end tell
|
||
|
||
my writeDebug("notification displayed OK")
|
||
on error errMsg
|
||
my writeDebug("showCompletionNotification ERROR: " & errMsg)
|
||
end try
|
||
my writeDebug("showCompletionNotification DONE")
|
||
end showCompletionNotification
|
||
|
||
on friendlyRunHeader(ts)
|
||
try
|
||
return do shell script "LANG=de_DE.UTF-8 date -j -f '%Y-%m-%dT%H:%M:%S%z' " & quoted form of ((do shell script "printf %s " & quoted form of ts & " | sed 's/://3'")) & " '+Sendelauf am %-d. %B %Y, Uhrzeit %H:%M:%S'"
|
||
on error
|
||
return "Sendelauf am " & ts
|
||
end try
|
||
end friendlyRunHeader
|
||
|
||
on run
|
||
|
||
with timeout of 3600 seconds
|
||
|
||
try
|
||
set selInfo to my getSelectedUUIDs()
|
||
set selCount to item 1 of selInfo
|
||
set selectedUUIDs to item 2 of selInfo
|
||
|
||
if (count of selectedUUIDs) is 0 then error "Keine versendbaren Belege ausgewählt."
|
||
|
||
set bookingYearInfo to my getSelectedBookingYearInfo(selectedUUIDs)
|
||
set bookingYearUUID to yearUUID of bookingYearInfo
|
||
set bookingYearName to yearName of bookingYearInfo
|
||
set isOutsider to hasOutsiders of bookingYearInfo
|
||
|
||
set cfg to my loadConfig()
|
||
|
||
set my cfgConfigRecordUUID to configUUID of cfg
|
||
set my cfgMailToDefault to mailToDefault of cfg
|
||
set my cfgMailCcToDefault to mailCcToDefault of cfg
|
||
set my cfgMailSubjectPrefix to subjectPrefix of cfg
|
||
set my cfgZipPasswordDefault to zipPasswordDefault of cfg
|
||
set my cfgLogSentGroupName to logSentGroupName of cfg
|
||
set my cfgBelegeRootUUID to belegeRootUUID of cfg
|
||
set my cfgWriteDebugLog to writeDebugLog of cfg
|
||
set my cfgMailBodyFolderName to mailBodyFolderName of cfg
|
||
set my cfgMailBodyRecordName to mailBodyRecordName of cfg
|
||
set my cfgSentTagForDocs to sentTagForDocs of cfg
|
||
set my cfgDryRunPreviewPath to dryRunPreviewPath of cfg
|
||
|
||
my resetDebugLog()
|
||
my writeDebug("Script start")
|
||
my writeDebug("Config record UUID=" & my cfgConfigRecordUUID)
|
||
my writeDebug("Config BELEGE_ROOT_UUID=" & my cfgBelegeRootUUID)
|
||
my writeDebug("Resolved booking year early: yearUUID=" & bookingYearUUID & " yearName=" & bookingYearName & " isOutsider=" & isOutsider)
|
||
my writeDebug("Selected raw count=" & selCount)
|
||
my writeDebug("Selected usable count=" & (count of selectedUUIDs))
|
||
|
||
repeat with u in selectedUUIDs
|
||
my writeDebug("Selected uuid=" & (u as text))
|
||
end repeat
|
||
|
||
set toSend to my filterUUIDsWithoutTag(selectedUUIDs, my cfgSentTagForDocs)
|
||
my writeDebug("ToSend count=" & (count of toSend))
|
||
|
||
repeat with u in toSend
|
||
my writeDebug("ToSend uuid=" & (u as text) & " name=" & my getRecordNameByUUID(u as text))
|
||
end repeat
|
||
|
||
if (count of toSend) is 0 then error "Alle ausgewählten Belege sind bereits als versendet markiert und werden übersprungen."
|
||
|
||
set drChoice to button returned of (display dialog "Modus waehlen:" buttons {"Dry Run", "Senden (Entwurf erstellen)", "Abbrechen"} default button "Dry Run")
|
||
|
||
if drChoice is "Abbrechen" then
|
||
my writeDebug("User hat Abbrechen geklickt")
|
||
return
|
||
end if
|
||
|
||
set isDryRun to (drChoice is "Dry Run")
|
||
my writeDebug("Mode isDryRun=" & (isDryRun as text))
|
||
|
||
set theTo to text returned of (display dialog "Empfaenger E-Mail:" default answer my cfgMailToDefault)
|
||
set theCcTo to text returned of (display dialog "CC-Empfaenger (optional):" default answer my cfgMailCcToDefault)
|
||
set zipPass to text returned of (display dialog "ZIP-Passwort:" default answer my cfgZipPasswordDefault with hidden answer)
|
||
|
||
my writeDebug("Recipient=" & theTo)
|
||
|
||
set nItems to count of toSend
|
||
set previewText to "Ausgewählt: " & selCount & return & "Zu senden (ungesendet): " & nItems & return & return & "Empfänger: " & theTo & return
|
||
|
||
if isOutsider then
|
||
set previewText to previewText & "(Außenseiter-Belege → globales Versendet-Log)" & return
|
||
else
|
||
set previewText to previewText & "Buchungsjahr: " & bookingYearName & return
|
||
end if
|
||
|
||
if isDryRun then
|
||
set previewPath to my writeDryRunPreviewFile(toSend, theTo)
|
||
set dryRunText to my dryRunSummaryText(toSend, previewPath)
|
||
|
||
set dryRunChoice to button returned of (display dialog dryRunText buttons {"OK", "Liste im Finder zeigen"} default button "OK")
|
||
if dryRunChoice is "Liste im Finder zeigen" then
|
||
my showDryRunPreviewInFinder(previewPath)
|
||
end if
|
||
|
||
my writeDebug("Dry run end")
|
||
return
|
||
end if
|
||
|
||
set proceed to button returned of (display dialog previewText & return & "Entwurf erstellen & ZIP bauen?" buttons {"Abbrechen", "Weiter"} default button "Weiter")
|
||
if proceed is "Abbrechen" then
|
||
my writeDebug("User aborted before export")
|
||
return
|
||
end if
|
||
|
||
set bid to my batchId()
|
||
set ts to my isoTimestamp()
|
||
my writeDebug("Batch=" & bid & " ts=" & ts & " isOutsider=" & isOutsider)
|
||
|
||
set tmpAlias to (path to temporary items) as alias
|
||
set stageRootPOSIX to (POSIX path of tmpAlias) & "DT-Belegversand/" & bid & "/"
|
||
set stageFilesPOSIX to stageRootPOSIX & "files/"
|
||
my ensureFolder(stageFilesPOSIX)
|
||
|
||
set zipName to "Belege_" & bid & ".zip"
|
||
set zipPath to stageRootPOSIX & zipName
|
||
set stageFolderAlias to (POSIX file stageFilesPOSIX) as alias
|
||
|
||
my writeDebug("Stage root=" & stageRootPOSIX)
|
||
my writeDebug("Stage files=" & stageFilesPOSIX)
|
||
my writeDebug("ZIP path=" & zipPath)
|
||
|
||
repeat with thisUUID in toSend
|
||
set uuidText to thisUUID as text
|
||
my exportRecordByUUID(uuidText, stageFolderAlias)
|
||
end repeat
|
||
|
||
my writeDebug("Stage content before cleanup:")
|
||
try
|
||
set stageListBefore to do shell script "find " & quoted form of stageRootPOSIX & " -print"
|
||
repeat with oneLine in paragraphs of stageListBefore
|
||
if oneLine is not "" then my writeDebug(" " & oneLine)
|
||
end repeat
|
||
on error errMsg
|
||
my writeDebug("Stage listing before cleanup failed: " & errMsg)
|
||
end try
|
||
|
||
do shell script "find " & quoted form of stageRootPOSIX & " -name '.DS_Store' -type f -delete"
|
||
do shell script "find " & quoted form of stageRootPOSIX & " -name '__MACOSX' -exec rm -rf {} + 2>/dev/null || true"
|
||
do shell script "find " & quoted form of stageRootPOSIX & " -name 'DEVONtech_storage' -exec rm -rf {} + 2>/dev/null || true"
|
||
|
||
my writeDebug("Stage content after cleanup:")
|
||
try
|
||
set stageListAfter to do shell script "find " & quoted form of stageRootPOSIX & " -print"
|
||
repeat with oneLine in paragraphs of stageListAfter
|
||
if oneLine is not "" then my writeDebug(" " & oneLine)
|
||
end repeat
|
||
on error errMsg
|
||
my writeDebug("Stage listing after cleanup failed: " & errMsg)
|
||
end try
|
||
|
||
if zipPass is "" then
|
||
do shell script "cd " & quoted form of stageFilesPOSIX & " ; " & ¬
|
||
"/usr/bin/find . -type f -print -quit | /usr/bin/grep -q . || { echo 'Keine exportierten Dateien gefunden.' >&2; exit 1; } ; " & ¬
|
||
"/usr/bin/zip -r -j " & quoted form of zipPath & " . "
|
||
else
|
||
do shell script "cd " & quoted form of stageFilesPOSIX & " ; " & ¬
|
||
"/usr/bin/find . -type f -print -quit | /usr/bin/grep -q . || { echo 'Keine exportierten Dateien gefunden.' >&2; exit 1; } ; " & ¬
|
||
"/usr/bin/zip -P " & quoted form of zipPass & " -r -j " & quoted form of zipPath & " . "
|
||
end if
|
||
|
||
my writeDebug("ZIP created")
|
||
|
||
set mailBodyText to my getMailBodyTextFromCurrentDB()
|
||
if mailBodyText is "" then
|
||
set mailBodyText to "Im Anhang die aktuellen Belege."
|
||
end if
|
||
|
||
set toAddresses to my parseCSVEmails(theTo)
|
||
set ccAddresses to my parseCSVEmails(theCcTo)
|
||
|
||
my writeDebug("toAddresses count=" & (count of toAddresses))
|
||
repeat with toAddr in toAddresses
|
||
my writeDebug(" toAddr=" & (toAddr as text))
|
||
end repeat
|
||
my writeDebug("ccAddresses count=" & (count of ccAddresses))
|
||
repeat with ccAddr in ccAddresses
|
||
my writeDebug(" ccAddr=" & (ccAddr as text))
|
||
end repeat
|
||
|
||
tell application "Mail"
|
||
set newMessage to make new outgoing message with properties {visible:true, subject:(my cfgMailSubjectPrefix), content:(mailBodyText & return & return)}
|
||
tell newMessage
|
||
repeat with toAddr in toAddresses
|
||
make new to recipient at end of to recipients with properties {address:(toAddr as rich text)}
|
||
end repeat
|
||
repeat with ccAddr in ccAddresses
|
||
make new cc recipient at end of cc recipients with properties {address:(ccAddr as rich text)}
|
||
end repeat
|
||
make new attachment with properties {file name:(POSIX file zipPath as alias)} at after the last paragraph
|
||
end tell
|
||
activate
|
||
end tell
|
||
|
||
my writeDebug("Mail draft created")
|
||
|
||
if isOutsider then
|
||
set refUUID to item 1 of toSend as text
|
||
set logGroupUUID to my getOrCreateGlobalSentGroupUUID(refUUID)
|
||
set ndjsonName to "sentlog_outsiders.ndjson"
|
||
set mdName to "sentlog_outsiders"
|
||
set mdHeader to "# Belegversand-Log (außerhalb von Buchungsjahren)" & return
|
||
my writeDebug("Using global outsider log group")
|
||
else
|
||
set logGroupUUID to my getOrCreateChildGroupUUID(bookingYearUUID, my cfgLogSentGroupName)
|
||
set ndjsonName to "sentlog_" & bookingYearName & ".ndjson"
|
||
set mdName to "sentlog_" & bookingYearName
|
||
set mdHeader to "# Belegversand-Log " & bookingYearName & return
|
||
my writeDebug("Using year-specific log group for year=" & bookingYearName)
|
||
end if
|
||
|
||
set ndjsonLogUUID to my getOrCreateTextLogUUID(logGroupUUID, ndjsonName, "")
|
||
set mdLogUUID to my getOrCreateMarkdownLogUUID(logGroupUUID, mdName, mdHeader)
|
||
set resultUUID to my getOrCreateMarkdownLogUUID(logGroupUUID, "!Ergebnis", "# Letzter Sendelauf" & return)
|
||
|
||
set runHeader to my friendlyRunHeader(ts)
|
||
set currentMdText to my getDTRecordTextByUUID(mdLogUUID)
|
||
if currentMdText is "" or currentMdText is mdHeader or currentMdText is (mdHeader & return) then
|
||
set mdRunIntro to return & "## " & runHeader & return & return & "| Zeit | Batch | Empfänger | ZIP | DEVONthink-Name | Dateiname | DEVONthink |" & return & "|---|---|---|---|---|---|---|"
|
||
else
|
||
set mdRunIntro to return & return & "## " & runHeader & return & return & "| Zeit | Batch | Empfänger | ZIP | DEVONthink-Name | Dateiname | DEVONthink |" & return & "|---|---|---|---|---|---|---|"
|
||
end if
|
||
my appendLineToDTRecordByUUID(mdLogUUID, mdRunIntro)
|
||
|
||
my writeDebug("ndjsonLogUUID=" & ndjsonLogUUID)
|
||
my writeDebug("mdLogUUID=" & mdLogUUID)
|
||
|
||
repeat with thisUUID in toSend
|
||
set uuidText to thisUUID as text
|
||
my writeDebug("PROCESS START uuid=" & uuidText)
|
||
|
||
my addTagByUUID(uuidText, my cfgSentTagForDocs)
|
||
|
||
set recName to my getRecordNameByUUID(uuidText)
|
||
my writeDebug("recName=" & recName)
|
||
|
||
set recFileName to my getRecordFileNameByUUID(uuidText)
|
||
my writeDebug("recFileName=" & recFileName)
|
||
|
||
set recRef to my getReferenceURLByUUID(uuidText)
|
||
my writeDebug("recRef=" & recRef)
|
||
|
||
set j to "{\"ts\":\"" & ts & "\",\"batch\":\"" & bid & "\",\"to\":\"" & my jsonEscape(theTo) & "\",\"zip\":\"" & my jsonEscape(zipName) & "\",\"uuid\":\"" & my jsonEscape(uuidText) & "\",\"ref\":\"" & my jsonEscape(recRef) & "\",\"name\":\"" & my jsonEscape(recName) & "\",\"filename\":\"" & my jsonEscape(recFileName) & "\"}"
|
||
my writeDebug("JSON built for uuid=" & uuidText)
|
||
|
||
if recRef is "" then
|
||
set mdLink to ""
|
||
else
|
||
set mdLink to "[oeffnen](" & recRef & ")"
|
||
end if
|
||
|
||
set mdLine to "| " & ts & " | `" & bid & "` | " & my mdEscape(theTo) & " | `" & my mdEscape(zipName) & "` | " & my mdEscape(recName) & " | " & my mdEscape(recFileName) & " | " & mdLink & " |"
|
||
my writeDebug("MD line built for uuid=" & uuidText)
|
||
|
||
my appendLineToDTRecordByUUID(ndjsonLogUUID, j)
|
||
my writeDebug("NDJSON append done uuid=" & uuidText)
|
||
|
||
my appendLineToDTRecordByUUID(mdLogUUID, mdLine)
|
||
my writeDebug("Markdown append done uuid=" & uuidText)
|
||
|
||
my writeDebug("PROCESS DONE uuid=" & uuidText)
|
||
end repeat
|
||
|
||
do shell script "rm -rf " & quoted form of stageFilesPOSIX
|
||
my writeDebug("Cleanup stage files done")
|
||
|
||
my writeResultRecordByUUID(resultUUID, ts, bid, toAddresses, ccAddresses, (count of toSend), isOutsider)
|
||
|
||
my showCompletionNotification((count of toSend))
|
||
|
||
my writeDebug("Script end - success")
|
||
|
||
on error errMsg number errNum
|
||
my writeDebug("ERROR number=" & errNum & " message=" & errMsg)
|
||
display dialog "Hinweis: " & errMsg buttons {"OK"} default button "OK"
|
||
end try
|
||
|
||
end timeout
|
||
|
||
end run
|