Skip to content

Commit 9dea58e

Browse files
RomanBachaloSigmaSoftwarekarissarjacobsenpaigesrossiconnorl-docusign
authored
Added responsive signing example (#64)
* update default api type * added the responsive signing example * updated path to html file * fixed sign_here tab * fixed imports * update tabs * wording * fixing wording Co-authored-by: Karissa Jacobsen <karissa.jacobsen@docusign.com> Co-authored-by: Paige Rossi <paige.rossi@docusign.com> Co-authored-by: Connor Lunsford <Connor.Lunsford@docusign.com>
1 parent 77e3869 commit 9dea58e

6 files changed

Lines changed: 261 additions & 1 deletion

File tree

app/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@
107107
app.register_blueprint(esignature_views.eg035)
108108
app.register_blueprint(esignature_views.eg036)
109109
app.register_blueprint(esignature_views.eg037)
110-
110+
app.register_blueprint(esignature_views.eg038)
111111
if "DYNO" in os.environ: # On Heroku?
112112
import logging
113113

Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
from os import path
2+
3+
from docusign_esign import (
4+
EnvelopesApi,
5+
RecipientViewRequest,
6+
Document,
7+
Signer,
8+
CarbonCopy,
9+
EnvelopeDefinition,
10+
Recipients,
11+
DocumentHtmlDefinition
12+
)
13+
from flask import session, url_for, request
14+
15+
from ...consts import authentication_method, demo_docs_path, pattern, signer_client_id
16+
from ...docusign import create_api_client
17+
18+
19+
class Eg038ResponsiveSigning:
20+
@staticmethod
21+
def get_args():
22+
"""Get request and session arguments"""
23+
# More data validation would be a good idea here
24+
# Strip anything other than characters listed
25+
# 1. Parse request arguments
26+
signer_email = pattern.sub("", request.form.get("signer_email"))
27+
signer_name = pattern.sub("", request.form.get("signer_name"))
28+
cc_email = pattern.sub("", request.form.get("cc_email"))
29+
cc_name = pattern.sub("", request.form.get("cc_name"))
30+
envelope_args = {
31+
"signer_email": signer_email,
32+
"signer_name": signer_name,
33+
"cc_email": cc_email,
34+
"cc_name": cc_name,
35+
"signer_client_id": signer_client_id,
36+
"ds_return_url": url_for("ds.ds_return", _external=True),
37+
"doc_file": path.join(demo_docs_path, "order_form.html")
38+
}
39+
args = {
40+
"account_id": session["ds_account_id"],
41+
"base_path": session["ds_base_path"],
42+
"access_token": session["ds_access_token"],
43+
"envelope_args": envelope_args
44+
}
45+
return args
46+
47+
@classmethod
48+
def worker(cls, args):
49+
"""
50+
1. Create the envelope request object
51+
2. Send the envelope
52+
3. Create the Recipient View request object
53+
4. Obtain the recipient_view_url for the embedded signing
54+
"""
55+
envelope_args = args["envelope_args"]
56+
# 1. Create the envelope request object
57+
envelope_definition = cls.make_envelope(envelope_args)
58+
59+
# 2. call Envelopes::create API method
60+
# Exceptions will be caught by the calling function
61+
api_client = create_api_client(base_path=args["base_path"], access_token=args["access_token"])
62+
63+
envelope_api = EnvelopesApi(api_client)
64+
results = envelope_api.create_envelope(account_id=args["account_id"], envelope_definition=envelope_definition)
65+
66+
envelope_id = results.envelope_id
67+
68+
# 3. Create the Recipient View request object
69+
recipient_view_request = RecipientViewRequest(
70+
authentication_method=authentication_method,
71+
client_user_id=envelope_args["signer_client_id"],
72+
recipient_id="1",
73+
return_url=envelope_args["ds_return_url"],
74+
user_name=envelope_args["signer_name"],
75+
email=envelope_args["signer_email"]
76+
)
77+
# 4. Obtain the recipient_view_url for the embedded signing
78+
# Exceptions will be caught by the calling function
79+
results = envelope_api.create_recipient_view(
80+
account_id=args["account_id"],
81+
envelope_id=envelope_id,
82+
recipient_view_request=recipient_view_request
83+
)
84+
85+
return {"envelope_id": envelope_id, "redirect_url": results.url}
86+
87+
@classmethod
88+
def make_envelope(cls, args):
89+
"""
90+
Creates envelope
91+
args -- parameters for the envelope:
92+
signer_email, signer_name, signer_client_id
93+
returns an envelope definition
94+
"""
95+
96+
html_definition = DocumentHtmlDefinition(
97+
source=cls.get_html_content(args)
98+
)
99+
100+
# Create the document model
101+
document = Document( # create the DocuSign document object
102+
html_definition=html_definition,
103+
name="doc1.html", # can be different from actual file name
104+
document_id=1 # a label used to reference the doc
105+
)
106+
107+
# Create the signer recipient model
108+
signer = Signer(
109+
# The signer
110+
email=args["signer_email"],
111+
name=args["signer_name"],
112+
recipient_id="1",
113+
routing_order="1",
114+
# Setting the client_user_id marks the signer as embedded
115+
client_user_id=args["signer_client_id"],
116+
role_name="Signer"
117+
)
118+
119+
cc = CarbonCopy(
120+
email=args["cc_email"],
121+
name=args["cc_name"],
122+
recipient_id="2",
123+
routing_order="2"
124+
)
125+
126+
# Next, create the top level envelope definition and populate it.
127+
envelope_definition = EnvelopeDefinition(
128+
email_subject="Example Signing Document",
129+
documents=[document],
130+
# The Recipients object wants arrays for each recipient type
131+
recipients=Recipients(signers=[signer], carbon_copies=[cc]),
132+
status="sent" # requests that the envelope be created and sent.
133+
)
134+
135+
return envelope_definition
136+
137+
@classmethod
138+
def get_html_content(cls, args):
139+
with open(args["doc_file"], "r") as file:
140+
doc_html = file.read()
141+
142+
return doc_html.replace("{signer_name}", args["signer_name"]) \
143+
.replace("{signer_email}", args["signer_email"]) \
144+
.replace("{cc_name}", args["cc_name"]) \
145+
.replace("{cc_email}", args["cc_email"]) \
146+
.replace("/sn1/", "<ds-signature data-ds-role=\"Signer\"/>") \
147+
.replace("/l1q/", "<input data-ds-type=\"number\"/>") \
148+
.replace("/l2q/", "<input data-ds-type=\"number\"/>")

app/eSignature/views/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,3 +34,4 @@
3434
from .eg035_scheduled_sending import eg035
3535
from .eg036_delayed_routing import eg036
3636
from .eg037_sms_delivery import eg037
37+
from .eg038_responsive_signing import eg038
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
"""Example 038: Responsive signing"""
2+
3+
from os import path
4+
5+
from docusign_esign.client.api_exception import ApiException
6+
from flask import render_template, redirect, Blueprint
7+
8+
from ..examples.eg038_responsive_signing import Eg038ResponsiveSigning
9+
from ...docusign import authenticate
10+
from ...ds_config import DS_CONFIG
11+
from ...error_handlers import process_error
12+
13+
eg = "eg038" # reference (and url) for this example
14+
eg038 = Blueprint("eg038", __name__)
15+
16+
17+
@eg038.route("/eg038", methods=["POST"])
18+
@authenticate(eg=eg)
19+
def embedded_signing():
20+
"""
21+
1. Get required arguments
22+
2. Call the worker method
23+
3. Redirect the user to the embedded signing
24+
"""
25+
try:
26+
# 1. Get required arguments
27+
args = Eg038ResponsiveSigning.get_args()
28+
# 2. Call the worker method
29+
results = Eg038ResponsiveSigning.worker(args)
30+
except ApiException as err:
31+
return process_error(err)
32+
33+
# 3. Redirect the user to the embedded signing
34+
# Don"t use an iFrame!
35+
# State can be stored/recovered using the framework"s session or a
36+
# query parameter on the returnUrl (see the makeRecipientViewRequest method)
37+
return redirect(results["redirect_url"])
38+
39+
40+
@eg038.route("/eg038", methods=["GET"])
41+
@authenticate(eg=eg)
42+
def get_view():
43+
"""responds with the form for the example"""
44+
return render_template(
45+
"eg038_responsive_signing.html",
46+
title="Responsive signing",
47+
source_file= "eg038_responsive_signing.py",
48+
source_url=DS_CONFIG["github_example_url"] + "eg038_responsive_signing.py",
49+
documentation=DS_CONFIG["documentation"] + eg,
50+
show_doc=DS_CONFIG["documentation"],
51+
signer_name=DS_CONFIG["signer_name"],
52+
signer_email=DS_CONFIG["signer_email"]
53+
)
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
<!-- extend base layout --> {% extends "base.html" %} {% block content %}
2+
3+
<h4>38. Create a signable HTML document</h4>
4+
<p>Demonstrates how to create an HTML document for responsive signing.</p>
5+
6+
{% if show_doc %}
7+
<p><a target='_blank' href='{{ documentation | safe }}'>Documentation</a> about this example.</p>
8+
{% endif %}
9+
10+
<p>API method used:
11+
<a target ='_blank' href="https://developers.docusign.com/docs/esign-rest-api/reference/envelopes/envelopes/create/">Envelopes::create</a> and
12+
<a target ='_blank' href="https://developers.docusign.com/docs/esign-rest-api/reference/envelopes/envelopeviews/createrecipient/">EnvelopeViews::createRecipient</a>.
13+
</p>
14+
15+
<p>
16+
View source file <a target="_blank" href="{{ source_url | safe }}">{{ source_file }}</a> on GitHub.
17+
</p>
18+
19+
<form class="eg" action="" method="post" data-busy="form">
20+
<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"
23+
aria-describedby="emailHelp" placeholder="pat@example.com" required
24+
value="{{ signer_email }}" />
25+
<small id="emailHelp" class="form-text text-muted">We'll never share your email with anyone else.</small>
26+
</div>
27+
<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 />
31+
</div>
32+
<div class="form-group">
33+
<label for="cc_email">CC Email</label>
34+
<input type="email" class="form-control" id="cc_email" name="cc_email"
35+
aria-describedby="emailHelp" placeholder="pat@example.com" required />
36+
<small id="emailHelp" class="form-text text-muted">The email for the cc recipient must be different from the signer's email.</small>
37+
</div>
38+
<div class="form-group">
39+
<label for="cc_name">CC Name</label>
40+
<input type="text" class="form-control" id="cc_name" placeholder="Pat Johnson" name="cc_name"
41+
required />
42+
</div>
43+
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}"/>
44+
<button type="submit" class="btn btn-docu">Submit</button>
45+
</form>
46+
47+
{% endblock %}

app/templates/home.html

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -364,6 +364,17 @@ <h4 id="example037">37. <a href="eg037">Request a signature by SMS delivery</a><
364364
href="https://developers.docusign.com/docs/esign-rest-api/reference/envelopes/envelopes/create/">Envelopes::create</a>.
365365
</p>
366366

367+
<h2>Responsive signing</h2>
368+
369+
<h4 id="example038">38. <a href="eg038">Create a signable HTML document</a></h4>
370+
<p>
371+
Demonstrates how to create an HTML document for responsive signing.
372+
<p>
373+
API methods used:
374+
<a target="_blank" href="https://developers.docusign.com/docs/esign-rest-api/reference/envelopes/envelopes/create/">Envelopes::create</a>,
375+
<a target='_blank'
376+
href="https://developers.docusign.com/docs/esign-rest-api/reference/envelopes/envelopeviews/createrecipient/">EnvelopeViews::createRecipient</a>.
377+
</p>
367378

368379
</div>
369380

0 commit comments

Comments
 (0)