I have created a macro for outlook which opens up a userform, asks a few questions to select from, and then there's a submit button to send the message and the userform data to a POST url (our ticketing system). I am stuck on the last part of attachments. I can see the attachments list and loop through the attachment item objects, but I don't know which object call to make to send the actual file data to the POST form... all i can see is file size, file name, etc.
Any ideas where i would get the file contents and mime type for adding them to the URL post action as a form var?
Public senderaddress As String
Public thisEmailContent As String
Public thisEmailTEXT As String
Public thisStaffMember As Integer
Public emailAttachments As String
Sub MacroName()
thisStaffMember = 1
Set objItem = GetCurrentItem()
senderaddress = objItem.SenderEmailAddress
thisEmailContent = objItem.HTMLBody
thisEmailTEXT = objItem.Body
emailAttachments = objItem.Attachments
UserformName.Show
End Sub
Function GetCurrentItem() As Object
Dim objApp As Outlook.Application
Set objApp = Application
On Error Resume Next
Select Case TypeName(objApp.ActiveWindow)
Case "Explorer"
Set GetCurrentItem = _
objApp.ActiveExplorer.Selection.Item(1)
Case "Inspector"
Set GetCurrentItem = _
objApp.ActiveInspector.CurrentItem
Case Else
End Select
End Function
Then here's the last part of the Userform code where it actually sends the data to our ticket system when the Send Now button is clicked...
Public Function encodeURL(str As String)
Dim ScriptEngine As Object
Dim encoded As String
Set ScriptEngine = CreateObject("scriptcontrol")
ScriptEngine.Language = "JScript"
encoded = ScriptEngine.Run("encodeURIComponent", str)
encodeURL = encoded
End Function
Private Sub SendNow_Click()
Set sendReq = CreateObject("MSXML2.XMLHTTP")
With sendReq
.Open "POST", "https://ticketsystemurl.whatever/outlookinterface.php?action=openticket&fromEmail=" & encodeURL(senderaddress) & "&selectedCustomer=" & encodeURL(CustomerSelect.Value) & "&selectedContact=" & encodeURL(ContactSelect.Value), False
.setRequestHeader "User-Agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.0)"
.setRequestHeader "Content-type", "application/x-www-form-urlencoded"
.Send ("notifyCustomer=" & NotifyCustomer.Value & "&staffID=" & thisStaffMember & "&emailContent=" & encodeURL(thisEmailContent) & "&emailTEXT=" & encodeURL(thisEmailTEXT) & "&appendTicket=" & AppendToTicket.Value & "&createNewTicket=" & CreateNewTicket.Value & "&selectedTicket=" & encodeURL(SelectTicket.Value) & "&attachments=" & encodeURL(emailAttachments))
End With
Unload Me
End Sub
so far that submits everything except i cant figure out how to populate the post var "attachments" with the actual attachment filedata. Is this even possible?
Outlook would not do that for you. You will need to save the attachment as a file (Attachment.SaveAsFile), the read the contents of that file (Scripting.FileSystemObject object?) to use it in your web request.
Related
I'm trying to change outlook email signatures automatically depending on a specific keyword on the subject.
On my first try I added the signature at the bottom of the email.
The signature came perfect including image and all but that there was an issue with the placement as the signature was appended at the very bottom of the email below the original text.
On my second try I set up a default signature that works as a placeholder. The macro then finds the placeholder and replaces it with the correct signature. The macro works and inserts the signature in the correct location but now the signature image is not showing up.
A couple weird things with the issue:
Image issue occurs only when composing new email. Image comes in correctly when replying or forwarding.
Signature looks okay on sender's outlook client (i.e. image is displayed before sending email).
Signature is not displayed on recipient's outlook client (tried outlook and iOS mail).
Private Sub Application_ItemSend(ByVal Item As Object, Cancel As Boolean)
Dim objMail As Outlook.MailItem
Dim strSignatureFile As String
Dim objFileSystem As Object
Dim objTextStream As Object
Dim strSignature As String
Dim sPath As String
If TypeOf Item Is MailItem Then
Set objMail = Item
emailSubject = "T " & LCase(objMail.Subject)
End If
test = "keyWord"
If InStr(emailSubject, test) = 0 Then
sPath = Environ("appdata") & "\Microsoft\Signatures\signature1.htm"
signImageFolderName = "signature1_files"
Else
sPath = Environ("appdata") & "\Microsoft\Signatures\signature2.htm"
signImageFolderName = "signature2_files"
End If
completeFolderPath = Environ("appdata") & "\Microsoft\Signatures\" & signImageFolderName
If Dir(sPath) <> "" Then
strSignature = GetSignature(sPath)
' Now replace this incomplete file path
' with complete path wherever it is used
strSignature = VBA.Replace(strSignature, signImageFolderName, completeFolderPath)
Else
strSignature = ""
End If
'Insert the signature to this email
bodySignature = "<HTML><BODY><br>" & strSignature & "</br></HTML></BODY>"
objMail.HTMLBody = Replace(objMail.HTMLBody, "SingaturePlaceHolder", bodySignature)
End Sub
Function GetSignature(fPath As String) As String
Dim fso As Object
Dim TSet As Object
Set fso = CreateObject("Scripting.FileSystemObject")
Set TSet = fso.GetFile(fPath).OpenAsTextStream(1, -2)
GetSignature = TSet.readall
TSet.Close
End Function
I am working with an Access application within Access 2016. The application outputs to a PDF file via the DoCmd.OutputTo method.
I want to either send this PDF attached to an email I build in code, or open a new Outlook email with the file attached.
When I click the button in the form which triggers the code that includes my sub(s) (which are located in separate modules), the email window is never displayed nor is an email sent (depending on the use of .Display vs .Send). I also do not receive any errors.
I think it may also be worth noting that the Call to the sub inside of a module that creates the PDF works as expected.
I am running Access 2016 and Outlook 2016 installed as part of Office 2016 Pro Plus on a Windows 7 64-bit machine. The Office suite is 32-bit.
The Module & Sub
(Email Address Redacted)
Dim objEmail As Outlook.MailItem
Dim objApp As Outlook.Application
Set objApp = CreateObject("Outlook.Application")
Set objEmail = oApp.CreateItem(olMailItem)
With objEmail
.Recipients.Add "email#domain.com"
.Subject = "Invoice"
.Body = "See Attached"
.Attachments.Add DestFile
.Display
End With
The Sub Call
MsgBox "Now saving the Invoice as a PDF"
strInvoiceNbr = Int(InvoiceNbr)
strWhere = "[InvoiceNbr]=" & Me!InvoiceNbr
strDocName = "Invoice Print One"
ScrFile = "Invoice Print One"
DestFile = "Inv" + strInvoiceNbr + " - " + Me.GetLastname + " - " + GetLocation
MsgBox DestFile, vbOKOnly
DoCmd.OpenForm strDocName, , , strWhere
Call ExportToPDF(SrcFile, DestFile, "INV")
Call EmailInvoice(DestFile)
Based on the fact that the PDF is being output within a sub in a Module file, should I be creating the email (or calling the sub) within the sub that creates the PDF?
NOTE: I have looked over this accepted answer here on Stack Overflow, as well as many others. My question differs due to the fact that I am asking why the message is not being displayed or sent, not how to build and send a message as the others are.
EDIT:
Outlook does not open and nothing occurs if Outlook is already open.
Final Note:
To add to the accepted answer, in the VBA editor for Access, you will likely have to go to Tools > References and enable Microsoft Outlook 16.0 Object Library or similar based on your version of Office/Outlook.
To pass full path try using Function EmailInvoice
Example
Option Explicit
#Const LateBind = True
Const olFolderInbox As Long = 6
Public Sub ExportToPDF( _
ByVal strSrcFileName As String, _
ByVal strNewFileName As String, _
ByVal strReportType As String _
)
Dim PathFile As String
Dim strEstFolder As String
strEstFolder = "c:\OneDrive\Estimates\"
Dim strInvFolder As String
strInvFolder = "c:\OneDrive\Invoices\"
' Export to Estimates or Invoices Folder based on passed parameter
If strReportType = "EST" Then
DoCmd.OutputTo acOutputForm, strSrcFileName, acFormatPDF, _
strEstFolder & strNewFileName & ".pdf", False, ""
PathFile = strEstFolder & strNewFileName & ".pdf"
ElseIf strReportType = "INV" Then
DoCmd.OutputTo acOutputForm, strSrcFileName, acFormatPDF, _
strInvFolder & strNewFileName & ".pdf", False, ""
PathFile = strEstFolder & strNewFileName & ".pdf"
End If
EmailInvoice PathFile ' call function
End Sub
Public Function EmailInvoice(FldrFilePath As String)
Dim objApp As Object
Set objApp = CreateObject("Outlook.Application")
Dim objNS As Object
Set objNS = olApp.GetNamespace("MAPI")
Dim olFolder As Object
Set olFolder = objNS.GetDefaultFolder(olFolderInbox)
'Open inbox to prevent errors with security prompts
olFolder.Display
Dim objEmail As Outlook.MailItem
Set objEmail = oApp.CreateItem(olMailItem)
With objEmail
.Recipients.Add "email#domain.com"
.Subject = "Invoice"
.Body = "See Attached"
.Attachments.Add FldrFilePath
.Display
End With
End Function
Your issue is with probably Outlook security. Normally Outlook would show a popup that says that a 3rd party application is attempting to send email through it. Would you like to allow it or not. However since you are doing this programmatically that popup never appears. There used to be a way to bypass this.
Test your program while the user is logged on and has Outlook open. See if there will be any difference in behavior. If that popup does come up, google the exact message and you will probably find a way to bypass it.
Any reason why you not using sendOject?
The advantage of sendobject, is that you not restriced to Outlook, and any email client should work.
So, this code can be used:
Dim strTo As String
Dim strMessage As String
Dim strSubject As String
strTo = "abc#abc.com;def#def.com"
strSubject = "Your invoice"
strMessage = "Please find the invoice attached"
DoCmd.SendObject acSendReport, "rptInvoice", acFormatPDF, _
strTo, , , strSubject, strMessage
Note that if you need to filter the report, then open it first before you run send object. And of course you close the report after (only required if you had to filter, and open the report before - if no filter is to be supplied, then above code will suffice without having to open the report first).
There is no need to separate write out the pdf file, and no need to write code to attach the resulting pdf. The above does everything in one step, and is effectively one line of code.
I'm from Brazil and sorry about my english but my error really driving me crazy!
I've already type a Code in VBA using this Directions API just for get the distance from A to B (in km).
Alright, I've talked to IT Security for allow my API Request and was permit and tested.
So today I set my macro to run perfectly and I discovered that my code return a INVALID_REQUEST, but more crazy about that is if I put the URL in the browser I get the response perfectly but when Excel try to run over coding, I get a INVALID_REQUEST from the XML return.
Look my code:
Function gglDirectionsResponse(ByVal strStartLocation, ByVal strEndLocation, ByRef strTravelTime, ByRef strDistance, ByRef strInstructions, Optional ByRef strError = "") As Boolean
On Error GoTo errorHandler
' Helper function to request and process XML generated by Google Maps.
Dim strURL As String
Dim objXMLHttp As Object
Dim objDOMDocument As Object
Dim nodeRoute As Object
Dim lngDistance As Long
Set objXMLHttp = CreateObject("MSXML2.XMLHTTP")
Set objDOMDocument = CreateObject("MSXML2.DOMDocument.6.0")
strStartLocation = Replace(strStartLocation, " ", "+")
strEndLocation = Replace(strEndLocation, " ", "+")
strURL = "https://maps.googleapis.com/maps/api/directions/xml" & _
"?origin=" & strStartLocation & _
"&destination=" & strEndLocation & _
"&key=MY_API_KEY" & _
"&sensor=false" & _
"&units=" & strUnits
'Send XML request
With objXMLHttp
.Open "GET", strURL, False
.setRequestHeader "Content-Type", "application/x-www-form-URLEncoded"
.send
objDOMDocument.LoadXML .responseText
End With
With objDOMDocument
If .SelectSingleNode("//status").Text = "OK" Then
'Get Distance
lngDistance = .SelectSingleNode("/DirectionsResponse/route/leg/distance/value").Text ' Retrieves distance in meters
Select Case strUnits
Case "imperial": strDistance = Round(lngDistance * 0.00062137, 1) 'Convert meters to miles
Case "metric": strDistance = Round(lngDistance / 1000, 1) 'Convert meters to miles
End Select
'Get Travel Time
strTravelTime = .SelectSingleNode("/DirectionsResponse/route/leg/duration/value").Text 'returns in seconds from google
strTravelTime = formatGoogleTime(strTravelTime) 'converts seconds to hh:mm
'Get Directions
For Each nodeRoute In .SelectSingleNode("//route/leg").ChildNodes
If nodeRoute.BaseName = "step" Then
strInstructions = strInstructions & nodeRoute.SelectSingleNode("html_instructions").Text & " - " & nodeRoute.SelectSingleNode("distance/text").Text & vbCrLf
End If
Next
strInstructions = CleanHTML(strInstructions) 'Removes MetaTag information from HTML result to convert to plain text.
Else
strError = .SelectSingleNode("//status").Text
GoTo errorHandler
End If
End With
gglDirectionsResponse = True
GoTo CleanExit
errorHandler:
If strError = "" Then strError = Err.Description
strDistance = -1
strTravelTime = "00:00"
strInstructions = ""
gglDirectionsResponse = False
CleanExit:
Set objDOMDocument = Nothing
Set objXMLHttp = Nothing
End Function
Function getGoogleDistance(ByVal strFrom, ByVal strTo) As String
'Returns the distance between strFrom and strTo
'where strFrom/To are address search terms recognisable by Google
'i.e. Postcode, address etc.
Dim strTravelTime As String
Dim strDistance As String
Dim strError As String
Dim strInstructions As String
If gglDirectionsResponse(strFrom, strTo, strTravelTime, strDistance, strInstructions, strError) Then
getGoogleDistance = strDistance
Else
getGoogleDistance = strError
End If
End Function
So, I just call the function gglDirectionsResponse sending, FROM and TO and the coding do the rest. As I said, I'd test and all works good, and now can't run. What I missing here guys?
The errorHandler is activate when the code try to run the specific line:
With objDOMDocument
If .SelectSingleNode("//status").Text = "OK" Then
Here return INVALID_REQUEST.
Look the URL after loaded:
https://maps.googleapis.com/maps/api/directions/xml?origin=Porto+Nacional-TO&destination=Silvanópolis-TO,+Brasil&key=MY_API_KEY&sensor=false&units=metric
Look the img from browser
browser img, from api url in the code
NOTE: I don't get macro or load or VBA code error, it's a errorHandler to validate the XML return, and the Return appear as invalid_request but browser load with success the same URL in the code.
Please please, somebody help me!
Try setting the request header to:
.setRequestHeader "User-Agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT enter code here5.0)"
Don't know if it will work though..
As I commented in the answer before. The problem was found when I wrote the line:
.SelectSingleNode("/").text
*Obviously after .Send command.
this return for me that I send a invalid parameter that contain a non-utf-8 character.
So the code in XML "/" it's like the root structure, and with this, I could get the error_message to understand more correctly the error.
So as I said, I'm from Brazil and our cities have a lot of (', í,á,é,ó ...) so I'd test and get finally the correct status.
And now my problem is how to convert any character, but easily if we compare :D
I have tried to make a script to pick up emails as they come in, reformat them and then forward on to the email in the body but I cannot work out how to read the email body. I currently have:
Sub Confirmation()
myMessage = "You recently made a request on the IT website, the details of your
request can be seen below:" & vbCr & vbCr & "Thank you, " & vbCr & "IT Support"
Dim itmOld As MailItem, itmNew As MailItem
Set itmOld = ActiveInspector.CurrentItem
Set itmNew = itmOld.Forward
itmNew.Body = myMessage & vbCr & vbCr & itmOld.Body
itmNew.Subject = "IT Web Request Confirmation"
itmNew.Display
Set itmOld = Nothing
Set itmNew = Nothing
End Sub
This opens the email adds some text to it and forwards it on.
I would like the script to open the email, read an email address from the body, use that as the to field and reformat the existing email to a nicer format.
This is the HTML from the email:
<html><body><br /><br /><table><tr><td><b>Fullname: </b></td><td>Alex Carter</td></tr><tr><td><b>OPS_Access: </b></td><td>Yes</td></tr><tr><td><b>Email_Account_Required: </b></td><td>Yes</td></tr><tr><td><b>Office_Email_Required: </b></td><td>Yes</td></tr><tr><td><b>Website_Access_Required: </b></td><td>Yes</td></tr><tr><td><b>Web_Access_Level: </b></td><td>Staff</td></tr><tr><td><b>Forum_Access_Required: </b></td><td>Yes</td></tr><tr><td><b>Date_Account_Required: </b></td><td>03/08/2013</td></tr><tr><td><b>Requested_By: </b></td><td>Alex Carter</td></tr><tr><td><b>Requestee_Email: </b></td><td>alex.carter#cars.co.uk</td></tr><tr><td><b>Office_Requesting: </b></td><td>Swindon</td></tr></table></body></html>
This shows that the email to go into the to field is in the 10th row of the table but I am not too sure how to go about selecting this from the body?
How would I go about reading the body, reformatting it and then selecting the requestee email and using it as the to field?
Thanks in advance!
This should help you get started (modifying your code), though you'll have to be more specific with regard to what formatting improvements you would like to see...:
Sub Confirmation()
myMessage = "You recently made a request on the IT website, the details of your request can be seen below:" & vbCr & vbCr & "Thank you, " & vbCr & "IT Support"
Dim sAddress As String ' Well need this to store the address
Dim itmOld As MailItem, itmNew As MailItem
Set itmOld = ActiveInspector.CurrentItem
Set itmNew = itmOld.Forward
sAddress = GetAddressFromMessage(itmOld) ' This is our new function
If Len(sAddress) > 0 Then
itmNew.To = sAddress ' If our new function found a value apply it to the To: field.
'!!! This should be checked as a valid address before continuing !!!
End If
itmNew.Body = myMessage & vbCr & vbCr & itmOld.Body
itmNew.Subject = "IT Web Request Confirmation"
itmNew.Display
Set itmOld = Nothing
Set itmNew = Nothing
End Sub
Private Function GetAddressFromMessage(msg As MailItem) As String
' Grabs the email from the standard HTML form described in the SO question.
Dim lStart As Long
Dim lStop As Long
Dim sItemBody As String
Const sSearchStart As String = "Requestee_Email: </b></td><td>" ' We will look for these tags to determine where the address can be found.
Const sSearchStop As String = "</td>"
sItemBody = msg.HTMLBody ' Read the body of the message as HTML to retain TAG info.
lStart = InStr(sItemBody, sSearchStart) + Len(sSearchStart)
If lStart > 0 Then ' Make sure we found the first TAG.
lStop = InStr(lStart, sItemBody, sSearchStop)
End If
GetAddressFromMessage = vbNullString
If lStop > 0 And lStart > 0 Then ' Make sure we really did find a valid field.
GetAddressFromMessage = Mid(sItemBody, lStart, lStop - lStart)
End If
End Function
How can I tell the current user language in a vba program?
I need this to show a form in an appropriate language.
My initial code (utilising this vbforum code) assumed that Windows and Excel share a common language - likely but not bulletproof.
updated
The revised code:
Returns the Locale ID (LCID).
Looks up the LCID from this msft link.
Parses the LCID using a regexp to get the language.
Sample output on my machine below
The code will let the user know if there are any errors in accessing the LCID website, or in parsing the country name.
Sub GetXlLang()
Dim lngCode As Long
lngCode = Application.LanguageSettings.LanguageID(msoLanguageIDUI)
MsgBox "Code is: " & lngCode & vbNewLine & GetTxt(lngCode)
End Sub
Function GetTxt(ByVal lngCode) As String
Dim objXmlHTTP As Object
Dim objRegex As Object
Dim objRegMC As Object
Dim strResponse As String
Dim strSite As String
Set objXmlHTTP = CreateObject("MSXML2.XMLHTTP")
strSite = "http://msdn.microsoft.com/en-us/goglobal/bb964664"
On Error GoTo ErrHandler
With objXmlHTTP
.Open "GET", strSite, False
.Send
If .Status = 200 Then strResponse = .ResponseText
End With
On Error GoTo 0
strResponse = Replace(strResponse, "</td><td>", vbNullString)
Set objRegex = CreateObject("vbscript.regexp")
With objRegex
.Pattern = "><td>([a-zA-Z- ]+)[A-Fa-f0-9]{4}" & lngCode
If .Test(strResponse) Then
Set objRegMC = .Execute(strResponse)
GetTxt = objRegMC(0).submatches(0)
Else
GetTxt = "Value not found from " & strSite
End If
End With
Set objRegex = Nothing
Set objXmlHTTP = Nothing
Exit Function
ErrHandler:
If Not objXmlHTTP Is Nothing Then Set objXmlHTTP = Nothing
GetTxt = strSite & " unable to be accessed"
End Function
dim lang_code as long
lang_code = Application.LanguageSettings.LanguageID(msoLanguageIDUI)
This is another variation of the code posted by brettdj
Sub Test_GetLocale_UDF()
Dim lngCode As Long
lngCode = Application.LanguageSettings.LanguageID(msoLanguageIDUI)
MsgBox "Code Is: " & lngCode & vbNewLine & GetLocale(lngCode)
End Sub
Function GetLocale(ByVal lngCode) As String
Dim html As Object
Dim http As Object
Dim htmlTable As Object
Dim htmlRow As Object
Dim htmlCell As Object
Dim url As String
Set html = CreateObject("htmlfile")
Set http = CreateObject("MSXML2.XMLHTTP")
url = "https://www.science.co.il/language/Locale-codes.php"
On Error GoTo ErrHandler
With http
.Open "GET", url, False
.send
If .Status = 200 Then html.body.innerHTML = .responseText
End With
On Error GoTo 0
Set htmlTable = html.getElementsByTagName("table")(0)
For Each htmlRow In htmlTable.getElementsByTagName("tr")
For Each htmlCell In htmlRow.Children
If htmlCell.innerText = CStr(lngCode) Then
GetLocale = htmlRow.getElementsByTagName("td")(0).innerText
Exit For
End If
Next htmlCell
Next htmlRow
If GetLocale = "" Then GetLocale = "Value Not Found From " & url
Exit Function
ErrHandler:
If Not http Is Nothing Then Set http = Nothing
GetLocale = url & " Unable To Be Accessed"
End Function
Select Case Application.International(xlApplicationInternational.xlCountryCode)
Case 1: Call MsgBox("English")
Case 33: Call MsgBox("French")
Case 49: Call MsgBox("German")
Case 81: Call MsgBox("Japanese")
End Select
Straight out of here: https://bettersolutions.com/vba/macros/region-language.htm
Relevant Documentation: https://learn.microsoft.com/en-us/office/vba/api/excel.xlapplicationinternational
VBA Application.LanguageSettings.LanguageID(msoLanguageIDUI) gets only Microsoft Office Access or Microsoft Excel User interface Language. But does not reflect the Windows System display language used for Windows Display, every thing as MsgBox() buttons etc.
If user changes his Windows Display language via Windows Control Panel, this Application level setting will not change. In which case, we can use this Kernel dll function to get the new LCID (Microsoft Language Code IDentifier, 1036=French, 1033=English, ...), with this code in a VBA Module:
Private Declare Function GetUserDefaultUILanguage Lib "kernel32.dll" () As Long
Public Function winGetUserDefaultUILanguage()
winGetUserDefaultUILanguage = GetUserDefaultUILanguage()
End Function
Then you can call the public function winGetUserDefaultUILanguage() every where in your VBA code to get Windows LCID.
The kernel dll function GetUserDefaultUILanguage() will reflect the changement via Windows Display Language ID.
For example, as I've French Windows display language, LCID=1036, my Office is also in French (LCID=1036),
VBA Debugger Console:
Now we switch to English Windows display language via Control Panel (LCID=1033 for English), but my Office language ID remains unchanged (LCID=1036):
VBA Debugger Console:
From LCID, you can further get Language Tag, Language Name from Microsoft API documents here or here.