Trying to add custom fields to envelope definition from docusign rails example - ruby-on-rails

I got the embedded signing method I got from rails example on how to implement docusign embedded signing into your rails app.
I added a custom_fields object and added to the envelope object I created from the example
def embedded_signing
# base_url is the url of this application. Eg http://localhost:3000
base_url = request.base_url
user = HiringManager.find params[:hiring_manager_id]
# Fill in these constants
# Obtain an OAuth token from https://developers.hqtest.tst/oauth-token-generator
access_token = Token.access_token
# Obtain your accountId from demo.docusign.com -- the account id is shown in the drop down on the
# upper right corner of the screen by your picture or the default picture.
account_id = ENV["docusign_client_id"]
# Recipient Information:
signer_name = user.full_name
signer_email = user.email
base_path = 'http://demo.docusign.net/restapi'
client_user_id = user.id # Used to indicate that the signer will use an embedded
# Signing Ceremony. Represents the signer's userId within
# your application.
authentication_method = 'None' # How is this application authenticating
# the signer? See the `authenticationMethod' definition
file_name = 'agreement.pdf' # The document to be signed.
# Step 1. Create the envelope request definition
envelope_definition = DocuSign_eSign::EnvelopeDefinition.new
envelope_definition.email_subject = "Please sign this Newcraft Placement Agreement"
doc = DocuSign_eSign::Document.new({
:documentBase64 => Base64.encode64(File.binread(File.join('data', file_name))),
:name => "Agreement signed", :fileExtension => "pdf", :documentId => "1"})
# The order in the docs array determines the order in the envelope
envelope_definition.documents = [doc]
# create a signer recipient to sign the document, identified by name and email
# We're setting the parameters via the object creation
signer = DocuSign_eSign::Signer.new ({
:email => signer_email, :name => signer_name,
:clientUserId => client_user_id, :recipientId => 1
})
sign_here = DocuSign_eSign::SignHere.new ({
:documentId => '1', :pageNumber => '4',
:recipientId => '1', :tabLabel => 'SignHereTab',
:xPosition => '75', :yPosition => '70'
})
# Tabs are set per recipient / signer
tabs = DocuSign_eSign::Tabs.new({:signHereTabs => [sign_here]})
signer.tabs = tabs
# Add the recipients to the envelope object
recipients = DocuSign_eSign::Recipients.new({:signers => [signer]})
envelope_definition.recipients = recipients
# Add custom fields to the envelope object
custom_fields = DocuSign_eSign::CustomFieldV2.new({
:configuration_type => 'text', :required => 'true',
:name => 'date', :fieldId => '', :value => 'Todays date'
})
envelope_definition.custom_fields = custom_fields
# Request that the envelope be sent by setting |status| to "sent".
# To request that the envelope be created as a draft, set to "created"
envelope_definition.status = "sent"
# Step 2. Call DocuSign with the envelope definition to have the
# envelope created and sent
configuration = DocuSign_eSign::Configuration.new
configuration.host = base_path
api_client = DocuSign_eSign::ApiClient.new configuration
api_client.default_headers["Authorization"] = "Bearer " + access_token
envelopes_api = DocuSign_eSign::EnvelopesApi.new api_client
results = envelopes_api.create_envelope account_id, envelope_definition
envelope_id = results.envelope_id
# Step 3. create the recipient view request for the Signing Ceremony
view_request = DocuSign_eSign::RecipientViewRequest.new
# Set the url where you want the recipient to go once they are done signing
# should typically be a callback route somewhere in your app.
view_request.return_url = "https://juice.newcraft.io/edit-manager"
# How has your app authenticated the user? In addition to your app's
# authentication, you can include authenticate steps from DocuSign.
# Eg, SMS authentication
view_request.authentication_method = authentication_method
# Recipient information must match embedded recipient info
# we used to create the envelope.
view_request.email = signer_email
view_request.user_name = signer_name
view_request.client_user_id = client_user_id
# Step 4. call the CreateRecipientView API
results = envelopes_api.create_recipient_view account_id, envelope_id, view_request
user.signed_agreement = true
user.save
# Step 5. 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)
render json: results
rescue DocuSign_eSign::ApiError => e
#error_msg = e.response_body
render json: #error_msg
end
I am finding it difficult understanding how to insert a custom field that user can manually fill on the pdf agreement document that is displayed for users signature. I also know I need to add the position the custom field tab will reside which the documentation does not really explain how to add to an envelop object you create from a method.

