Changing Look of the Domino Upload Control (DUC) on a Domino Web Page/Form

Mindwatering Incorporated

Author: Tripp W Black

Created: 11/01/2018 at 08:22 PM

 

Category:
Notes Developer Tips
JavaScript

Task:
The standard Domino File Upload Control (FUC) is easy to use, but not pretty to use. Below is a solution that solves the problem w/o refactoring to an XPage. There is nothing wrong with doing that; but that was not desired when the rest of the app was classic "Notes" and "web".

This reference document are the basic steps we took to update our 2008 web Send Files form for the Mindwatering web site to a more modern look still using "classic" Domino web development. JQuery and AngularJS are NOT required for this solution. JavaScript support is still required.

Background:
We have a form called Send Files | Files.
It has a WebQuerySave agent called wqsSendFiles.

The Files form has an Email field, a Comments field called Body, and the FUC control. It also has a hidden field that stores the attachment used with the JS validation to confirm that both the Email field and the FUC have been populated. The WQS agent has three purposes: to confirm the Email field and the FUC were indeed populated, to perform notification to Mindwatering staff that an upload has been received, and to move the comments, followed by the attachments into the new/permanent RTF in a field called WP_Body.

Although with positioning of a RTF field with a control, with one immediately following the other, allows for the attachments to automatically go into the Body RTF field, we didn't want this layout restriction. That is why the WQS agent moves the attachment into a new RTF field. The attachment move uses the system/OS temp folder; it could use a NotesStream but we have not bothered to rewrite that part of the WQS agent.

The DUC and the common <input type="file" multiple> can upload more than one file if the multiple attribute is added to the input/FUC. We didn't do that because we still had some IE 8 and 9 -based enterprise clients who had not updated all their employee machines to Windows 7 and 10 until 2016. So this solution does NOT support IE 8, and may not support IE 9. It is tested with IE 11, Firefox, Chrome, and Safari, all on Mac, and IE via a Windows 7 or 10 VM.


App and Form Access Notes:
a. The app must allow Anonymous access to read/write public access documents.
b. The form also has anonymous set-up with access to create documents with the form
Instructions:
Designer client --> Forms (view) --> open Form -->
- Design (top menu) --> Form properties (top menu choice) --> security ( dialog tab) --> check Available to Public Access users checkbox
-. Still in the form, at the top or bottom, create a new "hidden" field, Create (top menu) --> Field (option),
- - name: $$PublicAccess
- - type: Computed when composed
- - value: enter a text-value 1 with quotes, e.g. "1".
- Save the form.
c. The server document, Security tab, Programmability Restrictions (section/heading) --> Sign or run restricted LotusScript/Java agents field contains this form's signer's name (or last saver of the form) in the field.


Send Files Form Updates:

1. Reduce number of File Upload Controls to just one.
The old form had 3 FUC elements, so the first thing to do was to removed the extra two. We also made some minor layout changes. Below are the Old and New layouts.
Hide details for Old Form:Old Form:
Old Form:

Hide details for New Form:New Form:
New Form:



For the File Upload Control, we the following properties set the the HTML tab:
ID: file1_1
Class: inputfile
Other: data-multiple-caption="{count} Selected" multiple

Notes:
The ID file1_1, is for the label to reference its control, and for the JavaScript to get the control.
The class inputfile, is for applying CSS. We apply CSS so that the control is basically hidden, but has a 0.1 width and height for tabbing.
The data-multiple-caption="{count} Selected" is for the label to be dynamically updated by the JavaScript
The single word multiple toggles the FUC to more than one attachment.


2. Add the new label to be our new user interface to the FUC, and a div to contain the list of files selected.
For the label and for displaying the list of files selected, we used pass-thru HTML.

<label for="file1_1"><span> Select Files </span></label> *

<div id="filelist" class="filelist"></div>

Note:
The for="file1_1" makes clicking the label "button" the same as clicking the old Browse button of the control.


3. Update the JS Header and the onLoad form events with the following:

--> Add to the JS Header:
//===============
// File upload
var inputs;

// Start of form validation
function validate() {
var f = document._Files;
// check for attachment name, add to FileUploadName field if exists, if not return error
if(f.file1_1.value == ""){
msg = "No file was attached.";
alert (msg);
return false;
} else {
f.FileUploadName.value = f.file1_1.value;
}
// check email field populated
var emailFld = document.getElementById("Email");
if (emailFld == null) {
var emailaddr = "";
msg = "Please enter your e-mail.";
alert (msg);
f.Email.focus();
return false;
} else {
var emailaddr = emailFld.value;
var estr = new String(emailaddr);
var atindex = estr.indexOf("@");
if(atindex ==-1) {
msg="Your e-mail address isn't in a valid format.";
alert (msg);
f.Email.focus();
return false;
} else {
// address okay
}
}

window.status='Document Submitted Successfully';
f.submit();
}
// End of Field Validation

