ich habe ein Problem bei der Erstellung eines Serienbriefes mit VBS und der OpenOffice API, genauer gesagt bei der Verlinkung der Datenquelle.
Kurze Erläuterung der Umstände
Wir benutzen ein Customer Relationship Management (CRM) Programm (nicht SAP), welches eine Word-Serienbrieffunktion hat. Diese Funktion ist in Visual Basic Script programmiert. Meine Aufgabe ist es nun, die gleiche Funktion für OASIS Open Document Dokumente zu erstellen, ebenfalls in VBS.
Ich verwende dafür die OpenOffice.org API (https://www.openoffice.org/api/) und LibreOffice als Testumgebung (mit OpenOffice wird später auch getestet).
Im CRM klicke ich auf den "Word Serienbrief"-Button, das dahinterliegende Word Modul, welches mir als Vorlage dient, erstellt zuerst aus den ausgewählten CRM Daten eine .csv (z. B. Personendaten mit Anschrift, Firma, Anrede, etc.). Danach wird im Code aus einer vorher ausgewählte Vorlage (kann beliebiger Text sein) der Inhalt geladen. Aus dieser Vorlage wird ein neues Word-Dokument erzeugt mit dem selben Inhalt, zusätzlich wird für das neue Dokument die .csv als Datenquelle verlinkt. Bevor das Modul mit seiner Routine fertig ist, wird das neu erstellte Dokument geöffnet und der Benutzer kann nun die Felder eintragen, wo die Personenbezogenen Daten stehen sollen.
Mein System
Windows 7 Professional 64 Bit Version 6.1 (Build 7601: SP1)
LibreOffice 5.0.2.2
Java 1.8.0_66
Der Fehler
Soweit vom Grundablauf funktioniert es auch in LibreOffice bzw. OpenOffice, was ich zusammengebastelt habe. Allerdings nur beim 1. Öffnen. Wenn ich das Dokument schließe und neu aufmache, geht die Verlinkung mit der Datenquelle verloren. Der Eintrag der Datenquelle ("opog") ist immer noch zu sehen (F4 im Writer):
aber sobald ich die Tabellen anzeigen lassen möchte, kommt die Fehlermeldung
"The connection to the data source could not be established
The driver class '' could not be loaded"
"Error code: 1000
The driver class '' could not be loaded"
"SQL Status: HY000
The driver class '' could not be loaded"
http://i.imgur.com/7s368Dh.png
Warum erkennt er nicht mal den Treiber? Ich vermute, dass hier irgendwo der Fehler liegt.
Wenn ich den Writer offen lasse und die Datenbank mit LibreOffice Base öffne und wieder schließe, kommt ein "wollen Sie speichern?" Dialog. Wenn ich OK klicke und speichere, obwohl ich keine Änderungen vorgenommen habe, funktioniert die Verlinkung der Datenquelle auch beim erneuten Öffnen.
Im Internet habe ich zu dem Fehler Aussagen über fehlendes Java gefunden, aber daran wird das wohl nicht liegen, da es wahrscheinlich nur das manuelle Erstellen über LibreOffice/OpenOffice betrifft. Meinem LibreOffice ist Java bekannt:
muss ich Java evtl. im Code in irgendeiner Form für den Datenbanktreiber "mitgeben" bzw. mithilfe von Java die Datenquelle erstellen/registrieren?
Wenn ich das alles manuell mache, also über LibreOffice die Datenbank erstelle usw., funktioniert es ohne Probleme.
Technische Details
Als Basis für meinen Code habe ich das verwendet, was Dirk Reusswig hier gepostet hat: viewtopic.php?t=4572#p94841
Man beachte aber, dass sein Code einige Fehler enthält. In der Funktion "Oo_InstallDatasource" z. B. setzt er die PropertyValues "HeaderLines" und "Charset", welche korrekt "HeaderLine" und "CharSet" heißen. Falsche Schreibweisen bzw. nicht existierende Parameter werden ignoriert (ohne Fehlermeldung etc.).
In diesem Thread habe ich auch geguckt: viewtopic.php?t=9084
Ich benutze folgende OOo API Services:
- com.sun.star.sdb.DatabaseContext - für die Datenquelle
com.sun.star.text.MailMerge - für den Serienbrief
com.sun.star.ServiceManager - zum Erstellen der Service Objekte
Wenn ich nach der Erstellung beim 1. Öffnen die Datenbank mit Base öffne und die Eigenschaften checke, sieht alles korrekt aus:
Mein Code
Code: Alles auswählen
Dim objServiceManager
Dim objDesktop
Dim objCoreReflection
Dim objDatasource
Dim objConnection
Dim objMailMerge
Dim OODatei
Dim OOPfad
Dim OODataSourceName
Dim MyProps()
Set objServiceManager = Nothing
Set objDesktop = Nothing
Set objCoreReflection = Nothing
Sub ErstelleDok()
'[...] irrelevanter code vorher: Eigenschaften setzen, CSV generieren, Dateien kopieren und verschieben
'csv zu utf-8 konvertieren
KonvUTF8noBom(strDateiPfad_CSV)
'Verbindung zu OpenOffice
Set objServiceManager = CreateObject("com.sun.star.ServiceManager")
Set objDesktop = objServiceManager.CreateInstance("com.sun.star.frame.Desktop")
Set objCoreReflection = objServiceManager.CreateInstance("com.sun.star.reflection.CoreReflection")
'Pfadangaben zu Testdateien
OODatei = "file:///C:/Users/RVama/Desktop/Test.odt"
'OODatei = "file:///C:/Users/RVAdmin/Desktop/Test1.odt"
'OOPfad = "file:///C:/Users/RVama/Desktop/"
OOPfad = "C:/Users/RVama/Desktop/"
OODataSourceName = "opog"
'Datenquelle erstellen, registrieren, verbinden
Set objDatasource = OO_InstallDatasource(OOPfad, OODataSourceName)
Set objConnection = objDatasource.getConnection("", "")
'Set objConnection = objDatasource.connectWithCompletion("", "")
'Daten aus "Steuerdatei" einfügen
Set objMailMerge = objServiceManager.CreateInstance("com.sun.star.text.MailMerge")
With objMailMerge
.DataSourceName = OODataSourceName 'Datenbankname (nicht der Dateiname der Datenquelle)
.ActiveConnection = objConnection
.DocumentURL = OODatei
.CommandType = 0 '0 = Tabelle
.Command = strDateiName_CSV 'Tabellenname in der Datenbank (= Dateiname der Datenquelle)
.OutputType = 2
'.OutputURL = OOPfad
.SaveAsSingleFile = True
End With
Call objMailMerge.Execute(MyProps)
'[...] irrelevanter code danach: Dokument öffnen, Fehlermeldungen ausgeben bei Fehlern
End Sub
'Datei als UTF-8 no BOM speichern, dazu:
'1) Inhalt der Datenquelle (CSV) einlesen und zwischenspeichern
'2) den Inhalt an einen Stream übergeben
'3) Pointer-Position des Streams auf 3 setzten, um den BOM-Teil zu überspringen
'4) Inhalt (ohne BOM) in einen neuen, binären Stream kopieren und diesen als Datei speichern
'BOM: https://de.wikipedia.org/wiki/Byte_Order_Mark
Function KonvUTF8noBom(Datei)
dim content
'CSV einlesen
dim fso : set fso = CreateObject("Scripting.FileSystemObject")
'als Unicode öffnen
dim file : set file = fso.OpenTextFile(Datei, 1, false, -1)
'Inhalt speichern und Datei schließen
content = file.ReadAll
file.close
'MsgBox "length: " & len(content) & vbCrLf & "Inhalt: " & content
'UTF-8 (mit BOM) Stream erzeugen und CSV-Inhalt übergeben
dim stream : set stream = CreateObject("ADODB.Stream")
with stream
.Charset = "utf-8"
.Type = 2 'text
.Mode = 3 'ReadWrite
.LineSeparator = 10 'Line feed only
.Open
.WriteText content
'.Position = 0
'.SaveToFile Datei, 2 'überschreibe Datei
.Position = 3 'BOM überspringen
'.Close
end with
'binären Stream erzeugen, um Inhalt ohne BOM-Anteil übergeben zu können
dim bStream : set bStream = CreateObject("adodb.stream")
with bStream
.Type = 1 'binär
.Mode = 3 'ReadWrite
.Open
end with
'kopieren
stream.CopyTo bStream
stream.Flush
stream.Close
'speichern
bStream.SaveToFile Datei, 2 'überschreibe Datei
bStream.Flush
bStream.Close
End Function
Function OO_InstallDatasource(filePath, filename)
Dim databaseContext
Dim dataSource
Dim dsProps(4)
Dim IDs_props()
Dim continue
continue = True
Set databaseContext = objServiceManager.CreateInstance("com.sun.star.sdb.DatabaseContext")
'Wenn Datenquelle unter dem gleichen Namen bereits registriert ist -> entfernen
If (databaseContext.hasByName(filename)) Then
If (OO_RemoveDatasource(filename) = False) Then
continue = False
If Not (IsEmpty(dataSource)) Then Set dataSource = Nothing
If Not (IsEmpty(databaseContext)) Then Set databaseContext = Nothing
End If
End If
If (continue) Then
Set dataSource = databaseContext.CreateInstance()
Call dataSource.DatabaseDocument.StoreAsUrl("file:///" & filePath & "Test_ama" & ".odb", IDs_props)
dataSource.URL = "sdbc:flat:" & "file:///" & filePath & "Test_ama" & ".csv"
Set dsProps(0) = OO_createStruct("com.sun.star.beans.PropertyValue")
dsProps(0).Name = "HeaderLine"
dsProps(0).Value = true
Set dsProps(1) = OO_createStruct("com.sun.star.beans.PropertyValue")
dsProps(1).Name = "FieldDelimiter"
dsProps(1).Value = Chr(59) ' ;
Set dsProps(2) = OO_createStruct("com.sun.star.beans.PropertyValue")
dsProps(2).Name = "StringDelimiter"
dsProps(2).Value = Chr(34) ' "
Set dsProps(3) = OO_createStruct("com.sun.star.beans.PropertyValue")
dsProps(3).Name = "CharSet"
dsProps(3).Value = "UTF-8"
Set dsProps(4) = OO_createStruct("com.sun.star.beans.PropertyValue")
dsProps(4).Name = "Extension"
dsProps(4).Value = "csv"
dataSource.Info = dsProps
Call databaseContext.registerObject(filename, dataSource)
Set OO_InstallDatasource = dataSource
End If
End Function
Function OO_RemoveDatasource(RDs_strDatasourceName)
OO_RemoveDatasource = False
Dim RDs_objDatabaseContext
Set RDs_objDatabaseContext = objServiceManager.CreateInstance("com.sun.star.sdb.DatabaseContext")
If RDs_objDatabaseContext.hasByName(RDs_strDatasourceName) Then
Call RDs_objDatabaseContext.revokeObject(RDs_strDatasourceName)
OO_RemoveDatasource = True
End If
End Function
Function OO_createStruct(strTypeName)
Dim classSize
Set classSize = objCoreReflection.forname(strTypeName)
Dim aStruct
classSize.CreateObject aStruct
Set OO_createStruct = aStruct
End Function
http://pastebin.com/ivBYnn3N
Erklärung zum Code
- Die CSV wird vom vorhanden Word-Code generiert. Ich muss diese jedoch umkodieren, damit Sonderzeichen in LibreOffice/OpenOffice korrekt erkannt und angezeigt werden.
- obwohl ich bei dataSource.URL den Pfad zu einer bestimmten Datei angebe (diese befindet sich auf dem Desktop), werden in der Datenbank trotzdem alle .csv Dateien als Tabellen angelegt, die im selben Ordner liegen. Wenn ich bei dataSource.URL nur den Ordnerpfad eintrage, ohne Datei, passiert das gleiche.
Hat jemand Erfahrung mit der ganzen Kiste und weiß mehr? Ich weiß nicht was ich noch probieren kann
Evtl. andere Verbindungsmethoden als DataSource.getConnection()?