First, let's try to see if we understand your requirement. You want user to fill in some data on the envelope and then collect this data in your application after the envelope is complete, is that correct?
To do that, you don't need custom fields. You can easily to that with regular tabs. Text tabs are probably the simplest way to do so. You add a text tab to your envelope, similar to how you added a SignHere tab and the user would have to fill in the text/value. You can then get this information using other API calls.
Here is the API call to obtain the tab value:
https://developers.docusign.com/esign-rest-api/reference/Envelopes/EnvelopeRecipientTabs/
You basically do a GET /v2.1/accounts/{accountId}/envelopes/{envelopeId}/recipients/{recipientId}/tabs if you are using the v2 or V2.1 API (just replace 2.1 with 2)

Related

Rails how to save data upon creation with an external API?

In my app for bike_rental_shops, I'm making it possible for these shops to manage their bike rentals.
Context
Bike rental companies also offer their bikes on website of external parties, therefore I'm connecting my Rails application with these external websites. I'm currently handling this in my controller when a user goes to the index page. Before loading the index page an API call is made to the external rental website and new bike rentals should be saved in the database.
Question
How to save only new rentals and not all rentals linked to a certain external rental website?
Current consideration
The only thing I can come up with is adding a database column with {external_website}_rental_id for a specific external website, so I can match them. However, this would mean that I need to add a seperate rental_id for every external rental website.
Code
rentals_controller.rb
def index
shop = Shop.find(params[:id])
request_rental_api
#bikes = shop.bikes
end
private
def request_rental_api
# set variables
base_url = "https://www.rentalwebsite.com"
url = "/rest/api/rentals"
token = 'TOKEN'
# init connection object
connection = Faraday.new(:url => base_url) do |c|
c.use Faraday::Request::UrlEncoded
c.use Faraday::Response::Logger
c.use FaradayMiddleware::FollowRedirects
c.adapter Faraday::Adapter::NetHttp
end
# send request
response = connection.get url do |request|
request.headers["Authorization"] = token
request.headers["Accept"] = "application/json"
end
bookings = JSON.parse(response.body['results'])
# check if rental is unique, and if so save it.
# Rental.create(????)
end
JSON output API
{
"next": null,
"previous": null,
"results": [
{
"activation_requested": false,
"id": 21664,
"slug": "rental-test"
#more information....
}
}]
you can create 2 columns
provider_rental_id id returned in response JSON
provider name of provider, to which request was made
Then to only create new records
rental_ids = bookings['results'].map { |r| r['id'] }
return if rental_ids.none?
existing_rental_ids = Rental.where(provider_rental_id: rental_ids, provider: 'Name of Provider').pluck(:provider_rental_id)
new_rental_ids = rental_ids - existing_rental_ids
new_rental_ids.each do |rental_id|
Rental.create(
provider: 'Name of Provider',
provider_rental_id: rental_id,
...
or if you are using rails 6 you can check upsert_all
Note: It does not instantiate any models nor does it trigger Active Record callbacks or validations.
Additionally try to move this into a background cronjob

Giving up on Google Drive API - Server to Server oauth2

Just as I thought that I "hacked" this ... I realize that I don't understand it anyway.
Important note:
I want to use "server-to-server approved" access so I can avoid Google's crazy approval nightmare for all operations on google drive. My users hate having to approve the script actions on every new copy of my master spreadsheet that we create.
I am trying to use DriveV3 - gem 'google-api-client', '0.9.11'
And I am trying to get authentication working.
I have managed all the initial setup of both console.xxx.xx and admin.google.xxx
So now I have a .p12 file with a Server-to-server approved credentials.
The following code has been used to get a service:
def self.service_client
keypath = Rails.root.join('config','xxxxxxxx.p12').to_s
#client = Google::Apis::DriveV3::DriveService.new
#client.client_options.application_name = "Rails2"
#client.authorization = Signet::OAuth2::Client.new(
:token_credential_uri => 'https://accounts.google.com/o/oauth2/token',
:audience => 'https://accounts.google.com/o/oauth2/token', :scope => "https://www.googleapis.com/auth/drive",
:issuer => "rails2#autoa-automations.iam.gserviceaccount.com",
:signing_key => Google::APIClient::KeyUtils.load_from_pkcs12(keypath, "yyyy"))
#client.authorization.fetch_access_token!
end
I did this in my before_filter:
session[:google_token] = GoogleDrive::GoogleDocs.service_client
And then use it something like ...
def list_google_docs
gtoken = session[:google_token]
google_session = Google::Apis::DriveV3::DriveService.new
google_session.authorization = gtoken
#google_docs = []
page_token = nil
begin
(myfiles, page_token) = google_session.list_files(page_size: 10, q: "'MYROOTFOLDERID' in parents", fields: 'files(id, mimeType, name), nextPageToken')
#for file in google_session.drive.list_files(page_size: 10, fields: 'nextPageToken, files(id, mime name)')
myfiles.files.each do |file|
filename = file.name
if filename.include?('body')
#google_docs << filename
end
end
end while page_token
end
But I keep getting
dailyLimitExceededUnreg: Daily Limit for Unauthenticated Use Exceeded. Continued use requires signup.
It worked initially ...
Anyone who can help?
I simply don't get the hole Google authentication approach ... I have tried a lot but this is too complicated for me ...

