Skip to content

Commit 4a74c72

Browse files
committed
eg 16
1 parent 015b797 commit 4a74c72

File tree

6 files changed

+293
-19
lines changed

6 files changed

+293
-19
lines changed
29.2 KB
Binary file not shown.

index.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ const express = require('express')
3131
, eg013 = require('./lib/examples/eg013AddDocToTemplate')
3232
, eg014 = require('./lib/examples/eg014CollectPayment')
3333
, eg015 = require('./lib/examples/eg015EnvelopeTabData')
34+
, eg016 = require('./lib/examples/eg016SetTabValues')
3435
, eg018 = require('./lib/examples/eg018EnvelopeCustomFieldData')
3536
;
3637

@@ -135,6 +136,8 @@ let app = express()
135136
.post('/eg014', eg014.createController)
136137
.get('/eg015', eg015.getController)
137138
.post('/eg015', eg015.createController)
139+
.get('/eg016', eg016.getController)
140+
.post('/eg016', eg016.createController)
138141
.get('/eg018', eg018.getController)
139142
.post('/eg018', eg018.createController)
140143
;

lib/examples/eg016SetTabValues.js

Lines changed: 271 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,271 @@
1+
/**
2+
* @file
3+
* Example 016: Set optional and locked field values and an envelope custom field value
4+
* @author DocuSign
5+
*/
6+
7+
const path = require('path')
8+
, fs = require('fs-extra')
9+
, docusign = require('docusign-esign')
10+
, validator = require('validator')
11+
, dsConfig = require('../../ds_configuration.js').config
12+
;
13+
14+
const eg016SetTabValues = exports
15+
, eg = 'eg016' // This example reference.
16+
, mustAuthenticate = '/ds/mustAuthenticate'
17+
, minimumBufferMin = 3
18+
, signerClientId = 1000 // The id of the signer within this application.
19+
, dsReturnUrl = dsConfig.appUrl + '/ds-return'
20+
, dsPingUrl = dsConfig.appUrl + '/' // Url that will be pinged by the DocuSign Signing Ceremony via Ajax
21+
;
22+
23+
24+
/**
25+
* Create the envelope, the Signing Ceremony, and then redirect to the Signing Ceremony
26+
* @param {object} req Request obj
27+
* @param {object} res Response obj
28+
*/
29+
eg016SetTabValues.createController = async (req, res) => {
30+
// Step 1. Check the token
31+
// At this point we should have a good token. But we
32+
// double-check here to enable a better UX to the user.
33+
let tokenOK = req.dsAuthCodeGrant.checkToken(minimumBufferMin);
34+
if (! tokenOK) {
35+
req.flash('info', 'Sorry, you need to re-authenticate.');
36+
// We could store the parameters of the requested operation
37+
// so it could be restarted automatically.
38+
// But since it should be rare to have a token issue here,
39+
// we'll make the user re-enter the form data after
40+
// authentication.
41+
req.dsAuthCodeGrant.setEg(req, eg);
42+
res.redirect(mustAuthenticate);
43+
}
44+
45+
// Step 2. Call the worker method
46+
let body = req.body
47+
// Additional data validation might also be appropriate
48+
, signerEmail = validator.escape(body.signerEmail)
49+
, signerName = validator.escape(body.signerName)
50+
, envelopeArgs = {
51+
signerEmail: signerEmail,
52+
signerName: signerName,
53+
signerClientId: signerClientId,
54+
dsReturnUrl: dsReturnUrl,
55+
dsPingUrl: dsPingUrl
56+
}
57+
, args = {
58+
accessToken: req.user.accessToken,
59+
basePath: req.session.basePath,
60+
accountId: req.session.accountId,
61+
envelopeArgs: envelopeArgs
62+
}
63+
, results = null
64+
;
65+
66+
try {
67+
results = await eg016SetTabValues.worker (args)
68+
}
69+
catch (error) {
70+
let errorBody = error && error.response && error.response.body
71+
// we can pull the DocuSign error code and message from the response body
72+
, errorCode = errorBody && errorBody.errorCode
73+
, errorMessage = errorBody && errorBody.message
74+
;
75+
// In production, may want to provide customized error messages and
76+
// remediation advice to the user.
77+
res.render('pages/error', {err: error, errorCode: errorCode, errorMessage: errorMessage});
78+
}
79+
if (results) {
80+
req.session.envelopeId = results.envelopeId; // Save for use by other examples
81+
// which need an envelopeId
82+
// Redirect the user to the Signing Ceremony
83+
// Don't use an iFrame!
84+
// State can be stored/recovered using the framework's session or a
85+
// query parameter on the returnUrl (see the makeRecipientViewRequest method)
86+
res.redirect(results.redirectUrl);
87+
}
88+
}
89+
90+
91+
/**
92+
* This function does the work of creating the envelope and the
93+
* embedded Signing Ceremony
94+
* @param {object} args
95+
*/
96+
// ***DS.worker.start ***DS.snippet.1.start
97+
eg016SetTabValues.worker = async (args) => {
98+
let dsApiClient = new docusign.ApiClient();
99+
dsApiClient.setBasePath(args.basePath);
100+
dsApiClient.addDefaultHeader('Authorization', 'Bearer ' + args.accessToken);
101+
let envelopesApi = new docusign.EnvelopesApi(dsApiClient)
102+
, results = null
103+
, envelopeArgs = args.envelopeArgs;
104+
105+
// Step 1. Make the envelope request body
106+
let envelope = makeEnvelope(envelopeArgs)
107+
108+
// Step 2. call Envelopes::create API method
109+
// Exceptions will be caught by the calling function
110+
results = await envelopesApi.createEnvelope(args.accountId, {envelopeDefinition: envelope});
111+
let envelopeId = results.envelopeId;
112+
console.log(`Envelope was created. EnvelopeId ${envelopeId}`);
113+
114+
// Step 3. create the recipient view, the Signing Ceremony
115+
let viewRequest = docusign.RecipientViewRequest.constructFromObject({
116+
returnUrl: envelopeArgs.dsReturnUrl,
117+
authenticationMethod: 'none',
118+
email: envelopeArgs.signerEmail,
119+
userName: envelopeArgs.signerName,
120+
clientUserId: envelopeArgs.signerClientId});
121+
122+
// Step 4. Call the CreateRecipientView API
123+
// Exceptions will be caught by the calling function
124+
results = await envelopesApi.createRecipientView(args.accountId, envelopeId,
125+
{recipientViewRequest: viewRequest});
126+
127+
return ({envelopeId: envelopeId, redirectUrl: results.url})
128+
}
129+
// ***DS.worker.end ***DS.snippet.1.end
130+
131+
// ***DS.snippet.2.start
132+
/**
133+
* Creates envelope
134+
* @function
135+
* @param {Object} args parameters for the envelope:
136+
* @returns {Envelope} An envelope definition
137+
* @private
138+
*/
139+
function makeEnvelope(args){
140+
// document 1 (docx) has tags
141+
// /sn1/ - signature field
142+
// /salary/ - yearly salary
143+
// /legal/ - legal name
144+
// /familiar/ - person's familiar name
145+
//
146+
// The envelope has one recipient.
147+
// recipient 1 - signer
148+
//
149+
// The salary is set both as a readable number in the
150+
// /salary/ text field, and as a pure number in a
151+
// custom field ('salary') in the envelope.
152+
153+
// Salary that will be used.
154+
let salary = 123000;
155+
156+
// read file from a local directory
157+
// The read could raise an exception if the file is not available!
158+
let demoDocsPath = path.resolve(__dirname, '../../demo_documents')
159+
, docName = 'World_Wide_Corp_salary.docx'
160+
, docBytes = fs.readFileSync(path.resolve(demoDocsPath, docName))
161+
, doc1b64 = Buffer.from(docBytes).toString('base64')
162+
// create the document model
163+
, document = docusign.Document.constructFromObject({
164+
documentBase64: doc1b64,
165+
name: 'Lorem Ipsum', // can be different from actual file name
166+
fileExtension: 'docx',
167+
documentId: '1'
168+
})
169+
// Create a signer recipient to sign the document, identified by name and email
170+
// We set the clientUserId to enable embedded signing for the recipient
171+
, signer = docusign.Signer.constructFromObject({
172+
email: args.signerEmail,
173+
name: args.signerName,
174+
clientUserId: args.signerClientId,
175+
recipientId: 1
176+
})
177+
// Create signHere field (also known as tabs) on the document,
178+
, signHere = docusign.SignHere.constructFromObject({
179+
anchorString: '/sn1/', anchorUnits: 'pixels',
180+
anchorYOffset: '10', anchorXOffset: '20'
181+
})
182+
// Create the legal and familiar text fields.
183+
// Recipients can update these values if they wish to.
184+
, textLegal = docusign.Text.constructFromObject({
185+
anchorString: '/legal/', anchorUnits: 'pixels',
186+
anchorYOffset: '-9', anchorXOffset: '5',
187+
font: 'helvetica', fontSize: 'size11',
188+
bold: 'true', value: args.signerName,
189+
locked: 'false', tabId: 'legal_name',
190+
tabLabel: 'Legal name'
191+
})
192+
, textFamiliar = docusign.Text.constructFromObject({
193+
anchorString: '/familiar/', anchorUnits: 'pixels',
194+
anchorYOffset: '-9', anchorXOffset: '5',
195+
font: 'helvetica', fontSize: 'size11',
196+
bold: 'true', value: args.signerName,
197+
locked: 'false', tabId: 'familiar_name',
198+
tabLabel: 'Familiar name'
199+
})
200+
// Create the salary field. It should be human readable, so
201+
// add a comma before the thousands number, a currency indicator, etc.
202+
, usFormat = new Intl.NumberFormat('en-US',
203+
{style: 'currency', currency: 'USD', minimumFractionDigits: 0})
204+
, salaryReadable = usFormat.format(salary)
205+
, textSalary = docusign.Text.constructFromObject({
206+
anchorString: '/salary/', anchorUnits: 'pixels',
207+
anchorYOffset: '-9', anchorXOffset: '5',
208+
font: 'helvetica', fontSize: 'size11',
209+
bold: 'true', value: salaryReadable,
210+
locked: 'true', // mark the field as readonly
211+
tabId: 'salary', tabLabel: 'Salary'
212+
})
213+
;
214+
215+
// Add the tabs model (including the sign_here tab) to the signer.
216+
// The Tabs object wants arrays of the different field/tab types
217+
signer.tabs = docusign.Tabs.constructFromObject({
218+
signHereTabs: [signHere],
219+
textTabs: [textLegal, textFamiliar, textSalary]
220+
});
221+
222+
// Create an envelope custom field to save the "real" (numeric)
223+
// version of the salary
224+
salaryCustomField = docusign.TextCustomField.constructFromObject({
225+
name: 'salary',
226+
required: 'false',
227+
show: 'true', // Yes, include in the CoC
228+
value: salary
229+
});
230+
customFields = docusign.CustomFields.constructFromObject({
231+
textCustomFields: [salaryCustomField]
232+
});
233+
234+
// Next, create the top level envelope definition and populate it.
235+
envelopeDefinition = docusign.EnvelopeDefinition.constructFromObject({
236+
emailSubject: "Please sign this salary document",
237+
documents: [document],
238+
// The Recipients object wants arrays for each recipient type
239+
recipients: docusign.Recipients.constructFromObject({signers: [signer]}),
240+
status: "sent", // requests that the envelope be created and sent.
241+
customFields: customFields
242+
});
243+
244+
return envelopeDefinition;
245+
}
246+
// ***DS.snippet.2.end
247+
248+
249+
/**
250+
* Form page for this application
251+
*/
252+
eg016SetTabValues.getController = (req, res) => {
253+
// Check that the authentication token is ok with a long buffer time.
254+
// If needed, now is the best time to ask the user to authenticate
255+
// since they have not yet entered any information into the form.
256+
let tokenOK = req.dsAuthCodeGrant.checkToken();
257+
if (tokenOK) {
258+
res.render('pages/examples/eg016SetTabValues', {
259+
csrfToken: req.csrfToken(),
260+
title: "Embedded Signing Ceremony",
261+
sourceFile: path.basename(__filename),
262+
sourceUrl: dsConfig.githubExampleUrl + path.basename(__filename),
263+
documentation: dsConfig.documentation + eg,
264+
showDoc: dsConfig.documentation
265+
});
266+
} else {
267+
// Save the current operation so it will be resumed after authentication
268+
req.dsAuthCodeGrant.setEg(req, eg);
269+
res.redirect(mustAuthenticate);
270+
}
271+
}

