Automatically Remove Border Around Warning in Email Body - vba

I just suffered the same problem as described in the discussion linked to below, but with a catch: My organization has added border and highlighting to the warning banner added on all external emails.
Referenced discussion:
Automatically Remove Warning in Email Body
I have developed code to strip the text out, which had to be split because the HTML source code uses different formatting for parts of the warning banner:
Private Sub Application_ItemSend(ByVal Item As Object, Cancel As Boolean)
Item.HTMLBody = Replace(Item.HTMLBody, "Attention:", "")
Item.HTMLBody = Replace(Item.HTMLBody, "This email originated from outside the university.", "")
End Sub
This leaves behind an empty banner with a brown border and tan highlighting. All of this is prepended to the message in HTML code, but I don't know how to get VBA to search at the HTML level. I would like to modify the above to instead strip the following HTML from the message body:
<div style=3D"border:solid #9C6500 1.0pt;padding:2.0pt 2.0pt 2.0pt 2.0pt">
<p class=3D"MsoNormal" style=3D"line-height:11.0pt;background:#FFEB9C"><b><=
span style=3D"font-size:9.0pt;color:#9C6500">Attention:</span></b><span sty=
le=3D"font-size:9.0pt;color:black"> This email originated from outside the =
university.<o:p></o:p></span></p>
Can VBA edit at the HTML level, i.e., modify the source? The first line of the HTML code is what needs to go, but I am struggling to find the right command.

You need to remove the entire block containing this message.
<div> and </div> are like brackets around some Html that specify that everything between <div> and </div> is to be treated as a block. The author of the Html might what to create a block for any of several reasons. Here the author wants to specify the appearance of the block. So <div style=xxxx>yyyy</div> says apply style xxxx to yyyy.
Your question omits the trailing </div>. If this block appears at the very end of the message, the person who coded this addition might have omitted the </div although this is not good practice. More likely, you did not realise that the trailing <\div> was important.
My approach would be:
Use InStr to search for “This message originated …”
Use InStrRev to search backwards for the <div
Use InStr to search for the </div>
Delete everything between the div and the <\div>
If you need the code to do this, I will send myself an email with this block so I can test the code. I do not like posting untested code.
Edit
I have written and partially tested code for the approach I would favour. I have not fully tested my code because I do not understand your approach.
As I understand it, your university adds a warning to emails received from outside the university. This happens before the email is released to you. I would expect you to use the Item Add event but you are using the Item Send event. I do not understand how this would give you the effect you seek.
I created an email containing a warning message using a gmail account and sent it to my Outlook account. The appearance of that email is:
This is not the same as your warning message, but it is close enough for testing.
I have a diagnostic routine that will output selected properties of selected emails to the Immediate Window or “all” properties of selected emails to a file. The output for the relevant part of the Html body is:
<di|
|v style='mso-element:para-border-div;border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt'>|
|<p class=MsoNormal align=center style='text-align:center;border:none;padding:0cm'><span style='font-|
|family:"inherit",serif;color:#303336;border:none windowtext 1.0pt;padding:0cm;background:aqua;mso-hi|
|ghlight:aqua;mso-fareast-language:EN-GB'>This email originated from outside the university.</span><o|
|:p></o:p></p></div>
I have deleted everything from the output except the DIV block containing the warning. If you would like more information about my diagnostic routine, I am happy to supply it.
The code to update the Html body is:
Sub RemoveWarning(ByRef ItemCrnt As MailItem)
Dim LcHtmlBody As String
Dim PosDivEnd As Long
Dim PosDivStart As Long
Dim PosMessage As Long
With ItemCrnt
' Check message contains warning
PosMessage = InStr(1, .HtmlBody, "This email originated from outside the university.")
If PosMessage = 0 Then
' No message found
Exit Sub
End If
' Find start and end div
LcHtmlBody = .HtmlBody ' Allow for "<DIV" and "<div"
PosDivStart = InStrRev(LcHtmlBody, "<div", PosMessage)
PosDivEnd = InStr(PosMessage, LcHtmlBody, "</div>")
If PosDivStart = 0 Or PosDivEnd = 0 Then
' Start div or end div or both not found
Exit Sub
End If
' Delete Div block from Html
.HtmlBody = Mid$(.HtmlBody, 1, PosDivStart - 1) & Mid$(.HtmlBody, PosDivEnd + 6)
End With
End Sub
The result of running this code is:
This has removed the entire warning, including the coloured box, but has left a gap. This code may not leave a gap with your warning message. If it does leave a gap with your message, you will probably need to delete an empty paragraph as well as the Div block. I would need to see the Html before your “<div” or after your “</div” before I could recommend how to expand my code.

