I have a script (found on the web and adjusted to my needs) that can access a specific view of Lotus Notes, pick up some info from each email and save any attachment to a given folder. If I want to do this for more views I currently have the names of these views in my worksheet and loop through them.
What I would like to do is to loop through all views and do the same.
I am not a professional developer and not yet entirely familiar working with objects. This problem however is to complicated for me. The Lotus Notes object is also not the easiest to understand.
I am using the following code which of course is part of a larger (working) script. For starters it would be enough to just be able to loop through all views and print the Name of the view. From there on I think I can do the rest myself.
Can anyone adjust the code to make it work.
Public Sub Get_Notes_Email_Text()
Dim NSession As Object 'NotesSession
Dim NMailDB As Object 'NotesDatabase
Dim NDocs As Object 'NotesDocumentCollection
Dim NDoc As Object 'NotesDocument
Dim NNextDoc As Object 'NotesDocument
Dim view As String
'Start a Lotus Notes session
Set NSession = CreateObject("Notes.NotesSession")
'Connect to the Lotus Notes database
Set NMailDB = NSession.GetDatabase("", "C:\Users\" & Environ("Username") & "\AppData\Local\IBM\Notes\Data\mail\" & Environ("Username") & ".nsf") 'Default server en database
If Not NMailDB.IsOpen Then
NMailDB.OpenMail
End If
'Loop through all views and print .Name tot Immediate Window
' Dim Map As Variant
' Dim Mappen As Object
' Set Mappen = NMailDB.Views
'
' For Each Map In Mappen
' Debug.Print Map.Name
' Next Map
'
End Sub
Easier than going through all views is to just go to one view that shows all documents. If it is a mail database, then the view is called "($All)"
Set allDocsView = NMailDB.getView("($All)")
allDocsView.autoUpdate = false
You can then loop through the documents in the view using
Set docToProcess = allDocsView.GetFirstDocument
While Not ( docToProcess Is Nothing )
'Do what you plan to do here
Set docToProcess = allDocsView.GetNextDocument( docToProcess )
Wend
UPDATE:
As you want to loop through Folders, not views, you need to do something like the following
Dim sess As New notessession
Dim db As NotesDatabase
Dim views As Variant
Set db = sess.CurrentDatabase
views = db.Views
Forall v In views
If v.isFolder Then ' Needed so that we are only processing Folder objects and not views - you will need to check that you are not processing system folders e.g. $Inbox etc
'Do your processing of documents here
End If
End Forall
Related
The code below reads and inserts the email from lotus notes to an excel sheet. However I would like to read a lotus notes database and copy its content and paste it as a rich text into a word document.
I assume this line of code needs to be modified.
Set nitem = .GetFirstItem("Body")
What would be the best way to go about this?
Public Sub Lotus_Notes_Current_Email4()
Dim NSession As Object 'NotesSession
Dim NUIWorkSpace As Object 'NotesUIWorkspace
Dim NUIdoc As Object 'NotesUIDocument
Dim nitem As Object 'NotesItem
Dim lines As Variant
Set NSession = CreateObject("Notes.NotesSession")
Set NUIWorkSpace = CreateObject("Notes.NotesUIWorkspace")
Set NUIdoc = NUIWorkSpace.CurrentDocument
If Not NUIdoc Is Nothing Then
With NUIdoc.Document
Set nitem = .GetFirstItem("Body")
If Not nitem Is Nothing Then
lines = Split(nitem.Text, vbCrLf)
Sheets(1).Activate
Range("H8").Resize(UBound(lines) + 1, 1).Value = Application.WorksheetFunction.Transpose(lines)
End If
End With
Else
MsgBox "Lotus Notes is not displaying an email"
End If
Set NUIdoc = Nothing
Set NUIWorkSpace = Nothing
Set NSession = Nothing
End Sub
Your assumption is incorrect. Your entire script needs to be rewritten.
If you want to copy the contents of a view, then you need to start by opening a NotesView object. Your current code is opening a NotesDocument object. To get the NotesView, you might want to use the CurrentView property NotesUIWorkspace to get a NotesUIView object, and then use that objects View property.
Once you have the NotesView object, then I'm guessing you might want to want to use the columns property to get at the data, but there are other ways you could go about it. Pretty much no matter what you do, though, you're going to have to handle the formatting of the data on your own.
I'd like to be able to delete individual files that are stored as attachments in an Access Database through VB.Net
I've managed to get storing, opening and "downloading" from an Access Database to work effectively, but the last piece of the puzzle would be to allow my end-user to delete an individual file in the attachments field.
This seems to be much harder than the other options as I can only seem to find information on how to delete the entire database entry, not just a single attachment. It gets even more complicated if there are more than one attachments stored against the entry.
I am using the following code to check what happens when the datagrid is clicked:
If e.ColumnIndex = ViewFileColum.Index Then
'Get the file name and contents for the selected attachment.
Dim gridRow = childgrid.Rows(e.RowIndex)
Dim tableRow = DirectCast(gridRow.DataBoundItem, DataRowView)
Dim fileName = CStr(tableRow(FILE_NAME_COLUMN_NAME))
Dim fileContents = GetFileContents(DirectCast(tableRow(FILE_DATA_COLUMN_NAME), Byte()))
DisplayTempFile(fileName, fileContents)
End If
If e.ColumnIndex = DownloadFileColumn.Index Then
'Get the file name and contents for the selected attachment.
MoveFile = True
Dim gridRow = childgrid.Rows(e.RowIndex)
Dim tableRow = DirectCast(gridRow.DataBoundItem, DataRowView)
Dim fileName = CStr(tableRow(FILE_NAME_COLUMN_NAME))
Dim fileContents = GetFileContents(DirectCast(tableRow(FILE_DATA_COLUMN_NAME), Byte()))
DisplayTempFile(fileName, fileContents)
End If
I want to add a third section that states if the DeleteFileColumn button is clicked then to remove that particular attachment from the database, but this doesn't seem possible.
When retrieving the information for the above two options, I use the following code:
Dim tempFolderPath = Path.GetTempPath()
Dim tempFilePath = Path.Combine(tempFolderPath, fileName)
'If the specified file exists, add a number to the name to differentiate them.
If File.Exists(tempFilePath) Then
Dim fileNumber = 1
Do
tempFilePath = Path.Combine(tempFolderPath,
String.Format("{0} ({1}){2}",
Path.GetFileNameWithoutExtension(fileName),
fileNumber,
Path.GetExtension(fileName)))
fileNumber += 1
Loop Until Not File.Exists(tempFilePath)
End If
'Save the file and open it.
'If "DOWNLOAD" button is clicked
If MoveFile = True Then
File.WriteAllBytes(SaveLocation & "\" & Path.GetFileNameWithoutExtension(fileName) & Path.GetExtension(fileName), fileContents)
MoveFile = False
'If "OPEN" button is clicked
Else
File.WriteAllBytes(tempFilePath, fileContents)
Dim attachmentProcess = Process.Start(tempFilePath)
If attachmentProcess Is Nothing Then
'Remember the file and try to delete it when this app closes.
tempFilePaths.Add(tempFilePath)
Else
'Remember the file and try to delete it when the associated process exits.
attachmentProcess.EnableRaisingEvents = True
AddHandler attachmentProcess.Exited, AddressOf attachmentProcess_Exited
tempFilePathsByProcess.Add(attachmentProcess, tempFilePath)
End If
End If
This code copies the information before opening it, so I don't ever actually deal with the file in the database directly. I've used adapted this code from another example I found online, but am having a hard time working out how to physically deal with the file on the database, or if its even possible?
Thanks
You need to create an Access DAO Recordset2 object for the attachments field, find the record corresponding to the specific attachment you want to delete, and then Delete() that record.
The following example will remove a document named "testDocument.pdf" from the attachments field for the record where ID=1:
' required COM reference:
' Microsoft Office 14.0 Access Database Engine Object Library
'
' Imports Microsoft.Office.Interop.Access.Dao
'
Dim dbe As New DBEngine
Dim db As Database = dbe.OpenDatabase("C:\Users\Public\Database1.accdb")
Dim rstMain As Recordset = db.OpenRecordset(
"SELECT Attachments FROM AttachTest WHERE ID=1",
RecordsetTypeEnum.dbOpenDynaset)
rstMain.Edit()
Dim rstAttach As Recordset2 = CType(rstMain.Fields("Attachments").Value, Recordset2)
Do Until rstAttach.EOF
If rstAttach.Fields("FileName").Value.Equals("testDocument.pdf") Then
rstAttach.Delete()
Exit Do
End If
rstAttach.MoveNext()
Loop
rstAttach.Close()
rstMain.Update()
rstMain.Close()
db.Close()
I would like to create a program in Excel that loops through a list of Access databases and writes the VBA that exists in the Access modules. I have found some code that I can run from Access which writes the VBA that exists in the Access modules. I am trying to figure out how to reference the database files from Excel and run the program on each database file. I will probably be able to figure out how to loop through the database files. I just need help with referencing the database file in the below code.
I can open the database with something like this:
Dim cstrDbFile As String = "C:\Database51.accdb"
Dim objShell As Object
Set objShell = CreateObject("WScript.Shell")
objShell.Run cstrDbFile
I also tried to set up a reference to Access like this:
Dim appAccess As Object
Set appAccess = CreateObject("Access.Application")
appAccess.OpenCurrentDatabase ("C:\Database51.accdb")
I need to figure out how to refer to the Access database in:
Application.VBE.ActiveVBProject.VBComponents
I probably need to figure out how to create a reference to replace ActiveVBProject.
Below is some code I found which writes the contents of VBA modules. I don't remember where I found it.
For Each Component In Application.VBE.ActiveVBProject.VBComponents
With Component.CodeModule
'The Declarations
For Index = 1 To .CountOfDeclarationLines
Debug.Print .Lines(Index, 1)
Next Index
'The Procedures
For Index = .CountOfDeclarationLines + 1 To .CountOfLines
Debug.Print .Lines(Index, 1)
Next Index
End With
Next Component
The following code will let you see Access database objects, but I don't know how to export the code (DoCmd not in Excel?). Your task would be VERY simple to do from Access, so I would reconsider...
Option Explicit
' Add a reference to the DAO Object Library
Sub Read_Access_VBA()
Dim dbs As DAO.Database
Dim ctr As DAO.Container
Dim doc As DAO.Document
Dim iC As Integer
Dim iD As Integer
Dim i As Integer
Dim mdl As Module
Set dbs = DBEngine.OpenDatabase("c:\TEMP\106thRoster.mdb", False, False, _
"MS Access;")
Debug.Print "----------------------------------------"
For iC = 0 To dbs.Containers.Count - 1
Debug.Print "Container: " & dbs.Containers(iC).Name
If dbs.Containers(iC).Documents.Count > 0 Then
For iD = 0 To dbs.Containers(iC).Documents.Count - 1
Debug.Print vbTab & "Doc: " & dbs.Containers(iC).Documents(iD).Name
Next iD
Else
Debug.Print " No Documents..."
End If
Next iC
'Set ctr = dbs.Containers!Modules
dbs.Close
Set doc = Nothing
Set ctr = Nothing
Set dbs = Nothing
End Sub
I was able to find some code that will assist me with my final goal: Exporting MS Access Forms and Class / Modules Recursively to text files?
Below are the most significant lines that will allow me to make progress with the project.
LineCount = oApp.Forms(Name).Module.CountOfLines
FileName = Path & "\" & Name & ".vba"
F = FreeFile
Open FileName For Output Access Write As #F
Print #F, oApp.Forms(Name).Module.Lines(1, LineCount)
Close #F
I'm trying to write a Excel VBA code which allows me to automatically create and send a Lotus Notes Email. The problem I face is the difficulty to create a rich text Email, so I think it would be easier to open a draft email, with a marker text which will be replaced (for exameple PASTE EXCEL CELLS HERE) and then just:
.GotoField ("Body")
.FINDSTRING "PASTE EXCEL CELLS HERE"'
and replace.
Any help on how to open a certain draft email? Perhabs something as .CreateDocument property?
Thank you very much!
Others have proposed interesting concepts, but the most robust approach would be to use HTML in a MIME enitity that is mapped to the Body Rich Text item. Using NotesSession..Convertmime = False you can build the body as HTML and then send the message. Based on the post by Joseph Hoetzl here, the LotusScript equivalent is this:
Sub Initialize()
Dim s As New NotesSession
Dim db As NotesDatabase
Dim stime as Single
Dim alog As New NotesLog("debug")
Call alog.OpenAgentLog()
stime = Timer
On Error GoTo eh
Dim doc As NotesDocument
Dim body As NotesMIMEEntity
Dim header As NotesMIMEHeader
Dim stream As NotesStream
Dim child As NotesMIMEEntity
Dim sendTo As String
Dim subject As String
s.Convertmime = False
sendto = s.Effectiveusername
subject = "Demo Message"
Set db= s.Currentdatabase
Set doc=db.Createdocument()
Set stream = s.CreateStream
Set body = doc.CreateMIMEEntity
Set header = body.CreateHeader({MIME-Version})
Call header.SetHeaderVal("1.0")
Set header = body.CreateHeader("Content-Type")
Call header.SetHeaderValAndParams({multipart/alternative;boundary="=NextPart_="})
'Add the to field
Set header = body.CreateHeader("To")
Call header.SetHeaderVal(SendTo)
'Add Subject Line
Set header = body.CreateHeader("Subject")
Call header.SetHeaderVal(subject)
'Add the body of the message
Set child = body.CreateChildEntity
Call stream.WriteText("<h1>Demo HTML Message</h1>")
Call stream.WriteText(|<table colspacing="0" colpadding="0" border="none">|)
Call stream.WriteText(|<tr><td>cell 1.1</td><td>cell 1.2</td><td>cell 1.3</td></tr>|)
Call stream.WriteText(|<tr><td>cell 2.1</td><td>cell 2.2</td><td>cell 2.3</td></tr>|)
Call stream.WriteText(|<tr><td>cell 3.1</td><td>cell 3.2</td><td>cell 3.3</td></tr>|)
Call stream.WriteText(|</table>|)
Call stream.WriteText(|<div class="headerlogo">|)
Call stream.WriteText(|<!-- ...some more HTML -->|)
Call child.setContentFromText(stream, {text/html;charset="iso-8859-1"}, ENC_NONE)
Call stream.Truncate 'Not sure if I need this
Call stream.Close
Call doc.CloseMIMEEntities(True)
Call doc.replaceItemValue("Form", "Memo")
Call doc.Send(False, sendTo)
es:
Exit Sub
eh:
Dim emsg$
emsg = Error & " at " & Erl & " in " & s.Currentagent.name
Call alog.logError(Err, emsg)
MsgBox "ERROR: " & Err & ": " & emsg
Resume es
End Sub
All of this should convert fairly easily to VBA in Excel. You can, of course be as complex as you want with your HTML.
The word "draft" is probably inappropriate here, but then again so is the word "template". Both have specific meanings in Lotus Notes that aren't what you really want. Users can delete drafts, and templates are an entirely different thing. So let's just call it a boilerplate message.
I would recomemnd creating a special mail database (NSF file) on the Domino server, which will just serve as a repository for your boilerplate. You can create a folder in that mail database called "Boilerplates". Using Domino Designer you can modify that folder's design so that the Subject column is the first column in the view, and it is sorted.
Once you have that done and you have created some boilerplates and saved them in the folder, you can use VBA to do a NotesSession.getDatabase call, NotesDatabase.getView call (this is used for folders as well as views), and then use NotesView.getDocumentByKey() to retrieve a specific boilerplate by the Subject you have assigned to it. Note that you do not have to copy this document to the user's mail database in order to mail it.
What you want to do is non-trivial, but you mention a draft email so there may be a workaround.
In your mail settings you can specify a signature file which can be an external html file on disk. So modify the signature file, then create your new mail which will then populate the body field the way you want it.
For sample code, within the memo form there should be a button to specify what signature file to use. You can use that as the baseline.
Rich text is not that hard to work with, but you need to look at the Domino Designer help, especially the classes NotesRichTextItem and NotesRichTextStyle. You also need to understand the DOM (Domino Object Model). Then you can create your mail content programatically.
Otherwise I think Richard's solution is the best, that you have a separate database where you get the rich text snippets, and use the AppendRTItem method of the NotesRichText class to put it into your email.
Thank you all guys!
But I found exactly what I wanted, without having to recreate the whole Email everytime:
Sub EditSelectedMail()
Dim NSession As Object
Dim NDatabase As Object
Dim NUIWorkspace As Object
Dim NUIdoc As Object
Set NSession = CreateObject("Notes.NotesSession")
Set NUIWorkspace = CreateObject("Notes.NotesUIWorkspace")
Set NDatabase = NSession.GetDatabase("", "")
If Not NDatabase.IsOpen Then NDatabase.OPENMAIL
Set NUIdoc = NUIWorkspace.EDITDOCUMENT(True)
With NUIdoc
'Find the marker text in the Body item
.GotoField ("Body")
.FINDSTRING "**PASTE EXCEL CELLS HERE**"
'Copy Excel cells to clipboard
Sheets("Sheet1").Range("A1:E6").Copy
'Create a temporary Word Document
Set WordApp = CreateObject("Word.Application")
WordApp.Visible = False
WordApp.Documents.Add
'Paste into Word document and copy to clipboard
With WordApp.Selection
.PasteSpecial DataType:=10
'Enum WdPasteDataType: 10 = HTML; 2 = Text; 1 = RTF
.WholeStory
.Copy
End With
'Paste from clipboard (Word) to Lotus Notes document
.Paste
Application.CutCopyMode = False
'WordApp.Quit SaveChanges:=False
Set WordApp = Nothing
End With
End Sub
I just select my "template", copy it into a new message, and then run the macro.
I need to build a tool that will allow the user to select an email from his Outlook so I can then save that email as a .msg file or alternately save just the attachment as a file.
I'm stumbling a little bit over what might be the easiest and the best way to allow searching/filtering of emails. I need to give the user a view that is at least slightly similar to Outlook (for example, folders should be the same order/hierarchy.
Does the Outlook Object Model have some kind of Explorer/Picker/Selection dialog I can call that will return a storeid and an entryid after the user selects an email? Or do I need to roll my own?
I should mention that I already know how to save the email or attachment so my question is only about handling selection and filtering of emails.
FYI, I'm programming this in MS Access 2007 with Outlook 2007. The target machines have either 2007 or 2010 versions of Access and Outlook.
Linking to the Outlook table is fine. The problem is that Outlook doesn't provide a unique ID to each message and if the message is moved from one folder to another, its ID changes. Clearly not designed by someone who understands databases.
A better approach may be to create an Outlook add-in that runs within Outlook, then performs the tasks you need to send the info to Access.
I rarely program with Access but I moved some code across from Outlook, hacked it around a bit and it seems to work. This is not a solution but it should show you how to access all the information you need.
I had one problem. Neither Set OutApp = CreateObject("Outlook.Application") nor Set OutApp = New Outlook.Application create a new instance of Outlook if one is already open. So Quit closes Outlook whether or not it was open before the macro started. I suggest you post a new question on this issue; I am sure someone knows how to tell if Outlook is already open and therefore not to quit it.
The folder structure in Outlook is slightly awkward because the top level folders are of type Folders while all sub-folders are of type MAPIFolder. Once you have got past that it is fairly straightforward.
The code below includes function GetListSortedChildren(ByRef Parent As MAPIFolder) As String. This function finds all the children of Parent and returns a string such as "5,2,7,1,3,6,4" which lists the indices for the children in ascending sequence by name. I would use something like this to populates a ListView by expanding nodes as the user required.
I have provided a subroutine CtrlDsplChld() which controls the output to the immediate windows of all the folders in sequence. I believe that should give you enough guidance to get started on accessing the folder hierarchy.
Subroutine DsplChld(ByRef Parent As MAPIFolder, ByVal Level As Long) includes code to find the first message with attachments. This will you tell you how to look through a folder for a particular message.
Finally, CtrlDsplChld() displayes selected properties of the message: Subject, To, HTMLBody and the display names of the attachments.
Hope this helps.
Option Compare Database
Option Explicit
Dim ItemWithMultipleAttachments As Outlook.MailItem
Sub CtrlDsplChld()
Dim ArrChld() As String
Dim ListChld As String
Dim InxAttach As Long
Dim InxChld As Long
Dim InxTopLLCrnt As Long
Dim OutApp As Outlook.Application
Dim TopLvlList As Folders
Set ItemWithMultipleAttachments = Nothing
Set OutApp = CreateObject("Outlook.Application")
'Set OutApp = New Outlook.Application
With OutApp
Set TopLvlList = .GetNamespace("MAPI").Folders
For InxTopLLCrnt = 1 To TopLvlList.Count
' Display top level children and their children
Call DsplChld(TopLvlList.Item(InxTopLLCrnt), 0)
Next
If Not ItemWithMultipleAttachments Is Nothing Then
With ItemWithMultipleAttachments
Debug.Print .Subject
Debug.Print .HTMLBody
Debug.Print .To
For InxAttach = 1 To .Attachments.Count
Debug.Print .Attachments(InxAttach).DisplayName
Next
End With
End If
.Quit
End With
Set OutApp = Nothing
End Sub
Sub DsplChld(ByRef Parent As MAPIFolder, ByVal Level As Long)
Dim ArrChld() As String
Dim InxChld As Long
Dim InxItemCrnt As Long
Dim ListChld As String
Debug.Print Space(Level * 2) & Parent.Name
If ItemWithMultipleAttachments Is Nothing Then
' Look down this folder for a mail item with an attachment
For InxItemCrnt = 1 To Parent.Items.Count
With Parent.Items(InxItemCrnt)
If .Class = olMail Then
If .Attachments.Count > 1 Then
Set ItemWithMultipleAttachments = Parent.Items(InxItemCrnt)
Exit For
End If
End If
End With
Next
End If
ListChld = GetListSortedChildren(Parent)
If ListChld <> "" Then
' Parent has children
ArrChld = Split(ListChld, ",")
For InxChld = LBound(ArrChld) To UBound(ArrChld)
Call DsplChld(Parent.Folders(ArrChld(InxChld)), Level + 1)
Next
End If
End Sub
Function GetListSortedChildren(ByRef Parent As MAPIFolder) As String
' The function returns "" if Parent has no children.
' If the folder has children, the functions returns "P,Q,R, ..." where
' P, Q, R and so on indices of the children of Parent in ascending
' order by name.
Dim ArrInxFolder() As Long
'Dim ArrFolder() As MAPIFolder
Dim InxChldCrnt As Long
Dim InxName As Long
Dim ListChld As String
If Parent.Folders.Count = 0 Then
' No children
GetListSortedChildren = ""
Else
'ReDim ArrName(1 To Parent.Folders.Count)
'For InxChldCrnt = 1 To Parent.Folders.Count
' ArrFolder(InxChldCrnt) = Parent.Folders(InxChldCrnt)
'Next
Call SimpleSortMAPIFolders(Parent, ArrInxFolder)
ListChld = CStr(ArrInxFolder(1))
For InxChldCrnt = 2 To Parent.Folders.Count
ListChld = ListChld & "," & CStr(ArrInxFolder(InxChldCrnt))
Next
GetListSortedChildren = ListChld
End If
End Function
Sub SimpleSortMAPIFolders(ArrFolder As MAPIFolder, _
ByRef InxArray() As Long)
' On exit InxArray contains the indices into ArrFolder sequenced by
' ascending name. The sort is performed by repeated passes of the list
' of indices that swap adjacent entries if the higher come first.
' Not an efficient sort but adequate for short lists.
Dim InxIACrnt As Long
Dim InxIALast As Long
Dim NoSwap As Boolean
Dim TempInt As Long
ReDim InxArray(1 To ArrFolder.Folders.Count) ' One entry per sub folder
' Fill array with indices
For InxIACrnt = 1 To UBound(InxArray)
InxArray(InxIACrnt) = InxIACrnt
Next
If ArrFolder.Folders.Count = 1 Then
' One entry list already sorted
Exit Sub
End If
' Each repeat of the loop moves the folder with the highest name
' to the end of the list. Each repeat checks one less entry.
' Each repeats partially sorts the leading entries and may result
' in the list being sorted before all loops have been performed.
For InxIALast = UBound(InxArray) To 1 Step -1
NoSwap = True
For InxIACrnt = 1 To InxIALast - 1
If ArrFolder.Folders(InxArray(InxIACrnt)).Name > _
ArrFolder.Folders(InxArray(InxIACrnt + 1)).Name Then
NoSwap = False
' Move higher entry one slot towards the end
TempInt = InxArray(InxIACrnt)
InxArray(InxIACrnt) = InxArray(InxIACrnt + 1)
InxArray(InxIACrnt + 1) = TempInt
End If
Next
If NoSwap Then
Exit For
End If
Next
End Sub