Ruby on Rails : Add multiple signatures on a PDF

I would like to digital sign several times (twice would be fine) a PDF using Ruby on Rails.
I have tried using Origami Gem which works great for a single signature (thank you MrWater for your very helpful post: to Insert digital signature into existing pdf file)
However, I can't sign twice the document. When I do, using the same method, my pdf file displays a signature error (signature invalid).
Do you have any idea of how to make that work using Origami or any other Ruby Gem?
Thank you in advance for your help.
Update 2015-10-25:
You will find below my code to sign a document (maybe it can help to find a solution to the problem, and at least it shows you how to make a single signature, which works quite fine). In comment is my failing attempt for a double signature. I also tried to sign a first time doing the whole process, and sign a second time with the same process but without any success:
PDF_ORI = "contrat_old.pdf"
PDF_NEW = "contrat_new.pdf"
pdf = Origami::PDF.read(PDF_ORI)
# Open certificate files
key = OpenSSL::PKey::RSA.new 2048
cert = OpenSSL::X509::Certificate.new
cert.version = 2
cert.serial = 0
cert.not_before = Time.now
cert.not_after = Time.now + 10 * 365 * 24 * 60 * 60 # 10 years validity
cert.public_key = key.public_key
cert.issuer = OpenSSL::X509::Name.parse('CN=Test')
cert.subject = OpenSSL::X509::Name.parse('CN=test1 ESSAI1')
# Open certificate files
#key2 = OpenSSL::PKey::RSA.new 2048
#cert2 = OpenSSL::X509::Certificate.new
#cert2.version = 2
#cert2.serial = 0
#cert2.not_before = Time.now
#cert2.not_after = Time.now + 10 * 365 * 24 * 60 * 60 # 10 years validity
#cert2.public_key = key2.public_key
#cert2.issuer = OpenSSL::X509::Name.parse('CN=Test2')
#cert2.subject = OpenSSL::X509::Name.parse('CN=test2 ESSAI2')
sigannot = Origami::Annotation::Widget::Signature.new
sigannot.Rect = Origami::Rectangle[:llx => 89.0, :lly => 386.0, :urx => 190.0, :ury => 353.0]
pdf.get_page(1).add_annot(sigannot)
#sigannot2 = Origami::Annotation::Widget::Signature.new
#sigannot2.Rect = Origami::Rectangle[:llx => 89.0, :lly => 386.0, :urx => 190.0, :ury => 353.0]
#pdf.get_page(1).add_annot(sigannot2)
# Sign the PDF with the specified keys
pdf.sign(cert, key,
:method => 'adbe.pkcs7.sha1',
:annotation => sigannot,
:location => "France",
:contact => "tmp#security.org",
:reason => "Proof of Concept"
)
# Sign the PDF with the specified keys
#pdf.sign(cert2, key2,
# :method => 'adbe.pkcs7.sha1',
# :annotation => sigannot2,
# :location => "France",
# :contact => "tmp#security.org",
# :reason => "Proof of Concept"
#)
# Save the resulting file
pdf.save(PDF_NEW)
I know it is quite tricky, but no one can help me?
Or using another solution maybe?
you can use CombinePDF to add watermark to PDF i hve use it in the past and it works great:
To add content to existing PDF pages, first import the new content from an existing PDF file. After that, add the content to each of the pages in your existing PDF.
In this example, we will add a company logo to each page:
company_logo = CombinePDF.load("company_logo.pdf").pages[0]
pdf = CombinePDF.load "content_file.pdf"
pdf.pages.each {|page| page << company_logo}
# notice the << operator is on a page and not a PDF object.
pdf.save "content_with_logo.pdf"
Notice the << operator is on a page and not a PDF object. The << operator acts differently on PDF objects and on Pages.
The << operator defaults to secure injection by renaming references to avoid conflics. For overlaying pages using compressed data that might not be editable (due to limited filter support), you can use:
pdf.pages(nil, false).each {|page| page << stamp_page}
You can see more details here:
https://github.com/boazsegev/combine_pdf