--> Add to the onLoad:
;( function ( document, window, index ) {
var inputs = document.querySelectorAll( '.inputfile' );
Array.prototype.forEach.call( inputs, function( input )
{
var label = input.nextElementSibling,
labelVal = label.innerHTML;
input.addEventListener( 'change', function( e )
{
var fileNums = '';
var fileNms = '';
if( this.files ) {
fileNums = ( this.getAttribute( 'data-multiple-caption' ) || '' ).replace( '{count}', this.files.length );
for (var i = 0; i < this.files.length; i++ ) {
fileNms += this.files[i].name + '<br/>';
}
}
if( fileNums ) {
label.querySelector( 'span' ).innerHTML = fileNums;
document.getElementById('filelist').innerHTML = fileNms;
} else {
label.innerHTML = labelVal;
document.getElementById('filelist').innerHTML = '';
}
});
// Firefox bug fix
input.addEventListener( 'focus', function(){ input.classList.add( 'has-focus' ); });
input.addEventListener( 'blur', function(){ input.classList.remove( 'has-focus' ); });
});
}( document, window, 0 ));


4. The following elements remained unchanged:

- Email
Text - Editable
Default Value: ""
Input Translation: @Trim(@ThisValue);

- Body
RTF - Editable
ID: Body
Class fldBody

- FileUploadName
Text - Editable
Default Value: ""
Input Translation:
tmp:=@Trim(@ThisValue);
@If(@Contains(tmp; "\\"); @RightBack(tmp; "\\"); tmp)
HTML Attributes: "type=hidden"

- Send File(s) button
Name: btnSend
ID: btnSend
Common JavaScript: validate();


5. WebQuerySave agent remains unchanged:
Declare:
Option Public
Option Declare

Declarations:
Dim doc As NotesDocument ' user doc (current doc)

Sub Initialize
' this agent is used with files form to upload files to the CompanyEmail contact in this database
Dim s As New NotesSession
Dim db As NotesDatabase ' this db
' doc - dimmed globally
Dim opV As NotesView ' setup view
Dim opDoc As NotesDocument ' profile doc containing who to send memo
Dim objectItem As NotesItem ' the item containing the object (to copy to mDoc)
Dim companyemail As Variant ' name/email to send this document
Dim attachmentnm As String ' filename of the attachment
Dim readersLst(3) ' readers for web page
Dim rdrsItem As NotesItem ' readers author item

On Error Goto ErrorHandler

Set doc = s.DocumentContext
Set db = doc.ParentDatabase
Set opV = db.GetView("lupP")
Set opDoc = opV.GetFirstDocument

' get attachment's name
attachmentnm=doc.FileUploadName(0)
' check to see if there was an attachment
If attachmentnm="" Then
' we do not have one, return error
Print "<font face=Arial Size=3>Sorry<br><br>No attachment was found.</font>"
Exit Sub
Else
' we have it, we need to get it
Set objectItem=doc.GetFirstItem("$File")
End If
If ((objectItem Is Nothing)) Then
' cancel out - not attachment
Print "<font face=Arial Size=3>Error!<br><br>Unable to process attachment.</font>"
Exit Sub
End If
' check that we have the application settings
If (opDoc Is Nothing) Then
' cancel out - no profile created yet
Print "<font face=Arial Size=3>Error!<br><br>Unable to specify destination recipient.</font>"
Exit Sub
End If
' get the destination email accounts(s)
companyemail = doc.GetItemValue("CompanyEmail")
If (opDoc.SendAction(0) = "1" Or opDoc.SendAction(0) = "3") Then
' e-mail enabled
' remove the fields that will specifically cause issues
Call doc.RemoveItem("SaveOptions")
Call doc.RemoveItem("$$PublicAccess")
doc.SaveMessageOnSend=False
doc.Form = "Memo"
doc.Principal = doc.Email
doc.ReplyTo = doc.Email
doc.From = doc.Email
doc.Subject = doc.Title
doc.Body = doc.Body
doc.SendTo = companyemail(0)
' Send the built memo
doc.Send (False)
' put saveoptions back in with 0
doc.SaveOptions="0"
End If
If (opDoc.SendAction(0) = "2" Or opDoc.SendAction(0) = "3") Then
' save enabled - reset document and add page fields
Call doc.ReplaceItemValue("Form", "WP")
Call doc.RemoveItem("SaveOptions")
Call doc.RemoveItem("SaveMessageOnSend")
Call doc.RemoveItem("SendTo")
Call doc.RemoveItem("Subject")
Call doc.ReplaceItemValue("WP_SubTitleUI", "Upload from " & doc.Email(0))
Call doc.ReplaceItemValue("WP_Enabled", "0")
readersLst(0) = "LocalDomainAdmins"
readersLst(1) = "LocalDomainServers"
readersLst(2) = "[staffrep]"
readersLst(3) = "[Admin]"
Set rdrsItem = doc.ReplaceItemValue("Readers", readersLst)
rdrsItem.IsReaders = True
Call doc.ReplaceItemValue("ReadersList", "")
Call doc.ReplaceItemValue("DocType", "Page")
Call doc.ReplaceItemValue("StatsFlg", "No")
Call doc.ReplaceItemValue("WP_TitleUI", "Uploaded file from " & doc.Email(0) )
Call doc.ReplaceItemValue("HTMLCode", |<div name="wptitle" class="wptitle"><h1>Uploaded file from | & doc.Email(0) & |</h1></div><div name="wpsubtitle" class="wpsubtitle"><h2></h2></div>|)
' move attachments into Body
Call MoveAttachment(doc, "Body", "WP_Body")
End If

