From 6e4ecdbb92d537e98395e2e647217f8674678956 Mon Sep 17 00:00:00 2001 From: Joseph Walther Date: Sat, 21 Feb 2026 12:00:53 -0600 Subject: [PATCH 1/2] Email-For-External-Approver-Groups - Eliminate dependency on ESMTPTBL - Update address lists in REXX - Allow more than 16 email addresses - Associate an approver group with one or more Group-level and/or personal email addresses - Easily tailor your own email content --- .../C1UEXTR7.rex | 747 ++++++++++++++++++ .../GTEMADDS.rex | 30 + .../README.md | 22 + .../SENDMAIL.rex | 137 ++++ 4 files changed, 936 insertions(+) create mode 100644 endevor/Field-Developed-Programs/Email-For-External-Approver-Groups/C1UEXTR7.rex create mode 100644 endevor/Field-Developed-Programs/Email-For-External-Approver-Groups/GTEMADDS.rex create mode 100644 endevor/Field-Developed-Programs/Email-For-External-Approver-Groups/README.md create mode 100644 endevor/Field-Developed-Programs/Email-For-External-Approver-Groups/SENDMAIL.rex diff --git a/endevor/Field-Developed-Programs/Email-For-External-Approver-Groups/C1UEXTR7.rex b/endevor/Field-Developed-Programs/Email-For-External-Approver-Groups/C1UEXTR7.rex new file mode 100644 index 0000000..6b30bc8 --- /dev/null +++ b/endevor/Field-Developed-Programs/Email-For-External-Approver-Groups/C1UEXTR7.rex @@ -0,0 +1,747 @@ +/* rexx */ +/* Perform various Package actions in REXX */ +/* */ +/* A COBOL exit CALLS this REXX and provides values for */ +/* REXX variables, including these. */ +/* Find documentation on these in the TechDocs documentation */ +/* where each underscore appears as a dash in the documentation. */ +/* For example, PECB_PACKAGE_ID is documented as */ +/* PECB-PACKAGE-ID */ +/* */ +/* PECB_PACKAGE_ID PAPP_GROUP_NAME */ +/* PECB_FUNCTION_LITERAL PAPP_ENVIRONMENT */ +/* PECB_SUBFUNC_LITERAL PAPP_QUORUM_COUNT */ +/* PECB_BEF_AFTER_LITERAL PAPP_APPROVER_FLAG */ +/* PECB_USER_BATCH_JOBNAME PAPP_APPR_GRP_TYPE */ +/* PREQ_PKG_CAST_COMPVAL PAPP_APPR_GRP_DISQ */ +/* PHDR_PKG_SHR_OPTION PAPP_SEQUENCE_NUMBER */ +/* PHDR_PKG_ENV */ +/* PHDR_PKG_STGID */ +/* Address fields are provided for fields that may be */ +/* modified by the REXX. */ +/* Address_PECB_MESSAGE Address_MYSMTP_SUBJECT */ +/* Address_MYSMTP_MESSAGE Address_MYSMTP_TEXT */ +/* Address_MYSMTP_USERID Address_MYSMTP_URL */ +/* Address_MYSMTP_FROM Address_MYSMTP_EMAIL_IDS */ +/* MYSMTP_EMAIL_IDS MYSMTP_EMAIL_ID_SIZE */ +/* */ + /* If wanting to limit the use of this exit, uncomment... */ +/* + If USERID() /= 'IBMUSER' &, + USERID() /= 'JW61868' &, + USERID() /= 'JW618685' then Say USERID() +*/ + /* In case these are not already allocated, these are attempted */ + STRING = "ALLOC DD(SYSTSPRT) SYSOUT(A) " + CALL BPXWDYN STRING; + STRING = "ALLOC DD(SYSTSIN) DUMMY" + CALL BPXWDYN STRING; + /* If C1UEXTR7 is allocated to anything, turn on Trace */ + WhatDDName = 'C1UEXTR7' + CALL BPXWDYN "INFO FI("WhatDDName")", + "INRTDSN(DSNVAR) INRDSNT(myDSNT)" + if RESULT = 0 then Trace ?R + /* Initialize variables.... */ + Message = '' + MessageCode = ' ' + MyRc = 0 + /* Values to be set for your site...... */ + /* For package REVIEW (APPROVE/DENY)..... */ + /* Enter location of Approver Group sequencing ... */ + ApproverGroupSequence= 'YOURSITE.NDVR.PARMLIB(APPROVER)' + ApproverGroupSequence= '' + /* Do you want all CAST actions to be peformed in Batch? */ + Force_CAST_in_Batch = 'N' ; /* Y/N */ + If USERID() = 'IBMUSER' then, + Force_CAST_in_Batch = 'Y' ; /* Y/N */ + Cast_with_SonarQube= 'N' /* Y/N/?=Check Notes */ + If USERID() = 'IBMUSER' then, + Cast_with_SonarQube= 'Y' /* Y/N/?=Check Notes */ + /* Parms are REXX statements passed from COBOL exit */ + Arg Parms + Parms = Strip(Parms) + sa= 'Parms len=' Length(Parms) + If TraceRQ = 'Y' then, + Say 'C1UEXTR7 is called again:' + /* Parms from C1UEXT07 is a string of REXX statements */ + Interpret Parms + If TraceRQ = 'Y' & PECB_MODE = 'B' then Trace r + If Substr(PHDR_PKG_NOTE5,1,5) = 'TRACE' then TraceRc = 1 + where = 'C1UEXTR7' + what = 'C1UEXTR7-' PECB_FUNCTION_LITERAL, + PECB_BEF_AFTER_LITERAL, + PHDR_PACKAGE_STATUS + /* Validate Package prefix with ServiceNow */ + If PECB_FUNCTION_LITERAL ='CREATE' &, + PECB_BEF_AFTER_LITERAL ='BEFORE' &, + (Substr(PECB_PACKAGE_ID,1,3) = 'PRB' |, + Substr(PECB_PACKAGE_ID,1,3) = 'CHG' ) then, + Do + PackageSnowRef = Substr(PECB_PACKAGE_ID,1,10) + Message = SERVINOW('C1UEXTR7' PackageSnowRef ECB_TSO_BATCH_MODE) + If POS('**NOT**', Message) > 0 then, + Do + MyRc = 8 + Call SetExitReturnInfo + Exit + End; /* If POS('**NOT**', Message) > 0 */ + End; /* If PECB_FUNCTION_LITERAL ='CREATE' ... */ + /* If the package status just became IN-APPROVAL, send emails */ + /* to request approval(s). */ + IF PHDR_PACKAGE_STATUS = 'IN-APPROVAL' &, + PECB_BEF_AFTER_LITERAL = 'AFTER' &, + PECB_FUNCTION_LITERAL = 'CAST' &, + Substr(CALL_REASON,1,16) = 'APPROVER GROUP #' then, + Do + Call SENDMAIL PAPP_GROUP_NAME PECB_PACKAGE_ID, + 'Needs-Approval' PAPP_APPROVAL_IDS + Exit + End + /* If the exit says there are no more approver groups, */ + /* FREE the SONAROPT allocation. */ + If Substr(CALL_REASON,1,21) = 'NO MORE APPROVER GRPS' then, + CALL BPXWDYN "FREE DD(SONAROPT) " ; + /* If the package status just became Approved, submit EXECUTE */ + IF PHDR_PACKAGE_STATUS = 'APPROVED' &, + PECB_BEF_AFTER_LITERAL = 'AFTER' &, + (PECB_FUNCTION_LITERAL = 'CAST' |, + PECB_FUNCTION_LITERAL = 'REVIEW') Then, + Do + PKGEXECT_Parm = Copies(' ',055) + PKGEXECT_Parm = Overlay(PECB_PACKAGE_ID ,PKGEXECT_Parm,001) + PKGEXECT_Parm = Overlay(PHDR_PKG_ENV ,PKGEXECT_Parm,018) + PKGEXECT_Parm = Overlay(PHDR_PKG_STGID ,PKGEXECT_Parm,026) + PKGEXECT_Parm = Overlay(REXX_EXEC_MODE ,PKGEXECT_Parm,028) + PKGEXECT_Parm = Overlay(PHDR_PKG_CREATE_USER,PKGEXECT_Parm,029) + PKGEXECT_Parm = Overlay(PHDR_PKG_UPDATE_USER,PKGEXECT_Parm,037) + PKGEXECT_Parm = Overlay(PHDR_PKG_CAST_USER ,PKGEXECT_Parm,045) + Call PKGEXECT PKGEXECT_Parm + Exit + End + /* Prevent a package from Backed out/in in batch */ + If Substr(PECB_FUNCTION_LITERAL,1,4) = 'BACK' &, + PECB_MODE = 'B' then, + Do + message = 'C1UEXTR7 -', + 'Package Backout/Backin unAuthorized for Batch' + MyRc = 8 + Call SetExitReturnInfo + If TraceRQ = 'Y' then Say 'C1UEXTR7 is exiting @123 ' + Exit + End + /* Before a package is being Backed out/in .... */ + IF PECB_BEF_AFTER_LITERAL = 'BEFORE' & PECB_MODE = 'T' &, + Substr(PECB_FUNCTION_LITERAL,1,4) = 'BACK' then, + Do + what = 'C1UEXTR7 before Backout/Backin' + ADDRESS TSO "EXECIO 1 DISKR AUTHORIZ (Finis" + pull BakoutCCID + BakoutCCID = Strip(BakoutCCID) + Call BKOUTLOG PECB_PACKAGE_ID 'Before', + BakoutCCID USERID() + End + /* If a package is being Backed out/in .... */ + IF PECB_BEF_AFTER_LITERAL = 'AFTER' & PECB_MODE = 'T' &, + Substr(PECB_FUNCTION_LITERAL,1,4) = 'BACK' then, + Do + what = 'C1UEXTR7 after Backout/Backin' + ADDRESS TSO "EXECIO 1 DISKR AUTHORIZ (Finis" + pull BakoutCCID + CALL BPXWDYN "FREE DD(AUTHORIZ)" + BakoutCCID = Strip(BakoutCCID) + Call BKOUTLOG PECB_PACKAGE_ID 'After', + BakoutCCID USERID() + ModelMember = 'SHIPRUNS' + Call SubmitBatchJCL + Exit + End + /* If a package is executed, examine for package shipments */ + IF PECB_BEF_AFTER_LITERAL = 'AFTER' &, + (Substr(PECB_FUNCTION_LITERAL,1,4) = 'EXEC' |, + Substr(PECB_FUNCTION_LITERAL,1,4) = 'BACK') then, + Do + If TraceRQ = 'Y' then Say 'C1UEXTR7 is exiting @160 ' + PKGESHIP_Parm = Copies(' ',055) + PKGESHIP_Parm = Overlay(PECB_PACKAGE_ID ,PKGESHIP_Parm,001) + PKGESHIP_Parm = Overlay(PHDR_PKG_ENV ,PKGESHIP_Parm,018) + PKGESHIP_Parm = Overlay(PHDR_PKG_STGID ,PKGESHIP_Parm,027) + PKGESHIP_Parm = Overlay(REXX_EXEC_MODE ,PKGESHIP_Parm,028) + PKGESHIP_Parm = Overlay(PHDR_PKG_CREATE_USER,PKGESHIP_Parm,029) + PKGESHIP_Parm = Overlay(PHDR_PKG_UPDATE_USER,PKGESHIP_Parm,037) + PKGESHIP_Parm = Overlay(PHDR_PKG_CAST_USER ,PKGESHIP_Parm,045) + PKGESHIP_Parm = Overlay(PHDR_PKG_NOTE1 ,PKGESHIP_Parm,054) + PKGESHIP_Parm = Overlay(PHDR_PKG_NOTE2 ,PKGESHIP_Parm,114) + PKGESHIP_Parm = Overlay(PHDR_PKG_NOTE3 ,PKGESHIP_Parm,174) + PKGESHIP_Parm = Overlay(PHDR_PKG_NOTE4 ,PKGESHIP_Parm,234) + PKGESHIP_Parm = Overlay(PHDR_PKG_NOTE5 ,PKGESHIP_Parm,294) + PKGESHIP_Parm = Overlay(PHDR_PKG_NOTE6 ,PKGESHIP_Parm,354) + PKGESHIP_Parm = Overlay(PHDR_PKG_NOTE7 ,PKGESHIP_Parm,414) + PKGESHIP_Parm = Overlay(PHDR_PKG_NOTE8 ,PKGESHIP_Parm,474) + If Substr(PECB_FUNCTION_LITERAL,1,4) = 'BACK' then, + PKGESHIP_Parm = Overlay('BAK' ,PKGESHIP_Parm,584) + Else, + PKGESHIP_Parm = Overlay('OUT' ,PKGESHIP_Parm,584) + Call PKGESHIP PKGESHIP_Parm + If TraceRQ = 'Y' then Say 'C1UEXTR7 is exiting @183 ' + Exit + End + /* This code runs when you want to force CASTs to run in batch */ + If PECB_FUNCTION_LITERAL ='CAST' &, + PECB_SUBFUNC_LITERAL ='CAST' &, + PECB_BEF_AFTER_LITERAL ='BEFORE' &, + PHDR_PACKAGE_STATUS ='IN-EDIT' &, + PECB_MODE = "T" &, /* TSO foreground */ + (Force_CAST_in_Batch= 'Y' | Cast_with_SonarQube= 'Y') then, + Do + ModelMember = 'CAST#JCL' + Call SubmitBatchJCL + Message = JobData + MyRc = 8 + PACKAGE = PECB_PACKAGE_ID + MessageCode = 'U033' + Call SetExitReturnInfo + Exit + End + /* If we have any issues up at this point */ + /* set the Exit's return code and get out */ + If MyRc > 0 then, + Do + Call SetExitReturnInfo + Exit + End + /* Considering a SonarQube scan... ( +COB in description) */ + /* Does PACKAGE builder indicate the package has COBOL ? */ + thisPackageHasCobol = 'N' + If Substr(PREQ_PACKAGE_COMMENT,47,4) = '+COB' then, + thisPackageHasCobol = 'Y' + /* Considering a SonarQube scan... ( +COB in description) */ + /* If running in Batch and ... execute SonarQube Analysis*/ + IF Cast_with_SonarQube= 'Y' &, + thisPackageHasCobol= 'Y' &, + PECB_FUNCTION_LITERAL = 'CAST' &, + PECB_BEF_AFTER_LITERAL = 'MID' &, + PECB_MODE = "B" then, /* running in Batch */ + Do + AllNotes = PHDR_PKG_NOTE1 ||, + PHDR_PKG_NOTE2 ||, + PHDR_PKG_NOTE3 ||, + PHDR_PKG_NOTE4 ||, + PHDR_PKG_NOTE5 ||, + PHDR_PKG_NOTE6 ||, + PHDR_PKG_NOTE7 ||, + PHDR_PKG_NOTE8 + Upper AllNotes + If Pos('BYPASS SONARQUBE', AllNotes) > 0 then, + Say 'C1UEXTR7- A bypass of SonarQube processing', + 'is requested in the package notes' + Else, + Do /*Execute SonarQube*/ + Message = SONRQUBE(PECB_PACKAGE_ID); + If Message /= '' then, + Do + MyRc = 8 + Call SetExitReturnInfo + End + Exit + End /* Else.. If Pos('BYPASS SONARQUBE' */ + End /* IF Cast_with_SonarQube= 'Y' .... */ + /* Enforce packages to be Backout Enabled */ + IF PREQ_BACKOUT_ENABLED /= 'Y' then, + Do + Message = 'C1UEXTR7 - Package made to be Backout enabled' + MyRc = 4 + hexAddress = D2X(Address_PREQ_BACKOUT_ENABLED) + storrep = STORAGE(hexAddress,,'Y') + Call SetExitReturnInfo + Exit + End; + EXIT + /* Early outs .... */ + If PECB_FUNCTION_LITERAL = 'SETUP' then Exit + /* Work in progress .... */ + /* Unspecified about SonarQube? Let NOTES decide.... */ + If Cast_with_SonarQube /= 'N' then, + Do + End /* If Cast_with_SonarQube /= 'N' */ + If PECB_FUNCTION_LITERAL ='CAST' &, + PECB_SUBFUNC_LITERAL ='CAST' &, + PECB_BEF_AFTER_LITERAL ='AFTER' then, + Call ManageEmails ; + Exit +ManageEmails: + If TraceRQ = 'Y' then Trace ?R + whereami = 'ManageEmails' + /* Only run if the exit is giving us an Approver Group */ + If Substr(CALL_REASON,1,16) = 'APPROVER GROUP #' then, + Do + Call SaveOffApproverGrpInfo + Return + End + /*****************************************************************/ + /* Initializaztion and Example statements */ + /*****************************************************************/ + MySMTP_Message =, + 'YOURSITE.NDVR.REXX(C1UEXTR7)' + MySMTP_Message =, + 'SHARE.ENDV.SHARABLE.REXX(C1UEXTR7)' + MySMTP_Subject = 'Please Approve Package' PECB_PACKAGE_ID + MySMTP_From = Left('YOURSITE your testing Endevor',50) + MySMTP_textline.1 = 'Package' PECB_PACKAGE_ID, + ' has been CAST and is ready for APPROVAL.' + MySMTP_textline.2 = 'Your Review and approval of package', + PECB_PACKAGE_ID 'is reqested.' + MySMTP_textline.3 = ' ' + MySMTP_textline.4 = ' ' + MySMTP_textline.0 = 4 + MYSMTP_EMAIL_IDS = '' + /* Only run if the exit says all Approver Grps are done */ + If Substr(CALL_REASON,1,22) = 'NO MORE APPROVER GRPS ' then, + Do + Call CheckApproverGroupSequence + Return + End + Return +SaveOffApproverGrpInfo: + If TraceRQ = 'Y' then Trace ?R + PAPP_SEQUENCE_NUMBER = Substr(CALL_REASON,17,4) + numberQueued = QUEUED() + If PAPP_SEQUENCE_NUMBER = "0001" & numberQueued > 0 then, + Do numberQueued /* Clear out whatever is queued */ + pull leftovers + End + If PAPP_SEQUENCE_NUMBER = "0001" then, + CALL BPXWDYN , /* save Approver group data */ + "ALLOC DD(C1UEXTD7) LRECL(180) BLKSIZE(18000) SPACE(1,1) ", + " RECFM(F,B) TRACKS ", + " MOD UNCATALOG REUSE "; + pkgGrp# = Strip(PAPP_SEQUENCE_NUMBER,'L','0') + PAPP_GROUP_NAME = Strip(PAPP_GROUP_NAME) + Queue 'pkgGrp# = 'pkgGrp# + Queue 'GROUP_NAME.pkgGrp# ="'PAPP_GROUP_NAME'"' + Queue 'ENVIRONMENT.pkgGrp# ="'Strip(PAPP_ENVIRONMENT)'"' + Queue 'APPR_GRP_TYPE.pkgGrp# ="'Strip(PAPP_APPR_GRP_TYPE)'"' + Queue 'APPR_GRP_DISQ.pkgGrp# ="'Strip(PAPP_APPR_GRP_DISQ)'"' + Queue 'APPROVAL_FLAGS.pkgGrp# ="'Strip(PAPP_APPROVAL_FLAGS)'"' + Queue 'STATUS.'PAPP_GROUP_NAME '="'Strip(PAPP_APPROVER_FLAG)'"' + Queue 'QUORUM.'PAPP_GROUP_NAME '='Strip(PAPP_QUORUM_COUNT,'L','0') + Queue 'USRLST.'PAPP_GROUP_NAME '="'Strip(PAPP_APPROVAL_IDS)'"' + numberQueued = QUEUED() + "EXECIO" numberQueued " DISKW C1UEXTD7 " + Return; +CheckApproverGroupSequence: + If TraceRQ = 'Y' then Trace ?R + whereami = 'CheckApproverGroupSequence' + /* If C1UEXTD7 is allocated to anything, we have approvers */ + WhatDDName = 'C1UEXTD7' + CALL BPXWDYN "INFO FI("WhatDDName")", + "INRTDSN(DSNVAR) INRDSNT(myDSNT)" + If Substr(DSNVAR,1,1) = ' ' then Return + "EXECIO 0 DISKW C1UEXTD7 (Finis" + "EXECIO * DISKR C1UEXTD7 (Finis" + CALL BPXWDYN "FREE DD(C1UEXTD7)" + numberQueued = QUEUED() + /* Analyze Exit-provided Approver Group info */ + pkgGrp# = 0 + /* By default, ordered Approver Groups are not related */ + STATUS. = 'NotRelated' + QUORUM. = 0 + /* Return Approver Group info for this package */ + Do q# = 1 to numberQueued + Parse Pull something + If TraceRQ = 'Y' then say "@187" something + interpret something + End; + ThisEnvironment = ENVIRONMENT.pkgGrp# + /* Read the site's required Approver Group sequencing */ + CALL BPXWDYN, + "ALLOC DD(APPROVER) DA('"ApproverGroupSequence"') SHR REUSE" + "EXECIO * DISKR APPROVER (Stem ordered. Finis" + CALL BPXWDYN "FREE DD(APPROVER)" + /* Build a sequenced list of all named Approver Groups */ + OrderedApproverGroups = '' + /* Set a default value to be 1 greater than number of groups */ + NotOrdered = ordered.0 + 1 + Sequence. = NotOrdered + Do ord# = 1 to ordered.0 + orderedEntry = ordered.ord# + orderedEnv = Word(orderedEntry,1) + If orderedEnv /= thisEnvironment then iterate + orderedApproverGroup = Word(orderedEntry,2) + If orderedApproverGroup = 'AllOthers' then, + DefaultOrder = ord# + SEQUENCE.orderedApproverGroup = ord# + If TraceRQ = 'Y' then, + say "@201 Sequence for" orderedApproverGroup "is" ord# + End; /* Do ord# = 1 to ordered.0 */ + Sequence.NotOrdered = DefaultOrder + unsorted_list = "" + /* Build a list of Approver Groups for this package */ + PackageApproverGroups = '' + Do p# = 1 to pkgGrp# + PackageApproverGroup = GROUP_NAME.p# + thisSequence = SEQUENCE.PackageApproverGroup + entry = Right(thisSequence,4,'0') || '.' ||, + PackageApproverGroup + unsorted_list = unsorted_list entry + If TraceRQ = 'Y' then, + Say "unsorted_list=" unsorted_list + End; + Call SortApproverGroupList; + If TraceRQ = 'Y' then say "@220 PackageApproverGroups =", + sorted_list, + ' ThisEnvironment =' ThisEnvironment + /* Go through the Sorted list to identify the status */ + /* of the next group(s) to be approved */ + /* Find the 1st Approver group this user belongs to.... */ + thisApprover = USERID() + lastSequence = NotOrdered + Do seq# = 1 to Words(sorted_list) + entry = Word(sorted_list,seq#) + Parse Var entry thisSequence '.' orderedApproverGroup + orderedGroupStatus = STATUS.orderedApproverGroup + If orderedGroupStatus = 'APPROVED' then Iterate; + orderedGroupQuorum = QUORUM.orderedApproverGroup + If orderedGroupQuorum = 0 then Iterate; + If thisSequence > lastSequence then, + Do + Sa= 'We need to wait' + Leave; + End; + lastSequence = thisSequence + ListApprovers = USRLST.orderedApproverGroup + whereApprover = Wordpos(thisApprover,ListApprovers) + thisApproversFlag = " " + If whereApprover > 0 then, + Do + thisApproverGroup = orderedApproverGroup + thisApproversFlag = Word(APPROVAL_FLAGS.grp#,whereApprover) + End + If TraceRQ = 'Y' then, + Say orderedApproverGroup 'has status of' orderedGroupStatus, + " Quorum" orderedGroupQuorum + IF whereApprover > 0 &, + orderedGroupStatus /= 'NotRelated' then, + sa= 'You must wait for the' orderedApproverGroup, + " group's approval" + If Words(ListApprovers) > 0 then, + Do w# = 1 to Words(ListApprovers) + Approver = Word(ListApprovers,w#) + If Wordpos(Approver,MYSMTP_EMAIL_IDS) = 0 &, + Substr(Approver,1,1) > '00'X then, + MYSMTP_EMAIL_IDS = MYSMTP_EMAIL_IDS Approver + End; /* Do w# = 1 to Words(ListApprovers) */ + End; /* Do seq# = 1 to Words(sorted_list) */ + /* Prepare email to the usrids in MYSMTP_EMAIL_IDS */ + Call PrepareEmail + Return; +SortApproverGroupList: + If TraceRQ = 'Y' then Trace ?R + whereami = 'SortApproverGroupList' + sa= words(unsorted_list) unsorted_list; + drop sorted_list; + sorted_list = ""; + do forever ; + if words(unsorted_list) = 0 then leave; + lowest_entry = 1; + do entry = 1 to words(unsorted_list) + if word(unsorted_list,entry) <, + word(unsorted_list,lowest_entry) then, + lowest_entry = entry; + end; /* do entry = 1 .... */ + sorted_list = sorted_list word(unsorted_list,lowest_entry); + sa= "sorted_list=" sorted_list ; + position = wordindex(unsorted_list,lowest_entry) ; + len = length(word(unsorted_list,lowest_entry)); + unsorted_list =, + overlay(copies(" ",len),unsorted_list,position) ; + sa= "unsorted_list=" unsorted_list ; + end; /* do forever */ + drop unsorted_list; + sa= words(sorted_list) sorted_list; + Return; +PrepareEmail: + If TraceRQ = 'Y' then Trace ?R + whereami = 'PrepareEmail' +/* Here you can make last-moment adjustments to the email */ + shortlist = Substr(MYSMTP_EMAIL_IDS,1,100) + If Substr(MYSMTP_EMAIL_IDS,100,1) /= ' ' &, + Substr(MYSMTP_EMAIL_IDS,101,1) /= ' ' then, + Do + whereEnd = WordIndex(shortlist,Words(shortlist)) + shortlist = DELWORD(shortlist,whereEnd) + End + MySMTP_textline.4 = 'Sent to Group:' shortlist +/* Code in the section below should not be changed */ +/* Code in the section below should not be changed */ +/* Code in the section below should not be changed */ + MySMTP_Message = Left(MySMTP_Message,80) + hexAddress = D2X(Address_MYSMTP_MESSAGE) + storrep = STORAGE(hexAddress,,Message) + MySMTP_From = Left(MySMTP_From,50) + hexAddress = D2X(Address_MYSMTP_FROM) + storrep = STORAGE(hexAddress,,MySMTP_From) + MySMTP_Subject = Left(MySMTP_Subject,50) + hexAddress = D2X(Address_MySMTP_Subject) + storrep = STORAGE(hexAddress,,MySMTP_Subject) +/* If TraceRQ = 'Y' then Trace ?r */ + MYSMTP_COUNTER = '' + numberLines = Right(MySMTP_textline.0,2,'0') + Do l# = 1 to Length(numberLines) + MYSMTP_COUNTER = MYSMTP_COUNTER ||, + 'F' || Substr(numberLines,l#,1) + End + If TraceRQ = 'Y' then say 'MYSMTP_COUNTER=' MYSMTP_COUNTER + MYSMTP_TEXT = X2C(MYSMTP_COUNTER) + Do line# = 1 to numberLines + MYSMTP_TEXT = MYSMTP_TEXT || Left(MySMTP_textline.line#,133) + End; /* Do line# = 1 to MySMTP_textline.0 */ + hexAddress = D2X(Address_MYSMTP_TEXT) + storrep = STORAGE(hexAddress,,MYSMTP_TEXT) + MySMTP_URL = 'N' + hexAddress = D2X(Address_MYSMTP_URL) + storrep = STORAGE(hexAddress,,MySMTP_URL) + /* Provide distribution list ( list of userids ) to Exit */ + MYSMTP_EMAIL_IDS =, + Space(Strip(Translate(MYSMTP_EMAIL_IDS,' ','00'x))) + MYSMTP_EMAIL_IDS = MYSMTP_EMAIL_IDS '0000'x + MYSMTP_EMAIL_IDS = Left(MYSMTP_EMAIL_IDS,MYSMTP_EMAIL_ID_SIZE) + hexAddress = D2X(Address_MYSMTP_EMAIL_IDS) + storrep = STORAGE(hexAddress,,MYSMTP_EMAIL_IDS) + Return; +SubmitBatchJCL: + If TraceRQ = 'Y' then Trace ?R + whereami = 'SubmitBatchJCL' + /* Variable settings for each site ---> */ + WhereIam = WHERE@M1() + interpret 'Call' WhereIam "'MySENULibrary'" + MySENULibrary = Result + interpret 'Call' WhereIam "'MySEN2Library'" + MySEN2Library = Result + interpret 'Call' WhereIam "'MyCLS0Library'" + MyCLS0Library = Result + interpret 'Call' WhereIam "'MyCLS2Library'" + MyCLS2Library = Result + Unique_Name = GTUNIQUE() + /* Get job-related information from low address locations */ + MyAccountingCode = GETACCTC() + job_name = MVSVAR('SYMDEF',JOBNAME ) /*Returns JOBNAME */ + Jobname= BUMPJOB(job_name) + /* Prepare and run a Table Tool to build CAST jcl....... */ + CALL BPXWDYN , + "ALLOC DD(TABLE) LRECL(80) BLKSIZE(27920) SPACE(1,1) ", + " RECFM(F,B) TRACKS ", + " NEW UNCATALOG REUSE "; + Queue "* Do" + Queue " * " + "EXECIO 2 DISKW TABLE (FINIS "; /* count queued */ + CALL BPXWDYN "ALLOC DD(NOTHING) DUMMY" + CALL BPXWDYN , + "ALLOC DD(OPTIONS) LRECL(80) BLKSIZE(27920) SPACE(1,1) ", + " RECFM(F,B) TRACKS ", + " NEW UNCATALOG REUSE "; + QUEUE "$nomessages ='Y'" + QUEUE "MyAccountingCode='"MyAccountingCode"'" + QUEUE "MySEN2Library ='"MySEN2Library"'" + QUEUE "MySENULibrary ='"MySENULibrary"'" + QUEUE "MyCLS0Library ='"MyCLS0Library"'" + QUEUE "MyCLS2Library ='"MyCLS2Library"'" + QUEUE "Unique_Name ='"Unique_Name"'" + QUEUE "PECB_PACKAGE_ID ='"PECB_PACKAGE_ID"'" + QUEUE "Jobname= '"Jobname"'" + QUEUE "TBLOUT = 'SUBMTJCL'" + "EXECIO 10 DISKW OPTIONS (FINIS "; /* count queued */ + /* For CASTing a package in Batch */ + /* Build a JCL model, and name its location here.... */ + /* Name a work dataset to be created then deleted... */ + Jcl2SumbitModel = MySEN2Library || '(' || ModelMember || ')' + "ALLOC F(MODEL) DA('"Jcl2SumbitModel"') SHR REUSE" + /* Build the JCL for a Batch Cast */ + CastPackageJCL = USERID()".C1UEXTR7.SUBMIT."Unique_Name + "ALLOC F(SUBMTJCL) DA('"CastPackageJCL"') ", + "LRECL(80) BLKSIZE(16800) SPACE(5,5)", + "RECFM(F B) TRACKS ", + "NEW CATALOG REUSE " ; + myRC = ENBPIU00("A") + "EXECIO 0 DISKW SUBMTJCL (Finis" + "FREE DD(TABLE) " + "FREE DD(NOTHING) " + "FREE DD(OPTIONS) " + "FREE DD(MODEL) " + Call Submit_n_save_jobInfo ; + "FREE F(SUBMTJCL) DELETE " + Return; +Submit_n_save_jobInfo: /* submit Jcl2SumbitModel job and save job info */ + If TraceRQ = 'Y' then Trace ?R + whereami = 'Submit_n_save_jobInfo' + If TraceRQ = 'Y' then Say 'Submit_n_save_jobInfo:' + Address TSO "PROFILE NOINTERCOM" /* turn off msg notific */ + CALL MSG "ON" + CALL OUTTRAP "out." + ADDRESS TSO "SUBMIT '"CastPackageJCL"'" ; + If RC > 4 then, + Do + MyRC = 8 + Message = 'Cannot find Element member to submit.' + Call SetExitReturnInfo + Exit(12) + End + CALL OUTTRAP "OFF" + Address TSO "PROFILE INTERCOM" /* turn on msg notific */ + JobData = Strip(out.1); + jobinfo = Word(JobData,2) ; + If jobinfo = 'JOB' then, + jobinfo = Word(JobData,3) ; + SelectJobName = Word(Translate(jobinfo,' ',')('),1) ; + SelectJobNumber = Word(Translate(jobinfo,' ',')('),2) ; + Return; +Allocate_Files_For_CSV_and_API: + STRING = "ALLOC DD(C1MSGS1) DUMMY " + CALL BPXWDYN STRING; + STRING = "ALLOC DD(BSTERR) DA(*) " + CALL BPXWDYN STRING; + STRING = "ALLOC DD(BSTAPI) DA(*) " + CALL BPXWDYN STRING; + STRING = "ALLOC DD(MSGFILE) LRECL(133) BLKSIZE(26600) ", + " DSORG(PS) ", + " SPACE(5,5) RECFM(F,B) TRACKS ", + " NEW UNCATALOG REUSE "; + CALL BPXWDYN STRING; + Return; +FREE_Files_For_CSV_and_API: + CALL BPXWDYN STRING; + STRING = "FREE DD(C1MSGS1)" ; + CALL BPXWDYN STRING; + STRING = "FREE DD(BSTERR)" ; + CALL BPXWDYN STRING; + STRING = "FREE DD(BSTAPI)" ; + CALL BPXWDYN STRING; + STRING = "FREE DD(MSGFILE)"; + CALL BPXWDYN STRING; + Return; +CSV_to_List_Package_Actions: + /* Get Package Action information for SonarQube preparations */ + STRING = "ALLOC DD(EXTRACTM) LRECL(4000) BLKSIZE(32000) ", + " DSORG(PS) ", + " SPACE(1,5) RECFM(F,B) TRACKS ", + " NEW UNCATALOG REUSE "; + CALL BPXWDYN STRING; + STRING = "ALLOC DD(BSTIPT01) LRECL(80) BLKSIZE(800) ", + " DSORG(PS) ", + " SPACE(1,5) RECFM(F,B) TRACKS ", + " NEW UNCATALOG REUSE "; + CALL BPXWDYN STRING; + QUEUE "LIST PACKAGE ACTION FROM PACKAGE '"PECB_PACKAGE_ID"'" + QUEUE " TO DDNAME 'EXTRACTM' " + QUEUE " ." + "EXECIO" QUEUED() "DISKW BSTIPT01 (FINIS "; + ADDRESS LINK 'BC1PCSV0' ; /* load from authlib */ + call_rc = rc ; + "EXECIO * DISKR EXTRACTM (STEM CSV. finis" + STRING = "FREE DD(EXTRACTM)" ; + CALL BPXWDYN STRING; + STRING = "FREE DD(BSTIPT01)" ; + /* To Search the package action data in CSV format. */ + /* Identify matches with Rules file, determining Ship Dests */ + IF CSV.0 < 2 THEN RETURN; + /* CSV data heading - showing CSV variables */ + $table_variables= Strip(CSV.1,'T') + $table_variables = translate($table_variables,"_"," ") ; + $table_variables = translate($table_variables," ",',"') ; + $table_variables = translate($table_variables,"@","/") ; + $table_variables = translate($table_variables,"@",")") ; + $table_variables = translate($table_variables,"@","(") ; + WantedCSVVariables= , + "ELM_@S@ ENV_NAME_@S@ STG_ID_@S@ ", + "SYS_NAME_@S@ SBS_NAME_@S@ TYPE_NAME_@S@ " + Do rec# = 2 to CSV.0 + $detail = CSV.rec# + Drop SBS_NAME_@T@ + Trace Off + /* Parse CSV fields in the Detail record until done */ + Do $column = 1 to Words($table_variables) + Call ParseDetailCSVline + End + If TraceRc = 1 then Trace r + IF Substr(ENV_NAME_@S@,1,1) = '00'x |, + Substr(ENV_NAME_@S@,1,1) = ' ' then Iterate; + elm# = Elements.0 + 1 + Elements.elm# = ELM_@S@ ENV_NAME_@S@ STG_ID_@S@, + SYS_NAME_@S@ SBS_NAME_@S@ TYPE_NAME_@S@ + Elements.0 = elm# + Sa= 'Messages from C1UEXTR7:' Elements.elm# + Trace Off + End; /* Do rec# = 1 to CSV.0 */ + RETURN ; +ParseDetailCSVline: + /* Find the data for the current $column */ + $dlmchar = Substr($detail,1,1); + If $dlmchar = "'" then, + Do + SA= 'parsing with single quote ' + PARSE VAR $detail "'" $temp_value "'" $detail ; + If Substr($detail,1,1) = ',' then, + $detail = Strip(Substr($detail,2),'L') + End + Else, + If $dlmchar = '"' then, + Do + SA= 'parsing with double quote ' + PARSE VAR $detail '"' $temp_value '"' $detail ; + If Substr($detail,1,1) = ',' then, + $detail = Strip(Substr($detail,2),'L') + End + Else, + If $dlmchar = ',' then, + Do + SA= 'parsing with comma ' + PARSE VAR $detail ',' $temp_value ',' $detail ; + If Substr($detail,1,1)/= ',' then, + $detail = "," || $detail + $detail = Strip(Substr($detail,2),'L') */ + End + Else, + If Words($detail) = 0 then, + $temp_value = ' ' + Else, + Do + SA= 'parsing with comma ' + PARSE VAR $detail $temp_value ',' $detail ; + Sa= '$temp_value=>' $temp_value '<' + End + $temp_value = STRIP($temp_value) ; + $rslt = $temp_value + $rslt = Strip($rslt,'B','"') ; + $rslt = Strip($rslt,'B',"'") ; + if Length($rslt) < 1 then $rslt = ' ' + thisVariable = WORD($table_variables,$column) + If Wordpos(thisVariable,WantedCSVVariables) = 0 then Return + if Length($rslt) < 250 then, + $temp = WORD($table_variables,$column) '= "'$rslt'"'; + Else, + $temp = WORD($table_variables,$column) "=$rslt" + INTERPRET $temp; + If rec# < 3 then Say $temp + RETURN ; +SetExitReturnInfo: + If TraceRQ = 'Y' then Trace ?R + whereami = 'SetExitReturnInfo' + If TraceRQ = 'Y' then Say 'SetExitReturnInfo: ' + hexAddress = D2X(Address_PECB_MESSAGE) + storrep = STORAGE(hexAddress,,Message) + hexAddress = D2X(Address_PECB_ERROR_MESS_LENGTH) + storrep = STORAGE(hexAddress,,'0084'X) + hexAddress = D2X(Address_PECB_MODS_MADE_TO_PREQ) + storrep = STORAGE(hexAddress,,'Y') + If MessageCode /= ' ' then, + Do + hexAddress = D2X(Address_PECB_MESSAGE_ID) + storrep = STORAGE(hexAddress,,MessageCode) + End +/* Set the return code for the exit */ +/* for PECB-NDVR-EXIT-RC */ + hexAddress = D2X(Address_PECB_NDVR_EXIT_RC) + If MyRc = 4 then, + storrep = STORAGE(hexAddress,,'00000004'X) + Else, + storrep = STORAGE(hexAddress,,'00000008'X) + RETURN ; diff --git a/endevor/Field-Developed-Programs/Email-For-External-Approver-Groups/GTEMADDS.rex b/endevor/Field-Developed-Programs/Email-For-External-Approver-Groups/GTEMADDS.rex new file mode 100644 index 0000000..86ce5a9 --- /dev/null +++ b/endevor/Field-Developed-Programs/Email-For-External-Approver-Groups/GTEMADDS.rex @@ -0,0 +1,30 @@ +/* REXX */ + Arg ApproverGroup . + CALL BPXWDYN "INFO FI(GTEMADDS)", + "INRTDSN(DSNVAR) INRDSNT(myDSNT)" + If RESULT = 0 then Trace r + /* For the named Approver group, return zero to many */ + /* email addresses. Where possible, return */ + /* Group-level email addresses. Individuals are OK. */ + /* */ + /* There is no dependency on ESMTPTBL or XIT7MAIL . */ + /* The number of email addresses can exceed 16. */ + ApproverGroup = Strip(ApproverGroup) + EmailAddresses. = '' + EmailAddresses.GROUP01 = 'First1.Last1@yoursite.com ', + 'First2.Last2@yoursite.com ', + 'First3.Last3@yoursite.com ', + 'First4.Last4@yoursite.com ' + EmailAddresses.GROUP02 = 'First2.Last2@yoursite.com ' + EmailAddresses.GROUP03 = ' ' + EmailAddresses.GROUP04 = ' ' + EmailAddresses.GROUP05 = ' ' + EmailAddresses.GROUP06 = ' ' + EmailAddresses.GROUP07 = 'First4.Last4@yoursite.com' + EmailAddresses.GROUP08 = 'First3.Last3@yoursite.com ', + 'First4.Last4@yoursite.com ' + EmailAddresses.GROUP09 = 'First1.Last1@yoursite.com ', + 'First2.Last2@yoursite.com ', + 'First4.Last4@yoursite.com ' + EmailAddresses.GROUP10 = '' + Return EmailAddresses.ApproverGroup diff --git a/endevor/Field-Developed-Programs/Email-For-External-Approver-Groups/README.md b/endevor/Field-Developed-Programs/Email-For-External-Approver-Groups/README.md new file mode 100644 index 0000000..d2eb00f --- /dev/null +++ b/endevor/Field-Developed-Programs/Email-For-External-Approver-Groups/README.md @@ -0,0 +1,22 @@ + +# Email for External Approver Groups + +Send Email to Approvers in External Endevor Approver Groups. These are groups where individuals are identified in your ESM and not within the defined Approver Groups of Endevor. + +As with many of the folders on this GitHub, this one contains a response from requests of one or more customers. + +Leveraging content from the [Exit-Examples](https://github.com/BroadcomMFD/broadcom-product-scripts/tree/main/endevor/Field-Developed-Programs/Exit-Examples) folder, and possibly the [SonarQube](https://github.com/BroadcomMFD/broadcom-product-scripts/tree/main/endevor/Field-Developed-Programs/SonarQube-interface-to-Endevor) folder, the items in this folder allow you to: + + +- Eliminate dependency on ESMTPTBL +- Update address lists in REXX +- Allow more than 16 email addresses +- Associate an approver group with one or more Group-level and/or personal email addresses +- Easily tailor your own email content + +**C1UEXTR7.rex** is an example Rexx subroutine to the package exit programs, for example [C1UEXT07](https://github.com/BroadcomMFD/broadcom-product-scripts/blob/main/endevor/Field-Developed-Programs/Package-Automation/C1UEXT07.cob) in the **Package Automation** folder, or [C1UEXTT7](https://github.com/BroadcomMFD/broadcom-product-scripts/blob/main/endevor/Field-Developed-Programs/SonarQube-interface-to-Endevor/C1UEXTT7.cob) in the **SonarQube-interface-to-Endevor** folder. This version processes Approver Group names, provided by the exit, and calls **SENDMAIL.rex** to deliver email. +A subroutine to **SENDMAIL.rex** is the **GTEMADDS.rex** which provides a list of email addresses for each Approver Group. + +Code your own version of **GTEMADDS.rex** to associate your Approver Groups to email addresses, following the example content. + + diff --git a/endevor/Field-Developed-Programs/Email-For-External-Approver-Groups/SENDMAIL.rex b/endevor/Field-Developed-Programs/Email-For-External-Approver-Groups/SENDMAIL.rex new file mode 100644 index 0000000..921e0d7 --- /dev/null +++ b/endevor/Field-Developed-Programs/Email-For-External-Approver-Groups/SENDMAIL.rex @@ -0,0 +1,137 @@ +/* rexx */ +/* For the individuals associated in the ApproverGroup + send an email to alert them of the PackageCondition */ + Trace Off + CALL BPXWDYN "INFO FI(SENDMAIL)", + "INRTDSN(DSNVAR) INRDSNT(myDSNT)" + If RESULT = 0 then Trace r + Arg ApproverGroup Package PackageCondition TSOIds + ApproverGroup = Strip(ApproverGroup) + whattime = TIME('L') + EmailDDNAMESuffix = Substr(whattime,4,2) || Substr(whattime,10,5) + SonarWorkfile = '' + CALL BPXWDYN "INFO FI(SONAROPT)", + "INRTDSN(DSNVAR) INRDSNT(myDSNT)" + If RESULT = 0 then, + Do + "EXECIO * DISKR SONAROPT (stem sonar. Finis" + Do so# = 1 to sonar.0 + If Word(sonar.so#,1) = 'SonarWorkfile' then, + Do + SonarWorkfile = Strip(Word(sonar.so#,3),'B',"'") + Leave + End /* Do so# = 1 to sonar.0 */ + End /* If RESULT = 0 then */ + /* Individuals for receiving email are defined here */ + /* There is no dependency on ESMTPTBL or XIT7MAIL . */ + EmailAddresses. = '' + EmailAddresses.GROUP01 = 'First1.Last1@yoursite.com ', + 'First2.Last2@yoursite.com ', + 'First3.Last3@yoursite.com ', + 'First4.Last4@yoursite.com ' + EmailAddresses.GROUP02 = 'First2.Last2@yoursite.com ' + EmailAddresses.GROUP03 = ' ' + EmailAddresses.GROUP04 = ' ' + EmailAddresses.GROUP05 = ' ' + EmailAddresses.GROUP06 = ' ' + EmailAddresses.GROUP07 = 'First4.Last4@yoursite.com' + EmailAddresses.GROUP08 = 'First3.Last3@yoursite.com ', + 'First4.Last4@yoursite.com ' + EmailAddresses.GROUP09 = 'First1.Last1@yoursite.com ', + 'First2.Last2@yoursite.com ', + 'First4.Last4@yoursite.com ' + EmailAddresses.GROUP10 = '' + sa = ApproverGroup + EmailAddressList = EmailAddresses.ApproverGroup + If EmailAddressList = '' then Exit + myLrecl = 80 + Body.0 = 0 + If PackageCondition = 'NEEDS-APPROVAL' then, + Do + Subject_Line = 'Please Approve Package' Package + Body.1 = " " + Body.2 = "CC:" EmailAddressList + Body.3 = "Package" Package "is ready for your approval." + Body.4 = " " + Body.5 ="http://mstrsvw.your.site.com:" || , + "7080/endevorui/app/packages/" || Package + Body.0 = 5 + If SonarWorkfile /= '' then, + Do + ALLOC_CMD = "ALLOC FI(SONAR)", + "DA("SonarWorkfile".RESULTS) SHR REUSE" + rc = BPXWDYN(ALLOC_CMD) + "EXECIO * DISKR Sonar (Stem sonar. Finis" + Call BPXWDYN "FREE FI(SONAR)" + IncludeCount = Min(80,sonar.0) + bdy# = Body.0 + 1 + Body.bdy# = "============ SonarQube Analysis ==========" + sonar# = 0 + Do IncludeCount + bdy# = bdy# + 1 + sonar# = sonar# + 1 + Body.bdy# = sonar.sonar# + End ; /* Do IncludeCount */ + End /* If SonarQubed = 'Y' */ + Body.0 = bdy# + End /* If PackageCondition = 'Needs-Approval' */ + /* For each person in the Approver group, send an Email */ + Do e# = 1 to Words(EmailAddressList) + EMAIL_ADDRESS = Word(EmailAddressList,e#) + Call SendAnEmail + End; + Exit +SendAnEmail: + email.0=0 + Call SMTP "helo DEVRSVW" + Call SMTP "Mail From: " + Call SMTP "Rcpt To:<"EMAIL_ADDRESS">" + Call SMTP "data " + Call SMTP "To: "EMAIL_ADDRESS" " + Call SMTP "Subject: " Subject_Line + Call SMTP "MIME-VERSION: 1.0 " + Call SMTP "CONTENT-TYPE: TEXT/HTML" + Call SMTP " " + Call SMTP " " + Call SMTP " " + Call SMTP " " + Call SMTP " " + Call SMTP "