package-lock.json

Lines changed: 3 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

views/pages/examples/eg016SetTabValues.ejs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,16 +18,16 @@
1818

1919
<form class="eg" action="" method="post" data-busy="form">
2020
<div class="form-group">
21-
<label for="signer_email">Signer Email</label>
22-
<input type="email" class="form-control" id="signer_email" name="signer_email"
21+
<label for="signerEmail">Signer Email</label>
22+
<input type="email" class="form-control" id="signerEmail" name="signerEmail"
2323
aria-describedby="emailHelp" placeholder="pat@example.com" required
24-
value="<%= signer_email %>">
24+
value="<%= locals.dsConfig.signerEmail %>">
2525
<small id="emailHelp" class="form-text text-muted">We'll never share your email with anyone else.</small>
2626
</div>
2727
<div class="form-group">
28-
<label for="signer_name">Signer Name</label>
29-
<input type="text" class="form-control" id="signer_name" placeholder="Pat Johnson" name="signer_name"
30-
value="<%= signer_name %>" required>
28+
<label for="signerName">Signer Name</label>
29+
<input type="text" class="form-control" id="signerName" placeholder="Pat Johnson" name="signerName"
30+
value="<%= locals.dsConfig.signerName %>" required>
3131
</div>
3232
<input type="hidden" name="_csrf" value="<%- csrfToken %>">
3333
<button type="submit" class="btn btn-primary">Submit</button>