' send user back to a previous page
Dim vPath As Variant
'set URL path for return
vPath=Evaluate({@WebDbName})
Print "[" + vPath(0) + "/FilesSuccess?ReadForm" + "]"
Exit Sub

ErrorHandler:
Print "Sorry. An error has occurred. Please notify the administrator. Error: " & Err() & ": " & Error() & " on line number " & Str(Erl)
Exit Sub
End Sub

Function MoveAttachment(doc As notesDocument, oldFldNm As String, moveToFieldName As String) As Boolean
' Files attached via the Web with the File Upload Control are stored as old style "V2 Attachments".
' These files are displayed at the bottom of the Notes form rather than in a rich text field.
' This function moves V2 Attachments to a rich text field.
' old field name where attachment could be
' new field name where attachment is to be moved

Dim session As New notesSession
Dim tempDir As String
Dim rtItem As NotesRichTextItem
Dim tempItem As NotesItem
Dim rtffldtxt As String ' any text to copy over from old to new rtf

On Error GoTo FErrorHandler

MoveAttachment = False
tempDir = session.GetEnvironmentString("Directory", True)

' put a trailing slash at the end of the directory if it is needed.
If Instr(tempDir, "/") <> 0 And Right(tempDir, 1) <> "/" Then tempDir = tempDir & "/"
If Instr(tempDir, "\") <> 0 And Right(tempDir, 1) <> "\" Then tempDir = tempDir & "\"

' get any text from old rtf
Set tempItem = doc.GetFirstItem(oldFldNm)
If Not (tempItem Is Nothing) Then
rtffldtxt = tempItem.Text
Else
rtffldtxt = ""
End If
' create target RFT if not already existing
Set rtitem = New NotesRichTextItem(doc, moveToFieldName)
Call rtitem.Appendtext(rtffldtxt)
Call rtitem.AddNewLine(1, True)

' walk the items and extract the V2 files to the file system.
Forall item In doc.Items
If item.type = ATTACHMENT Then
' get the attachment.
Dim attachedFile As NotesEmbeddedObject
Set attachedFile = doc.GetAttachment(item.values(0))

' is the attachment already in the target RTF?
Dim object As NotesEmbeddedObject
Set object = rtitem.GetEmbeddedObject(attachedFile.Name)
If Not(object Is Nothing) Then
Goto NextItem
End If
' extract the attachment to the temp directory.
Dim filePath As String
filePath = tempDir & attachedFile.Name
Call attachedFile.ExtractFile(filePath)
' Remove the attachment.
Call attachedFile.Remove
' Embed the extracted files into the rich text field then remove the temp file.
Call rtItem.EmbedObject(1454, "", filePath)
Kill filePath
' return success
MoveAttachment = True

End If
NextItem:
End Forall

' kill old RFT if not same as new RTF
If Not (oldFldNm = moveToFieldName) Then
Call doc.Removeitem(oldFldNm)
End If

FExit:
Exit Function

FErrorHandler:
Print "Sorry. An error has occurred receiving attachment. Please notify the administrator. Error: " & Err() & ": " & Error() & " on line number " & Str(Erl)
Resume FExit
End Function


6. The CSS is loaded from a style sheet element.

It's loaded into the header via a JS Header @DbLookup which creates the following HTML:
<link rel="stylesheet" media="" title="SendFiles" type="text/css" href="/MWTransfer.nsf/webHH/sendfiles.css">

The CSS tags are below. The File Upload Control (FUC) his hidden via the it's class ID inputfile. The label "button" is styled via its inputfile + label CSS below.

body { font-family: futura-medium, futura, helvetica, arial, sans-serif; font-size: 10pt; }

.wptitle {padding-left: 0px;}.wpbodytable {margin-top: 20px;}

.inputfile { width: 90.1px; height: 90.1px; opacity: 0; overflow: hidden; position: absolute; z-index: -1;}

.inputfile + label { font-size: 14pt; color: #000000; border: solid 1px #604578; background-color: #d0c0f9; display: inline-block; cursor: pointer; padding: 8px;}

.inputfile:focus + label { outline: 1px dotted #000; outline: -webkit-focus-ring-color auto 5px;}

.inputfile + label:hover { background-color: #604578; color: #ffffff;}

.filelist { padding: 10px 0px 10px 0px; color: #553377;}

.fldEmail {width: 95%;}

.fldBody {width: 95%;}

#btnsend { margin-top: 10px; margin-bottom: 10px; min-width: 200px; font-size: 14pt; display: inline-block; cursor: pointer; padding: 8px;}


previous page