OpenOffice Macro to Access Contents of a Table - linux

I wrote a Macro, which should take two Dates (dd.mm.yyyy) as a String from a table in an OpenOffice Document (Writer, not Calc). These two Dates should be merged to this:
ddmmyyyy-ddmmyyyy. This should be used as the filename then.
The Table has only one row and 6 columns, the first Date being in table2:D1:D1 and the second one in table2:F1:F1.
I "translated" this to table2(1, 4) and table2(1, 6)
This German site is doing what I want to do, but with spreadsheets in a OOCalc Document and not OOWriter.
Enough talk, here is my code:
sub save
oDoc=thisComponent
sStartDate = oDoc.Table2(1, 4)
sEndDate = oDoc.Table2(1, 6))
sFilename = sStartDate.String & sEndDate.String
sURL = ConvertToURL("file:///home/cp/Documents/" & sFilename & ".odt")
msgbox sURL
' oDoc.StoreAsURL(sURL, Array())
end sub
Yes, I run linux, so the path should be correct.
When I try to run this script it says:
Property or Method not found table2
I of course tried google but somehow I could not find a solution. A hint in the right direction could be enough, I also guessed I have to write "more":
sStartDate = oDoc.getString(table2(1, 4))
or something similar. Didnt work either. Another thing I tried was using (0, 3) instead of (1, 4).
Well I would appreciate it, if someone could help me a bit! :)
And I hope I have done everything right how I posted here.
Vaelor
EDIT:
I have now modified the script to this, according to the PDF found HERE in chapter 14.9.
It now looks like this,
sub save
oDoc=thisComponent
Dim oTable
Dim sTableName As String
sTableName = "Table2"
oTable = oDoc.getTextTables().getByName(sTableName)
' oTable = oTables.getByName(sTableName)
sStartDate = oTable.getCellByPosition(0, 3)
sEndDate = oTable.getCellByPosition(0, 5)
sFilename = sStartDate.String & sEndDate.String
sURL = ConvertToURL("file:///home/cp/Documents/" & sFilename & ".odt")
msgbox sURL
' oDoc.StoreAsURL(sURL, Array())
end sub
But, still not working. Now I get this exception IndexOutOfBoundsException.
(I wanted to link it, but it says, I cant post more than 2 links :-( )
My first thought was I had to change the cels to 0, 3 and 0, 5. After changing that, the error still occurs. :-(
Edit2:
Since I got no response, I think I will try this in Python, maybe it yields better results.

This code demonstrates how to locate a text table with the given name and how to access individual cells.
function get_table_by_name(name as string) as object
dim oenum as object
dim oelem as object
oenum = thisComponent.text.createEnumeration
while oenum.hasMoreelements
oelem = oenum.nextElement
if oelem.supportsService("com.sun.star.text.TextTable") then
if oelem.Name = name then
get_table_by_name = oelem
exit function
end if
end if
wend
end function
Sub Main
dim table as object
table = get_table_by_name("Table1")
if not isNull(table) then
msgbox "Got " & table.Name & " " & table.getRows().getCount() & "x" & table.getColumns().getCount()
msgbox "Cell[0,0] is " & table.getCellByPosition(0, 0).getString()
end if
End Sub

Related

Cell Value String Variable in Path Errors to "Bad File Name or Number"

Sorry for the long one, and it's probably something simple that I'm overlooking after so much time.
I'm writing a small program that in just a few clicks, pulls data from an actor's "deal memo" pdf and put's that info into one of four possible excel templates (in different sheets) to export as a new "contract" pdf.
The application identifies certain key words/values from other cells that determines which sheet/template is used.
The issue comes at the following step:
-Based on the keywords, I need the exported document to identify if a series of folders are created, and if not, create them, and step in to create more relevant folders before finally saving the file.
The structure example is as follows, relevant created folders in Bold:
C:\Work Folder\ Deal Memo to Contract\Exported Contracts\Episode Number\Actor Name\Contract Type\final.pdf
Each folder name is created based on variables pulled from cell values. It works just fine when I put the string variables in quotes for testing, and even when the variables are stated by themselves, it pastes the proper path in the admin cells as shown in the following picture - Range A14:A21
Screenshot of Dashboard Page, admin column to be hidden
But even though it looks like a proper path address in the cells, VBA throws a
Runtime error 52:Bad file name or number on line 56, "PlayerExFolder = Dir(PlayerExPath, vbDirectory)"
Like I said, it's probably something simple. Any help would be great as I'm still pretty new to this. Oh, and I'm working on the Daily_Direct section of the if statements, the others will be identical once this starts working. Thanks!
UPDATE - It turns out that I had narrowed it down to what I thought were extra spaces that were ruining the path/folder creation. They were invisible "Ghost" characters. Ended up using the Clean function on the cells that were being used to name the folders. Hope this helps someone in the future.
Sub export_pdf()
Application.ScreenUpdating = False
Dim MainExPath As String
Dim MainExFolder As String
MainExPath = Worksheets("Deal2Contract").Range("C3").Value & "\Exported Contracts"
MainExFolder = Dir(MainExPath, vbDirectory)
If MainExFolder = vbNullString Then
Answer = MsgBox("An export folder for the generated contracts does not exist, I will create one for you", vbOKCancel, "Create Contract Export Folder?")
Select Case Answer
Case vbOK
VBA.FileSystem.MkDir (MainExPath)
Case Else
End Select
End If
Worksheets("Deal2Contract").Range("A15").Value = MainExPath
Dim EpiExPath As String
Dim EpiExFolder As String
Dim currEp As Integer
currEp = Worksheets("Data").Range("F14").Value
EpiExPath = Worksheets("Deal2Contract").Range("A15").Value & "\" & currEp
Worksheets("Deal2Contract").Range("A17").Value = EpiExPath
Dim PlayerExPath As String
Dim PlayerExFolder As String
Dim CurrPlayer As String
CurrPlayer = Worksheets("Data").Range("F8").Value
PlayerExPath = Worksheets("Deal2Contract").Range("A17").Value & "\" & CurrPlayer
Worksheets("Deal2Contract").Range("A19").Value = PlayerExPath
Dim TypeExPath As String
Dim TypeExFolder As String
Dim CurrType As String
CurrType = Worksheets("Deal2Contract").Range("A7").Value
TypeExPath = Worksheets("Deal2Contract").Range("A19").Value & "\" & CurrType
Worksheets("Deal2Contract").Range("A21").Value = TypeExPath
If Worksheets("Deal2Contract").Range("A7").Value = "Weekly_Direct" Then
ElseIf Worksheets("Deal2Contract").Range("A7").Value = "Weekly_Loan" Then
ElseIf Worksheets("Deal2Contract").Range("A7").Value = "Daily_Direct" Then
EpiExFolder = Dir(EpiExPath, vbDirectory)
If EpiExFolder = vbNullString Then
VBA.FileSystem.MkDir (EpiExPath)
Else
End If
PlayerExFolder = Dir(PlayerExPath, vbDirectory)
If PlayerExFolder = vbNullString Then
VBA.FileSystem.MkDir (PlayerExPath)
Else
End If
TypeExFolder = Dir(TypeExPath, vbDirectory)
If TypeExFolder = vbNullString Then
VBA.FileSystem.MkDir (TypeExPath)
Else
End If
'dateExFolder = Dir(dateExPath, vbDirectory)
'If typeExFolder = vbNullString Then
' VBA.FileSystem.MkDir (currType)
'Else
'End If
ElseIf Worksheets("Deal2Contract").Range("A7").Value = "Daily_Loan" Then
End If
Worksheets("Deal2Contract").Range("A15").WrapText = False
Worksheets("Deal2Contract").Range("A17").WrapText = False
Worksheets("Deal2Contract").Range("A19").WrapText = False
Worksheets("Deal2Contract").Range("A21").WrapText = False
Worksheets("Deal2Contract").Range("A23").WrapText = False
Application.ScreenUpdating = True
End Sub

Excel VBA code error '1004' while searching external links

I need your help. I found the attached vba code but when I run the code I am getting a very strange 1004 error. Could you please give an explanation or try to fix this error?
Thank you so much all!
' Module to remove all hidden names on active workbook
Sub Remove_Hidden_Names()
' Dimension variables.
Dim xName As Variant
Dim Result As Variant
Dim Vis As Variant
' Loop once for each name in the workbook.
For Each xName In ActiveWorkbook.Names
'If a name is not visible (it is hidden)...
If xName.Visible = True Then
Vis = "Visible"
Else
Vis = "Hidden"
End If
' ...ask whether or not to delete the name.
Result = MsgBox(prompt:="Delete " & Vis & " Name " & _
Chr(10) & xName.Name & "?" & Chr(10) & _
"Which refers to: " & Chr(10) & xName.RefersTo, _
Buttons:=vbYesNo)
' If the result is true, then delete the name.
If Result = vbYes Then xName.Delete
' Loop to the next name.
Next xName
End Sub
These Excel built-in range names appear in the Excel name manager when using SUMIFS,IFERROR, COUNTIFS and other formulas.
There are a lot of ways around this, as suggested in the comments.
You can add either of these:
If Not xName.Name Like "_xlfn*" Then
'Or
If InStr(xName.Name, "_xlfn") = 0 Then
first thing in the loop (don't forget to close it), or something similar.
If you for some reason still want to see it, you can add it to the delete if:
If Result = vbYes And Not xName.Name Like "_xlfn*" Then xName.Delete

VBA - Macro to open a file based on part of text in cell

I've been trying to adapt a Macro I've used to open files which were referenced in an Excel Spreadsheet. However, this time around the reference in the Spreadsheet is prefaced with "work_" followed directly by the filename e.g.: work_1234
I am pretty sure I need to change something in the part of the code that is in bold but I do not know how to adapt ActiveCell.Text to only use the digits as the information needed to search the directories.
Dim directories(10) As String, fileName As String, i As Integer
directories(0) = "Users/username/folder/subfolder/goal/"
directories(1) = "Users/username/folder/subfolder/goal/1"
directories(2) = "Users/username/folder/subfolder/goal/2"
directories(3) = "Users/username/folder/subfolder/goal/3"
directories(4) = "/Users/username/folder/subfolder/goal/4"
i = 0
Do While i < 5
If ActiveCell.Text() = "" Then
Exit Do
End If
'look for the numbers in the active cell that will be used when searching the folders listed above'
**fileName = Dir(directories(i) & "*" & ActiveCell.Text() & "*", MacID("Macintosh HD"))**
'fileName = "Users/username/folder/subfolder/goal/1234.html"'
If fileName <> "" Then
CreateObject("Shell.Application").Open (fileName) 'directories(i) & fileName)
fileName = Dir()
Exit Do
End If
i = i + 1
Loop
End Sub
I hope managed to convey what I am trying to do and would be really grateful for any insights you could give me to help me with this. I'm not super familiar with VBA and after scouring the interwebs for what feels like forever I thought I'd check here to find some help!
welcome to SO.
You could get the value of a cell in this way:
MyValue = Worksheets("Sheet1").Range("C2").Value
MyValue = Worksheets("Sheet1").Cells(2,3).Value
Next, you could do this:
AnotherValue = Right(MyValue,Len(MyValue)-Len("work_"))
To get a quick start with VBA, try e.g. these free courses: https://homeandlearn.org/ or https://www.excel-pratique.com/en/
Good luck!

Using function to open and update values in external workbooks, but returning source errors

I've been using a function from another StackOverflow question (I'm SO sorry I can't find the original answer!) to help go through a number of cells in Column L that contains a formula that spits our a hyperlinked filepath. It is meant to open each one (workbook), update the values, then save and close the workbook before opening the next one. See below.
Sub List_UpdateAndSave()
Dim lr As Long
Dim i As Integer
Dim WBSsource As Workbook
Dim FileNames As Variant
Dim msg As String
' Update the individual credit models
With ThisWorkbook.Sheets("List")
lr = .Cells(.Rows.Count, "L").End(xlUp).Row
FileNames = .Range("L2:L" & lr).Value
End With
For i = LBound(FileNames, 1) To UBound(FileNames, 1)
On Error Resume Next
If FileNames(i, 1) Like "*.xls*" Then
Set WBSsource = Workbooks.Open(FileNames(i, 1), _
ReadOnly:=False, _
Password:="", _
UpdateLinks:=3)
If Err = 0 Then
With WBSsource
'do stuff here
.Save
.Close True
End With
Else
msg = msg & FileNames(i, 1) & Chr(10)
On Error GoTo 0
End If
End If
Set WBSsource = Nothing
Next i
If Len(msg) > 0 Then
MsgBox "The Following Files Could Not Be Opened" & _
Chr(10) & msg, 48, "Error"
End If
End Sub
The problem now is I am using this to work on a Network drive, and as a result it cause pathing issues with the Connections/Edit Links part. Each of the files are stored on S:\... which as a result of using the Hyperlink formula, won't be able to find the source data. See below the example image of a file that as been opened through a hyperlink cell from my original workbook. When I go to update the Edit Links section of it, it shows these errors.
If I open that lettered drive in Windows Explorer and find the file, it works with no problems. Open, Update Values > Save > Close, it says unknown...
(but if I click Update values here they update correctly.)
If opened using a Hyperlink formula in a cell (Also directing to S:\..) it says it contains links that cannot be updated. I choose to edit links and they're all "Error: Source not found". The location on them also starts off with \\\corp\... and not S:\.
Anyway to fix this? Apologies for the long winded question.
I'm adding this as an answer as it contains code and is a bit long for a comment.
I'm not sure if it's what you're after though.
The code will take the mapped drive and return the network drive, or visa-versa for Excel files. DriveMap is the variable containing the final string - you may want to adapt into a function.
Sub UpdatePath()
Dim oFSO As Object
Dim oDrv As Object
Dim FileName As String
Dim DriveMap As String
Set oFSO = CreateObject("Scripting.FileSystemObject")
FileName = Range("A1")
If InStr(oFSO.GetExtensionName(FileName), "xls") > 0 Then
For Each oDrv In oFSO.drives
If oDrv.sharename <> "" Then
'Changes \\corp\.... to S:\
If InStr(FileName, oDrv.sharename) = 1 Then
DriveMap = Replace(FileName, oDrv.sharename, oDrv.Path)
End If
'Changes S:\ to \\corp\....
' If InStr(FileName, oDrv.Path) = 1 Then
' DriveMap = Replace(FileName, oDrv.Path, oDrv.sharename)
' End If
End If
Next oDrv
End If
End Sub

Create a VBA version of dictionaries with 2 values per key

I am trying to make my excel macro dynamic. The excel macro essentially looks at only 2 columns, one which contains the name and the other contains the numeric part. I have my macro working perfectly, the only problem is that it is hard coded when I created the program. In my code, I hard coded the name in column 2 and the numeric part in column 3. However, that is not the case in real life. The name and numeric data could appear in column 1 and 5, for example. I've been manually rearranging the data in the columns so that it fits into what hard coded. However, I want to make this process dynamic and less manual work for the user.
There are 5 different versions of spreadsheets this macro will be used on and in each spreadsheet, the name and number columns are different. I am looking to make a user form box of some sort, where the user selects "Vendor XYZ" and since Vendor XYZ always sends their data sheets the same way I know that Vendor XYZ's name column is 2 and number is 4. So I was thinking that the dictionary would be something in the form of {Vendor XYZ: 2,4} (where the first number is the name column and the second number is the numeric columnnumber...I know the syntax is wrong)
I think my work around this would be to hard code the different vendors and then use if statements ( I haven't tried it yet)
I will have a user input/dropdown box of 5 different vendors. Then something like
If userinput="A"
then namecol=2 and numcol=1
If userinput="B"
then namecol="3" and numcol="4"
I don't know if that would even work. The problem with that is that the number of vendors is small now, but will be scaling up and I can't do that if we have 100 or 1000 vendors.
Any ideas?
Depending on how your initial dataset is retrieved, you can use something like this:
Public Function GetHeaderIndices(ByVal InputData As Variant) As Scripting.Dictionary
If IsEmpty(InputData) Then Exit Function
Dim HeaderIndices As Scripting.Dictionary
Set HeaderIndices = New Scripting.Dictionary
HeaderIndices.CompareMode = TextCompare
Dim i As Long
For i = LBound(InputData, 2) To UBound(InputData, 2)
If Not HeaderIndices.Exists(Trim(InputData(LBound(InputData, 1), i))) Then _
HeaderIndices.Add Trim(InputData(LBound(InputData, 1), i)), i
Next
Set GetHeaderIndices = HeaderIndices
End Function
This Function takes an array as an input and gives the user a dictionary with the indices of the headers from the input.
If you are smart (and I say this because too many users just don't use tables) you will have your data in a table, and you will have named that table. If you did, you could do something like this:
Sub DoSomething()
Dim MyData as Variant
MyData = ThisWorkbook.Worksheets("MyDataSheet").ListObjects("MyTableName").Range.Value
End Sub
So, if you data looked like this:
Foo Baz Bar
1 Car Apple
3 Van Orange
2 Truck Banana
The function would give you a dictionary like:
Keys Items
Foo 1
Baz 2
Bar 3
Then your subroutines could do something like this:
Sub DoEverything()
Dim MyData as Variant
MyData = ThisWorkbook.Worksheets("MyDataSheet").ListObjects("MyTableName").Range.Value
DoSomething(MyData)
End Sub
Sub DoSomething(ByRef MyData as Variant)
Dim HeaderIndices as Scripting.Dictionary
Set HeaderIndices = GetHeaderIndices(MyData)
Dim i as Long
' Loop through all the rows after the header row.
For i = LBound(MyData, 1) + 1 to Ubound(MyData, 1)
If MyData(i, HeaderIndices("Baz")) = "Truck" Then
?MyData(i, HeaderIndices("Foo"))
?MyData(i, HeaderIndices("Baz"))
?MyData(i, HeaderIndices("Bar"))
End If
Next
End Sub
This does require a reference to Scripting.Runtime so if you don't want to add a reference you will need to change any reference to As Scripting.Dictionary to As Object and any New Scripting.Dictionary to CreateObject("Scripting.Dictionary").
Alternatively, I use the following code module to take care of adding references programmatically for all my users:
Public Sub PrepareReferences()
If CheckForAccess Then
RemoveBrokenReferences
AddReferencebyGUID "{420B2830-E718-11CF-893D-00A0C9054228}"
End If
End Sub
Public Sub AddReferencebyGUID(ByVal ReferenceGUID As String)
Dim Reference As Variant
Dim i As Long
' Set to continue in case of error
On Error Resume Next
' Add the reference
ThisWorkbook.VBProject.References.AddFromGuid _
GUID:=ReferenceGUID, Major:=1, Minor:=0
' If an error was encountered, inform the user
Select Case Err.Number
Case 32813
' Reference already in use. No action necessary
Case vbNullString
' Reference added without issue
Case Else
' An unknown error was encountered, so alert the user
MsgBox "A problem was encountered trying to" & vbNewLine _
& "add or remove a reference in this file" & vbNewLine & "Please check the " _
& "references in your VBA project!", vbCritical + vbOKOnly, "Error!"
End Select
On Error GoTo 0
End Sub
Private Sub RemoveBrokenReferences()
' Reference is a Variant here since it requires an external reference.
' It isnt possible to ensure that the external reference is checked when this process runs.
Dim Reference As Variant
Dim i As Long
For i = ThisWorkbook.VBProject.References.Count To 1 Step -1
Set Reference = ThisWorkbook.VBProject.References.Item(i)
If Reference.IsBroken Then
ThisWorkbook.VBProject.References.Remove Reference
End If
Next i
End Sub
Public Function CheckForAccess() As Boolean
' Checks to ensure access to the Object Model is set
Dim VBP As Variant
If Val(Application.Version) >= 10 Then
On Error Resume Next
Set VBP = ThisWorkbook.VBProject
If Err.Number <> 0 Then
MsgBox "Please pay attention to this message." _
& vbCrLf & vbCrLf & "Your security settings do not allow this procedure to run." _
& vbCrLf & vbCrLf & "To change your security setting:" _
& vbCrLf & vbCrLf & " 1. Select File - Options - Trust Center - Trust Center Settings - Macro Settings." & vbCrLf _
& " 2. Place a checkmark next to 'Trust access to the VBA project object model.'" _
& vbCrLf & "Once you have completed this process, please save and reopen the workbook." _
& vbCrLf & "Please reach out for assistance with this process.", _
vbCritical
CheckForAccess = False
Err.Clear
Exit Function
End If
End If
CheckForAccess = True
End Function
And I have the following command in each Workbook_Open event (less than ideal, but only good solution I have so far)
Private Sub Workbook_Open()
PrepareReferences
End Sub

Resources