Validating email To field value in VBA - ms-access

I am looking to validate values sent to an Outlook email in VBA
I have found several examples, such as :-
http://www.geeksengine.com/article/validate-email-vba.html
Using the code from the site above, the email address 1#1.com is returned True, or valid. However, 1#1.com; 2#1.com is returned as invalid. Whilst this isn't a valid email address, it is a valid value for a To field in Outlook.
Is it possible to validate a value such as 1#1.com; 2#1.com using VBA?

Validating an Outlook To field is a hard task.
Consider the following lines:
a#a.com<SomeName;b#b.com 'Valid, 2 addresses, first one named SomeName
a#a<a.com 'Invalid, < needs to be escaped
a#a.com.com;;b#b.com; 'Valid, 2 addresses
a#a.com;a 'Invalid, second address is not valid
a<b#a.com 'Weirdly enough, this is valid according to outlook, mails to b#a.com
'(ignores part before the <)
a#a.com<b#a.com 'But this isn't valid
'(closing > needed in this specific case, mail address = a#a.com)
The only reasonable way to validate an Outlook To field in my opinion, is to check if Outlook thinks it's valid. Any approximation is bound to go wrong.
You can use the following code to let Outlook validate the to string, and check if it can determine a mail address for each field
Public Function IsToValid(ToLine As String) As Boolean
Dim olApp As Object 'Outlook.Application
Dim mail As Object 'Outlook.MailItem
On Error Resume Next
Set olApp = GetObject(, "Outlook.Application")
If Err.Number = 429 Then
Set olApp = CreateObject("Outlook.Application")
End If
On Error GoTo 0
Set mail = olApp.CreateItem(0)
Dim rp As Object 'Outlook.Recipient
With mail
.To = ToLine
.Recipients.ResolveAll
For Each rp In .Recipients
If rp.Address & "" = "" Then
mail.Delete
Exit Function
End If
Next
End With
mail.Delete
IsToValid = True
End Function

Use the Split() function to split the string into the individual addresses, and check these in a loop with your function.
If all addresses are valid, the original string is valid.
The nice thing about it: you don't need separate cases. A single address without ; will return a single array element from Split(), and the loop will simply run once.

To validate multiple email ids using regex use below function:
Public Function ValidateEmailAddress(ByVal strEmailAddress As String) As Boolean
On Error GoTo Catch
Dim objRegExp As New RegExp
Dim blnIsValidEmail As Boolean
objRegExp.IgnoreCase = True
objRegExp.Global = True
objRegExp.Pattern = "^((\w+([-+.]\w+)*#\w+([-.]\w+)*\.\w+([-.]\w+)*)\s*[;]{0,1}\s*)+$"
blnIsValidEmail = objRegExp.test(strEmailAddress)
ValidateEmailAddress = blnIsValidEmail
Exit Function
Catch:
ValidateEmailAddress = False
MsgBox "Module: " & MODULE_NAME & " - ValidateEmailAddress function" & vbCrLf & vbCrLf _
& "Error#: " & Err.Number & vbCrLf & vbCrLf & Err.Description
End Function

Related

Unwanted Scientific Notation in MS Access

