« Flagging good Notes.net Forum posts | Main| Notes Developer Contract in Charlotte, NC »

Limiting Attachment Size Without the Wait

Category

Several threads in the Notes 4/5 Forum cover the topic of preventing the attachment of overly large files to a Notes document. The one major hurdle seems to be how to do so without first saving the document, let alone without first attaching the file. Without covering all the reasons why this is a challenge, I thought it might be helpful for me to detail the solution that I came up with after reading several posts in the forum and piecing everything together. I'm not sure but there may be a simpler way to achieve these goals in ND6, and I'd love to hear from anybody who might have found this useful or who might have suggestions for improvement...

Design Goals:

1 - Limit the allowable size of attachments (total size in this case, not file by file) to any specific document
2 - Since it can take an eternity to attach a file that may ultimately be disallowed (the longer the eternity, the greater the liklihood it will be refused), we ideally want to check the size of a file BEFORE it is attached to a document. This translates into the need to check the filesize of a file at the file system level.
3 - Preserve whatever user-friendliness that already exists in the attachment process (I know "What user-friendliness?"). In short, when we attach files, the open file dialog should continue to open to the directory we last accessed, and not always back to the Notes Data directory.

Steps:
1 - Create a subform called "Attachments"
2 - Put the following code in the Global Declarations:

Dim ws As NotesUIWorkspace
Dim Session As NotesSession
Dim db As NotesDatabase
Dim uidoc As NotesUIDocument
Dim doc As NotesDocument
Dim GlobalSaveFlag As Integer 'will be used to check whether document was actually saved later on

Declare Function NEMGetFile Lib "NNOTESWS" Alias "NEMGetFile" _
( zero As Integer, Byval filename As String, Byval filter As String, Byval title As String ) As Integer

3 - Put this function in the subform globals:

Function LocalBrowse(title As String, default As String, filter As String) As String
Dim filename As String*1024
filename = default
If filter = "" Then filter = "All Files|*.*|"
status% = NEMGetFile(0, filename, filter, title)

Select Case status%
Case 0 : LocalBrowse = "" ' cancelled
Case 1 : LocalBrowse = Trim$(filename)
Case Else : Error 1000, "Error &H" & Hex$(status%) & " in LocalBrowse"
End Select
End Function

4 - PostOpen Event:

'Need to initialize several global variables
Set ws = New NotesUIWorkspace
Set Session = New NotesSession
Set db = Session.currentdatabase
Set uidoc = Source
Set doc = uidoc.document

5 - PostRecalc (@Functions instead of script)

@If(AttachFileName = ""; @Return(""); "");
@Command([EditGotoField]; "Attachments");
@Command( [EditInsertFileAttachment] ; AttachFileName ; "0" );
FIELD AttachFileName := "";

6 - QuerySave:

Dim DBProfile As NotesDocument 'optional - if you want to be able to configure your size limit
Dim SizeLimit as Long

GlobalSaveFlag = Continue ' So that if anything else modifies Continue, it is detected.

Set DBProfile = db.GetProfileDocument("DBSettings") 'This is my generic DB Profile, but you can use whatever you want here

'Either use this Profile Document somehow to store the limit, or hard code the limit (in bytes) here
SizeLimit = DBProfile.AttachSizeLimit(0)
'SizeLimit = 1048576 '1 MB

'Let's add up the total size of existing attachments to see if it exceeds our limit
'This script should only be necessary if a user was sneeky and used a traditional "Attach"
'command from the file menu instead of the buttons for selecting a file
If doc.AttachmentSizeTotal(0) > SizeLimit Then
Msgbox "You have exceeded the 1 MB limit on attachments. " & Chr(10) & Chr(10) & _
"Please remove attachments or compress your file(s)." , 16, "Attachment Error"
Continue = False
Exit Sub
Else
Continue = True
End If

7 - QueryClose:

'Reset the AttachFileName field if we've left it filled and have saved the document
'Probably redundant but just in case
If GlobalSaveFlag = True And doc.AttachFileName(0) <> "" Then
doc.AttachFileName = ""
Call doc.Save(True, False)
End If