" + Call SMTP " " + Call SMTP " " + Call SMTP " " + Call SMTP ' " + Call SMTP "
" + Call SMTP "  ' + Call SMTP "
" + Call SMTP, + '


' + Call SMTP "

" + Call SMTP "

       "
+  Call SMTP "             "
+  DO B# =  1 to Body.0
+     Call SMTP Body.B#
+  End
+  Call SMTP " 
" + Call SMTP "

" + Call SMTP " " + Call SMTP ". " + EmailDDNAME = 'E' || EmailDDNAMESuffix + String = "ALLOC DD("EmailDDNAME") ", + "SYSOUT(A) WRITER(SMTP) REUSE" + Call BPXWDYN String + "EXECIO * DISKW "EmailDDNAME" (Stem email. finis" + Call BPXWDYN "FREE DD("EmailDDNAME")" + EmailDDNAMESuffix = EmailDDNAMESuffix + 1 + Return +smtp: procedure expose email. + parse arg aline + al# = email.0 + 1 + email.al# = aline + email.0 = al# + Return From 81c57f14abb7e75335a6c5a6154f27d1aa54ca9e Mon Sep 17 00:00:00 2001 From: Joseph Walther Date: Sat, 21 Feb 2026 12:11:28 -0600 Subject: [PATCH 2/2] Update README.md --- .../Email-For-External-Approver-Groups/README.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/endevor/Field-Developed-Programs/Email-For-External-Approver-Groups/README.md b/endevor/Field-Developed-Programs/Email-For-External-Approver-Groups/README.md index d2eb00f..e1bc645 100644 --- a/endevor/Field-Developed-Programs/Email-For-External-Approver-Groups/README.md +++ b/endevor/Field-Developed-Programs/Email-For-External-Approver-Groups/README.md @@ -1,7 +1,10 @@ # Email for External Approver Groups -Send Email to Approvers in External Endevor Approver Groups. These are groups where individuals are identified in your ESM and not within the defined Approver Groups of Endevor. +Send Email to Approvers defined in External Endevor Approver Groups. These are groups where individuals are not defined within the Endevor Approver Group, but rather are defined in the external security product in use at your site. + +See the discussion of External Groups in [Endevor's tech docs documentation](https://techdocs.broadcom.com/us/en/ca-mainframe-software/devops/ca-endevor-software-change-manager/18-1/administrating/package-administration/approver-groups.html). + As with many of the folders on this GitHub, this one contains a response from requests of one or more customers.