I'm encountering a very strange problem with MS Access. I have some VBA code used on a password reset form. The code hashes the input password and then saves the hash to a table of users. Here's a relevant snippit:
If newPW1 = newPW2 Then
MsgBox ("Passwords Match!")
hashPW = Encrypt(newPW1)
MsgBox ("HashedPW is " & hashPW)
updatePW = "UPDATE Users SET Password = " & hashPW & " WHERE Username = pwChangeUsrnm"
DoCmd.RunSQL (updatePW)
the MSGboxes are my debugging notes. I know the hash generates properly as a long string of numbers, all well and good. When I go into the datasheet for the Users table though, the number has always been converted into scientific notation.
Here's a screenshot of the data sheet. bob.smith is an example of what I end up with after the code runs, the other two are gibberish I entered manually. The field is formatted as a string, so I'm not sure why it would even try to convert the number into SN when as far as I can tell the item is always a string.
I'm thinking the error must creep in around the SQL query? If there's a better way of doing this then I'm all ears.
Thanks in advance for your help!
datasheet
design view
Complete code, just in case:
Option Compare Database
Private Sub Command84_Click()
Dim hashPW As String
Dim updatePW As String
Dim checkName As String
checkName = Nz(DLookup("Username", "Users", "Username = pwChangeUsrnm"), "aaa")
MsgBox ("checkName set to " & checkName)
If pwChangeUsrnm = checkName Then
MsgBox ("Username Found")
If newPW1 = newPW2 Then
MsgBox ("Passwords Match!")
hashPW = Encrypt(newPW1)
MsgBox ("HashedPW is " & hashPW)
updatePW = "UPDATE Users SET Password = " & hashPW & " WHERE Username = pwChangeUsrnm"
DoCmd.RunSQL (updatePW)
Else
MsgBox ("Passwords Do Not Match!")
End If
Else
MsgBox ("Username not found")
End If
End Sub
I think Andre has the right of it. I tried adjusting the hashing code to add a letter character and this worked, but then I needed to go back and add the single quote around the hashed PW value- which probably would have made the code work even without adding the letter:
If newPW1 = newPW2 Then
MsgBox ("Passwords Match!")
hashPW = Encrypt(newPW1)
MsgBox ("HashedPW is " & hashPW)
updatePW = "UPDATE Users SET Password = '" & hashPW & "' WHERE Username = pwChangeUsrnm"
DoCmd.RunSQL (updatePW)
A thanks to Zaph's second comment on security as well, I'll take that all into account. For the purposes of this database security isn't too much of a concern as it will be sitting behind existing security measures. The hashing of passwords is more just to avoid ever displaying the passwords in plain text. Nevertheless it's useful to know about these extra functions.

Use VBA variables inside an Access table field

