Retrieving inline images from Lotus notes using lotusscript - lotus-notes

I have some NotesDocument where some RichText fields have both text and inline images. I can get text part of that items but can't retrieve inline images using lotusscript. Could any one please suggest me a way to retrieve inline images from that documents.
LotusScript code:
Sub Click(Source As Button)
Dim session As New NotesSession
Dim db As NotesDatabase
Dim mainDoc As NotesDocument
Dim v As NotesView
Set db = session.CurrentDatabase
Dim fileName As String
Dim fileNum As Integer
fileNum% = Freefile()
fileName$ = "D:\data.txt"
Open FileName$ For Append As fileNum%
Set v = db.GetView("MyView")
Set mainDoc = v.GetFirstDocument
While Not ( mainDoc Is Nothing )
Forall i In mainDoc.Items
If i.Type = RICHTEXT Then
Write #fileNum% , i.Name & ":" & i.text 'how the images??
End If
End Forall
Set mainDoc = v.GetNextDocument( mainDoc )
Wend
End Sub
Thanks.

Midas is the easiest way to do it, but it isn't free. (It's more than worth the money in overall time saved, but if your organisation is anything like the ones I've worked for, the entire cost of the tool is going to be foisted off on the billing unit that owns the current project, rather than have it amortized over the entire org, and they're likely to change their requirements before agreeing to the cost.) There is another approach, and that's to export the database to DXL (Domino XML) using the export option ConvertNotesBitmapToGIF. The images will turn up in the XML as <picture> elements with the data Base64-encoded. If you're operating entirely within the Notes environment, you'll need to create a temporary document with a rich text field used as a NotesMIMEEntity to transform the encoded picture to binary before streaming it to a file (using NotesStream). All of this assumes that you are working with version 6 or higher; if you are on R5 or earlier, Midas or directly accessing the CD records using the C API are the only way to fly.

