Skip to content

Commit b148cf1

Browse files
committed
eg 1 and 2 working
1 parent 5b5134d commit b148cf1

24 files changed

+2951
-46
lines changed

app/eg001_embedded_signing.py

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,14 @@
22

33
from flask import render_template, url_for, redirect, session, flash, request
44
from os import path
5+
import json
56
from app import app, ds_config, views
67
import base64
7-
from docusign_esign import ApiClient, EnvelopesApi, EnvelopeDefinition, Signer, SignHere, Tabs, Recipients, Document, RecipientViewRequest
8+
import re
9+
from docusign_esign import *
10+
from docusign_esign.rest import ApiException
811

9-
eg = "eg001" # reference (and url) for this exampl
12+
eg = "eg001" # reference (and url) for this example
1013
signer_client_id = 1000 # Used to indicate that the signer will use an embedded
1114
# Signing Ceremony. Represents the signer's userId within
1215
# your application.
@@ -36,9 +39,11 @@ def create_controller():
3639
minimum_buffer_min = 3
3740
if views.ds_token_ok(minimum_buffer_min):
3841
# 2. Call the worker method
39-
# Data validation would be a good idea here
40-
signer_email = request.form.get('signer_email')
41-
signer_name = request.form.get('signer_name')
42+
# More data validation would be a good idea here
43+
# Strip anything other than characters listed
44+
pattern = re.compile('([^\w \-\@\.\,])+')
45+
signer_email = pattern.sub('', request.form.get('signer_email'))
46+
signer_name = pattern.sub('', request.form.get('signer_name'))
4247
envelope_args = {
4348
'signer_email': signer_email,
4449
'signer_name': signer_name,
@@ -54,16 +59,16 @@ def create_controller():
5459

5560
try:
5661
results = worker(args)
57-
except ImportError as error:
58-
error_body = error and 'response' in error and error['response']['body']
62+
except ApiException as err:
63+
error_body_json = err and hasattr(err, 'body') and err.body
5964
# we can pull the DocuSign error code and message from the response body
65+
error_body = json.loads(error_body_json)
6066
error_code = error_body and 'errorCode' in error_body and error_body['errorCode']
6167
error_message = error_body and 'message' in error_body and error_body['message']
62-
6368
# In production, may want to provide customized error messages and
6469
# remediation advice to the user.
6570
return render_template('error.html',
66-
err=error,
71+
err=err,
6772
error_code=error_code,
6873
error_message=error_message
6974
)

app/eg002_signing_via_email.py

Lines changed: 267 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,267 @@
1+
""" Example 002: Remote signer, cc, envelope has three documents """
2+
3+
from flask import render_template, url_for, redirect, session, flash, request
4+
from os import path
5+
from app import app, ds_config, views
6+
import base64
7+
import re
8+
import json
9+
from docusign_esign import *
10+
from docusign_esign.rest import ApiException
11+
12+
eg = "eg002" # reference (and url) for this example
13+
demo_docs_path = path.abspath(path.join(path.dirname(path.realpath(__file__)), 'static/demo_documents'))
14+
15+
16+
def controller():
17+
"""Controller router using the HTTP method"""
18+
if request.method == 'GET':
19+
return get_controller()
20+
elif request.method == 'POST':
21+
return create_controller()
22+
else:
23+
return render_template('404.html'), 404
24+
25+
26+
def create_controller():
27+
"""
28+
1. Check the token
29+
2. Call the worker method
30+
"""
31+
minimum_buffer_min = 3
32+
if views.ds_token_ok(minimum_buffer_min):
33+
# 2. Call the worker method
34+
# More data validation would be a good idea here
35+
# Strip anything other than characters listed
36+
pattern = re.compile('([^\w \-\@\.\,])+')
37+
signer_email = pattern.sub('', request.form.get('signer_email'))
38+
signer_name = pattern.sub('', request.form.get('signer_name'))
39+
cc_email = pattern.sub('', request.form.get('cc_email'))
40+
cc_name = pattern.sub('', request.form.get('cc_name'))
41+
envelope_args = {
42+
'signer_email': signer_email,
43+
'signer_name': signer_name,
44+
'cc_email': cc_email,
45+
'cc_name': cc_name,
46+
'status': 'sent',
47+
}
48+
args = {
49+
'account_id': session['ds_account_id'],
50+
'base_path': session['ds_base_path'],
51+
'ds_access_token': session['ds_access_token'],
52+
'envelope_args': envelope_args
53+
}
54+
55+
try:
56+
results = worker(args)
57+
except ApiException as err:
58+
error_body_json = err and hasattr(err, 'body') and err.body
59+
# we can pull the DocuSign error code and message from the response body
60+
error_body = json.loads(error_body_json)
61+
error_code = error_body and 'errorCode' in error_body and error_body['errorCode']
62+
error_message = error_body and 'message' in error_body and error_body['message']
63+
# In production, may want to provide customized error messages and
64+
# remediation advice to the user.
65+
return render_template('error.html',
66+
err=err,
67+
error_code=error_code,
68+
error_message=error_message
69+
)
70+
if results:
71+
session["envelope_id"] = results["envelope_id"] # Save for use by other examples
72+
# which need an envelopeId
73+
return render_template('example_done.html',
74+
title="Envelope sent",
75+
h1="Envelope sent",
76+
message=f"""The envelope has been created and sent!<br/>
77+
Envelope ID {results["envelope_id"]}."""
78+
)
79+
80+
else:
81+
flash('Sorry, you need to re-authenticate.')
82+
# We could store the parameters of the requested operation
83+
# so it could be restarted automatically.
84+
# But since it should be rare to have a token issue here,
85+
# we'll make the user re-enter the form data after
86+
# authentication.
87+
session['eg'] = url_for(eg)
88+
return redirect(url_for('ds_must_authenticate'))
89+
90+
91+
def worker(args):
92+
"""
93+
1. Create the envelope request object
94+
2. Send the envelope
95+
"""
96+
envelope_args = args["envelope_args"]
97+
# 1. Create the envelope request object
98+
envelope_definition = make_envelope(envelope_args)
99+
100+
# 2. call Envelopes::create API method
101+
# Exceptions will be caught by the calling function
102+
api_client = ApiClient()
103+
api_client.host = args['base_path']
104+
api_client.set_default_header("Authorization", "Bearer " + args['ds_access_token'])
105+
106+
envelope_api = EnvelopesApi(api_client)
107+
results = envelope_api.create_envelope(args['account_id'], envelope_definition=envelope_definition)
108+
109+
envelope_id = results.envelope_id
110+
app.logger.info(f'Envelope was created. EnvelopeId {envelope_id}')
111+
112+
return {'envelope_id': envelope_id}
113+
114+
115+
# ***DS.worker.end ***DS.snippet.1.end
116+
117+
118+
# ***DS.snippet.2.start
119+
def make_envelope(args):
120+
"""
121+
Creates envelope
122+
Document 1: An HTML document.
123+
Document 2: A Word .docx document.
124+
Document 3: A PDF document.
125+
DocuSign will convert all of the documents to the PDF format.
126+
The recipients' field tags are placed using <b>anchor</b> strings.
127+
"""
128+
129+
# document 1 (html) has sign here anchor tag **signature_1**
130+
# document 2 (docx) has sign here anchor tag /sn1/
131+
# document 3 (pdf) has sign here anchor tag /sn1/
132+
#
133+
# The envelope has two recipients.
134+
# recipient 1 - signer
135+
# recipient 2 - cc
136+
# The envelope will be sent first to the signer.
137+
# After it is signed, a copy is sent to the cc person.
138+
139+
# create the envelope definition
140+
env = EnvelopeDefinition(
141+
email_subject='Please sign this document set'
142+
)
143+
doc1_b64 = base64.b64encode(bytes(create_document1(args), 'utf-8')).decode('ascii')
144+
# read files 2 and 3 from a local directory
145+
# The reads could raise an exception if the file is not available!
146+
with open(path.join(demo_docs_path, ds_config.DS_CONFIG['doc_docx']), "rb") as file:
147+
doc2_docx_bytes = file.read()
148+
doc2_b64 = base64.b64encode(doc2_docx_bytes).decode('ascii')
149+
with open(path.join(demo_docs_path, ds_config.DS_CONFIG['doc_pdf']), "rb") as file:
150+
doc3_pdf_bytes = file.read()
151+
doc3_b64 = base64.b64encode(doc3_pdf_bytes).decode('ascii')
152+
153+
# Create the document models
154+
document1 = Document( # create the DocuSign document object
155+
document_base64=doc1_b64,
156+
name='Order acknowledgement', # can be different from actual file name
157+
file_extension='html', # many different document types are accepted
158+
document_id='1' # a label used to reference the doc
159+
)
160+
document2 = Document( # create the DocuSign document object
161+
document_base64=doc2_b64,
162+
name='Battle Plan', # can be different from actual file name
163+
file_extension='docx', # many different document types are accepted
164+
document_id='2' # a label used to reference the doc
165+
)
166+
document3 = Document( # create the DocuSign document object
167+
document_base64=doc3_b64,
168+
name='Lorem Ipsum', # can be different from actual file name
169+
file_extension='pdf', # many different document types are accepted
170+
document_id='3' # a label used to reference the doc
171+
)
172+
# The order in the docs array determines the order in the envelope
173+
env.documents = [document1, document2, document3]
174+
175+
176+
# Create the signer recipient model
177+
signer1 = Signer(
178+
email=args['signer_email'], name=args['signer_name'],
179+
recipient_id="1", routing_order="1"
180+
)
181+
# routingOrder (lower means earlier) determines the order of deliveries
182+
# to the recipients. Parallel routing order is supported by using the
183+
# same integer as the order for two or more recipients.
184+
185+
# create a cc recipient to receive a copy of the documents
186+
cc1 = CarbonCopy(
187+
email=args['cc_email'], name=args['cc_name'],
188+
recipient_id="2", routing_order="2")
189+
190+
# Create signHere fields (also known as tabs) on the documents,
191+
# We're using anchor (autoPlace) positioning
192+
#
193+
# The DocuSign platform searches throughout your envelope's
194+
# documents for matching anchor strings. So the
195+
# signHere2 tab will be used in both document 2 and 3 since they
196+
# use the same anchor string for their "signer 1" tabs.
197+
sign_here1 = SignHere(
198+
anchor_string = '**signature_1**', anchor_units = 'pixels',
199+
anchor_y_offset = '10', anchor_x_offset = '20')
200+
sign_here2 = SignHere(
201+
anchor_string = '/sn1/', anchor_units = 'pixels',
202+
anchor_y_offset = '10', anchor_x_offset = '20')
203+
204+
# Add the tabs model (including the sign_here tabs) to the signer
205+
# The Tabs object wants arrays of the different field/tab types
206+
signer1.tabs = Tabs(sign_here_tabs=[sign_here1, sign_here2])
207+
208+
# Add the recipients to the envelope object
209+
recipients = Recipients(signers=[signer1], carbon_copies=[cc1])
210+
env.recipients = recipients
211+
212+
# Request that the envelope be sent by setting |status| to "sent".
213+
# To request that the envelope be created as a draft, set to "created"
214+
env.status = args["status"]
215+
216+
return env
217+
218+
219+
# ***DS.snippet.3.start
220+
def create_document1(args):
221+
""" Creates document 1 -- an html document"""
222+
223+
return f"""
224+
<!DOCTYPE html>
225+
<html>
226+
<head>
227+
<meta charset="UTF-8">
228+
</head>
229+
<body style="font-family:sans-serif;margin-left:2em;">
230+
<h1 style="font-family: 'Trebuchet MS', Helvetica, sans-serif;
231+
color: darkblue;margin-bottom: 0;">World Wide Corp</h1>
232+
<h2 style="font-family: 'Trebuchet MS', Helvetica, sans-serif;
233+
margin-top: 0px;margin-bottom: 3.5em;font-size: 1em;
234+
color: darkblue;">Order Processing Division</h2>
235+
<h4>Ordered by {args['signer_name']}</h4>
236+
<p style="margin-top:0em; margin-bottom:0em;">Email: {args['signer_email']}</p>
237+
<p style="margin-top:0em; margin-bottom:0em;">Copy to: {args['cc_name']}, {args['cc_email']}</p>
238+
<p style="margin-top:3em;">
239+
Candy bonbon pastry jujubes lollipop wafer biscuit biscuit. Topping brownie sesame snaps sweet roll pie. Croissant danish biscuit soufflé caramels jujubes jelly. Dragée danish caramels lemon drops dragée. Gummi bears cupcake biscuit tiramisu sugar plum pastry. Dragée gummies applicake pudding liquorice. Donut jujubes oat cake jelly-o. Dessert bear claw chocolate cake gummies lollipop sugar plum ice cream gummies cheesecake.
240+
</p>
241+
<!-- Note the anchor tag for the signature field is in white. -->
242+
<h3 style="margin-top:3em;">Agreed: <span style="color:white;">**signature_1**/</span></h3>
243+
</body>
244+
</html>
245+
"""
246+
# ***DS.snippet.3.end
247+
248+
249+
def get_controller():
250+
"""responds with the form for the example"""
251+
252+
if views.ds_token_ok():
253+
return render_template("eg002_signing_via_email.html",
254+
title="Signing via email",
255+
source_file=path.basename(__file__),
256+
source_url=ds_config.DS_CONFIG['github_example_url'] + path.basename(__file__),
257+
documentation=ds_config.DS_CONFIG['documentation'] + eg,
258+
show_doc=ds_config.DS_CONFIG['documentation'],
259+
signer_name=ds_config.DS_CONFIG['signer_name'],
260+
signer_email=ds_config.DS_CONFIG['signer_email']
261+
)
262+
else:
263+
# Save the current operation so it will be resumed after authentication
264+
session['eg'] = url_for(eg)
265+
return redirect(url_for('ds_must_authenticate'))
266+
267+

0 commit comments

Comments
 (0)