Skip to content

Commit

Permalink
eg 16
Browse files Browse the repository at this point in the history
  • Loading branch information
LarryKlugerDS committed Dec 8, 2018
1 parent 015b797 commit 4a74c72
Show file tree
Hide file tree
Showing 6 changed files with 293 additions and 19 deletions.
Binary file added demo_documents/World_Wide_Corp_salary.docx
Binary file not shown.
3 changes: 3 additions & 0 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ const express = require('express')
, eg013 = require('./lib/examples/eg013AddDocToTemplate')
, eg014 = require('./lib/examples/eg014CollectPayment')
, eg015 = require('./lib/examples/eg015EnvelopeTabData')
, eg016 = require('./lib/examples/eg016SetTabValues')
, eg018 = require('./lib/examples/eg018EnvelopeCustomFieldData')
;

Expand Down Expand Up @@ -135,6 +136,8 @@ let app = express()
.post('/eg014', eg014.createController)
.get('/eg015', eg015.getController)
.post('/eg015', eg015.createController)
.get('/eg016', eg016.getController)
.post('/eg016', eg016.createController)
.get('/eg018', eg018.getController)
.post('/eg018', eg018.createController)
;
Expand Down
271 changes: 271 additions & 0 deletions lib/examples/eg016SetTabValues.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,271 @@
/**
* @file
* Example 016: Set optional and locked field values and an envelope custom field value
* @author DocuSign
*/

const path = require('path')
, fs = require('fs-extra')
, docusign = require('docusign-esign')
, validator = require('validator')
, dsConfig = require('../../ds_configuration.js').config
;

const eg016SetTabValues = exports
, eg = 'eg016' // This example reference.
, mustAuthenticate = '/ds/mustAuthenticate'
, minimumBufferMin = 3
, signerClientId = 1000 // The id of the signer within this application.
, dsReturnUrl = dsConfig.appUrl + '/ds-return'
, dsPingUrl = dsConfig.appUrl + '/' // Url that will be pinged by the DocuSign Signing Ceremony via Ajax
;


/**
* Create the envelope, the Signing Ceremony, and then redirect to the Signing Ceremony
* @param {object} req Request obj
* @param {object} res Response obj
*/
eg016SetTabValues.createController = async (req, res) => {
// Step 1. Check the token
// At this point we should have a good token. But we
// double-check here to enable a better UX to the user.
let tokenOK = req.dsAuthCodeGrant.checkToken(minimumBufferMin);
if (! tokenOK) {
req.flash('info', 'Sorry, you need to re-authenticate.');
// We could store the parameters of the requested operation
// so it could be restarted automatically.
// But since it should be rare to have a token issue here,
// we'll make the user re-enter the form data after
// authentication.
req.dsAuthCodeGrant.setEg(req, eg);
res.redirect(mustAuthenticate);
}

// Step 2. Call the worker method
let body = req.body
// Additional data validation might also be appropriate
, signerEmail = validator.escape(body.signerEmail)
, signerName = validator.escape(body.signerName)
, envelopeArgs = {
signerEmail: signerEmail,
signerName: signerName,
signerClientId: signerClientId,
dsReturnUrl: dsReturnUrl,
dsPingUrl: dsPingUrl
}
, args = {
accessToken: req.user.accessToken,
basePath: req.session.basePath,
accountId: req.session.accountId,
envelopeArgs: envelopeArgs
}
, results = null
;

try {
results = await eg016SetTabValues.worker (args)
}
catch (error) {
let errorBody = error && error.response && error.response.body
// we can pull the DocuSign error code and message from the response body
, errorCode = errorBody && errorBody.errorCode
, errorMessage = errorBody && errorBody.message
;
// In production, may want to provide customized error messages and
// remediation advice to the user.
res.render('pages/error', {err: error, errorCode: errorCode, errorMessage: errorMessage});
}
if (results) {
req.session.envelopeId = results.envelopeId; // Save for use by other examples
// which need an envelopeId
// Redirect the user to the Signing Ceremony
// Don't use an iFrame!
// State can be stored/recovered using the framework's session or a
// query parameter on the returnUrl (see the makeRecipientViewRequest method)
res.redirect(results.redirectUrl);
}
}