views/pages/examples/eg017SetTemplateTabValues.ejs

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -19,26 +19,26 @@ radio button and checkbox tabs.</p>
1919
<% if (templateOk) { %>
2020
<form class="eg" action="" method="post" data-busy="form">
2121
<div class="form-group">
22-
<label for="signer_email">Signer Email</label>
23-
<input type="email" class="form-control" id="signer_email" name="signer_email"
22+
<label for="signerEmail">Signer Email</label>
23+
<input type="email" class="form-control" id="signerEmail" name="signerEmail"
2424
aria-describedby="emailHelp" placeholder="pat@example.com" required
25-
value="<%= signer_email %>">
25+
value="<%= signerEmail %>">
2626
<small id="emailHelp" class="form-text text-muted">We'll never share your email with anyone else.</small>
2727
</div>
2828
<div class="form-group">
29-
<label for="signer_name">Signer Name</label>
30-
<input type="text" class="form-control" id="signer_name" placeholder="Pat Johnson" name="signer_name"
31-
value="<%= signer_name %>" required>
29+
<label for="signerName">Signer Name</label>
30+
<input type="text" class="form-control" id="signerName" placeholder="Pat Johnson" name="signerName"
31+
value="<%= signerName %>" required>
3232
</div>
3333
<div class="form-group">
34-
<label for="cc_email">CC Email</label>
35-
<input type="email" class="form-control" id="cc_email" name="cc_email"
34+
<label for="ccEmail">CC Email</label>
35+
<input type="email" class="form-control" id="ccEmail" name="ccEmail"
3636
aria-describedby="emailHelp" placeholder="pat@example.com" required
3737
<small id="emailHelp" class="form-text text-muted">The email for the cc recipient must be different from the signer's email.</small>
3838
</div>
3939
<div class="form-group">
40-
<label for="cc_name">CC Name</label>
41-
<input type="text" class="form-control" id="cc_name" placeholder="Pat Johnson" name="cc_name"
40+
<label for="ccName">CC Name</label>
41+
<input type="text" class="form-control" id="ccName" placeholder="Pat Johnson" name="ccName"
4242
required>
4343
</div>
4444
<input type="hidden" name="_csrf" value="<%- csrfToken %>">

0 commit comments

Comments
 (0)