Can't get Google Analytics user data to show in Rails app

I'm attempting a server-to-server connection between my Google Analytics account and my Rails app. For this, I'm using the Legato, omniauth-google-oauth2, and google-api-client gems. My intention is to have a rake task that sieves out pageview data from a particular site. However, I can't seem to get any user data out of it. Here's the code:
require 'google/api_client'
def service_account_user(scope="https://www.googleapis.com/auth/analytics.readonly")
client = Google::APIClient.new(
:application_name => "Listmaker",
:application_version => "2.0"
)
key = OpenSSL::PKey::RSA.new(Figaro.env.google_private_key, "notasecret")
service_account = Google::APIClient::JWTAsserter.new(Figaro.env.google_app_email_address, scope, key)
client.authorization = service_account.authorize
oauth_client = OAuth2::Client.new("", "", {
:authorize_url => 'https://accounts.google.com/o/oauth2/auth',
:token_url => 'https://accounts.google.com/o/oauth2/token'
})
token = OAuth2::AccessToken.new(oauth_client, client.authorization.access_token)
Legato::User.new(token)
end
class Pageviews
extend Legato::Model
metrics :pageviews
dimensions :page_path
filter :for_page_path, &lambda {|page_path| matches(:page_path, page_path)}
end
puts profile = service_account_user.profiles.first
I appear to be getting an empty array for the profile variable after running the task. I've definitely added the developer email address to the Google Analytics View I'm interested in. Not sure what's wrong.
For a service account to work with Google Analytics the Service account email must be added at the Account level. It wont work if it was only added at the view level.

Get large images from Page Feed? using Koala Gem & Rails 3.2

Does anyone know how to pull different size images from the Page Feed?
I was trying to use the Type hash that works great for friends and profile pictures.
#page-feed = #graph.get_connections("somepage", "feed", {"type" => "large"})
but for some reason I'm always getting the same picture size for all posts.
Thanks !
Reading the code here: https://github.com/arsduo/koala/blob/81e66f459df840d9d5e122c0d498e2fb9d146655/lib/koala/api/graph_api.rb (line 178, def get_picture) you can see that the method accepts options hash:
Gem source:
# Fetches a photo.
# (Facebook returns the src of the photo as a response header; this method parses that properly,
# unlike using get_connections("photo").)
#
# #param options options for Facebook (see #get_object).
# To get a different size photo, pass :type => size (small, normal, large, square).
# #param block (see Koala::Facebook::API#api)
#
# #note to delete photos or videos, use delete_object(id)
#
# #return the URL to the image
def get_picture(object, args = {}, options = {}, &block)
# Gets a picture object, returning the URL (which Facebook sends as a header)
resolved_result = graph_call("#{object}/picture", args, "get", options.merge(:http_component => :headers)) do |result|
result ? result["Location"] : nil
end
block ? block.call(resolved_result) : resolved_result
end
So you can call it like .get_picture(id, type: :large). Like this:
graph = Koala::Facebook::API.new(token)
profile = graph.get_object('me')
graph.get_picture(profile['id'], type: :large)
Just in case anyone else comes across this, this is what I had to do in order to retrieve the large images. Note that I'm only grabbing the first post in the feed.
In my controller:
#fb_post = #facebook.get_connections(page_id, 'posts').first
#photo = #facebook.get_connections(#fb_post['id'], 'attachments').first
Then, to grab the proper link in the view inside of an img tag, I used:
#photo["media"]["image"]["src"]
For anyone still struggling with this, I was able to use the 'full_picture' field in my Koala object to retrieve a full-resolution URLs of the images:
fields = ['id','picture','full_picture']
graphObj = Koala::Facebook::API.new(accessToken)
hashes = graphObj.get_connection(groupId, 'feed', { limit: 10, fields: fields })
hashes.each do |hash|
mash = Hashie::Mash.new(hash)
fullPicture = mash.full_picture
end

Resources