/**
* This function does the work of creating the envelope and the
* embedded Signing Ceremony
* @param {object} args
*/
// ***DS.worker.start ***DS.snippet.1.start
eg016SetTabValues.worker = async (args) => {
let dsApiClient = new docusign.ApiClient();
dsApiClient.setBasePath(args.basePath);
dsApiClient.addDefaultHeader('Authorization', 'Bearer ' + args.accessToken);
let envelopesApi = new docusign.EnvelopesApi(dsApiClient)
, results = null
, envelopeArgs = args.envelopeArgs;

// Step 1. Make the envelope request body
let envelope = makeEnvelope(envelopeArgs)

// Step 2. call Envelopes::create API method
// Exceptions will be caught by the calling function
results = await envelopesApi.createEnvelope(args.accountId, {envelopeDefinition: envelope});
let envelopeId = results.envelopeId;
console.log(`Envelope was created. EnvelopeId ${envelopeId}`);

// Step 3. create the recipient view, the Signing Ceremony
let viewRequest = docusign.RecipientViewRequest.constructFromObject({
returnUrl: envelopeArgs.dsReturnUrl,
authenticationMethod: 'none',
email: envelopeArgs.signerEmail,
userName: envelopeArgs.signerName,
clientUserId: envelopeArgs.signerClientId});

// Step 4. Call the CreateRecipientView API
// Exceptions will be caught by the calling function
results = await envelopesApi.createRecipientView(args.accountId, envelopeId,
{recipientViewRequest: viewRequest});

return ({envelopeId: envelopeId, redirectUrl: results.url})
}
// ***DS.worker.end ***DS.snippet.1.end

// ***DS.snippet.2.start
/**
* Creates envelope
* @function
* @param {Object} args parameters for the envelope:
* @returns {Envelope} An envelope definition
* @private
*/
function makeEnvelope(args){
// document 1 (docx) has tags
// /sn1/ - signature field
// /salary/ - yearly salary
// /legal/ - legal name
// /familiar/ - person's familiar name
//
// The envelope has one recipient.
// recipient 1 - signer
//
// The salary is set both as a readable number in the
// /salary/ text field, and as a pure number in a
// custom field ('salary') in the envelope.

// Salary that will be used.
let salary = 123000;

// read file from a local directory
// The read could raise an exception if the file is not available!
let demoDocsPath = path.resolve(__dirname, '../../demo_documents')
, docName = 'World_Wide_Corp_salary.docx'
, docBytes = fs.readFileSync(path.resolve(demoDocsPath, docName))
, doc1b64 = Buffer.from(docBytes).toString('base64')
// create the document model
, document = docusign.Document.constructFromObject({
documentBase64: doc1b64,
name: 'Lorem Ipsum', // can be different from actual file name
fileExtension: 'docx',
documentId: '1'
})
// Create a signer recipient to sign the document, identified by name and email
// We set the clientUserId to enable embedded signing for the recipient
, signer = docusign.Signer.constructFromObject({
email: args.signerEmail,
name: args.signerName,
clientUserId: args.signerClientId,
recipientId: 1
})
// Create signHere field (also known as tabs) on the document,
, signHere = docusign.SignHere.constructFromObject({
anchorString: '/sn1/', anchorUnits: 'pixels',
anchorYOffset: '10', anchorXOffset: '20'
})
// Create the legal and familiar text fields.
// Recipients can update these values if they wish to.
, textLegal = docusign.Text.constructFromObject({
anchorString: '/legal/', anchorUnits: 'pixels',
anchorYOffset: '-9', anchorXOffset: '5',
font: 'helvetica', fontSize: 'size11',
bold: 'true', value: args.signerName,
locked: 'false', tabId: 'legal_name',
tabLabel: 'Legal name'
})
, textFamiliar = docusign.Text.constructFromObject({
anchorString: '/familiar/', anchorUnits: 'pixels',
anchorYOffset: '-9', anchorXOffset: '5',
font: 'helvetica', fontSize: 'size11',
bold: 'true', value: args.signerName,
locked: 'false', tabId: 'familiar_name',
tabLabel: 'Familiar name'
})
// Create the salary field. It should be human readable, so
// add a comma before the thousands number, a currency indicator, etc.
, usFormat = new Intl.NumberFormat('en-US',
{style: 'currency', currency: 'USD', minimumFractionDigits: 0})
, salaryReadable = usFormat.format(salary)
, textSalary = docusign.Text.constructFromObject({
anchorString: '/salary/', anchorUnits: 'pixels',
anchorYOffset: '-9', anchorXOffset: '5',
font: 'helvetica', fontSize: 'size11',
bold: 'true', value: salaryReadable,
locked: 'true', // mark the field as readonly
tabId: 'salary', tabLabel: 'Salary'
})
;

// Add the tabs model (including the sign_here tab) to the signer.
// The Tabs object wants arrays of the different field/tab types
signer.tabs = docusign.Tabs.constructFromObject({
signHereTabs: [signHere],
textTabs: [textLegal, textFamiliar, textSalary]
});

// Create an envelope custom field to save the "real" (numeric)
// version of the salary
salaryCustomField = docusign.TextCustomField.constructFromObject({
name: 'salary',
required: 'false',
show: 'true', // Yes, include in the CoC
value: salary
});
customFields = docusign.CustomFields.constructFromObject({
textCustomFields: [salaryCustomField]
});

// Next, create the top level envelope definition and populate it.
envelopeDefinition = docusign.EnvelopeDefinition.constructFromObject({
emailSubject: "Please sign this salary document",
documents: [document],
// The Recipients object wants arrays for each recipient type
recipients: docusign.Recipients.constructFromObject({signers: [signer]}),
status: "sent", // requests that the envelope be created and sent.
customFields: customFields
});

return envelopeDefinition;
}
// ***DS.snippet.2.end