I'm placing my email body inside a table to easily edit the content. The problem is that there is some variables I need to use inside that body content.
For example:
I use the code bellow to send the email. And as you can see, my email body comes from a memo field inside my table (.HTMLBody = "" & Me.Html_Email_Body & "") and I need to use some variables inside the Html_Email_Body field like so:
(This is the text that I have inside my memo field)
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head></head>
<body>
Hi " & Me:PersonName & ", how are you?
</body>
</html>
The output result is: Hi " & Me.PersonName & ", how are you?
And the output result should be: Hi Bob, how are you?
Is this possible?
(This is the code I use to send my emails)
Sub SendEmail_Click()
Dim NewMail As CDO.Message
Set NewMail = New CDO.Message
'Enable SSL Authentication
NewMail.Configuration.Fields.Item _
("http://schemas.microsoft.com/cdo/configuration/smtpusessl") = True
'Make SMTP authentication Enabled=true (1)
NewMail.Configuration.Fields.Item _
("http://schemas.microsoft.com/cdo/configuration/smtpauthenticate") = 1
'Set the SMTP server and port Details
'To get these details you can get on Settings Page of your Gmail Account
NewMail.Configuration.Fields.Item _
("http://schemas.microsoft.com/cdo/configuration/smtpserver") = "smtp.gmail.com"
NewMail.Configuration.Fields.Item _
("http://schemas.microsoft.com/cdo/configuration/smtpserverport") = 25
NewMail.Configuration.Fields.Item _
("http://schemas.microsoft.com/cdo/configuration/sendusing") = 2
'Set your credentials of your Gmail Account
NewMail.Configuration.Fields.Item _
("http://schemas.microsoft.com/cdo/configuration/sendusername") = "mysite#gmail.com"
NewMail.Configuration.Fields.Item _
("http://schemas.microsoft.com/cdo/configuration/sendpassword") = "mypassword7"
'Update the configuration fields
NewMail.Configuration.Fields.Update
'Set All Email Properties
With NewMail
Dim strPath As String
strPath = ".mysite/wp-content/uploads/2017/07/myimage.png"
.subject = "this is the subject"
.From = "<mail#mysite.com>"
.To = Me.EMAIL
'.CC = ""
'.BCC = ""
.HTMLBody = "" & Me.Html_Email_Body & ""
.AddAttachment "https://mysite/temp/contacts.vcf"
End With
NewMail.Send
MsgBox ("This email was sent!")
'Set the NewMail Variable to Nothing
Set NewMail = Nothing
End Sub
I do this kind of thing in many of my applications. I insert Field References into my email templates and then use a routine I wrote to replace them dynamically with the correct values at runtime. In my case, this is usually done in a loop with a RecordSet that contains several people who are each receiving an individual copy of the email message and I am customizing the template for each recipient.
Here is a small sample email template:
<p>Hello [RecipFirstName],</p> This auto-generated email has been sent to notify you that:
<h4>Approval Mailed is <b>LATE</b>.</h4>
Approval Mailed Date: [ApprovalMailed_Date]
[ProjectTemplate1]
Then my code to fill the template looks like:
'[mBody] is a string that will be the Body of the email
'[templateBody] is a string that was previously set to the email
' template for the message being processed
'[rstProject] is a DAO.RecordSet that was previously set to the
' required dataset for my purposes
'Notice that I use a combination of [Replace] functions and
' my custom function [sitsProcessFieldReferences] to update the
' template with the appropriate values.
'In my case, replace CRLF in the template with <BR>
mBody = Replace(templateBody, vbCrLf, "<BR>")
mBody = sitsProcessFieldReferences(mBody, rstProject)
'The following examples use variables I have already defined and
' populated to replace the field refernces.
mBody = Replace(mBody, "[RecipFirstName]", FirstName)
mBody = Replace(mBody, "[RecipLastName]", LastName)
mBody = Replace(mBody, "[RecipFullName]", FirstName & " " & LastName)
mBody = Replace(mBody, "[ProjectTemplate1]", pTemplate1)
Finally the function that does the field reference replacement. Notice that I have a special case that if I name a field reference with "price" in the name, I want the replacement value formatted as Currency. You can customize this code for any situation. It just requires some pre-planning to keep a consistent naming convention for your field references.
This function takes an email template (or any text string) and searches it for field names matching any field in the RecordSet (enclosed in square brackets) and replaces that reference with the value from the corresponding field in the RecordSet
Public Function sitsProcessFieldReferences(ByVal orgString As String, rstFields As DAO.Recordset) As String
On Error GoTo Err_PROC
Dim ErrMsg As String
Dim fld As DAO.Field
For Each fld In rstFields.Fields
If InStr(fld.Name, "price") Then
orgString = Replace(orgString, "[" & fld.Name & "]", Format(Nz(fld.Value, 0), "Currency"))
Else
orgString = Replace(orgString, "[" & fld.Name & "]", Nz(fld.Value, ""))
End If
Next fld
Set fld = Nothing
Exit_PROC:
sitsProcessFieldReferences = orgString
Exit Function
Err_PROC:
Select Case Err.Number
Case Else
ErrMsg = "Module: " & strModName & vbCrLf
ErrMsg = ErrMsg & "Error: " & Err.Number & vbCrLf
ErrMsg = ErrMsg & "Line: " & Erl() & vbCrLf
ErrMsg = ErrMsg & Err.Description
DoCmd.Hourglass False
MsgBox ErrMsg, vbOKOnly + vbCritical, "Function sitsProcessFieldReferences"
Resume Exit_PROC
Resume
End Select
End Function
In your email template, you would change the following line:
Hi " & Me:PersonName & ", how are you?
to something like:
Hi [PersonName], how are you?
Then either do a Replace(emailTemplate, [PersonName], "Bob") if you have the replacement values already in a variable or something.
Or, if the value is in a RecordSet, you would change [PersonName] in the template to match the name of the field in the RecordSet that contains the value Bob and then use my custom function: sitsProcessFieldReferences(emailTemplate, YourRecordSet)
I manage to find the solution myself because I couldn't implement #Jericho Johnson although it was somehow useful...
What I did was setup a new variable (MyHTMLBody) for the email body and several replacements as I need (see bellow).
After that, I setup the .HTMLBody = MyHTMLBody this way, and now I can use some bookmarks in the HTML like this: Hi [r_name], how are you? This is your [r_email].
MyHTMLBody = Me.Body
MyHTMLBody = Replace(MyHTMLBody, "[r_name]", Me.Client_Name)
MyHTMLBody = Replace(MyHTMLBody, "[r_email]", Me.Client_Email)
.HTMLBody = MyHTMLBody

