Skip to content

Commit

Permalink
Merge pull request #135 from docusign/feature/focused-view-code-example
Browse files Browse the repository at this point in the history
Added focused view code example
  • Loading branch information
raileendr authored Oct 17, 2023
2 parents 1f639ab + eb22611 commit 5d1d778
Show file tree
Hide file tree
Showing 6 changed files with 325 additions and 0 deletions.
1 change: 1 addition & 0 deletions app/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@
app.register_blueprint(esignature_views.eg041)
app.register_blueprint(esignature_views.eg042)
app.register_blueprint(esignature_views.eg043)
app.register_blueprint(esignature_views.eg044)

if "DYNO" in os.environ: # On Heroku?
import logging
Expand Down
146 changes: 146 additions & 0 deletions app/eSignature/examples/eg044_focused_view.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
import base64
from os import path

from docusign_esign import EnvelopesApi, RecipientViewRequest, Document, Signer, EnvelopeDefinition, SignHere, Tabs, \
Recipients
from flask import session, url_for, request

from ...consts import authentication_method, demo_docs_path, pattern, signer_client_id
from ...docusign import create_api_client
from ...ds_config import DS_CONFIG


class Eg044FocusedViewController:
@staticmethod
def get_args():
"""Get request and session arguments"""
# More data validation would be a good idea here
# Strip anything other than characters listed
# 1. Parse request arguments
signer_email = pattern.sub("", request.form.get("signer_email"))
signer_name = pattern.sub("", request.form.get("signer_name"))
envelope_args = {
"signer_email": signer_email,
"signer_name": signer_name,
"signer_client_id": signer_client_id,
"ds_return_url": url_for("ds.ds_return", _external=True),
}
args = {
"account_id": session["ds_account_id"],
"base_path": session["ds_base_path"],
"access_token": session["ds_access_token"],
"envelope_args": envelope_args
}
return args

@classmethod
def worker(cls, args):
"""
1. Create the envelope request object
2. Send the envelope
3. Create the Recipient View request object
4. Obtain the recipient_view_url for the embedded signing
"""
#ds-snippet-start:eSign44Step3
envelope_args = args["envelope_args"]
# 1. Create the envelope request object
envelope_definition = cls.make_envelope(envelope_args)

# 2. call Envelopes::create API method
# Exceptions will be caught by the calling function
api_client = create_api_client(base_path=args["base_path"], access_token=args["access_token"])

envelope_api = EnvelopesApi(api_client)
results = envelope_api.create_envelope(account_id=args["account_id"], envelope_definition=envelope_definition)

envelope_id = results.envelope_id
#ds-snippet-end:eSign44Step3

# 3. Create the Recipient View request object
#ds-snippet-start:eSign44Step4
recipient_view_request = RecipientViewRequest(
authentication_method=authentication_method,
client_user_id=envelope_args["signer_client_id"],
recipient_id="1",
return_url=envelope_args["ds_return_url"],
user_name=envelope_args["signer_name"],
email=envelope_args["signer_email"],
frame_ancestors=["http://localhost:3000", "https://apps-d.docusign.com"],
message_origins=["https://apps-d.docusign.com"]
)
#ds-snippet-end:eSign44Step4

# 4. Obtain the recipient_view_url for the embedded signing
# Exceptions will be caught by the calling function

#ds-snippet-start:eSign44Step5
results = envelope_api.create_recipient_view(
account_id=args["account_id"],
envelope_id=envelope_id,
recipient_view_request=recipient_view_request
)

return {"envelope_id": envelope_id, "redirect_url": results.url}
#ds-snippet-end:eSign44Step5

@classmethod
#ds-snippet-start:eSign44Step2
def make_envelope(cls, args):
"""
Creates envelope
args -- parameters for the envelope:
signer_email, signer_name, signer_client_id
returns an envelope definition
"""

# document 1 (pdf) has tag /sn1/
#
# The envelope has one recipient.
# recipient 1 - signer
with open(path.join(demo_docs_path, DS_CONFIG["doc_pdf"]), "rb") as file:
content_bytes = file.read()
base64_file_content = base64.b64encode(content_bytes).decode("ascii")

# Create the document model
document = Document( # create the DocuSign document object
document_base64=base64_file_content,
name="Example document", # can be different from actual file name
file_extension="pdf", # many different document types are accepted
document_id=1 # a label used to reference the doc
)

# Create the signer recipient model
signer = Signer(
# The signer
email=args["signer_email"],
name=args["signer_name"],
recipient_id="1",
routing_order="1",
# Setting the client_user_id marks the signer as embedded
client_user_id=args["signer_client_id"]
)

# Create a sign_here tab (field on the document)
sign_here = SignHere(
# DocuSign SignHere field/tab
anchor_string="/sn1/",
anchor_units="pixels",
anchor_y_offset="10",
anchor_x_offset="20"
)

# 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 = Tabs(sign_here_tabs=[sign_here])

# Next, create the top level envelope definition and populate it.
envelope_definition = EnvelopeDefinition(
email_subject="Please sign this document sent from the Python SDK",
documents=[document],
# The Recipients object wants arrays for each recipient type
recipients=Recipients(signers=[signer]),
status="sent" # requests that the envelope be created and sent.
)

return envelope_definition
#ds-snippet-end:eSign44Step2
1 change: 1 addition & 0 deletions app/eSignature/views/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,3 +40,4 @@
from .eg041_cfr_embedded_signing import eg041
from .eg042_document_generation import eg042
from .eg043_shared_access import eg043
from .eg044_focused_view import eg044
63 changes: 63 additions & 0 deletions app/eSignature/views/eg044_focused_view.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
"""Example 044: Focused view"""

