1
+ /**
2
+ * @file
3
+ * Example 017: Set template tab (field) values and an envelope custom field value
4
+ * @author DocuSign
5
+ */
6
+
7
+ const path = require ( 'path' )
8
+ , docusign = require ( 'docusign-esign' )
9
+ , validator = require ( 'validator' )
10
+ , dsConfig = require ( '../../ds_configuration.js' ) . config
11
+ ;
12
+
13
+ const eg017SetTemplateTabValues = exports
14
+ , eg = 'eg017' // This example reference.
15
+ , mustAuthenticate = '/ds/mustAuthenticate'
16
+ , minimumBufferMin = 3
17
+ ;
18
+
19
+
20
+ /**
21
+ * Send envelope with a template
22
+ * @param {object } req Request obj
23
+ * @param {object } res Response obj
24
+ */
25
+ eg017SetTemplateTabValues . createController = async ( req , res ) => {
26
+ // Step 1. Check the token
27
+ // At this point we should have a good token. But we
28
+ // double-check here to enable a better UX to the user.
29
+ let tokenOK = req . dsAuthCodeGrant . checkToken ( minimumBufferMin ) ;
30
+ if ( ! tokenOK ) {
31
+ req . flash ( 'info' , 'Sorry, you need to re-authenticate.' ) ;
32
+ // We could store the parameters of the requested operation
33
+ // so it could be restarted automatically.
34
+ // But since it should be rare to have a token issue here,
35
+ // we'll make the user re-enter the form data after
36
+ // authentication.
37
+ req . dsAuthCodeGrant . setEg ( req , eg ) ;
38
+ res . redirect ( mustAuthenticate ) ;
39
+ }
40
+
41
+ if ( ! req . session . templateId ) {
42
+ res . render ( 'pages/examples/eg017SetTemplateTabValues' , {
43
+ csrfToken : req . csrfToken ( ) ,
44
+ title : "Send envelope using a template" ,
45
+ templateOk : req . session . templateId ,
46
+ sourceFile : path . basename ( __filename ) ,
47
+ sourceUrl : dsConfig . githubExampleUrl + path . basename ( __filename ) ,
48
+ documentation : dsConfig . documentation + eg ,
49
+ showDoc : dsConfig . documentation
50
+ } ) ;
51
+ }
52
+
53
+ // Step 2. Call the worker method
54
+ let body = req . body
55
+ // Additional data validation might also be appropriate
56
+ , signerEmail = validator . escape ( body . signerEmail )
57
+ , signerName = validator . escape ( body . signerName )
58
+ , ccEmail = validator . escape ( body . ccEmail )
59
+ , ccName = validator . escape ( body . ccName )
60
+ , envelopeArgs = {
61
+ templateId : req . session . templateId ,
62
+ signerEmail : signerEmail ,
63
+ signerName : signerName ,
64
+ signerClientId : 1000 ,
65
+ ccEmail : ccEmail ,
66
+ ccName : ccName ,
67
+ dsReturnUrl : dsConfig . appUrl + '/ds-return'
68
+ }
69
+ , args = {
70
+ accessToken : req . user . accessToken ,
71
+ basePath : req . session . basePath ,
72
+ accountId : req . session . accountId ,
73
+ envelopeArgs : envelopeArgs
74
+ }
75
+ , results = null
76
+ ;
77
+
78
+ try {
79
+ results = await eg017SetTemplateTabValues . worker ( args )
80
+ }
81
+ catch ( error ) {
82
+ let errorBody = error && error . response && error . response . body
83
+ // we can pull the DocuSign error code and message from the response body
84
+ , errorCode = errorBody && errorBody . errorCode
85
+ , errorMessage = errorBody && errorBody . message
86
+ ;
87
+ // In production, may want to provide customized error messages and
88
+ // remediation advice to the user.
89
+ res . render ( 'pages/error' , { err : error , errorCode : errorCode , errorMessage : errorMessage } ) ;
90
+ }
91
+ if ( results ) {
92
+ req . session . envelopeId = results . envelopeId ; // Save for use by other examples
93
+ // which need an envelopeId
94
+ // Redirect the user to the Signing Ceremony
95
+ // Don't use an iFrame!
96
+ // State can be stored/recovered using the framework's session or a
97
+ // query parameter on the returnUrl (see the makeRecipientViewRequest method)
98
+ res . redirect ( results . redirectUrl ) ;
99
+ }
100
+ }
101
+
102
+ /**
103
+ * This function does the work of creating the envelope
104
+ * @param {object } args
105
+ */
106
+ // ***DS.worker.start ***DS.snippet.1.start
107
+ eg017SetTemplateTabValues . worker = async ( args ) => {
108
+ // 1. Create envelope definition
109
+ let envelopeArgs = args . envelopeArgs
110
+ , envelopeDefinition = makeEnvelope ( envelopeArgs ) ;
111
+
112
+ // 2. Create the envelope
113
+ let dsApiClient = new docusign . ApiClient ( ) ;
114
+ dsApiClient . setBasePath ( args . basePath ) ;
115
+ dsApiClient . addDefaultHeader ( 'Authorization' , 'Bearer ' + args . accessToken ) ;
116
+ let envelopesApi = new docusign . EnvelopesApi ( dsApiClient )
117
+ , results = await envelopesApi . createEnvelope (
118
+ args . accountId , { envelopeDefinition : envelopeDefinition } )
119
+ , envelopeId = results . envelopeId
120
+ ;
121
+
122
+ // 3. create the recipient view, the Signing Ceremony
123
+ let viewRequest = docusign . RecipientViewRequest . constructFromObject ( {
124
+ returnUrl : envelopeArgs . dsReturnUrl ,
125
+ authenticationMethod : 'none' ,
126
+ email : envelopeArgs . signerEmail ,
127
+ userName : envelopeArgs . signerName ,
128
+ clientUserId : envelopeArgs . signerClientId } ) ;
129
+
130
+ // 4. Call the CreateRecipientView API
131
+ // Exceptions will be caught by the calling function
132
+ results = await envelopesApi . createRecipientView ( args . accountId , envelopeId ,
133
+ { recipientViewRequest : viewRequest } ) ;
134
+
135
+ return ( { envelopeId : envelopeId , redirectUrl : results . url } )
136
+ }
137
+ // ***DS.worker.end ***DS.snippet.1.end
138
+
139
+ // ***DS.snippet.2.start
140
+ /**
141
+ * Creates envelope from the template
142
+ * @function
143
+ * @param {Object } args
144
+ * @returns {Envelope } An envelope definition
145
+ * @private
146
+ */
147
+ function makeEnvelope ( args ) {
148
+ // The envelope has two recipients.
149
+ // recipient 1 - signer
150
+ // recipient 2 - cc
151
+ // This method sets values for many of the template's tabs.
152
+ // It also adds a new tab, and adds a custom metadata field
153
+
154
+ // create the envelope definition with the template id
155
+ let envelopeDefinition = docusign . EnvelopeDefinition . constructFromObject ( {
156
+ templateId : args . templateId , status : 'sent'
157
+ } ) ;
158
+
159
+ // Set the values for the fields in the template
160
+ // List item
161
+ let list1 = docusign . List . constructFromObject ( {
162
+ value : "green" , documentId : "1" , pageNumber : "1" , tabLabel : "list" } ) ;
163
+
164
+ // Checkboxes
165
+ let check1 = docusign . Checkbox . constructFromObject ( {
166
+ tabLabel : 'ckAuthorization' , selected : "true" } )
167
+ , check3 = docusign . Checkbox . constructFromObject ( {
168
+ tabLabel : 'ckAgreement' , selected : "true" } ) ;
169
+ // The NOde.js SDK has a bug so it cannot create a Number tab at this time.
170
+ //number1 = docusign.Number.constructFromObject({
171
+ // tabLabel: "numbersOnly", value: '54321' });
172
+ let radioGroup = docusign . RadioGroup . constructFromObject ( {
173
+ groupName : "radio1" ,
174
+ // You only need to provide the radio entry for the entry you're selecting
175
+ radios :
176
+ [ docusign . Radio . constructFromObject ( { value : "white" , selected : "true" } ) ]
177
+ } ) ;
178
+ let text = docusign . Text . constructFromObject ( {
179
+ tabLabel : "text" , value : "Jabberwocky!" } ) ;
180
+
181
+ // We can also add a new tab (field) to the ones already in the template:
182
+ let textExtra = docusign . Text . constructFromObject ( {
183
+ document_id : "1" , page_number : "1" ,
184
+ x_position : "280" , y_position : "172" ,
185
+ font : "helvetica" , font_size : "size14" , tab_label : "added text field" ,
186
+ height : "23" , width : "84" , required : "false" ,
187
+ bold : 'true' , value : args . signerName ,
188
+ locked : 'false' , tab_id : 'name' } ) ;
189
+
190
+ // Pull together the existing and new tabs in a Tabs object:
191
+ let tabs = docusign . Tabs . constructFromObject ( {
192
+ checkboxTabs : [ check1 , check3 ] , // numberTabs: [number1],
193
+ radioGroupTabs : [ radioGroup ] , textTabs : [ text , textExtra ] ,
194
+ listTabs : [ list1 ]
195
+ } ) ;
196
+ // Create the template role elements to connect the signer and cc recipients
197
+ // to the template
198
+ let signer = docusign . TemplateRole . constructFromObject ( {
199
+ email : args . signerEmail , name : args . signerName ,
200
+ roleName : 'signer' ,
201
+ clientUserId : args . signerClientId , // change the signer to be embedded
202
+ tabs : tabs // Set tab values
203
+ } ) ;
204
+ // Create a cc template role.
205
+ let cc = docusign . TemplateRole . constructFromObject ( {
206
+ email : args . ccEmail , name : args . ccName ,
207
+ roleName : 'cc'
208
+ } ) ;
209
+ // Add the TemplateRole objects to the envelope object
210
+ envelopeDefinition . templateRoles = [ signer , cc ] ;
211
+ // Create an envelope custom field to save the our application's
212
+ // data about the envelope
213
+ let customField = docusign . TextCustomField . constructFromObject ( {
214
+ name : 'app metadata item' ,
215
+ required : 'false' ,
216
+ show : 'true' , // Yes, include in the CoC
217
+ value : '1234567' } )
218
+ , customFields = docusign . CustomFields . constructFromObject ( {
219
+ textCustomFields : [ customField ] } ) ;
220
+ envelopeDefinition . customFields = customFields ;
221
+
222
+ return envelopeDefinition ;
223
+ }
224
+ // ***DS.snippet.2.end
225
+
226
+ /**
227
+ * Form page for this application
228
+ */
229
+ eg017SetTemplateTabValues . getController = ( req , res ) => {
230
+ // Check that the authentication token is ok with a long buffer time.
231
+ // If needed, now is the best time to ask the user to authenticate
232
+ // since they have not yet entered any information into the form.
233
+ let tokenOK = req . dsAuthCodeGrant . checkToken ( ) ;
234
+ if ( tokenOK ) {
235
+ res . render ( 'pages/examples/eg017SetTemplateTabValues' , {
236
+ csrfToken : req . csrfToken ( ) ,
237
+ title : "Set template tab values" ,
238
+ templateOk : req . session . templateId ,
239
+ sourceFile : path . basename ( __filename ) ,
240
+ sourceUrl : dsConfig . githubExampleUrl + path . basename ( __filename ) ,
241
+ documentation : dsConfig . documentation + eg ,
242
+ showDoc : dsConfig . documentation
243
+ } ) ;
244
+ } else {
245
+ // Save the current operation so it will be resumed after authentication
246
+ req . dsAuthCodeGrant . setEg ( req , eg ) ;
247
+ res . redirect ( mustAuthenticate ) ;
248
+ }
249
+ }
0 commit comments