Related

Comment.Add in Word VBA (2019) is inconsistent in adding text to the comment

Context: I am using Word 2019 (Office 365 subscription, if that matters). I have added a series of buttons to my Review ribbon, each set to create a specific comment on some text. (Writing teacher. I make a lot of repeated comments.)
The structure for each comment macro is as follows:
Sub AddSomeComment()
Dim someComment As String
someComment = "Long explanation of some revision recommendation."
ActiveDocument.Comments.Add Range:=Selection.Range, Text:=someComment
End Sub
Problem: The comment balloon is always added, but it is frequently left blank. On some documents, every comment will work (1/10 maybe). On others, the first comment will work but all others will be left blank. A majority of documents will not place the text into the comment. All documents are saved as docx prior to commenting.
I have tried debugging and rewriting the code several times. I've hard coded strings in to each comment and gotten the same results. I have tried using a MsgBox() to verify that someComment has a string in it (it does).
I don't know why it might be behaving like that. Perhaps there's a "race" condition (the text is sent to the comment before it's been created).
Try it like this: create the comment and after it's been created assign the text. (Note: I'm on a mobile device so can't test for syntax errors)
Sub AddSomeComment()
Dim cmt as Comment
Dim someComment As String
someComment = "Long explanation of some revision recommendation."
Set cmt = ActiveDocument.Comments.Add Range:=Selection.Range
cmt.Range.Text = someComment
End Sub

How to get the last child of an HTMLElement

I have written a macro in Excel that opens and parses a website and pulls the data from it. The trouble I'm having is once I'm done with all of the data on the current page I want to go to the next page. To do this I want to get the last child of the "result-stats" node. I found the lastChild function, and so came up with the following code:
'Checks to see if there is a next page
If html.getElementById("result-stats").LastChild.innerText = "Next" Then
html.getElementById("result-stats").LastChild.Click
End If
And here is the HTML that it is accessing:
<p id="result-stats">
949 results
<span class="optional"> (1.06 seconds)</span>
Modify search
Show more columns
Next
</p>
When I try to run this, I get an error. After a lot of searching I think I found the reason. According to what I read, getElementById returns an element and not a node. lastChild only works on nodes, which is why the function doesn't work here.
My question is this. Is there a clean and simple way to grab the last child of an element? Or is there a way to typecast an element to that of a node? I feel like I'm missing something obvious, but I've been at this way longer than I should have been. Any help anyone could provide would be greatly appreciated.
Thanks.
Here's a shell of how to do it. If my comments are not clear, ask away. I assumed knowledge of how to navigate to the page, wait for the browser, etc.
Sub ClickLink()
Dim IE As Object
Set IE = CreateObject("InternetExplorer.Application")
'load up page and all that stuff
'process data ...
'click link
Dim doc As Object
Set doc = IE.document
Dim aLinks As Object, sLink As Object
For Each sLink In doc.getElementsByTagName("a")
If sLink.innerText = "Next" Then 'may need to play with this, if `innerttext' doesn't work
sLink.Click
Exit For
End If
Next
End Sub

Programming VBA in an Outlook form

I created my own Outlook form to use it as standard surface to enter certain orders instead of the normal message form. The creation, editing and sending works perfectly fine and in the next step I want to insert some code via VBA.
My problem is that I can´t access the objects of my form in the VBA editor. E.g. I want to show a message box when a certain checkbox is checked. According code would be:
Sub example()
If CheckBox1.Value = True Then
MsgBox("Checkbox 1 is checked.")
End If
End Sub
When I run the code I get the error that the object could not be found. The same goes for every other object, like textboxes or labels etc.
I guess the solution is pretty simple, like putting Item. or sth. like that in front of each object. But so far I wasn't able to find the solution.
I´m using Outlook 2010.
I know this is a year too late but you'll want to do something like this example below. It's kinda a work around but you can get whatever value was selected.
Sub ComboBox1_Click()
Set objPage = Item.GetInspector.ModifiedFormPages("Message")
Set Control = objPage.Controls("ComboBox1")
MsgBox "The value in the " & Control.Name & _
"control has changed to " & Control.Value & "."
End Sub
You should be able to get the value, just get a handle on the object you want using the Inspector
The following is an excerpt from here
When you use a custom form, Outlook only supports the Click event for
controls. This is a natural choice for buttons but not optimal for
controls like the combo box. You write the code by inserting it into a
form’s VBScript editor. You need to have the Outlook form open in the
Form Designer and click the View Code button found in the Form group
of the Developer tab.
Sub CheckBox1_Click()
msgbox "Hello World"
End Sub
The code page is fairly minimal with no syntax highlighting. I just tried this now and it does work. Dont forget to Publish your form to pick up the new changes.
I know this is almost 6 years late but, in VB and VBA, simply start with the form name. (And if that doesn't work, just keep going up a parent object and you'll get there.) So, your code becomes:
Sub example()
If MYFORMNAME.CheckBox1.Value = True Then
MsgBox("Checkbox 1 is checked.")
End If
End Sub
Of course, after typing "MYFORMNAME." you'll know if it will work because typomatic will kick in when the system recognizes "MYFORMNAME" after you hit the period.

How to find and disable a content control by tag to delete it and its contents?

I have the unfortunate task of being forced to design a Word-based electronic production card for the unit at my company, even though I've never worked with VBA. I would much rather have done this in Excel since I wouldn't have to wrestle with content control and hard-to-find locations in various tables over the pages, but the company's documentation-system forces this particular one to be in Word.
My issue is that for proper form of the production card I need to use tables, and I need the production card to be dynamic to limit its size to what operations that are relevant for a specific order. My chosen solution is to create a full form, and to use a user form/prompt where they can choose which parts to use and which parts to ommit, and the ommitted ones will then be deleted. Part of reason for the solution is because that is how their previous (and Excel-based) production card works, so it would make it more familiar for the end user.
Because MS Word is finicky I need to use content control within these tables to not have the end user accidentally destroy half of it, but after a full workday I still cannot figure out how find and shut off the content control of the cells in tables that I want to delete. I do have the content controls tagged since that seems like the only reasonable way to find them.
This is my current code for the subprocedure, but for some reason I cannot get the ID through the ccID line, even though I have verified that the string supplied as argument is correct.
Private Sub DeleteCCByTag(ccTag As String)
Dim cc As ContentControl
Dim ccID As String
ccID = ThisDocument.SelectContentControlsByTag(ccTag).Item(1).ID
'MsgBox ccID 'Debug prompt
Set cc = ThisDocument.ContentControls(ccID)
cc.LockContentControl = False
cc.LockContents = False
cc.Delete (False)
End Sub
First of all- your code is working find for me but...
ContentControls tag is case-sensitive which could be a problem in your situation
You could solve your problem without searching for ID value in this way:
Private Sub DeleteCCByTag_Alternative(ccTag As String)
Dim cc As ContentControl
Set cc = ThisDocument.SelectContentControlsByTag(ccTag).Item(1)
With cc
.LockContentControl = False
.LockContents = False
.Range.Delete 'to delete CC content
.Delete (False)
End With
End Sub
CC.Delete in your code deletes only ContentControl objects itself but not its content. To delete content you need to add additional line which I did in my code above.
I would add this as a comment to KazJaw's answer but I don't have the rep.
According to Microsoft's documentation, if you pass True to the Delete method it removes both the content control and its contents.
So: just get the Item as KazJaw showed, without jumping through the hoop of getting its ID:
Set cc = ThisDocument.SelectContentControlsByTag(ccTag).Item(1)
then call .Delete(True) on it.

Outlook - check email address type

I am trying to make a macro in Outlook that will scan the To: list for a certain text string, and spit out a message if all but one (or two, etc) addresses have it. Is there a simple way to do this?
Essentially, I am trying to write something that'll avoid being able to send a restricted message to a bunch of people with the string 'xyz' in the address, if one or more do not have it. AutoComplete makes this difficult, without checking through one-by-one.
This is possible using Outlook VBA.
You'd have to write an event hook for when the user sends an email. This is done using the Application_ItemSend(ByVal Item As Object, Cancel As Boolean) where Item is the item being sent (email or appointment), and cancel is a boolean you can set to stop the email from being sent.
In your code you would want to look at the recipients collection on the Item object to see who is going to be receiving the email.
For example:
Dim CurrRecip As Recipient
For Each CurrRecip in Item.Recipients
If InStr(1, CurrRecip.Address , "your search text here" , vbCompareText ) Then
debug.print "Message here..."
End If
Next CurrRecip
Hopefully that helps...