from docusign_esign.client.api_exception import ApiException
from flask import render_template, Blueprint, session

from ..examples.eg044_focused_view import Eg044FocusedViewController
from ...docusign import authenticate, ensure_manifest, get_example_by_number
from ...ds_config import DS_CONFIG
from ...error_handlers import process_error
from ...consts import API_TYPE

example_number = 44
api = API_TYPE["ESIGNATURE"]
eg = f"eg0{example_number}" # reference (and url) for this example
eg044 = Blueprint(eg, __name__)


@eg044.route(f"/{eg}", methods=["POST"])
@ensure_manifest(manifest_url=DS_CONFIG["example_manifest_url"])
@authenticate(eg=eg, api=api)
def embedded_signing():
"""
1. Get required arguments
2. Call the worker method
3. Redirect the user to the embedded signing
"""
try:
# 1. Get required arguments
args = Eg044FocusedViewController.get_args()
# 2. Call the worker method
results = Eg044FocusedViewController.worker(args)
except ApiException as err:
return process_error(err)

session["envelope_id"] = results["envelope_id"]
example = get_example_by_number(session["manifest"], example_number, api)

return render_template(
"eSignature/eg044_embed.html",
title=example["ExampleName"],
url=results["redirect_url"],
integration_key=DS_CONFIG["ds_client_id"]
)


@eg044.route(f"/{eg}", methods=["GET"])
@ensure_manifest(manifest_url=DS_CONFIG["example_manifest_url"])
@authenticate(eg=eg, api=api)
def get_view():
"""responds with the form for the example"""
example = get_example_by_number(session["manifest"], example_number, api)

return render_template(
"eSignature/eg044_focused_view.html",
title=example["ExampleName"],
example=example,
source_file="eg044_focused_view.py",
source_url=DS_CONFIG["github_example_url"] + "eg044_focused_view.py",
documentation=DS_CONFIG["documentation"] + eg,
show_doc=DS_CONFIG["documentation"],
signer_name=DS_CONFIG["signer_name"],
signer_email=DS_CONFIG["signer_email"]
)
83 changes: 83 additions & 0 deletions app/templates/eSignature/eg044_embed.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
<!-- extend base layout --> {% extends "base.html" %} {% block content %}

<br />
<h2>The document has been embedded with focused view.</h2>
<br />

<!--
//ds-snippet-start:eSign44Step6
-->

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Signing</title>
<style>
html,
body {
padding: 0;
margin: 0;
font: 13px Helvetica, Arial, sans-serif;
}

.docusign-agreement {
width: 75%;
height: 800px;
}
</style>
</head>
<body>
<div class="docusign-agreement" id="agreement"></div>
<script src="script.js"></script>
</body>
</html>

<p><a href="/">Continue</a></p>

<script src='https://docucdn-a.akamaihd.net/demo/1ds/libs/@embedded-js/core/latest/dist/bundle.js'></script>
<script>
window.DocuSign.loadDocuSign('{{ integration_key }}')
.then((docusign) => {
const signing = docusign.signing({
url: '{{ url }}',
displayFormat: 'focused',
style: {
/** High-level variables that mirror our existing branding APIs. Reusing the branding name here for familiarity. */
branding: {
primaryButton: {
/** Background color of primary button */
backgroundColor: '#333',
/** Text color of primary button */
color: '#fff',
}
},

/** High-level components we allow specific overrides for */
signingNavigationButton: {
finishText: 'You have finished the document! Hooray!',
position: 'bottom-center'
}
}
});

signing.on('ready', (event) => {
console.log('UI is rendered');
});

signing.on('sessionEnd', (event) => {
/** The event here denotes what caused the sessionEnd to trigger, such as signing_complete, ttl_expired etc../ **/
console.log('sessionend', event);
});

signing.mount('#agreement');
})
.catch((ex) => {
// Any configuration or API limits will be caught here
});</script>

<!--
//ds-snippet-end:eSign44Step6
-->

{% endblock %}
31 changes: 31 additions & 0 deletions app/templates/eSignature/eg044_focused_view.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<!-- extend base layout --> {% extends "base.html" %} {% block content %}

{% include 'example_info.html' %}

{% set signer_form_index = 0 %}
{% set signer_email_index = 0 %}
{% set signer_name_index = 1 %}

<form class="eg" action="" method="post" data-busy="form">
{% if 'FormName' in example['Forms'][signer_form_index] %}
<p>{{ example['Forms'][signer_form_index]['FormName'] | safe }}</p>
{% endif %}

<div class="form-group">
<label for="signer_email">{{ example['Forms'][signer_form_index]['Inputs'][signer_email_index]['InputName'] }}</label>
<input type="email" class="form-control" id="signer_email" name="signer_email"
aria-describedby="emailHelp" placeholder="{{ example['Forms'][signer_form_index]['Inputs'][signer_email_index]['InputPlaceholder'] }}" required
value="{{ signer_email }}">
<small id="emailHelp" class="form-text text-muted">{{ session['manifest']['SupportingTexts']['HelpingTexts']['EmailWontBeShared'] | safe}}</small>
</div>
<div class="form-group">
<label for="signer_name">{{ example['Forms'][signer_form_index]['Inputs'][signer_name_index]['InputName'] }}</label>
<input type="text" class="form-control" id="signer_name" placeholder="{{ example['Forms'][signer_form_index]['Inputs'][signer_name_index]['InputPlaceholder'] }}" name="signer_name"
value="{{ signer_name }}" required>
</div>
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}"/>
{% include 'submit_button.html' %}
</form>

{% endblock %}

0 comments on commit 5d1d778

Please sign in to comment.