I'm having some problems with some pieces of code that I've Frankenstein'd from the web. I have a Word document that uses a command button to run a piece of code, the end result being an Outlook Appointment being generated with a template added, as well as recipients. I've used 2 different methods, and they each present their own problem.
Method 1: Generates the Appointment, includes and displays the attendees, but does not allow for HTML formatting of the body
Dim xOutlookObj As Object
Dim OMail As Object
Dim xMeeting As Object
Dim xDoc As Object
Dim myRequiredAttendee As Outlook.Recipient
Dim myOptionalAttendee As Outlook.Recipient
Application.ScreenUpdating = False
Set xOutlookObj = CreateObject("Outlook.Application")
Set xEmail = xOutlookObj.CreateItem(olMailItem)
Set xDoc = ActiveDocument
opCancel = False
Set xOutlookObj = CreateObject("Outlook.Application")
Set xMeeting = xOutlookObj.CreateItem(olAppointmentItem)
Set myRequiredAttendee = xMeeting.Recipients.Add(EMAIL ADDRESSES)
myRequiredAttendee.Type = olRequired
Set xDoc = ActiveDocument
With xMeeting
.MeetingStatus = olMeeting
.Display
.Subject = "MEETING SUBJECT"
.Duration = 60
.Body = "MESSAGE BODY THAT I'D LIKE TO FORMAT, BUT THIS METHOD DOESN'T PERMIT HTML"
End With
Set xDoc = Nothing
Set xMeeting = Nothing
Set xOutlookObj = Nothing
Application.ScreenUpdating = True
Method 2: Generates the Appointment, allows for HTML formatting of the body, loads the attendees but does not display them. When I click the 'Invite Attendee' button in the invite, they all appear (so they're obviously being loaded).
Dim olapp As Outlook.Application, appt As Outlook.AppointmentItem
Dim m As Outlook.MailItem
Dim rtf() As Byte
Set olapp = New Outlook.Application
Set m = olapp.CreateItem(olMailItem)
Set appt = olapp.CreateItem(olAppointmentItem)
appt.Display
appt.Subject = "MEETING SUBJECT"
appt.Duration = 60
appt.RequiredAttendees = "EMAIL ADDRESSES"
m.BodyFormat = olFormatHTML
m.HTMLBody = "<font style=""color: red;"">VERIFY SUBJECT LINE & MEETING START TIME ARE CORRECT, THEN DELETE THIS LINE" & _
"<font style=""color: black;""><p>REST OF TEXT BODY</P>
m.GetInspector().WordEditor.Range.FormattedText.Copy
appt.GetInspector().WordEditor.Range.FormattedText.Paste
m.Close False 'don't save...
What I'm looking for is code that creates the Appointment, loads and displays the attendees, and allows HTML formatting of the body. Also, I can't use the .send command because the body of the invite still needs to be edited before it's sent - which is why I'd like the attendees to be displayed to avoid confusion.
Thanks!
#1 is fine, but AppointmentItem object does not directly support HTML - you get either plain text Body property or RTF formatted (array of byte) RtfBody property. You needed to either generate the appropriate RTF, or use AppointmentItem.GetInspector().WordEditor (returns Word's Document object) to produce the suitably formatted body.
The following code generates the appointment, loads the recipients and displays them, as well as formats the message body in HTML.
Dim xOutlookObj As Object
Dim OMail As Object
Dim xMeeting As Object
Dim xDoc As Object
Dim myRequiredAttendee As Outlook.Recipient
Dim myOptionalAttendee As Outlook.Recipient
Application.ScreenUpdating = False
Set xOutlookObj = CreateObject("Outlook.Application")
Set xEmail = xOutlookObj.CreateItem(olMailItem)
Set xDoc = ActiveDocument
Set xOutlookObj = CreateObject("Outlook.Application")
Set xMeeting = xOutlookObj.CreateItem(olAppointmentItem)
Set myRequiredAttendee = xMeeting.Recipients.Add(EMAIL LIST)
myRequiredAttendee.Type = olRequired
Set xDoc = ActiveDocument
With xMeeting
.MeetingStatus = olMeeting
.Display
.Subject = "MEETING SUBJECT"
.Duration = 60
'**
xEmail.BodyFormat = olFormatHTML
xEmail.HTMLBody = "<font style=""color: red;"">VERIFY SUBJECT LINE & MEETING START TIME ARE CORRECT, THEN DELETE THIS LINE" & _
"<font style=""color: black;""><p>THE REST OF APPT MESSAGE</p>"
xEmail.GetInspector().WordEditor.Range.FormattedText.Copy
xMeeting.GetInspector().WordEditor.Range.FormattedText.Paste
'**
End With
Set xDoc = Nothing
Set xMeeting = Nothing
Set xOutlookObj = Nothing
Application.ScreenUpdating = True
The ** part is what I changed to make this work.
Related
I search my Sent Mail in Outlook and open the last email to a specified email address (this part is done).
I want to add text to the email chain, while keeping the previous messages intact.
The code below creates a "blank-slate" so that all of the previous email correspondence is lost.
What do I need to do to add text to the Body of the email?
FunctionComposeResponse(searchEmail As String, emailBody As String)
Dim currDateTime As Date: currDateTime = Now()
Dim tenDayPrior As Date: tenDayPrior = DateValue(CStr(Now())) - 10 & " 07:00:00 AM"
Dim olApp As Outlook.Application
Dim olNS As NameSpace
Dim Fldr As Folder
Dim olReply As Outlook.MailItem
Dim msg As Object
Set olApp = New Outlook.Application
Set olNS = olApp.GetNamespace("MAPI")
Set Fldr = olNS.GetDefaultFolder(olFolderSentMail)
For Each msg In Fldr.Items
If TypeName(msg) = "MailItem" Then
For Each recipient in msg.recipients
If recip.Address = searchEmail Then
If msg.SentOn >= tenDayPrior And msg.SentOn <= currDateTime Then
Set olReply = msg.ReplyAll
With olReply
.BodyFormat = olFormatHTML
.HTMLBody = emailBody
.Save
.Close olSave
End With
End If
End If
Next recip
End If
Next msg
End Function
By setting
.HTMLBody = emailBody
you overwrite everything that was there before.
You need to insert your text into the existing .HTMLBody.
For new mailitems, where I want to preserve the default HTML signature, I use the following - inspect your existing .HTMLBody to find out if this will also work for Reply (if not: adapt).
' emailBody is plain text -> encode as HTML
emailBody = HtmlEncode(emailBody)
' Outlook-HTML: mail text begins with this line:
' <p class=MsoNormal><o:p> </o:p></p>
' Insert my text instead of the first
oItem.HtmlBody = Replace(oItem.HtmlBody, " ", emailBody, Count:=1)
I have a form in word, whenever a submit is clicked upon it send a notification to email address and saves the file to a location. I have a script as below, I want when the data is input in the form and clicked on submit for 2nd time the saved file should have a different name because if the below script is run again it will overwrite the current form which is saved on that location.
Private Sub CommandButton1_Click()
Dim OL As Object
Dim EmailItem As Object
Dim Doc As Document
Application.ScreenUpdating = False
Set OL = CreateObject("Outlook.Application")
Set EmailItem = OL.CreateItem(olMailItem)
Set Doc = ActiveDocument
Doc.Save
Doc.SaveAs2 "d:/abcd.docx"
With EmailItem
.Subject = "Test"
.Body = "Test"
.To = "jaiswalrohitkr#gmail.com"
.Importance = olImportanceNormal 'Or olImprotanceHigh Or olImprotanceLow
.Attachments.Add Doc.FullName
.Send
End With
Application.ScreenUpdating = True
Set Doc = Nothing
Set OL = Nothing
Set EmailItem = Nothing
End Sub
You can use a global counter and the FileSystemObject to accomplish this.
'Global Variable to hold our iteration count
Public docCount As Integer
Private Sub CommandButton1_Click()
Dim OL As Object
Dim EmailItem As Object
Dim Doc As Document
Dim DocName As String
'add a reference to Microsoft Scripting Runtime
Dim fso As FileSystemObject
Set fso = New FileSystemObject
Application.ScreenUpdating = False
Set OL = CreateObject("Outlook.Application")
Set EmailItem = OL.CreateItem(olMailItem)
Set Doc = ActiveDocument
Doc.Save
DocName = "d:/abcd.docx"
'use the fileSystemObject to check if the file already exists
Do While fso.FileExists(DocName) = True
DocName = "d:/abcd" & CStr(docCount) & ".docx"
'add one to the counter to check again
docCount = docCount + 1
Loop
Doc.SaveAs2 DocName
With EmailItem
If docCount > 0 Then
'more than one iteration so adjust name
.Subject = "Test" & CStr(docCount)
Else
'first iteration so leave it as test
.Subject = "Test"
End If
.Body = "Test"
.To = "jaiswalrohitkr#gmail.com"
.Importance = olImportanceNormal 'Or olImprotanceHigh Or olImprotanceLow
.Attachments.Add Doc.FullName
.Send
End With
Application.ScreenUpdating = True
Set Doc = Nothing
Set OL = Nothing
Set EmailItem = Nothing
End Sub
I'm looking to use the get function in vba in order to activate a specific email in Outlook and then copy the body into a new email and send. I can use the getlast function to get the latest email in the inbox, however I would like to refine the code some more by selecting the latest email from a specific email address.
Also, I'd love how to know how to delete the signature from the text pasted into the new email.
Sub Negotiations()
Dim objMsg As Outlook.MailItem
Dim objItem As Outlook.MailItem
Dim BodyText As Object
Dim myinspector As Outlook.Inspector
Dim myItem As Outlook.MailItem
Dim NewMail As MailItem, oInspector As Inspector
Set myItem = Application.Session.GetDefaultFolder(olFolderInbox).Items.GetLast
myItem.Display
'copy body of current item
Set activeMailMessage = ActiveInspector.CurrentItem
activeMailMessage.GetInspector().WordEditor.Range.FormattedText.Copy
' Create the message.
Set objMsg = Application.CreateItem(olMailItem)
'paste body into new email
Set BodyText = objMsg.GetInspector.WordEditor.Range
BodyText.Paste
'set up and send notification email
With objMsg
.To = "#gmail.com"
.Subject = "Negotiations"
.HTMLBody = activeMailMessage.HTMLBody
.Display
End With
End Sub
any help would be appreciated, thank you guys!
Open the Inbox folder using Namespace.GetDefaultFolder(olFolderInbox), retrieve the Items collection from MAPIFolder.Items. Sort the items (Items.Sort) on the ReceivedTime property, retrieve the latest email using Items.Find on the SenderEmailAddress property.
Dependant on what your property of .SenderEmailAddress returns, you can adapt what the while statement evaluates for. This should work for you, by first looking at the last e-mail, and then checking each previous e-mail for the correct sender address.
Sub display_mail()
Dim outApp As Object, objOutlook As Object, objFolder As Object
Dim myItems As Object, myItem As Object
Dim strSenderName As String
Set outApp = CreateObject("Outlook.Application")
Set objOutlook = outApp.GetNamespace("MAPI")
Set objFolder = objOutlook.GetDefaultFolder(olFolderInbox)
Set myItems = objFolder.Items
strSenderName = UCase(InputBox("Enter the e-mail Alias."))
Set myItem = myItems.GetLast
While Right(myItem.SenderEmailAddress, Len(strSenderName)) <> strSenderName
Set myItem = myItems.GetPrevious
Wend
myItem.Display
End Sub
Application.Session.GetDefaultFolder(olFolderInbox).Items.GetLast
activeMailMessage.GetInspector().WordEditor.Range.FormattedText.Copy
First of all, I'd recommend breaking the chain of calls. Declare each property or method call on a separate line of code, so you will be able to debug the code at any time and see what happens under the hood.
The GetLast method returns the last object in the collectio. But it doesn't mean that the item is recieved last. You need to sort the collection using the Sort method as Dmitry suggested passing the ReceivedTime property as a parameter to sort on. Only in that case you will get the last recieved item from the collection.
The Outlook object model doesn't provide any special method or property for identifying signatures. You need to parse the message body and find it programmatically.
Sub Nego()
Dim objMsg As Outlook.MailItem
Dim myItem As Outlook.MailItem
Dim BodyText As Object
Dim Inspector As Outlook.MailItem
Dim olNameSpace As Outlook.NameSpace
Dim olfolder As Outlook.MAPIFolder
Dim msgStr As String
Dim endStr As String
Dim endStrStart As Long
Dim endStrLen As Long
Dim myItems As Outlook.Items
'Access folder Nego
Const olFolderInbox = 6
Set objOutlook = CreateObject("Outlook.Application")
Set objNamespace = objOutlook.GetNamespace("MAPI")
Set objInbox = objNamespace.GetDefaultFolder(olFolderInbox)
strFolderName = objInbox.Parent
Set objMailbox = objNamespace.Folders(strFolderName)
Set objFolder = objMailbox.Folders("Nego")
'Mark as read
For Each objMessage In objFolder.Items
objMessage.UnRead = False
Next
'Sort
Set myItems = objFolder.Items
For Each myItem In myItems
myItems.Sort "Received", False
Next myItem
myItems.GetLast.Display
'copy body of current item
Set activeMailMessage = ActiveInspector.CurrentItem
activeMailMessage.GetInspector().WordEditor.Range.FormattedText.Copy
' Create the message.
Set objMsg = Application.CreateItem(olMailItem)
'paste body into new email
Set BodyText = objMsg.GetInspector.WordEditor.Range
BodyText.Paste
'Search Body
Set activeMailMessage = ActiveInspector.CurrentItem
endStr = "first line of signature"
endStrLen = Len(endStr)
msgStr = activeMailMessage.HTMLBody
endStrStart = InStr(msgStr, endStr)
activeMailMessage.HTMLBody = Left(msgStr, endStrStart + endStrLen)
'set up and send email
With objMsg
.To = "#email"
.Subject = "Nego"
.HTMLBody = activeMailMessage.HTMLBody
.HTMLBody = Replace(.HTMLBody, "First line of signature", " ")
.Send
End With
End Sub
I have a little script that converts an email into a Task in my outlook.
My main frustration is that it won't preserve the html format, and deals with embedded images as attachments. I was wondering if anyone could help. I know it is possible as I've copied the body of an email directly across to the body of a task manually and it is preserved fine.
Sub ConvertSelectedMailtoTask()
Dim objApp As Outlook.Application
Dim objTask As Outlook.TaskItem
Dim objMail As Outlook.MailItem
Set objTask = Application.CreateItem(olTaskItem)
Set objApp = Application
If TypeName(objApp.ActiveWindow) = "Explorer" Then
For Each objMail In Application.ActiveExplorer.Selection
If Left(objMail.Subject, 3) = "RE:" Or Left(objMail.Subject, 3) = "FW:" Then
subj = Right(objMail.Subject, Len(objMail.Subject) - 4)
Else
subj = objMail.Subject
End If
With objTask
.Subject = subj
.Importance = objMail.Importance
.StartDate = objMail.ReceivedTime
.Body = objMail.Body
.DueDate = Date + 3
If objMail.Attachments.Count > 0 Then
CopyAttachments objMail, objTask
End If
.ReminderSet = True
.ReminderTime = Date + 2.5
.Sensitivity = olPrivate
.Save
End With
Next
ElseIf TypeName(objApp.ActiveWindow) = "Inspector" Then
Set objMail = objApp.ActiveInspector.CurrentItem
If Left(objMail.Subject, 3) = "RE:" Or Left(objMail.Subject, 3) = "FW:" Then
subj = Right(objMail.Subject, Len(objMail.Subject) - 4)
Else
subj = objMail.Subject
End If
With objTask
.Subject = subj
.Importance = objMail.Importance
.StartDate = objMail.ReceivedTime
.Body = objMail.Body
.DueDate = Date + 3
If objMail.Attachments.Count > 0 Then
CopyAttachments objMail, objTask
End If
.ReminderSet = True
.ReminderTime = Date + 2.5
.Sensitivity = olPrivate
.Save
End With
End If
Set objTask = Nothing
Set objMail = Nothing
Set objApp = Nothing
End Sub
And here is the script for the attachments
Sub CopyAttachments(objSourceItem, objTargetItem)
Set fso = CreateObject("Scripting.FileSystemObject")
Set fldTemp = fso.GetSpecialFolder(2) ' TemporaryFolder
strPath = fldTemp.Path & "\"
For Each objAtt In objSourceItem.Attachments
strFile = strPath & objAtt.FileName
objAtt.SaveAsFile strFile
objTargetItem.Attachments.Add strFile, , , objAtt.DisplayName
fso.DeleteFile strFile
Next
Set fldTemp = Nothing
Set fso = Nothing
End Sub
Update:
I found a bit of code that uses a word document to preserve the formatting:
Sub CopyFullBody(sourceItem As Object, targetItem As Object)
Dim objDoc As Word.Document
Dim objSel As Word.Selection
Dim objDoc2 As Word.Document
Dim objSel2 As Word.Selection
On Error Resume Next
' get a Word.Selection from the source item
Set objDoc = sourceItem.GetInspector.WordEditor
If Not objDoc Is Nothing Then
Set objSel = objDoc.Windows(1).Selection
objSel.WholeStory
objSel.Copy
Set objDoc2 = targetItem.GetInspector.WordEditor
If Not objDoc2 Is Nothing Then
Set objSel2 = objDoc2.Windows(1).Selection
objSel2.PasteAndFormat wdPasteDefault
Else
MsgBox "Could not get Word.Document for " & _
targetItem.Subject
End If
Else
MsgBox "Could not get Word.Document for " & _
sourceItem.Subject
End If
Set objDoc = Nothing
Set objSel = Nothing
Set objDoc2 = Nothing
Set objSel2 = Nothing
End Sub
This doesn't seem like it would be the only solution hence updating my own post instead of answering my question as this seems a bit long winded (using another application just to give me formatting, when I can copy and paste text manually just fine all in Outlook). If anyone has any other thoughts on this/defining attachment types please carry on answering!
in the line
.Body = objMail.Body
you only ask fot the non-formatted Body. Try instead:
.Body = objMail.htmlBody
and something completely different: I just put reminders onto the emails themselves, so I have no need to create extra Tasks at all...
Keep in mind that Outlook tasks, appointments and tasks work with RTF, not HTML. hence TaskItem, ContactItem and AppointmentItem objects only expose the RtfBody property, but not HTMLBody (like MailItem does).
You will need to either convert HTML to RTF (you can try the Word Object Model for that) or use Redemption (I am its author): unlike Outlook Object Model, it exposes the RDOTaskItem.HTMLBody property and dynamically converts HTML to the native (for tasks) RTF when that property is set.
I have a user who wants to redirect any email to other people in their department so that when that person replies to the email it will go back to the person who originally sent it.
I am trying to make VBA code to forward all emails in a specified folder and change the reply to address so that they don't have to manually put it in every time.
Sub SendFolder()
Dim olApp As Outlook.Application
Dim olNS As Outlook.NameSpace
Dim MyFolder As Outlook.MAPIFolder
Dim ObjMail As Outlook.MailItem
Set olApp = Outlook.Application
Set olNS = olApp.GetNamespace("MAPI")
Set MyFolder = Application.Session.Folders("me#us.com").Folders("test")
For i = MyFolder.Items.Count To 0 Step -1
Set ObjMail.Subject = MyFolder.Itmes(i).Subject
Set ObjMail.ReplyRecipients = MyFolder.Itmes(i).ReplyRecipients
Set ObjMail.Body = MyFolder.Itmes(i).Body
Set ObjMail.Attachments = MyFolder.Itmes(i).Attachments
Set ObjMail.BodyFormat = MyFolder.Itmes(i).BodyFormat
Set ObjMail.To = "test#us.com"
ObjMail.Send
Next
End Sub
You are missing
Set ObjMail = Application.CreateItem(olMailItem)
Then your code would become
With ObjMail
.Subject = MyFolder.Itmes(i).Subject
.ReplyRecipients = MyFolder.Items(i).ReplyRecipients
.Body = MyFolder.Items(i).Body
.Attachments = MyFolder.Items(i).Attachments
.BodyFormat = MyFolder.Items(i).BodyFormat
.To = "test#us.com"
.Send
End with
It it runs now, the ReplyTo does not change.
You will want to set the ObjMail's ReplyRecipients property
Something like .ReplyRecipients.Add MyFolder.Items(i).SenderEmailAddress
To simplify the issue, .Forward the mail as is, and set only the ReplyRecipients property.
Check out this alternative. The mail is sent as an attachment. The receiver automatically replies to the original sender.
Untested
Sub SendFolderItemsAsAttachments()
' Run this VBA code while in Outlook
Dim MyFolder As MAPIFolder
Dim notMyItems as Items
Dim notReplyingToMe as mailitem
Dim i as long
Set MyFolder = Application.Session.Folders("me#us.com").Folders("test")
Set notMyItems = MyFolder.Items
For i = notMyItems.Count To 1 Step -1
If TypeOf notMyItems(i) Is MailItem Then
Set notReplyingToMe = Application.CreateItem(olMailItem)
With notReplyingToMe
.Subject = notMyItems(i).Subject & " - " & _
notMyItems(i).SenderName
.HTMLBody = "Redirecting for your action."
.Attachments.Add notMyItems(i), olEmbeddeditem
.To = "test#us.com"
.Send
End With
notMyItems(i).Delete
End If
Next
Set MyFolder = = Nothing
Set notMyItems = Nothing
Set notReplyingToMe = Nothing
End Sub