8 - Create a button labeled "Attach File" with the following lotuscript:

Sub Click(Source As Button)
Dim NewFilename As String
Dim currentpath As String
Dim DBProfile As NotesDocument
Dim verLen As Long
Dim SizeLimit as Long

Set DBProfile = db.GetProfileDocument("DBSettings") 'This is my generic DB Profile, but you can use whatever you want here

'Either create this Profile Document somehow to store the limit, or hard code the limit (in bytes) here
SizeLimit = DBProfile.AttachSizeLimit(0)
'SizeLimit = 1048576 '1 MB

NewFilename = LocalBrowse("Attach File", "", "")
doc.AttachFileName = NewFileName
currentpath = Strleftback(NewFilename, "\")

'This will reset the default browse directory to the one just searched
Call Session.SetEnvironmentVar( "FileDlgDirectory" , currentpath)

verLen& = Filelen(doc.AttachFileName(0)) 'the FileLen function is the real trick to all this

'Let's add up the total size of existing attachments AND the new file selected to see if it exceeds our limit
If (verLen& + doc.AttachmentSizeTotal(0)) > SizeLimit Then
Msgbox "This file will exceed the 1 MB limit on attachments. " & Chr(10) & Chr(10) & _
"Please remove attachments or compress your file(s)." , 16, "Attachment Error"
doc.AttachFileName = ""
Else
doc.AttachmentSizeTotal = doc.AttachmentSizeTotal(0) + verLen&
End If

Call uidoc.Refresh 'triggers the postrecalc event which will complete the attachment process

End Sub

9 - Create an editable text field called "AttachFileName". Don't enter any formulas, and hide the field from view.

10 - Create a hidden, computed number field called "AttachmentSizeTotal" with the following formula:

REM "Do not truly recalculate this field if we\'re in the middle of an attachment process";
tmp := @If(AttachFileName = ""; @Sum(@AttachmentLengths); AttachmentSizeTotal);
@If(@IsError(tmp); 0; tmp)

11 - Now create the actual Rich Text field that will store the attachments. Call this editable field "Attachments", and deselect the "show field delimiters" checkbox (this will allow you to make it "look" like a computed field to users and help prevent them from doing the normal "File - Attach..." routine that would subvert our goals.

That's all you need to do. Simple huh? . Assuming you've done it all correctly (and that I've described it correctly), the user should be able to use our button to select a file for attachment, and continue to click it until they've selected a file that will put them over the limit. At that point, the attachment operation will stop and they will see a warning message. So, the next time, your users try to attach a 100MB file, they won't have to wait until after lunch to find out they did a bad thing. If they somehow manage to attach files that exceed the limit using the normal "File - Attach..." approach, the querysave will catch that situation. So to recap, the three main "breakthroughs" in this approach are as follows:

1 - Use of the NEMGetFile API function to call a normal Open File dialog
2 - Writing a value for the most recent directory to the FileDlgDirectory environment variable in the Notes.ini
3 - Using the FileLen function to check the size of a file in the user's file system, given the filepath as input

Comments

1 - Thanks for the tip.. this was very useful.

Your Host

KevinPettitt.jpg
Kevin Pettitt View Kevin Pettitt's profile on LinkedIn

Tools I Use

Idea Jam

Subscribe to This Blog

 Full Posts  Comments

MyYahoo
netvibes Add to Netvibes

Contact

Hosted by

OpenNTF

Disclaimer

This site is in no way affiliated, endorsed, sanctioned, supported, nor blessed by Lotus Software nor IBM Corporation, nor any of my past or future clients (although they are welcome to do so). The opinions, theories, facts, etc. presented here are my own and in no way represent any official pronouncement by me on behalf of any other entity.

© 2005-2017 Kevin Pettitt - all rights reserved as listed below.

Creative Commons License
Unless otherwise labeled by its originating author, the content found on this site is made available under the terms of an Attribution / NonCommercial / ShareAlike