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. 
 
 
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
 
  |