scribla
asked on
View formula: Attachment names, only possible per document or possible per field?
This is my current formula, however I looking to see if I can get a result on specific fields rather than per document.
Either that or check to see if a rich text field (that would store the attachment) is null or not?
Either that or check to see if a rich text field (that would store the attachment) is null or not?
@If(@Contains(@AttachmentNames;".doc") & @Contains(@AttachmentNames;".xls");"1. DOC & XLS attached";
@Contains(@AttachmentNames;".DOC") & @Contains(@AttachmentNames;".XLS");"1. DOC & XLS attached";
@Contains(@AttachmentNames;".doc") & @Contains(@AttachmentNames;".XLS");"1. DOC & XLS attached";
@Contains(@AttachmentNames;".DOC") & @Contains(@AttachmentNames;".xls");"1. DOC & XLS attached";
@Contains(@AttachmentNames;".doc") ;"2. DOC Attached";
@Contains(@AttachmentNames;".DOC") ;"2. DOC Attached";
@Contains(@AttachmentNames;".xls") ;"3. XLS Attached";
@Contains(@AttachmentNames;".XLS") ;"3. XLS Attached";
"4. No Attachments")
ASKER
Thanks for that, I did that quite sometime ago and didn't consider using a varible, nor @lowercase!
Is there anyway I could do this? using LS or perhaps during field validation on the form I could report the contents (even if its just evaluted against null) in a hidden field I can then reference in my view formula?
Is there anyway I could do this? using LS or perhaps during field validation on the form I could report the contents (even if its just evaluted against null) in a hidden field I can then reference in my view formula?
ASKER CERTIFIED SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Bill is the file attachment maniac :)
Thanks! :)
It's one of my pet peaves, and one of the things that really confused me starting out with Notes.
It's one of my pet peaves, and one of the things that really confused me starting out with Notes.
ASKER
Thanks for the response!
Perhaps this would be simpler if I explained what I was trying to achieve?
I'm not really interested in what the files are called, or the extention. My old formula was the way it was because I felt it was my only option at the time. I am more interested into which RT field that the file has been attached into.
So if it make things any easier, I would be looking to update the hidden text field valves with a simple 0/1 or yes/no if the attachement existed in the relevant field or not. I'm looking to do this for 3 RT fields, however checking for the file extention would be a neat way of trying to prevent users from attaching the wrong doc type in the wrong field.
I'm hoping to check for this in 3 RT fields: Quotes (.doc) CSS (.xls) and Order Acknowledgement (.doc)
Failing that I could ask users to stick to a strict filenaming covention, that way I would not need to check the existance per RT field, I could just get the attachment names and catagorise those in the view.
Perhaps this would be simpler if I explained what I was trying to achieve?
I'm not really interested in what the files are called, or the extention. My old formula was the way it was because I felt it was my only option at the time. I am more interested into which RT field that the file has been attached into.
So if it make things any easier, I would be looking to update the hidden text field valves with a simple 0/1 or yes/no if the attachement existed in the relevant field or not. I'm looking to do this for 3 RT fields, however checking for the file extention would be a neat way of trying to prevent users from attaching the wrong doc type in the wrong field.
I'm hoping to check for this in 3 RT fields: Quotes (.doc) CSS (.xls) and Order Acknowledgement (.doc)
Failing that I could ask users to stick to a strict filenaming covention, that way I would not need to check the existance per RT field, I could just get the attachment names and catagorise those in the view.
That is exactly what my example does. The only difference is that I checked 2 fields where you have 3, and the names of the fields are different.
In my example, I wrote to a single hidden field (FileDisplay) which contained one of the three strings from your question.
It should be very simple to take my example and modify it slightly to meet your needs.
What part don't you understand?
In my example, I wrote to a single hidden field (FileDisplay) which contained one of the three strings from your question.
It should be very simple to take my example and modify it slightly to meet your needs.
What part don't you understand?
ASKER
Very sorry, I havent actually tried your example yet, as I am at home and don't have designer here.
After speed reading your post on a blackberry device, I came to the conclusion that your example did something different so, hence my post. Having read it again in a proper browser, it is much clearer!
Many thanks, I look forward to trying out your example in the morning.
After speed reading your post on a blackberry device, I came to the conclusion that your example did something different so, hence my post. Having read it again in a proper browser, it is much clearer!
Many thanks, I look forward to trying out your example in the morning.
ASKER
I am having a little trouble with the DocGetFileLocations function, I have never defined a function before in an application. Where do I put your code? I've looked at the Designer help, and I didn't really shed much light on the subject.
Thanks
Thanks
The best place for this code is in a script library so that you can reuse it.
To create a script library:
1. In the Domino Designer, open your database and goto "Shared Code \ Script Libraries".
2. From the "Script Libraries" view, click on the "New LotusScript Library" action.
3. Once the new library is open, you may see something like this:
Option Public
Option Declare
4. Remove the text in this window, and paste ALL of the code below (don't include the line numbers).
5. Save the library with a name you will remember (like "common_functions").
Now that we have a library, we can use it anywhere that LotusScript is allowed.
To use the library in your form:
1. Open your form in the Designer.
2. In the form's "Objects" window, locate the "(Options)" event, and make sure it looks like this:
Option Declare
Use "common_functions"
3. Save your form to make sure that the library is included properly. If there are any problems, the compiler will let you know (let me know too).
4. OK, now we have everything we need. The only thing left is to make use of the library by adding the PostSave code.
5. In the form's "Objects" window, locate the "Postsave" event, and make sure it looks like this:
Sub Postsave(Source As Notesuidocument)
' Save file metadata.
Dim doc As NotesDocument
Dim fileMap List As Variant
Dim hasDoc As Boolean
Dim hasXl As Boolean
Set doc = Source.Document
Call DocGetFileLocations(doc, fileMap)
If (Isempty(fileMap)) Then
doc.FileDisplay = "4. No Attachments"
Else
Forall fileNames In fileMap
Select Case Listtag(fileNames)
Case "WordDocs"
Forall fileName In fileNames
If (Right(Lcase(fileName), 4) = ".doc") Then
hasDoc = True
Exit Forall
End If
End Forall
Case "ExcelDocs"
Forall fileName In fileNames
If (Right(Lcase(fileName), 4) = ".xls") Then
hasXl = True
Exit Forall
End If
End Forall
End Select
End Forall
If (hasDoc And hasXl) Then
doc.FileDisplay = "1. DOC & XLS Attached"
Elseif (hasDoc) Then
doc.FileDisplay = "2. DOC Attached"
Elseif (hasXl) Then
doc.FileDisplay = "3. XLS Attached"
Else
doc.FileDisplay = "4. No Attachments"
End If
End If
Call doc.Save(True, False)
End Sub
6. That's it!
Here's what the Postsave code does.
First, DocGetFileLocations determines where all files are and builds a HashTable keyed by field name. If any V2 style attachments are found, it lists them under the "$DOCUMENT" key since they are not in any field.
Next, we check the HashTable (called fileMap) to see what was found. If fileMap is empty, there are no attachments; otherwise, we loop through the entries. In the Forall loop, "Listtag(fileNames)" is the name of the current field, and fileNames is an array of file names found in that field. The existence of a field name in the hashtable is enough to verify that there is a file attachment in that field, but this example goes one step further and checks the name of each file in each field.
I hope this helps.
To create a script library:
1. In the Domino Designer, open your database and goto "Shared Code \ Script Libraries".
2. From the "Script Libraries" view, click on the "New LotusScript Library" action.
3. Once the new library is open, you may see something like this:
Option Public
Option Declare
4. Remove the text in this window, and paste ALL of the code below (don't include the line numbers).
5. Save the library with a name you will remember (like "common_functions").
Now that we have a library, we can use it anywhere that LotusScript is allowed.
To use the library in your form:
1. Open your form in the Designer.
2. In the form's "Objects" window, locate the "(Options)" event, and make sure it looks like this:
Option Declare
Use "common_functions"
3. Save your form to make sure that the library is included properly. If there are any problems, the compiler will let you know (let me know too).
4. OK, now we have everything we need. The only thing left is to make use of the library by adding the PostSave code.
5. In the form's "Objects" window, locate the "Postsave" event, and make sure it looks like this:
Sub Postsave(Source As Notesuidocument)
' Save file metadata.
Dim doc As NotesDocument
Dim fileMap List As Variant
Dim hasDoc As Boolean
Dim hasXl As Boolean
Set doc = Source.Document
Call DocGetFileLocations(doc, fileMap)
If (Isempty(fileMap)) Then
doc.FileDisplay = "4. No Attachments"
Else
Forall fileNames In fileMap
Select Case Listtag(fileNames)
Case "WordDocs"
Forall fileName In fileNames
If (Right(Lcase(fileName), 4) = ".doc") Then
hasDoc = True
Exit Forall
End If
End Forall
Case "ExcelDocs"
Forall fileName In fileNames
If (Right(Lcase(fileName), 4) = ".xls") Then
hasXl = True
Exit Forall
End If
End Forall
End Select
End Forall
If (hasDoc And hasXl) Then
doc.FileDisplay = "1. DOC & XLS Attached"
Elseif (hasDoc) Then
doc.FileDisplay = "2. DOC Attached"
Elseif (hasXl) Then
doc.FileDisplay = "3. XLS Attached"
Else
doc.FileDisplay = "4. No Attachments"
End If
End If
Call doc.Save(True, False)
End Sub
6. That's it!
Here's what the Postsave code does.
First, DocGetFileLocations determines where all files are and builds a HashTable keyed by field name. If any V2 style attachments are found, it lists them under the "$DOCUMENT" key since they are not in any field.
Next, we check the HashTable (called fileMap) to see what was found. If fileMap is empty, there are no attachments; otherwise, we loop through the entries. In the Forall loop, "Listtag(fileNames)" is the name of the current field, and fileNames is an array of file names found in that field. The existence of a field name in the hashtable is enough to verify that there is a file attachment in that field, but this example goes one step further and checks the name of each file in each field.
I hope this helps.
Option Public
Option Declare
%INCLUDE "lsconst.lss"
Sub DocGetFileLocations(doc As NotesDocument, fileMap List As Variant)
'/**
' * Locates all file attachments in a document and returns the result in a hash table (list).
' * @param doc The document that contains the attachments.
' * @param fileList (Return) A hash table (list) used to store and return the results of this function.
' */
Dim allFiles As Variant ' all files embedded within the specified document regardless of which (if any) richtext field contains them.
Dim rtFiles As Variant ' files found embedded within any rich text field.
Erase fileMap ' clear the return param.
' Get all attachment names.
allFiles = ArrayTrimArray(Evaluate("@AttachmentNames", doc))
If (Isempty(allFiles)) Then Exit Sub
' Check all richtext items for embedded files.
Forall item In doc.Items
If (item.Type = RICHTEXT) Then
If (Not Isempty(item.EmbeddedObjects)) Then
Forall obj In item.EmbeddedObjects
fileMap(item.Name) = ArrayAdd(fileMap(item.Name), obj.Name)
rtFiles = ArrayAdd(rtFiles, obj.Name)
End Forall
End If
End If
End Forall
' Get the files that are not embedded within an item.
Forall file In allFiles
If (Not ArrayIsMember(rtFiles, file, False)) Then
fileMap("$DOCUMENT") = ArrayAdd(fileMap("$DOCUMENT"), file)
End If
End Forall
End Sub
Function ArrayAdd(source As Variant, values As Variant) As Variant
'/**
' * Appends one or more element to an array and returns the result as a third array.
' * Unlike ArrayAppend, this function supports scalar, null, and byte array arguments.
' * @param source The source array.
' * @param values The value or values to append to the array.
' * @return A new array containing all elements from <i>source</i> and <i>values</i>.
' */
Dim ta1 As Variant ' temp array
Dim ta2 As Variant ' temp array
' Check for empty arrays.
If (ArrayElements(values) = 0) Then
Redim ta1(0)
If (Isobject(source)) Then
Set ta1(0) = source
Elseif (Isarray(source)) Then
ta1 = source
Else
ta1(0) = source
End If
ArrayAdd = ta1
Exit Function
End If
If (ArrayElements(source) = 0) Then
Redim ta2(0)
If (Isobject(values)) Then
Set ta2(0) = values
Elseif (Isarray(values)) Then
ta2 = values
Else
ta2(0) = values
End If
ArrayAdd = ta2
Exit Function
End If
' Check for scalar values and objects.
If (Isarray(source)) Then
ta1 = source
Else
Dim td1 As Variant ' temp data
If (Isobject(source)) Then Set td1= source Else td1= source
Redim ta1(0)
If (Isobject(td1)) Then Set ta1(0) = td1 Else ta1(0) = td1
End If
If (Isarray(values)) Then
ta2 = values
Else
Dim td2 As Variant ' temp data
If (Isobject(values)) Then Set td2= values Else td2= values
Redim ta2(0)
If (Isobject(td2)) Then Set ta2(0) = td2 Else ta2(0) = td2
End If
' Check for byte arrays - Arrayappend throws Type Mismatch on Byte arrays in R7.
If ((Typename(ta1(0)) = "BYTE") Or (Typename(ta2(0)) = "BYTE")) Then
ArrayAdd = ArrayAppendEx(ta1, ta2)
Else
ArrayAdd = Arrayappend(ta1, ta2)
End If
End Function
Function ArrayAppendEx(a1 As Variant, a2 As Variant) As Variant
'/**
' * Appends one array to the end of another array and returns the result as a third array.
' * Unlike ArrayAppend, this function supports byte arrays.
' * @param a1 Any variant containing an array.
' * @param a2 Any variant containing an array.
' * @return A variant containing an array.
' */
Dim retval As Variant
Dim start As Long
Dim i As Long
retval = a1
start = Ubound(a1) + 1
Redim Preserve retval(start + Ubound(a2))
For i = 0 To Ubound(a2)
retval(start+i) = a2(i)
Next
ArrayAppendEx = retval
End Function
Function ArrayElements(source As Variant) As Long
'/**
' * Determines the number of elements in an array.
' * @param source The array to check.
' */
Select Case Datatype(source)
Case V_EMPTY, V_NULL
ArrayElements = 0
Exit Function
Case V_INTEGER, V_LONG, V_SINGLE, V_DOUBLE, V_CURRENCY, V_DATE, V_STRING, V_LSOBJ, V_PRODOBJ
ArrayElements = 1
Exit Function
Case V_BYTE, V_BOOLEAN
ArrayElements = 1
Exit Function
Case Else
If Isempty(source) Then
ArrayElements = 0
Exit Function
Else
ArrayElements = Ubound(source) - Lbound(source) + 1
End If
If (ArrayElements = 1) Then
Select Case Datatype(source(Lbound(source)))
Case V_EMPTY, V_NULL
ArrayElements = 0
Case V_LSOBJ, V_PRODOBJ
If (source(Lbound(source)) Is Nothing) Then ArrayElements = 0
End Select
End If
End Select
End Function
Function ArrayTrimArray(source As Variant) As Variant
'/**
' * Removes all empty elements from an array.
' * @param source The source array.
' * @return A new array that has all empty elements removed.
' */
Dim a As Variant
If (Not Isarray(source)) Then
ArrayTrimArray = source
Exit Function
End If
Forall value In source
If (Datatype(value) = V_STRING) Then
If (Trim(value) <> "") Then a = ArrayAdd(a, value)
Elseif (Not Isempty(value)) Then
a = ArrayAdd(a, value)
End If
End Forall
ArrayTrimArray = a
End Function
Function ArrayIsMember(source As Variant, values As Variant, Byval caseSensitive As Boolean) As Boolean
'/**
' * Searches an array for an exact value or values.
' * @param source The source array.
' * @param values The value(s) to search for. This can be a scalar, a string, or an array or values.
' * @param caseSensitive (Boolean) Indicates whether string matching should be case sensitive.
' * @return (Boolean) True if an exact value was found.
' */
Dim a1 As Variant
Dim a2 As Variant
Dim comp As Integer
ArrayIsMember = True
a1 = ArrayCreate(source)
a2 = ArrayCreate(values)
If (caseSensitive) Then comp = 0 Else comp = 1
' Search for any value in a2 that is present in a1.
Forall value1 In a1
Forall value2 In a2
If ((Datatype(value1) = V_STRING) And (Datatype(value2) = V_STRING)) Then
If (Strcompare(value1, value2, comp) = 0) Then Exit Function
Else
If (value1 = value2) Then Exit Function
End If
End Forall
End Forall
ArrayIsMember = False
End Function
Function ArrayCreate(source As Variant) As Variant
'/**
' * Creates an array from the source.
' * @param source An array or string containing a list of array elements separated by a comma.
' * @return A new array containing the elements found in the source.
' */
Dim result(0) As Variant
If (Isarray(source)) Then
ArrayCreate = source
Elseif (Isobject(source)) Then
Set result(0) = source
ArrayCreate = result
Elseif (Instr(1, source, ",", 0) <> 0) Then
ArrayCreate = Split(source, ",")
Else
result(0) = source
ArrayCreate = result
End If
End Function
ASKER
Thanks for such a full explaination. Sorry for the slow response, I'm being pulled in all directions here.
I'll knuckle down and give it go on monday. Have a good weekend.
I'll knuckle down and give it go on monday. Have a good weekend.
No problem. Let me know if you have any questions. I know this can seem overwhelming if you are not familiar with LotusScript, but it's the only way to do what you need.
ASKER
Thanks Bill, I had this working in less than 5 minutes!
I am going to go it alone with getting this to work exactly the way I want it (as the scope has changed) and hopefully learn a little LS on the way. Seems only fair to wrap this solution up and open another question should I run into trouble.
Thanks for your help and effort, absolutely first class!
I am going to go it alone with getting this to work exactly the way I want it (as the scope has changed) and hopefully learn a little LS on the way. Seems only fair to wrap this solution up and open another question should I run into trouble.
Thanks for your help and effort, absolutely first class!
ASKER
11/10! Thank you very much for your time, expertise and patience.
I can help clean up your current formula, though. This is faster, more accurate, and does the same thing:
Open in new window