Sending html email from VBA email program

I have written an email program for my organization the handles some very specialized things very well, things I could use Outlook or Gmail for. Now, the manager would like to send an occasional email to our small customer base, but I want the email body tto look professional and not send it as an attachment. I have cobbled together an html document that present in all browsers and has been validated. My problem is I can't figure out how to point the message body at the html document. Here is the salient code.
This is where all is set up:
Do While mailRs.EOF = False
'Me.AttachDoc = "C:\EmailFolder\CouponForm.pdf"
emTo = mailRs.Fields("EmailAddr").Value
emFrom = "SportsParkInfo#skokieparks.org"
emSubject = Me.Subject
emtextBody = Me.TextMessage
Here is a the call for sending the email
Call SendAMessage(emFrom, mailRs.Fields("EmailAddr").Value, _
emSubject, emtextBody, emAttach)
(I got the code for sending the email off the web and it works great through our mail server.)
In the above, before the call # emtextBody = Me.TextMessage is where I need to replace Me.TextMessage with the address/body of the html document. And the message box is a textBox on the ACCESS form. I can't find any control in ACCESS that takes html. I can't use the path to the html document because that generates an error. Is there a way of getting around this
If more information is required I'll be happy to supply it.
Thanks for your time.
jpl
Use something like the below code. I've included elements for attachment as well as html formatting but pretty much anything you can write in html can also be done within vba.
Sub SharePerformance()
Dim OutApp As Object
Dim OutMail As Object
Dim rng As Range
Set OutApp = CreateObject("Outlook.Application")
Set OutMail = OutApp.createitem(0)
'& "\\server\folder" &
msg1 = "Team,<br><br><b><DL>" & Range("b5").Value & "</b><br><ul><b><u>" & Range("b6").Value & "</b></u>"
msg1 = msg1 & "<DT><a HREF=C:\USER\Desktop\File1.xlsb>"
msg1 = msg1 & Range("b7").Value & "</a><br>"
msg1 = msg1 & "<b><u>" & Range("b9").Value & "</b></u></DL><br><br>"
msg1 = msg1 & "<p><img src=file://" & "C:\temp\Chart1.png" & "></p>" & "<br>"
On Error Resume Next
' Change the mail address and subject in the macro before you run it.
With OutMail
.To = Range("B1").Value
.cc = ""
.BCC = ""
.Subject = Range("B3").Value
.HTMLBody = msg1
'.Attachments.Add ActiveWorkbook.FullName
'.Attachments.Add ("C:\temp\Chart1.png")
'.Attachments.Add ("C:\temp\Chart2.png")
.display
End With
SendKeys "^{ENTER}"
On Error GoTo 0
Set OutMail = Nothing
Set OutApp = Nothing
End Sub
I can't tell what code is inside that SendAMessage function you are using but all the VBA examples I've worked with seem to work the same way with the CDO.Message object like in this MS Knowledge Base article KB286431. At some point SendAMessage is going to have a line that assigns the message object's .TextBody value to be equal to the emtextBody parameter you pass in.
One solution may be to copy your SendAMessage function into a new function SendAMessageHTML and replace the line where they are setting someMessage.TextBody = emtextBody so that you are setting someMessage.HTMLBody = emtextBody
Assuming your textbox has text along the lines of "<html><head><body></body></html>" you could modify your existing function to do a naive check like this:
if Left(UCase(emtextBody),6) = "<HTML>" then
someMessage.HTMLBody = emtextBody
else
someMessage.TextBody = emtextBody
end if

failed to send email to multiple recipient through microsoft access 2007, only sent to first recipients