Seven years later, and I've been pulling my hair out over this one. Rod H's answer is for attachments, but embedded images are another thing entirely.
My best luck came from #andre-guirard's LotusScript Gold Collection code located here: https://www.openntf.org/main.nsf/project.xsp?r=project/LotusScript%20Gold%20Collection However, that doesn't get everything because it doesn't handle documents where the embedded images were embedded the old way. (Notes changed the way it stores embedded images.)
I tried very hard to combine it with AGECOM's information presented here: https://www.agecom.com.au/support/agecomkb.nsf/0/58cbf10f0ab723c9ca25803e006c7de8?OpenDocument by changing Andre's EmbeddedImage object to seamlessly handle both formats by looking to see if the embedded image within a rich text field is really just a pointer to a $FILE field and then, if so, getting a FileItem object, but eventually I exhausted my understanding and options to a degree that I couldn't justify spending my employer's resources (my time) on it.
So if you have embedded images that are all contained in the new way, I think Andre's code will work unmolested. Otherwise, I tried my best but I don't have an answer... I have what is (for me) a dead end, presented in the hopes that you or someone else who stumbles upon it can embarrass me by explaining what I was doing wrong!
Basically, I started with Andre's code and changed it in the following ways...
In DOMUtils, add the following method:
%REM
Function DU_GetMeOrNextSiblingWithAttr
Description: Starting with a particular node, return that node or the next sibling with an attribute that has a particular value.
Does not recurse into the tree; looks only at the node passed and later siblings.
Parameters:
nodeStart: node to start your search with.
targetElement: element name of desired node.
attrName: attribute name you want to check.
attrValue: attribute value of element you're looking for.
flags: string-matching flags to compare attribute, e.g. 1 for case insensitive.
%END REM
Function DU_GetMeOrNextSiblingWithAttr(nodeStart As NotesDOMNode, ByVal targetElement$, ByVal attrName$, ByVal attrValue$, ByVal flags%) As NotesDOMElementNode
Dim node As NotesDOMNode, elTmp As NotesDOMElementNode
Set node = nodeStart
Do Until node.Isnull
If node.Nodetype = DOMNODETYPE_ELEMENT_NODE Then
If node.Nodename = targetElement Then
Set elTmp = node
If StrComp(elTmp.Getattribute(attrName), attrValue, flags) = 0 Then
Set DU_GetMeOrNextSiblingWithAttr = elTmp
Exit Function
End If
End If
End If
Set node = node.Nextsibling
Loop
End Function
Replace FileItem.New with the following code:
%REM
Sub New
Description: Arguments are the parsed DOM node of the element representing a
design element, and the name of the composite item you would like to read,
modify or create.
%END REM
Sub New(parent As FileItemParent, elNote As NotesDOMElementNode, itemName$, fileName$)
Set m_elNote = elNote
SetItem elNote, itemName$, fileName$
Dim node As NotesDOMNode
Set node = m_elNote.Parentnode
While node.Nodetype <> DOMNODETYPE_DOCUMENT_NODE
Set node = node.Parentnode
Wend
Set m_domd = node
parent.RegisterFileItem Me ' make sure the design element knows about us.
' (in case someone gets smart and invokes the constructor directly
' instead of using the nice methods we've provided).
End Sub
%REM
Sub SetItem
<!-- Created Dec 6, 2017 by JSmart523 -->
If fileName$ is blank, returns the XPath equivalent of elNote/ancestor::document/item[#name=itemName$][position()=1]
If fileName$ is not blank, returns the XPath equivalent of elNote/ancestor::document/item[#name=itemName$][object/file/#name=fileName$][position()=1]
Case insensitive. Changes itemName$ and fileName$ to the correct case if found.
Also sets Me.m_elItem to the returned NotesDOMElementNode
Also sets Me.m_elRawData to the file contents
%END REM
Sub SetItem(elNote As NotesDOMElementNode, itemName$, fileName$)
Dim elFile As NotesDOMElementNode
Dim node As NotesDOMNode
'set node to ancestor::document
Set node = elNote
Do Until node.NodeName = "document"
Set node = node.ParentNode
Loop
'If fileName$ = "", get the first ancestor::document/item[#name=itemName$]
'Otherwise, get the first ancestor::document/item[#name=itemName$][/object/file/#name=fileName$]
Set m_elItem = DU_GetChildOfType(node, DOMNODETYPE_ELEMENT_NODE)
QualifyingItem m_elItem, itemName$, m_elRawData, fileName$
m_itemName = itemName$
m_fileName = fileName$
End Sub
%REM
Sub QualifyingItem
<!-- Created Dec 8, 2017 by JSmart523 -->
Starting with incoming elItem node, ensures it's an item we want or changes elItem to the first sibling that qualifies.
%END REM
Sub QualifyingItem(elItem As NotesDOMElementNode, itemName$, elRawData As NotesDOMElementNode, fileName$)
Dim elFile As NotesDOMElementNode
Dim node As NotesDOMNode
Dim elObject As NotesDOMElementNode
If Not elItem Is Nothing Then
'Initially, elItem is just a starting point, not necessarily the item we want.
'If it's an item with the right name, great, otherwise change elItem to the next sibling item with the right name.
Set elItem = DU_GetMeOrNextSiblingWithAttr(elItem, "item", "name", itemName$, 1)
If Not elItem Is Nothing Then
If fileName$ = "" Then
'we have the right item, and aren't looking for a file node, which means we want the rawitemdata node
Set elRawData = DU_getChildNamed("rawitemdata", elItem)
Else
'We are looking for a $FILE item that contains a file.
'There are possibly several $FILE items within a document, one for each file. We've got the right one if ./object/file/#name = fileName$
Do
Set elObject = DU_GetChildNamed("object", elItem)
If Not elObject Is Nothing Then
Set elFile = DU_GetChildWithAttr(elObject, "file", "name", fileName$, 1)
If Not elFile Is Nothing Then
'Yay! We have the right elItem node!
Set elRawData = DU_GetChildNamed("filedata", elFile)
fileName$ = elFile.GetAttribute("name")
Exit Do
End If
End If
Set elItem = DU_GetMeOrNextSiblingWithAttr(elItem.NextSibling, "item", "name", itemName$, 1)
Loop Until elItem Is Nothing
'At this point, either we jumped out of the loop with a valid elItem and elRawData, or elItem is Nothing
End If
End If
End If
If elItem Is Nothing Then
'we didn't find the correct item
'make sure elRawData is changed to Nothing, too.
Set elRawData = Nothing
Else
itemName$ = elItem.GetAttribute("name")
End If
End Sub
Also in FileItem script library, add a new class, FileItemParent
%REM
Class FileItemParent
<!-- Created Dec 5, 2017 by JSmart523 -->
This is a base class for objects that use FileItem objects
%END REM
Class FileItemParent
m_elElRoot As NotesDOMElementNode
m_elFD As NotesDOMElementNode
Public m_fileItem As FileItem
m_fItems List As FileItem ' list of FileItems we've created and returned to caller.
m_iMode As Integer
%REM
Property Get DOMElement
Description: Return the element node representing the design element.
%END REM
Public Property Get DOMElement As NotesDOMElementNode
Set DOMElement = m_elElRoot
End Property
%REM
Sub New
Arguments:
db: the database containing the design element.
elElement: the DOM element corresponding to the design note (e.g. the <note>
element).
domp: The DOM parser object containing elElement.
%END REM
Sub New(elElement As NotesDOMElementNode)
Set m_elElRoot = elElement
End Sub
Sub Delete
On Error Resume Next
ForAll thing In m_fItems
Delete thing
End ForAll
End Sub
%REM
Function HasItem
Description: Determine whether there's an item element in the note DXL with a
given item name.
Note that the presence of an item doesn't guarantee it's formatted as a file
CD record.
%END REM
Function HasItem(ByVal itemName$) As Boolean
HasItem = Not (DU_GetChildWithAttr(m_elElRoot, "item", "name", itemName, 1) Is Nothing)
End Function
%REM
Function RegisterFileItem
Description: For internal use -- lets the FileItem class notify us that it's
referencing our DOM tree so that we can delete the object if we erase the
corresponding item element.
%END REM
Sub RegisterFileItem(x As FileItem)
Set m_fItems(LCase(x.itemName)) = x
If m_FileItem Is Nothing Then
Set m_FileItem = x
End If
End Sub
%REM
Function GetFileItem
Description: Retrieve the FileItem object associated with a CD-record item.
An object will be returned even if the item doesn't exist, which you can
use to create the item via UpdateFile method.
%END REM
Function GetFileItem(itemName$, fileName$) As FileItem
Set GetFileItem = New FileItem(Me, m_elElRoot, itemName, fileName)
End Function
End Class
The FileItemParent class is primarily code taken from Andre's FileResource class so that both FileResource and EmbeddedImage can use it. Change FileResource to extend FileItemParent, removing any duplicated code.
Now we want to change EmbeddedImage so that, even if the embedded image node contains a link to a $FILE item rather than the actual contents, return the actual contents.
So, change EmbeddedImage to extend FileItemParent
Add/replace the following methods to EmbededImage
%REM
Sub InitFileItem
<!-- Created Dec 6, 2017 by JSmart523 -->
Called by New
%END REM
Sub InitFileItem()
Dim buffer As Variant 'byte array
Dim iFileNameLen As Integer
Dim sFileName As String
Dim sItemName As String
Dim stream As NotesStream
If Len(m_b64) < 30000 Then
'If content is short then maybe it's a link to a $FILE item instead of the actual content?
Dim session As New NotesSession
Set stream = session.CreateStream()
Base64ToBinary m_b64, stream
stream.Position = 0
buffer = stream.Read(1)
If buffer(0) = 196 Then
'this is a link to a $FILE, not the actual image contents!
stream.Position = 10
buffer = stream.Read(2)
iFileNameLen = ConvertWordByteArray(buffer)
stream.Position = 24
buffer = stream.Read(iFileNameLen)
sFileName = BytesToString(buffer)
sItemName = "$FILE"
GetFileItem sItemName, sFileName 'sets m_fileItem to a FileItem object
End If
End If
End Sub
%REM
Property Get SuggestedFileName
%END REM
Public Property Get SuggestedFileName As String
If m_fileItem Is Nothing Then
SuggestedFileName = "Embedded-" + ItemName + "." + SuggestedFileType
Else
SuggestedFileName = m_fileItem.FileName
If InStr(SuggestedFileName, ".") = 0 Then
SuggestedFileName = SuggestedFileName + "." + SuggestedFileType
End If
End If
End Property
%REM
Property Get SuggestedFileType
%END REM
Public Property Get SuggestedFileType As String
If ImageType = "notesbitmap" Then
SuggestedFileType = "bmp"
Else
SuggestedFileType = ImageType
End If
End Property
%REM
Sub ReadFileToStream
%END REM
Sub ReadFileToStream(streamOut As NotesStream)
If m_FileItem Is Nothing Then
ReadToStream streamOut
Else
Set m_FileItem.Stream = streamOut
m_FileItem.Load
End If
End Sub
and then change EmbeddedItem.New to, at the end, call InitFileItem so that if it's a link then getting the contents returns the contents rather than the link.
Okay, so far so good as far as I know, but the problem is that CD Records of embedded images stored within $FILE items (i.e. the rich text field's embedded image node simply contains a link rather than the actual image) are/were documented in a way that was, for me, impenetrable, despite AGECOM's code and explanations. I could use the above code and Andre's EmbeddedImageList object to grab every single embedded image but I simply couldn't get a "ConvertOldCDToNew" method working so I couldn't convert the old CD Record format into solid, uncorrupted files! I don't know if I was stripping too many bytes, not stripping the right ones, or maybe I just forgot to carry the two!

I suggest you look at the Genii Software MidasLSX product. They offer a package of LotusScript extensions that make it easier to deal with the complexities of Lotus Notes Rich Text items.
http://www.geniisoft.com/showcase.nsf/MidasHelp
Otherwise, you can experiment with the NotesRichTextNavigator class to gain access to the image in the rich text item (in theory). There is very little documentation on this sort of thing. I couldn't quite tell what an image would appear as using that class, but assuming you navigate through the rich text item and are able to get a handle the image as a NotesEmbeddedObject, I know there's a way to save the object to disk from that class.
Another (crazy) thought is to email the document, and have it received by another program that can more easily process the body of the email. Notes just isn't very helpful with processing its own rich text fields.

Here is an agent I use to detach files from a richtext field on my documents.
Option Public
Dim uidoc As notesuidocument
Dim doc As NotesDocument
Dim db As NotesDatabase
Dim obj As NotesEmbeddedObject
Dim collection As NotesDocumentCollection
Dim rt As Variant
Dim attachNames As Variant
Dim i As Integer, x As Integer
Dim j As Integer
' Agent - Detach Attachments to C drive for later reattachment
' Copies all attachment in richtext field to personal directory.
Sub Initialize
Dim ws As New notesuiworkspace
Dim ses As New NotesSession
Set db = ses.CurrentDatabase
Set collection = db.UnprocessedDocuments
' get first doc in collection
For j = 1 To collection.Count
Set doc = collection.GetNthDocument( j )
' --- create array of filenames of all the attachments in the document
i = 0
Redim attachNames(i)
Forall x In doc.items
If x.name = "$FILE" Then
attachNames(i) = x.values(0)
i = i + 1
Redim Preserve attachNames(i)
End If
End Forall
If i > 0 Then
Redim Preserve attachNames(i-1)
End If
' --- for all of the filenames in attachNames, if it exists in the rich text field, detatch them
If doc.hasItem("richtextfieldname") Then
Set rt = doc.GetFirstItem("richtextfieldname")
End If
If attachNames(0) <> "" Then
Forall x In attachNames
Set obj = rt.GetEmbeddedObject( x )
If Not( obj Is Nothing ) Then
Call obj.ExtractFile( "C:\path\" & Cstr(x) )
End If
End Forall
End If
Call doc.save(True, False)
Next
End Sub

Related

How to capture email subject lines in a column?

I have Excel VBA code that is supposed to take the subject lines from my inbox in the Lotus Notes application and then copy it into a column in Excel.
It seems to populate the subject into a single cell and over-write it.
A second issue is, half way through the loop I get an error
Type:mismatch
Sub Subject_Info()
Dim v As Object
Dim vn As Object
Dim e As Object
Dim doc As Object
Dim rtitem As Variant
Dim nit As Object
View = "$All"
Set NSession = CreateObject("Notes.NotesSession")
Set NMailDb = NSession.GetDatabase("", "")
If Not NMailDb.IsOpen Then
NMailDb.OPENMAIL
End If
Set v = NMailDb.GetView(View)
Set vn = v.CreateViewNav()
Set e = vn.GetFirstDocument()
Do While Not (e Is Nothing)
Set doc = e.Document
Set nit = doc.GetFirstItem("subject")
Lines = Split(nit.Text, vbCrLf)
Range("A:A").Resize(UBound(Lines) + 1, 1).Value = Application.WorksheetFunction.Transpose(Lines)
Set e = vn.GetNextDocument(e)
Loop
End Sub
Background: in Notes there is no fixed format for all the items that are present in a Document. One document might contain an item "Subject" whereas another one does not... and still both documents are totally valid. This is normal in a document based database in contrast to a relational database.
In your case your code line Set nit = doc.GetFirstItem("subject") will return Nothing for a document that does not contain a subject item. Then the next line will fail as well and therefor you do not have a valid array in your "Lines" variable.
You could wrap your code in a If doc.HasItem("Subject") then clause. But that makes it unnecessarily complicated as there is already a method that fits better.
Simply replace
Set nit = doc.GetFirstItem("subject")
Lines = Split(nit.Text, vbCrLf)
with
Lines = doc.GetItemValue("subject")
GetItemValue always returns an Array, independent of the existence of an item. If there is no item, then it returns an array with one empty string element.
That should solve your "Type:Mismatch" error.

Lotusscript: Retrieve the images in the body of an email

In my agent, I try to retrieve all the files that are in the current email. My attached code works fine, except for the images in the body of the email. I manage to retrieve all the files and images that were attached to the email except the pictures that were copied and pasted in the middle of the email text. Here is my code:
Dim session As New NotesSession
Dim doc As NotesDocument
Dim db As NotesDatabase
Dim item As Variant
Dim CurrentDocColl As NotesDocumentCollection
Set db = Session.Currentdatabase
Set CurrentDocColl = db.Unprocesseddocuments
Set doc = CurrentDocColl.Getfirstdocument
While Not doc Is Nothing
Set item = doc.GETFIRSTITEM("Body")
If doc.HasEmbedded Then
ForAll attachment In item.EmbeddedObjects
Call attachment.ExtractFile (pathname & "\" & attachment.Name)
End ForAll
End If
Set doc=CurrentDocColl.Getnextdocument(doc)
Wend
How can I retrieve these images?
Thank you very much for your help
I have an agent that does a lot of that, but it's not short. What you have to do is run the document through an XML DomParser, walk down the DOM tree and when you find a node with "JPEG" or "PNG" in the name (the inline images themselves), stream the data to a file and save it. The code is combination of an agent I found online (which I couldn't find again, otherwise I would give credit) and work I've done. You won't be able to copy/paste this sample code and expect it to work, I've removed things (like declaring variables and supporting functions) for brevity.
Sub Initialize
Dim dxlExp As NotesDXLExporter
Set dxlExp = s.CreateDXLExporter
Call dxlExp.setInput(Doc)
Set DomParser=s.CreateDOMparser()
Call DomParser.Setinput(dxlExp)
Dim dxlImp As NotesDXLImporter
Set dxlImp = s.Createdxlimporter()
Call dxlImp.Setinput(domParser)
Call dxlImp.SetOutput(db)
On Event PostDomParse From DomParser Call DomInputProcessed
Call dxlExp.Process
End Sub
Sub DomInputProcessed(DomParser As NotesDomParser)
Dim DomNode As NotesDomNode
Set DomNode = DomParser.Document
Call walkTree(DomParser, DomNode)
Exit Sub
End Sub
Sub walkTree (DomParser As NotesDOMParser, node As NotesDOMNode)
Select Case node.NodeType
Case DOMNODETYPE_DOCUMENT_NODE: ' If it is a Document node
domParser.Output( "<?xml version='1.0' encoding='utf-8'?>"+LF )
Set child = node.FirstChild ' Get the first node
Dim numChildNodes As Integer
numChildNodes = node.NumberOfChildNodes
While numChildNodes > 0
Set child = child.NextSibling ' Get next node
numChildNodes = numChildNodes - 1
Call walkTree(DOMParser, child)
Wend
Case DOMNODETYPE_DOCUMENTTYPE_NODE: ' It is a <!DOCTYPE> tag
domParser.Output("<!DOCTYPE "+ node.NodeName+ ">" + LF)
Case DOMNODETYPE_TEXT_NODE: ' Plain text node
value = xmlReplace(node.NodeValue)
domParser.Output(value)
Case DOMNODETYPE_ELEMENT_NODE: ' Most nodes are Elements
Select Case node.NodeName
Case "jpeg"
Dim jpegfile As String
' Step 1, write the MIME file
Dim base64node As NotesDOMNode
Set base64Node = node.Firstchild
Dim base64Out As NotesStream
Set base64Out = s.createStream()
Dim bytesWritten As Long
bytesWritten = base64Out.Writetext(base64Node.NodeValue)
' Step 2, Read the MIME file and decode it.
Set db=s.currentdatabase
Set doc=db.createDocument()
Set m=doc.Createmimeentity("Image1")
Call m.setContentFromText(base64Out, "image/jpeg", 1727)
Call m.Decodecontent()
Dim JPEGOut As NotesStream
Set JPEGOut = s.createStream()
jpegFile = RandomFileName(baseDir, ".jpg")
JPEGOut.open(jpegFile)
Call m.Getcontentasbytes(JPEGOut, True)
Call JPEGOut.Close()
attachmentNamesStr = attachmentNamesStr + jpegFile + "~"
' Step 3, remove the jpeg and its child node
' We do this by just not sending anything to the DomParser output.
Case "png"
' Same as JPEG except it's PNG.
End Select
End Select 'node.NodeType
End If 'Not node.IsNull
End Sub

How do I copy the contents of one word document to the end of another using vba?

Goal for my project:
I want to be able to copy the contents of one document and append that selection to the end of another document.
What it does... (this is just background info so you understand why I am trying to do this):
I am trying to dynamically produce a document which quotes a variety of information regarding different parts and materials involved for a product.
The document itself has a consistent format which I have broken down and separated into two documents. The first contains a bunch of data that needs to be entered manually, and is where I want to append all additional content. The second contains roughly a dozen custom fields which are updated from an excel spreadsheet in VBA. For a single part and as a single doc this works as I want it (my base case). However my issue is when there are multiple parts for a project.
The Problem:
For multiple parts I have to store information in an array which changes in size dynamically as each additional part is added. When someone has added all the necessary parts they can select a button called "Create Quote".
Create quote runs a procedure which creates/opens separate copies of the two template documents mentioned above (saved on my computer). It then iterates through the array of parts and updates all the custom field in the 2nd document (no problems). Now I just need the contents of the 2nd document appended to the end of the first which is my problem.
What I want:
Ideally, my procedure will continue to iterate through every part in the array - updating custom fields, copy then paste the updated text, repeat... Until every part is included in the newly generated quote.
What I Tried - this code can be found in my generate quote procedure
I have tried many of the examples and suggestions provided by people who had similar question, but I don't know if its because I am operating from an excel doc, but many of their solution have not worked for me.
This is my most recent attempt and occurs after each iteration of the for loop
wrdDoc2.Fields.Update 'Update all the fields in the format document
wrdDoc2.Activate
Selection.WholeStory ' I want to select the entire document
Selection.Copy ' Copy the doc
wrdDoc1.Activate ' Set focus to the target document
Selection.EndKey wdStory ' I want the selection to be pasted to the end of the document
Selection.PasteAndFormat wdPasteDefault
QUOTE PROCEDURE - I am only including a handful of the fields I am updating because its not necessary to show them all
Private Sub quote_button_Click()
On Error GoTo RunError
Dim wrdApp1, wrdApp2 As Word.Application
Dim wrdDoc1, wrdDoc2 As Word.Document
Set wrdApp1 = CreateObject("Word.Application")
Set wrdApp2 = CreateObject("Word.Application")
wrdApp1.Visible = True
wrdApp2.Visible = True
Set wrdDoc1 = wrdApp1.Documents.Add(Template:="C:\MWES\AQT_v1.1(start).docm", NewTemplate:=False, DocumentType:=0)
Set wrdDoc2 = wrdApp2.Documents.Add(Template:="C:\MWES\AQT_v2.1(format).docm", NewTemplate:=False, DocumentType:=0)
Dim propName As String
For i = LBound(part_array, 1) To UBound(part_array, 1)
For Each prop In wrdDoc2.CustomDocumentProperties
propName = prop.name
' Looks for and sets the property name to custom values of select properties
With wrdDoc2.CustomDocumentProperties(propName)
Select Case propName
Case "EST_Quantity"
.value = part_array(i, 0) ' "QTY" ' Sheet1.Cells(1, 3) 'NA
Case "EST_Metal_Number"
.value = part_array(i, 1) ' "METAL_#" ' Sheet1.Cells(2, 3) 'NA"
Case "EST_Metal_Name"
.value = part_array(i, 2) ' "METAL_N" ' Sheet1.Cells(5, 2)
End Select
End With
Next prop ' Iterates until all the custom properties are set
wrdDoc2.Fields.Update 'Update all the fields in the format document
wrdDoc2.Activate
Selection.WholeStory ' I want to select the entire document
Selection.Copy ' Copy the doc
wrdDoc1.Activate ' Set focus to the target document
Selection.EndKey wdStory ' I want the selection to be pasted to the end of the document
Selection.PasteAndFormat wdPasteDefault
Next i ' update the document for the next part
RunError: ' Reportd any errors that might occur in the system
If Err.Number = 0 Then
Debug.Print "IGNORE ERROR 0!"
Else
Dim strError As String
strError = "ERROR: " & Err.Number & vbCrLf & Err.Description & vbCrLf & Erl
MsgBox strError
Debug.Print strError & " LINE: " & Erl
End If
End Sub
I apologize this was so long winded. Let me know if there is anything confusing or you may want clarified. I think I included everything though.
I think you're close, so here are a couple of comments and an example.
First of all, you're opening two separate MS Word Application objects. You only need one. In fact, it's possible that the copy/paste is failing because you're trying to copy from one Word app to a document opened in the other. (Trust me, I've seen weird things like this.) My example below shows how to do this by only opening a single application instance.
Dim mswApp As Word.Application
Set mswApp = AttachToMSWordApplication() 'more on this function below...
Dim doc1 As Word.Document
Dim doc2 As Word.Document
Set doc1 = mswApp.Documents.Open("C:\Temp\combined.docx")
Set doc2 = mswApp.Documents.Open("C:\Temp\control.docx")
While I don't often write code for Word, I've found that there are so many different ways to get at the same content using different objects or properties. This is always a source of confusion.
Based on this answer, which has worked well for me in the past, I then set up the source and destination ranges to perform the "copy":
Dim destination As Word.Range
Dim source As Word.Range
Set source = doc1.Content
Set destination = doc2.Content
destination.Collapse Direction:=Word.wdCollapseEnd
destination.FormattedText = source
Here is the whole module for reference:
Option Explicit
Sub AddDocs()
Dim wordWasRunning As Boolean
wordWasRunning = IsMSWordRunning()
Dim mswApp As Word.Application
Set mswApp = AttachToMSWordApplication()
Dim doc1 As Word.Document
Dim doc2 As Word.Document
Set doc1 = mswApp.Documents.Open("C:\Temp\combined.docx")
Set doc2 = mswApp.Documents.Open("C:\Temp\control.docx")
Dim destination As Word.Range
Dim source As Word.Range
Set source = doc1.Content
Set destination = doc2.Content
destination.Collapse Direction:=Word.wdCollapseEnd
destination.FormattedText = source
doc2.Close SaveChanges:=True
doc1.Close
If Not wordWasRunning Then
mswApp.Quit
End If
End Sub
Here's the promised note on a couple functions I use in the sample. I've built up a set of library functions, several of which help me access other Office applications. I save these modules as .bas files (by using the Export function in the VBA Editor) and import them as needed. So if you'd like to use it, just save the code below in using a plain text editor (NOT in the VBA Editor!), then import that file into your project.
Suggested filename is Lib_MSWordSupport.bas:
Attribute VB_Name = "Lib_MSWordSupport"
Attribute VB_Description = "Variety of support functions operating on MS Word"
Option Explicit
Public Function IsMSWordRunning() As Boolean
Attribute IsMSWordRunning.VB_Description = "quick check to see if an instance of MS Word is running"
'--- quick check to see if an instance of MS Word is running
Dim msApp As Object
On Error Resume Next
Set msApp = GetObject(, "Word.Application")
If Err > 0 Then
'--- not running
IsMSWordRunning = False
Else
'--- running
IsMSWordRunning = True
End If
End Function
Public Function AttachToMSWordApplication() As Word.Application
Attribute AttachToMSWordApplication.VB_Description = "finds an existing and running instance of MS Word, or starts the application if one is not already running"
'--- finds an existing and running instance of MS Word, or starts
' the application if one is not already running
Dim msApp As Word.Application
On Error Resume Next
Set msApp = GetObject(, "Word.Application")
If Err > 0 Then
'--- we have to start one
' an exception will be raised if the application is not installed
Set msApp = CreateObject("Word.Application")
End If
Set AttachToMSWordApplication = msApp
End Function

Runtime Error 13 - Type Mismatch when trying to edit richtext item of Lotus Notes

I'm trying to edit an existing notes document via VBA and then send it automatically.
I've already created pretty much everything - just need to figure out how exactly I can add a certain text at a certain position within the richtext element.
Sub sendMail() 'inputIndID As String, inputRecipient As String, inputIncDescription As String)
Dim mailDB As Object
Dim mailToSend As Object
Dim body As Object
Dim session As Object
Dim view As Object
Dim entries As Object
Dim docIDs() As String
Dim docSubjects() As String
Dim incID, incDescription As String
Dim element As String
Dim bodyNavigator As Object
incID = "<INC-ID>"
incDescription = "<INC-Betreff>"
'Start a session to notes
Set session = CreateObject("Notes.NotesSession")
'This line prompts for password of current ID noted in Notes.INI
'Call Session.Initialize
'or use below to supply password of the current ID
'Open the mail database in notes
Set mailDB = session.GetDatabase("Eschen10/Presta", "mail\qcpcsupport.nsf")
If mailDB.IsOpen = False Then
Call mailDB.Open
End If
'Search for all the messages in the folder "Umfrage"
Set view = mailDB.GetView("Umfrage")
Set entries = view.AllEntries
If entries.Count = 0 Then
MsgBox "Keine Nachricht im Umfrage Ordner."
End If
ReDim docIDs(entries.Count - 1)
ReDim docSubjects(entries.Count - 1)
Set entry = entries.GetFirstEntry
Do Until entry Is Nothing
docIDs(i) = entry.NoteID
docSubjects(i) = entry.Document.GetItemValue("Subject")(0) 'based on standard R5 mail template column order
'If the documents title matches the searched one it will be taken and worked with later
If docSubjects(i) = "Umfrage PC-Support Servicequalität" Then
Set mailToSend = entry.Document
End If
i = i + 1
Set entry = entries.GetNextEntry(entry)
Loop
'Set the recipient
Call mailToSend.ReplaceItemValue("SendTo", "simon.hartmann#thyssenkrupp.com")
'Get and change the body content
Set body = mailToSend.GetFirstItem("Body")
Set bodyNavigator = body.CreateNavigator()
'Replace markers with correct text
element = "<"
If (body.Type = RICHTEXT) Then
Call bodyNavigator.FindFirstString(element)
Call body.BeginInsert(bodyNavigator, True)
Call body.AppendText("123456")
Call bodyNavigator.FindNextString(element)
Call body.BeginInsert(bodyNavigator, True)
Call body.AppendText("Antrag Guest WLAN")
End If
'Example to save the message (optional)
mailToSend.SaveMessageOnSend = True
'Send the document
'Gets the mail to appear in the Sent items folder
mailToSend.Save True, False
Call mailToSend.ReplaceItemValue("PostedDate", Now())
Call mailToSend.Send(False)
'changes the body back and saves the document in the folder "Umfrage" so it can be resent next time
Call mailToSend.PutInFolder("Umfrage")
'Clean Up
Set mailDB = Nothing
Set mailToSend = Nothing
Set body = Nothing
Set session = Nothing
End Sub
Currently I am failing at the following line:
Call body.BeginInsert(bodyNavigator, True)
I get the error - Runtime Error 13 - Type Mismatch
I also already tried to give all variables the correct data type of Lotus Notes - but then I have the problem with each of those variables.
Is there a way I can "force" the bodynavigator to be of the correct type? Or where do I have my mistake? Am I missing a library or anything?
Thanks in advance!!!
Regards,
Simon
Did you read the documentation for NotesRichtextNavigator?
You find the following information there:
Parameters
target$
String. The search string.
options$
Long. Any of the following. Specify multiple options by combining them with addition or logical ORs.
RT_FIND_ACCENTINSENSITIVE (4) (default is accent sensitive)
RT_FIND_CASEINSENSITIVE (1) (default is case sensitive)
RT_FIND_PITCHINSENSITIVE (2) (default is pitch insensitive)
So: Your second parameter "true" is simply the wrong type... therefor the type mismatch...

Call to getEmbeddedObjects(); (Domino Server API) returns wrong results

The Domino server API getEmbeddedObjects(); returns the wrong result (zero) when a mail containing an attachment (as embedded object) is sent from the script.
Though an attachment is sent as an EmbeddedOBject, getEmbeddedObjects(); returns ZERO.
The mail type is NOT MIME.
This is a Java application. Is there is any workaround for this problem?
I take the body from the document. If the body is of richtextitem, I call the getEmbeddedObjects() which returns zero though an attachment is present as embedded object.
Looking through all of the items in a document for the possibility of an attachment is doing a lot of work for nothing. All you need to do is get the collection of attachment names using the #AttachmentNames formula (available through the evaluate() method of the Session object, using the Document argument), and if the collection contains more than an empty string, use the getAttachment() method of the document to get a handle to the corresponding EmbeddedObject.
getAttachment() can grab any attachment to a document, whether it's associated with a RichTextItem or a V2-style attachment (as would be created by a web UI or when converting external mail). And never be afraid to use Formula Language when it's appropriate -- it can make your life a whole lot simpler.
Attachments do not necessarily have to be embedded inside a RichText field. To quote from the designer-help:
If you need access to OLE/2 embedded objects that exist in a document but are not part of a rich text item (for example, because the object was originally created on the document's form), use the EmbeddedObjects property in Document.
Another source of your problem could be, that there are several "Body" RichText items you would have to check.
HTH
Lotus Notes does not provide a single reliable method for extracting attachments from a NotesDocument object, unfortunately. To be thorough, you'll need to check through all richtext items it contains, as well as the document object itself.
I wrote the following code to extract attachments from selected emails in a mailbox, in an effort to cut down the file size (my users saved everything). The main loop is relevant to your question, though. It shows the process of looping through all of the document's items looking for richtext items with attachments, followed by a loop through all items again looking for items of type "Attachment".
(forgive the hackiness of the code. It wasn't written for efficiency)
Sub Initialize
Set s = New NotesSession
Set db = s.CurrentDatabase
Set dc = db.UnprocessedDocuments
Set doc = dc.GetFirstDocument
Dim rtItem As NotesRichTextItem
Dim RichTextItemNames List As String
Dim DocumentItemNames List As String
Dim itemCount as Integer
While Not (doc Is Nothing)
'Scan all richtext items in document for embedded objects
Forall i In doc.Items
If i.Type = RICHTEXT Then
Set rtItem = doc.GetFirstItem(i.Name)
If Not Isempty(rtItem.EmbeddedObjects) Then
RichTextItemNames(itemCount) = Cstr(i.Name)
itemCount = itemCount + 1
End If
End If
End Forall
'Loop through richtext items and extract the embedded attachments
For j = 0 To itemCount - 1
Set rtItem = doc.GetfirstItem(RichTextItemNames(j))
Forall Obj In rtItem.EmbeddedObjects
If ( Obj.Type = EMBED_ATTACHMENT ) Then
Call ExportAttachment(Obj)
Call Obj.Remove
Call doc.Save( False, True ) 'creates conflict doc if conflict exists
End If
End Forall
Next
'Scan all items in document for Attachment type items
itemCount = 0
Forall i In doc.Items
If i.Type = ATTACHMENT Then
DocumentItemNames(itemCount) = i.Values(0)
itemCount = itemCount + 1
End If
End Forall
'Loop through all attachment items in document and extract them
For j = 0 To itemCount - 1
Set attachmentObject = doc.GetAttachment(DocumentItemNames(j))
Call ExportAttachment(attachmentObject)
Call attachmentObject.Remove
Call doc.Save( False, True ) 'creates conflict doc if conflict exists
Next
Set doc = dc.GetNextDocument(doc)
Wend
End Sub
Sub ExportAttachment(o As Variant)
Dim sAttachmentName As String
Dim sNum As String
Dim sTemp As String
' Append number to end of filename if filename exists.
sAttachmentName = sDir & "\" & o.Source
While Not (Dir$(sAttachmentName, 0) = "")
sNum = Right(Strleftback(sAttachmentName, "."), 2)
If Isnumeric(sNum) Then
sTemp = Strleftback(sAttachmentName, ".")
sTemp = Left(sTemp, Len(sTemp) - 2)
sAttachmentName = sTemp & Format$(Cint(sNum) + 1, "##00") & _
"." & Strrightback(sAttachmentName, ".")
Else
sAttachmentName = Strleftback(sAttachmentName, ".") & _
"01." & Strrightback(sAttachmentName, ".")
End If
Wend
Print "Exporting " & sAttachmentName
'Save the file
Call o.ExtractFile( sAttachmentName )
End Sub
If you get the embedded objects from the Document object, they won't contain attachments. Using getEmbeddedObjects with the "Body" RichTextItem gets the attachments too.
Does that help?

Resources