diff --git a/index.js b/index.js index 01ae8f0c..9489fa37 100755 --- a/index.js +++ b/index.js @@ -32,6 +32,7 @@ const express = require('express') , eg014 = require('./lib/examples/eg014CollectPayment') , eg015 = require('./lib/examples/eg015EnvelopeTabData') , eg016 = require('./lib/examples/eg016SetTabValues') + , eg017 = require('./lib/examples/eg017SetTemplateTabValues') , eg018 = require('./lib/examples/eg018EnvelopeCustomFieldData') ; @@ -138,6 +139,8 @@ let app = express() .post('/eg015', eg015.createController) .get('/eg016', eg016.getController) .post('/eg016', eg016.createController) + .get('/eg017', eg017.getController) + .post('/eg017', eg017.createController) .get('/eg018', eg018.getController) .post('/eg018', eg018.createController) ; diff --git a/lib/examples/eg017SetTemplateTabValues.js b/lib/examples/eg017SetTemplateTabValues.js new file mode 100644 index 00000000..466010e8 --- /dev/null +++ b/lib/examples/eg017SetTemplateTabValues.js @@ -0,0 +1,249 @@ +/** + * @file + * Example 017: Set template tab (field) values and an envelope custom field value + * @author DocuSign + */ + +const path = require('path') + , docusign = require('docusign-esign') + , validator = require('validator') + , dsConfig = require('../../ds_configuration.js').config + ; + +const eg017SetTemplateTabValues = exports + , eg = 'eg017' // This example reference. + , mustAuthenticate = '/ds/mustAuthenticate' + , minimumBufferMin = 3 + ; + + +/** + * Send envelope with a template + * @param {object} req Request obj + * @param {object} res Response obj + */ +eg017SetTemplateTabValues.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); + } + + if (!req.session.templateId) { + res.render('pages/examples/eg017SetTemplateTabValues', { + csrfToken: req.csrfToken(), + title: "Send envelope using a template", + templateOk: req.session.templateId, + sourceFile: path.basename(__filename), + sourceUrl: dsConfig.githubExampleUrl + path.basename(__filename), + documentation: dsConfig.documentation + eg, + showDoc: dsConfig.documentation + }); + } + + // 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) + , ccEmail = validator.escape(body.ccEmail) + , ccName = validator.escape(body.ccName) + , envelopeArgs = { + templateId: req.session.templateId, + signerEmail: signerEmail, + signerName: signerName, + signerClientId: 1000, + ccEmail: ccEmail, + ccName: ccName, + dsReturnUrl: dsConfig.appUrl + '/ds-return' + } + , args = { + accessToken: req.user.accessToken, + basePath: req.session.basePath, + accountId: req.session.accountId, + envelopeArgs: envelopeArgs + } + , results = null + ; + + try { + results = await eg017SetTemplateTabValues.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 + * @param {object} args + */ +// ***DS.worker.start ***DS.snippet.1.start +eg017SetTemplateTabValues.worker = async (args) => { + // 1. Create envelope definition + let envelopeArgs = args.envelopeArgs + , envelopeDefinition = makeEnvelope(envelopeArgs); + + // 2. Create the envelope + let dsApiClient = new docusign.ApiClient(); + dsApiClient.setBasePath(args.basePath); + dsApiClient.addDefaultHeader('Authorization', 'Bearer ' + args.accessToken); + let envelopesApi = new docusign.EnvelopesApi(dsApiClient) + , results = await envelopesApi.createEnvelope( + args.accountId, {envelopeDefinition: envelopeDefinition}) + , envelopeId = results.envelopeId + ; + + // 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}); + + // 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 from the template + * @function + * @param {Object} args + * @returns {Envelope} An envelope definition + * @private + */ +function makeEnvelope(args){ + // The envelope has two recipients. + // recipient 1 - signer + // recipient 2 - cc + // This method sets values for many of the template's tabs. + // It also adds a new tab, and adds a custom metadata field + + // create the envelope definition with the template id + let envelopeDefinition = docusign.EnvelopeDefinition.constructFromObject({ + templateId: args.templateId, status: 'sent' + }); + + // Set the values for the fields in the template + // List item + let list1 = docusign.List.constructFromObject({ + value: "green", documentId: "1", pageNumber: "1", tabLabel: "list"}); + + // Checkboxes + let check1 = docusign.Checkbox.constructFromObject({ + tabLabel: 'ckAuthorization', selected: "true"}) + , check3 = docusign.Checkbox.constructFromObject({ + tabLabel: 'ckAgreement', selected: "true"}); + // The NOde.js SDK has a bug so it cannot create a Number tab at this time. + //number1 = docusign.Number.constructFromObject({ + // tabLabel: "numbersOnly", value: '54321'}); + let radioGroup = docusign.RadioGroup.constructFromObject({ + groupName: "radio1", + // You only need to provide the radio entry for the entry you're selecting + radios: + [docusign.Radio.constructFromObject({value: "white", selected: "true"})] + }); + let text = docusign.Text.constructFromObject({ + tabLabel: "text", value: "Jabberwocky!"}); + + // We can also add a new tab (field) to the ones already in the template: + let textExtra = docusign.Text.constructFromObject({ + document_id: "1", page_number: "1", + x_position: "280", y_position: "172", + font: "helvetica", font_size: "size14", tab_label: "added text field", + height: "23", width: "84", required: "false", + bold: 'true', value: args.signerName, + locked: 'false', tab_id: 'name'}); + + // Pull together the existing and new tabs in a Tabs object: + let tabs = docusign.Tabs.constructFromObject({ + checkboxTabs: [check1, check3], // numberTabs: [number1], + radioGroupTabs: [radioGroup], textTabs: [text, textExtra], + listTabs: [list1] + }); + // Create the template role elements to connect the signer and cc recipients + // to the template + let signer = docusign.TemplateRole.constructFromObject({ + email: args.signerEmail, name: args.signerName, + roleName: 'signer', + clientUserId: args.signerClientId, // change the signer to be embedded + tabs: tabs // Set tab values + }); + // Create a cc template role. + let cc = docusign.TemplateRole.constructFromObject({ + email: args.ccEmail, name: args.ccName, + roleName: 'cc' + }); + // Add the TemplateRole objects to the envelope object + envelopeDefinition.templateRoles = [signer, cc]; + // Create an envelope custom field to save the our application's + // data about the envelope + let customField = docusign.TextCustomField.constructFromObject({ + name: 'app metadata item', + required: 'false', + show: 'true', // Yes, include in the CoC + value: '1234567'}) + , customFields = docusign.CustomFields.constructFromObject({ + textCustomFields: [customField]}); + envelopeDefinition.customFields = customFields; + + return envelopeDefinition; +} +// ***DS.snippet.2.end + +/** + * Form page for this application + */ +eg017SetTemplateTabValues.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/eg017SetTemplateTabValues', { + csrfToken: req.csrfToken(), + title: "Set template tab values", + templateOk: req.session.templateId, + 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); + } +} \ No newline at end of file diff --git a/views/pages/examples/eg017SetTemplateTabValues.ejs b/views/pages/examples/eg017SetTemplateTabValues.ejs index 64ec1e86..858d9ff0 100644 --- a/views/pages/examples/eg017SetTemplateTabValues.ejs +++ b/views/pages/examples/eg017SetTemplateTabValues.ejs @@ -22,13 +22,13 @@ radio button and checkbox tabs.

+ value="<%= locals.dsConfig.signerEmail %>"> We'll never share your email with anyone else.
+ value="<%= locals.dsConfig.signerName %>" required>
diff --git a/views/pages/index.ejs b/views/pages/index.ejs index 04890e8b..cd6c54cb 100644 --- a/views/pages/index.ejs +++ b/views/pages/index.ejs @@ -150,7 +150,7 @@ Envelopes::create.

-

17. Set template tab values

+

17. Set template tab values

This example sets the tab (field) values for a template being used by an envelope.

API method used: Envelopes::create.