i want to send email through microsoft access interface silently. user just need to select the recipients in the listbox and click a single button to send the email to multiple recipient. i dont want lotus-notes interface appear to the user. i have no problem in using those command to send email:
DoCmd.SendObject objecttype:=acSendTable, _
objectname:=strDocName, outputformat:=acFormatXLS, _
To:=strEmail, Subject:=strMailSubject, MessageText:=strMsg, EditMessage:=False
but those method is not what i'm looking for because it will appear in the screen while sending the email. although i have set EditMessage:=False.
i have a procedure to send the email from access through lotus notes in the background. the procedure runs fine with single recipient but it will only send email to only one recipient if i select multiple recipients. i think the problem have something to do with the recipients string.
recipients string example :
eg1 : duwey#yahoo.com, mridzuan#gmail.com, mridzuan#yahoo.com
eg2 : duwey#yahoo.com; mridzuan#gmail.com; mridzuan#yahoo.com
email will be sent to the first recipient only
here's the sub procedure :
Sub SendNotesMail(Subject As String, Attachment As String, Recipient As String, BodyText As String, SaveIt As Boolean)
Dim Maildb As Object 'The mail database
Dim UserName As String 'The current users notes name
Dim MailDbName As String 'The current users notes mail database name
Dim MailDoc As Object 'The mail document itself
Dim AttachME As Object 'The attachment richtextfile object
Dim Session As Object 'The notes session
Dim EmbedObj As Object 'The embedded object (Attachment)
Set Session = CreateObject("Notes.NotesSession")
'Get the sessions username and then calculate the mail file name
UserName = Session.UserName
MailDbName = Left$(UserName, 1) & Right$(UserName, (Len(UserName) - InStr(1, UserName, " "))) & ".nsf"
'Open the mail database in notes
Set Maildb = Session.GETDATABASE("", MailDbName)
If Maildb.ISOPEN = False Then
Maildb.OPENMAIL
End If
'Set up the new mail document
Set MailDoc = Maildb.CREATEDOCUMENT
MailDoc.Form = "Memo"
MailDoc.sendto = Recipient
MailDoc.Subject = Subject
MailDoc.Body = BodyText & vbCrLf & vbCrLf
MailDoc.PostedDate = Now()
MailDoc.SAVEMESSAGEONSEND = SaveIt
'Set up the embedded object and attachment and attach it
If Attachment <> "" Then
Set AttachME = MailDoc.CREATERICHTEXTITEM("Attachment")
Set EmbedObj = AttachME.EMBEDOBJECT(1454, "", Attachment, "Attachment")
MailDoc.CREATERICHTEXTITEM ("Attachment")
End If
'Send the document
MailDoc.send 0, Recipient
'Clean Up
Set Maildb = Nothing
Set MailDoc = Nothing
Set AttachME = Nothing
Set Session = Nothing
Set EmbedObj = Nothing
End Sub
Calling the email procedure on button click event :
Private Sub cmdSendEmail_Click()
Dim EmailSubject As String, EmailAttachment As String, EmailRecipient As String, EmailBodyText As String, EmailSaveIt As Boolean
EmailSubject = Me.txtSubject.Value
EmailAttachment = Me.txtAttachment.Value
EmailRecipient = Me.txtSelected.Value
EmailBodyText = Me.txtMessage.Value
EmailSaveIt = True
Call SendNotesMail(EmailSubject, EmailAttachment, EmailRecipient, EmailBodyText, EmailSaveIt)
End Sub
and here's how i take the multiple recipient string from the listbox :
Private Sub lstEmail_Click()
On Error Resume Next
Dim varItem As Variant
Dim strList As String
With Me.lstEmail
If .MultiSelect = 0 Then
Me.txtSelected = .Value
Else
For Each varItem In .ItemsSelected
strList = strList & .Column(0, varItem) & ", "
Next varItem
strList = Left$(strList, Len(strList) - 2) 'eliminate ", " at the end of recipient's string
Me.txtSelected.Value = strList
End If
End With
End Sub
i really cannot use the docmd.sendObject method because it still appear on the screen although i set EditMessage:=False. i dont know if it works okay with other electronic mail but my lotus-notes 8.5 doesn't work with that docmd.sendObject for sending email on the background.
any help or suggestion?
The recipents field needs to be an array (list). Can you use split to make it an array?
MailDoc.sendto = split(Recipient, ", ")