/**
* Form page for this application
*/
eg016SetTabValues.getController = (req, res) => {
// Check that the authentication token is ok with a long buffer time.
// If needed, now is the best time to ask the user to authenticate
// since they have not yet entered any information into the form.
let tokenOK = req.dsAuthCodeGrant.checkToken();
if (tokenOK) {
res.render('pages/examples/eg016SetTabValues', {
csrfToken: req.csrfToken(),
title: "Embedded Signing Ceremony",
sourceFile: path.basename(__filename),
sourceUrl: dsConfig.githubExampleUrl + path.basename(__filename),
documentation: dsConfig.documentation + eg,
showDoc: dsConfig.documentation
});
} else {
// Save the current operation so it will be resumed after authentication
req.dsAuthCodeGrant.setEg(req, eg);
res.redirect(mustAuthenticate);
}
}
6 changes: 3 additions & 3 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

12 changes: 6 additions & 6 deletions views/pages/examples/eg016SetTabValues.ejs
Original file line number Diff line number Diff line change
Expand Up @@ -18,16 +18,16 @@

<form class="eg" action="" method="post" data-busy="form">
<div class="form-group">
<label for="signer_email">Signer Email</label>
<input type="email" class="form-control" id="signer_email" name="signer_email"
<label for="signerEmail">Signer Email</label>
<input type="email" class="form-control" id="signerEmail" name="signerEmail"
aria-describedby="emailHelp" placeholder="pat@example.com" required
value="<%= signer_email %>">
value="<%= locals.dsConfig.signerEmail %>">
<small id="emailHelp" class="form-text text-muted">We'll never share your email with anyone else.</small>
</div>
<div class="form-group">
<label for="signer_name">Signer Name</label>
<input type="text" class="form-control" id="signer_name" placeholder="Pat Johnson" name="signer_name"
value="<%= signer_name %>" required>
<label for="signerName">Signer Name</label>
<input type="text" class="form-control" id="signerName" placeholder="Pat Johnson" name="signerName"
value="<%= locals.dsConfig.signerName %>" required>
</div>
<input type="hidden" name="_csrf" value="<%- csrfToken %>">
<button type="submit" class="btn btn-primary">Submit</button>
Expand Down
20 changes: 10 additions & 10 deletions views/pages/examples/eg017SetTemplateTabValues.ejs
Original file line number Diff line number Diff line change
Expand Up @@ -19,26 +19,26 @@ radio button and checkbox tabs.</p>
<% if (templateOk) { %>
<form class="eg" action="" method="post" data-busy="form">
<div class="form-group">
<label for="signer_email">Signer Email</label>
<input type="email" class="form-control" id="signer_email" name="signer_email"
<label for="signerEmail">Signer Email</label>
<input type="email" class="form-control" id="signerEmail" name="signerEmail"
aria-describedby="emailHelp" placeholder="pat@example.com" required
value="<%= signer_email %>">
value="<%= signerEmail %>">
<small id="emailHelp" class="form-text text-muted">We'll never share your email with anyone else.</small>
</div>
<div class="form-group">
<label for="signer_name">Signer Name</label>
<input type="text" class="form-control" id="signer_name" placeholder="Pat Johnson" name="signer_name"
value="<%= signer_name %>" required>
<label for="signerName">Signer Name</label>
<input type="text" class="form-control" id="signerName" placeholder="Pat Johnson" name="signerName"
value="<%= signerName %>" required>
</div>
<div class="form-group">
<label for="cc_email">CC Email</label>
<input type="email" class="form-control" id="cc_email" name="cc_email"
<label for="ccEmail">CC Email</label>
<input type="email" class="form-control" id="ccEmail" name="ccEmail"
aria-describedby="emailHelp" placeholder="pat@example.com" required
<small id="emailHelp" class="form-text text-muted">The email for the cc recipient must be different from the signer's email.</small>
</div>
<div class="form-group">
<label for="cc_name">CC Name</label>
<input type="text" class="form-control" id="cc_name" placeholder="Pat Johnson" name="cc_name"
<label for="ccName">CC Name</label>
<input type="text" class="form-control" id="ccName" placeholder="Pat Johnson" name="ccName"
required>
</div>
<input type="hidden" name="_csrf" value="<%- csrfToken %>">
Expand Down

0 comments on commit 4a74c72

Please sign in to comment.