Send email from Access if DOB is within 'n' days

I needed to make it in access so that I send an email to a certain customer when their date of birth is within 3 days.
Dim rs as dao.recordset
set rs = currentdb.openrecordset(“DiscountEmail”)
with rs
if .eof and .bof then (No Records found for this query.)
Msgbox “ No emails will be sent because there are no records from the query ‘DiscountEmail’ “
else
do until .eof
DoCmd.SendObject acSendNoObject, , , ![Email Address Field], , , “Happy Birthday!”, “Hello ” & ![First Name Field] & _
“, ” & Chr(10) & “Come in on your birthday and receive a 10% discount!”, False
.edit
![Email_Sent_Date] = now()
.update
.movenext
loop
End If
end with
If Not rs Is Nothing Then
rs.Close
Set rs = Nothing
End If
I have this code, but now I just need to make it so that if a certain customer's birthday (In my table 'CustomerInfo') is within 3 days, it sends them an email saying that they can come in on their birthday and receive a discount.
Also, I want to make it so that this happens automatically (so I don't have to press any button), but so that it sends it only once, and so I can send it again next year.
Thanks in advance! :)
You need to have some event in order to fire this event. An Access database is just a file, so when you're not using it, it's not running any code.
Doing a simple check every time the database is opened, maybe on the first form's On Load event would be the way to go. I assume your DiscountEmail RecordSet is the one querying for emails within the 3 day period.
Your solution is to either put this in the onLoad event for your first form or to use another service. As far as not spamming the emailee more than once, just add a emailSent field or log sent emails to a different table, and handle it after the email is sent.
Example query to find the relevant emails:
Select email from Users Where dateOfBirth between dateAdd("d",-3,Date()) AND dateAdd("d",3,Date());
Do send an email, you could use SMTP and CDO. Create an email function called something like sendEmail
Public Sub SendEmail(strTo as STring, strFrom as String, strSubj as String, strBody as String)
Dim imsg As Object
Dim iconf As Object
Dim flds As Object
Dim schema As String
Set imsg = CreateObject("CDO.Message")
Set iconf = CreateObject("CDO.Configuration")
Set flds = iconf.Fields
' send one copy with SMTP server (with autentication)
schema = "http://schemas.microsoft.com/cdo/configuration/"
flds.Item(schema & "sendusing") = cdoSendUsingPort
flds.Item(schema & "smtpserver") = "mail.myserver.com" 'your info here
flds.Item(schema & "smtpserverport") = 25
flds.Item(schema & "smtpauthenticate") = cdoBasic
flds.Item(schema & "sendusername") = "email#email.com" 'more of your info
flds.Item(schema & "sendpassword") = "password"
flds.Item(schema & "smtpusessl") = False
flds.Update
With imsg
.To = strTo
.From = strFrom
.Subject = strSubj
.HTMLBody = strBody
'.body = strBody
'.Sender = "Sender"
'.Organization = "My Company"
'.ReplyTo = "address#mycompany.com"
Set .Configuration = iconf
.Send
End With
Set iconf = Nothing
Set imsg = Nothing
Set flds = Nothing
End Sub
You could either loop through the resultset of your query and call your sendmail function for each email, or write a quick helper function that fetches and concatenates your email fields into a ";" delimited list, and just send the email once with multiple recipients.
If the essence of your question is about the actual sending of the email message itself, then you may find that DoCmd.SendObject may not be the best method. It has several limitations, most significantly (ref: here)
the message text is limited to 255 characters
it depends on interaction with an email client application (via MAPI, I assume) so it may not work if there is no mail client configured, or if the mail client is not a Microsoft product
Instead, you might consider sending the messages via CDO. There is a good write-up and some ready-to-use VBA code here:
http://www.cpearson.com/excel/Email.aspx