rightsignature-railstyle 1.1.9

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,51 @@
1
+ module RightSignature
2
+ class ResponseError < Exception
3
+ attr_reader :response
4
+
5
+ # Creates new instance of RightSignature::ResponseError to make API calls
6
+ # * <b>response</b>: Net::HTTP response or HTTParty response
7
+ # * <b>message</b>: (Optional) Custom error message
8
+ #
9
+ def initialize(response, message=nil)
10
+ self.set_backtrace(caller[1..-1]) if self.backtrace.nil?
11
+ @response = response
12
+ super((message || @response.message))
13
+ end
14
+
15
+ # returns HTTP Code from response
16
+ def code
17
+ @response.code
18
+ end
19
+
20
+ # Suggestions on how to resolve a certain error
21
+ def common_solutions
22
+ if @response.code.to_i == 406
23
+ "Check the Content-Type and makes sure it's the correct type (usually application/json or application/xml), ensure url has .xml or .json at the end, check 'Accept' header to allow xml or json ('*/*' for anything)"
24
+ elsif @response.code.to_i == 401
25
+ "Check your credentials and make sure they are correct and not expired"
26
+ elsif @response.code.to_i >= 500 && @response.code.to_i < 600
27
+ "Check the format of your xml or json"
28
+ end
29
+ end
30
+
31
+ # Attempts to parse an error message from the response body
32
+ def detailed_message
33
+ if @response.is_a? Net::HTTPResponse
34
+ parsed_response = MultiXml.parse(@response.body)
35
+
36
+ parsed_response["error"]["message"] if parsed_response && parsed_response["error"]
37
+ else
38
+ if @response.parsed_response.is_a? Hash
39
+ @response.parsed_response["error"]["message"] if @response.parsed_response["error"]
40
+ end
41
+ end
42
+ end
43
+
44
+
45
+ end
46
+
47
+ class TokenResponseError < ResponseError # :nodoc:
48
+ end
49
+ class OAuthResponseError < ResponseError # :nodoc:
50
+ end
51
+ end
@@ -0,0 +1,137 @@
1
+ require 'pry'
2
+ module RightSignature::Helpers #:nodoc:
3
+ module TagsHelper #:nodoc:
4
+ class <<self #:nodoc:
5
+ def array_and_metadata_to_string_array(array_of_tags, hash_of_metadata)
6
+ array_of_tags ||= []
7
+ hash_of_metadata ||= {}
8
+ if !(array_of_tags.is_a?(Array) && hash_of_metadata.is_a?(Hash))
9
+ return raise ArgumentError, "Objects must be an array and a hash"
10
+ end
11
+
12
+ tags_array =
13
+ array_of_tags.collect{|t| CGI.escape(t)} +
14
+ hash_of_metadata.collect{ |k,v|
15
+ "#{CGI.escape(k.to_s)}:#{CGI.escape(v.to_s)}" if ! v.to_s.strip.empty?
16
+ }.select{ |t|
17
+ ! t.to_s.strip.empty?
18
+ }
19
+
20
+ tags_array.join ','
21
+ end
22
+
23
+ def tags_array_from_tags_string tags_string
24
+ tags_string ||= ""
25
+
26
+ if ! tags_string.is_a? String
27
+ return raise ArgumentError, "Object must be a string"
28
+ end
29
+
30
+ tags_string.split(',')
31
+ .select{|t| t !~ /:/}
32
+ .collect{|t| CGI.unescape(t)}
33
+ end
34
+
35
+ def metadata_hash_from_tags_string tags_string
36
+ tags_string ||= ""
37
+
38
+ if ! tags_string.is_a? String
39
+ return raise ArgumentError, "Object must be a string"
40
+ end
41
+
42
+ Hash[
43
+ tags_string.split(',')
44
+ .select{ |t| t =~ /:/}
45
+ .collect{ |t|
46
+ md = t.split(':')
47
+ [CGI.unescape(md[0]), CGI.unescape(md[1])]
48
+ }
49
+ ]
50
+ end
51
+
52
+ def mixed_array_to_string_array(array_of_tags)
53
+ return array_of_tags unless array_of_tags.is_a?(Array)
54
+
55
+ tags = []
56
+ array_of_tags.each_with_index do |tag, idx|
57
+ if tag.is_a? Hash
58
+ tags << tag.collect{|k,v| "#{CGI.escape(k.to_s)}:#{CGI.escape(v.to_s)}"}
59
+ elsif tag.is_a? String
60
+ tags << CGI.escape(tag.to_s)
61
+ else
62
+ raise "Tags should be an array of Strings ('tag_name') or Hashes ({'tag_name' => 'value'})"
63
+ end
64
+ end
65
+
66
+ tags.join(',')
67
+ end
68
+
69
+ def array_to_xml_hash(tags_array)
70
+ tags_array.map do |t|
71
+ if t.is_a? Hash
72
+ name, value = t.first
73
+ {:tag => {:name => name.to_s, :value => value.to_s}}
74
+ else
75
+ {:tag => {:name => t.to_s}}
76
+ end
77
+ end
78
+ end
79
+
80
+ end
81
+ end
82
+
83
+ module RolesHelper #:nodoc:
84
+ class <<self #:nodoc:
85
+ # Converts [{"Role_ID" => {:name => "John", :email => "email@example.com"}}] to
86
+ # [{:role => {:name => "John", :email => "email@example.com", "@role_id" => "Role_ID"} }]
87
+ # Tries to guess if it's using Role ID or Role Name
88
+ def array_to_xml_hash(roles_array)
89
+ roles_hash_array = []
90
+ roles_array.each do |role_hash|
91
+ name, value = role_hash.first
92
+ raise "Hash '#{role_hash.inspect}' is malformed, should be something like {ROLE_NAME => {:name => \"a\", :email => \"a@a.com\"}}" unless value.is_a? Hash and (name.is_a? String or name.is_a? Symbol)
93
+ if name.match(/^signer_[A-Z]+$/) || name.match(/^cc_[A-Z]+$/)
94
+ roles_hash_array << {:role => value.merge({"@role_id" => name.to_s})}
95
+ else
96
+ roles_hash_array << {:role => value.merge({"@role_name" => name.to_s})}
97
+ end
98
+ end
99
+
100
+ roles_hash_array
101
+ end
102
+
103
+ end
104
+ end
105
+
106
+
107
+ module MergeFieldsHelper #:nodoc:
108
+ class <<self #:nodoc:
109
+ # Converts [{"Role Name" => {:name => "John", :email => "email@example.com"}}] to
110
+ # [{"role roles_name=\"Role Name\"" => {:role => {:name => "John", :email => "email@example.com"}} }]
111
+ def array_to_xml_hash(merge_fields_array, use_id=false)
112
+ merge_fields = []
113
+ merge_fields_array.each do |merge_field_hash|
114
+ name, value = merge_field_hash.first
115
+ if use_id
116
+ merge_fields << { :merge_field => {:value => value.to_s, "@merge_field_id" => name.to_s}}
117
+ else
118
+ merge_fields << { :merge_field => {:value => value.to_s, "@merge_field_name" => name.to_s}}
119
+ end
120
+ end
121
+
122
+ merge_fields
123
+ end
124
+
125
+ end
126
+ end
127
+
128
+ #:nodoc:
129
+ def array_to_acceptable_names_hash(acceptable_names)
130
+ converted_fields = []
131
+ acceptable_names.each do |name|
132
+ converted_fields << {:name => name}
133
+ end
134
+
135
+ converted_fields
136
+ end
137
+ end
@@ -0,0 +1,23 @@
1
+ module RefineHashToIndifferentAccess
2
+
3
+ # Turn all hashes into "with indifferent access" hashes
4
+ refine Hash do
5
+ regular_indexing = instance_method(:[])
6
+ define_method(:[]) do |key|
7
+ if has_key? key.to_sym
8
+ regular_indexing.bind(self).(key.to_sym)
9
+ else
10
+ regular_indexing.bind(self).(key.to_s)
11
+ end
12
+ end
13
+
14
+ regular_deletion = instance_method(:delete)
15
+ define_method(:delete) do |key|
16
+ if has_key? key.to_sym
17
+ regular_deletion.bind(self).(key.to_sym)
18
+ else
19
+ regular_deletion.bind(self).(key.to_s)
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,89 @@
1
+ using RefineHashToIndifferentAccess
2
+
3
+ module RightSignature
4
+ class RailsStyle < Connection
5
+ def prepackage_and_send guid, roles, options
6
+ # These conversions are basically from Rails-style structures to XML-style structures
7
+
8
+ # Convert hashes to array-of-hashes
9
+ # {k1: v1, k2: v2} => [{k1: v1}, {k2: v2}]
10
+
11
+ options[:merge_fields] = (options.delete(:merge_fields)||{}).collect{|k,v| {k => v}}
12
+
13
+ roles = roles.collect{|k,v| {k => v}}
14
+
15
+ options[:tags] =
16
+ (options[:tags]||[]) +
17
+ (options.delete(:metadata)||{}).collect{|k,v| {k => v} if v && ! v.to_s.strip.empty?}.select{|h| ! h.nil?}
18
+
19
+ # TODO: Resolve RS choking on anything a HashWithIndifferentAccess
20
+ super guid, roles, options
21
+ end
22
+
23
+ #TODO: Copy pasta
24
+ def template_details guid
25
+ doc = super(guid)[:template]
26
+
27
+ tags_string = doc.delete(:tags)
28
+ doc[:metadata] = TagsHelper.metadata_hash_from_tags_string tags_string
29
+ doc[:tags] = TagsHelper.tags_array_from_tags_string tags_string
30
+
31
+ # Convert a deeply nested array of objects into a normal hash
32
+ # Note: Must check if it's a deeply nested hash instead, since that is what occurs
33
+ # for single-item XLM "arrays"
34
+ # {..., recipients: {recipient: [{...},{...}]}} => {..., recipients: {...: {...}, ...:{...}}}
35
+ tmp = doc[:roles][:role].is_a?(Hash) ? [doc[:roles][:role]] : doc[:roles][:role]
36
+ doc[:roles] = tmp.reduce({}){|h, v| h[v[:document_role_id]] = v and h}
37
+
38
+ tmp = doc[:merge_fields][:merge_field].is_a?(Hash) ? [doc[:merge_fields][:merge_field]] : doc[:merge_fields][:merge_field]
39
+ doc[:merge_fields] = tmp.reduce({}){|h, v| h[v[:name]] = v and h}
40
+
41
+ # Extract a few fields from a deeply nested array
42
+ tmp = doc[:pages][:page].is_a?(Hash) ? doc[:pages][:page] : doc[:pages][:page].first
43
+ %i(original_template_guid original_template_filename).each do |sym|
44
+ doc[sym] = tmp[sym]
45
+ end
46
+ doc.delete(:pages)
47
+
48
+ %i(thumbnail_url).each do |sym|
49
+ doc[sym] = CGI.unescape doc[sym]
50
+ end
51
+
52
+ doc
53
+ end
54
+
55
+ def document_details guid
56
+ doc = super(guid)[:document]
57
+
58
+ tags_string = doc.delete(:tags)
59
+ doc[:metadata] = TagsHelper.metadata_hash_from_tags_string tags_string
60
+ doc[:tags] = TagsHelper.tags_array_from_tags_string tags_string
61
+
62
+ # Convert a deeply nested array of objects into a normal hash
63
+ # Note: Must check if it's a deeply nested hash instead, since that is what occurs
64
+ # for single-item XLM "arrays"
65
+ # {..., recipients: {recipient: [{...},{...}]}} => {..., recipients: {...: {...}, ...:{...}}}
66
+ tmp = doc[:recipients][:recipient].is_a?(Hash) ? [doc[:recipients][:recipient]] : doc[:recipients][:recipient]
67
+ doc[:recipients] = tmp.reduce({}){|h, v| h[v[:role_id]] = v and h}
68
+
69
+ tmp = doc[:audit_trails][:audit_trail].is_a?(Hash) ? [doc[:audit_trails][:audit_trail]] : doc[:audit_trails][:audit_trail]
70
+ doc[:audit_trails] = tmp.reduce({}){|h, v| h[v[:timestamp]] = v and h}
71
+
72
+ tmp = doc[:form_fields][:form_field].is_a?(Hash) ? [doc[:form_fields][:form_field]] : doc[:form_fields][:form_field]
73
+ doc[:form_fields] = tmp.reduce({}){|h, v| h[v[:name]] = v and h}
74
+
75
+ # Extract a few fields from a deeply nested array
76
+ tmp = doc[:pages][:page].is_a?(Hash) ? doc[:pages][:page] : doc[:pages][:page].first
77
+ %i(original_template_guid original_template_filename).each do |sym|
78
+ doc[sym] = tmp[sym]
79
+ end
80
+ doc.delete(:pages)
81
+
82
+ %i(original_url pdf_url thumbnail_url large_url signed_pdf_url).each do |sym|
83
+ doc[sym] = CGI.unescape doc[sym]
84
+ end
85
+
86
+ doc
87
+ end
88
+ end
89
+ end
@@ -0,0 +1,299 @@
1
+ using RefineHashToIndifferentAccess
2
+
3
+ module RightSignature
4
+ module Template
5
+ include RightSignature::Helpers
6
+ # List Templates with optional filters
7
+ # * <b>Options</b>: (optional) Hash of filters to use
8
+ # * <b>page</b>: page number
9
+ # * <b>per_page</b>: number of templates to return per page.
10
+ # API only supports 10, 20, 30, 40, or 50. Default is 10.
11
+ # * <b>tags</b>: filter templates by given tags. Array of strings, for name/value tags colon (:) should separate name and value.
12
+ # Ex. "single_tag,tag_key:tag_value" would find templates with 'single_tag' and the name/value of 'tag_key' with value 'tag_value'.
13
+ # * <b>search</b>: term to search for in templates.
14
+ #
15
+ # Ex.
16
+ # options = {
17
+ # :state => ['completed', 'trashed'],
18
+ # :page => 1,
19
+ # :per_page => 20,
20
+ # :search => "me",
21
+ # :tags => ["single_tag", "key" => "with_value"]
22
+ # }
23
+ # @rs_connection.templates_list(options)
24
+ def templates_list(options={})
25
+ if options[:metadata]
26
+ options[:tags] = TagsHelper.array_and_metadata_to_string_array(options[:tags], options.delete(:metadata))
27
+ elsif options[:tags]
28
+ options[:tags] = TagsHelper.mixed_array_to_string_array(options[:tags])
29
+ end
30
+ get "/api/templates.xml", options
31
+ end
32
+
33
+ # Gets template details
34
+ # * <b>guid</b>: templates guid. Ex. a_1_zcfdidf8fi23
35
+ def template_details(guid)
36
+ get "/api/templates/#{guid}.xml", {}
37
+ end
38
+
39
+ # Clones a template so it can be used for sending. Always first step in sending a template.
40
+ # * <b>guid</b>: templates guid. Ex. a_1_zcfdidf8fi23
41
+ def prepackage(guid)
42
+ post "/api/templates/#{guid}/prepackage.xml", {}
43
+ end
44
+
45
+ # Prefills template. Should use a <b>prepackaged</b> template first.
46
+ # * <b>guid</b>: templates guid. Ex. a_1_zcfdidf8fi23
47
+ # * <b>subject</b>: subject of the document that'll appear in email
48
+ # * <b>roles</b>: Recipients of the document, should be an array of role names and emails in a hash with keys as role_names.
49
+ # Ex. [{"Employee" => {:name => "John Employee", :email => "john@employee.com"}}]
50
+ # is equivalent to
51
+ # <role role_name="Employee">
52
+ # <name>John Employee</name>
53
+ # <email>john@employee.com</email>
54
+ # </role>
55
+ # * <b>options</b>: other optional values
56
+ # * <b>description</b>: document description that'll appear in the email
57
+ # * <b>merge_fields</b>: document merge fields, should be an array of merge_field_values in a hash with the merge_field_name.
58
+ # Ex. [{"Salary" => "$1,000,000"}]
59
+ # is equivalent to
60
+ # <merge_field merge_field_name="Salary">
61
+ # <value>$1,000,000</value>
62
+ # </merge_field>
63
+ # * <b>expires_in</b>: number of days before expiring the document. API only allows 2,5,15, or 30.
64
+ # * <b>tags</b>: document tags, an array of string or hashes 'single_tag' (for simple tag) or {'tag_name' => 'tag_value'} (for tuples pairs)
65
+ # Ex. ['sent_from_api', {"user_id" => "32"}]
66
+ # * <b>callback_location</b>: A URI encoded URL that specifies the location for API to POST a callback notification to when the document has been created and signed.
67
+ # Ex. "http://yoursite/callback"
68
+ #
69
+ # Ex. call with all options used
70
+ # @rs_connection.prefill(
71
+ # "a_1_zcfdidf8fi23",
72
+ # "Your Employee Handbook",
73
+ # [{"employee" => {:name => "John Employee", :email => "john@employee.com"}}],
74
+ # {
75
+ # :description => "Please read over the handbook and sign it.",
76
+ # :merge_fields => [
77
+ # { "Department" => "Fun and games" },
78
+ # { "Salary" => "$1,000,000" }
79
+ # ],
80
+ # :expires_in => 5,
81
+ # :tags => [
82
+ # {:name => 'sent_from_api'},
83
+ # {:name => 'user_id', :value => '32'}
84
+ # ],
85
+ # :redirect_location => "http://yoursite/redirect",
86
+ # :callback_location => "http://yoursite/callback"
87
+ # })
88
+ def prefill(guid, subject, roles, options={})
89
+ use_merge_field_ids = options.delete(:use_merge_field_ids)
90
+ xml_hash = {
91
+ :template => {
92
+ :guid => guid,
93
+ :action => "prefill",
94
+ :subject => subject
95
+ }.merge(options)
96
+ }
97
+
98
+ xml_hash[:template][:roles] = RolesHelper.array_to_xml_hash(roles)
99
+
100
+ # Optional arguments
101
+ xml_hash[:template][:merge_fields] = MergeFieldsHelper.array_to_xml_hash(options[:merge_fields], use_merge_field_ids) if options[:merge_fields]
102
+ xml_hash[:template][:tags] = TagsHelper.array_to_xml_hash(options[:tags]) if options[:tags]
103
+
104
+ post "/api/templates.xml", xml_hash
105
+ end
106
+
107
+ # Prepackages and sends template.
108
+ # * <b>guid</b>: templates guid. Ex. a_1_zcfdidf8fi23
109
+ # * <b>roles</b>: Recipients of the document, should be an array of role names and emails in a hash with keys as role_names.
110
+ # Ex. [{"Employee" => {:name => "John Employee", :email => "john@employee.com"}}]
111
+ # is equivalent to
112
+ # <role role_name="Employee">
113
+ # <name>John Employee</name>
114
+ # <email>john@employee.com</email>
115
+ # </role>
116
+ # * <b>options</b>: other optional values
117
+ # * <b>subject</b>: subject of the document that'll appear in email. Defaults to template's subject
118
+ # * <b>description</b>: document description that'll appear in the email
119
+ # * <b>merge_fields</b>: document merge fields, should be an array of merge_field_values in a hash with the merge_field_name.
120
+ # Ex. [{"Salary" => "$1,000,000"}]
121
+ # is equivalent to
122
+ # <merge_field merge_field_name="Salary">
123
+ # <value>$1,000,000</value>
124
+ # </merge_field>
125
+ # * <b>expires_in</b>: number of days before expiring the document. API only allows 2,5,15, or 30.
126
+ # * <b>tags</b>: document tags, an array of {:name => 'tag_name'} (for simple tag) or {:name => 'tag_name', :value => 'value'} (for tuples pairs)
127
+ # Ex. [{:name => 'sent_from_api'}, {:name => "user_id", :value => "32"}]
128
+ # * <b>callback_location</b>: A URI encoded URL that specifies the location for API to POST a callback notification to when the document has been created and signed.
129
+ # Ex. "http://yoursite/callback"
130
+ #
131
+ # Ex. call with all options used
132
+ # @rs_connection.prepackage_and_send(
133
+ # "a_1_zcfdidf8fi23",
134
+ # "Your Employee Handbook",
135
+ # [{"employee" => {:name => "John Employee", :email => "john@employee.com"}}],
136
+ # {
137
+ # :description => "Please read over the handbook and sign it.",
138
+ # :merge_fields => [
139
+ # { "Department" => "Fun and games" },
140
+ # { "Salary" => "$1,000,000" }
141
+ # ],
142
+ # :expires_in => 5,
143
+ # :tags => [
144
+ # {:name => 'sent_from_api'},
145
+ # {:name => 'user_id', :value => '32'}
146
+ # ],
147
+ # :callback_location => "http://yoursite/callback"
148
+ # })
149
+ def prepackage_and_send(guid, roles, options={})
150
+ response = prepackage(guid)
151
+ new_guid = response["template"]["guid"]
152
+ send_template(new_guid, options.delete(:subject) || response["template"]["subject"], roles, options)
153
+ end
154
+
155
+ # Sends template. Should use a <b>prepackaged</b> template first. Easier to use <b>prepackage_and_send</b> for most cases.
156
+ # * <b>guid</b>: templates guid. Ex. a_1_zcfdidf8fi23
157
+ # * <b>subject</b>: subject of the document that'll appear in email
158
+ # * <b>roles</b>: Recipients of the document, should be an array of role names and emails in a hash with keys as role_names.
159
+ # Ex. [{"Employee" => {:name => "John Employee", :email => "john@employee.com"}}]
160
+ # is equivalent to
161
+ # <role role_name="Employee">
162
+ # <name>John Employee</name>
163
+ # <email>john@employee.com</email>
164
+ # </role>
165
+ # * <b>options</b>: other optional values
166
+ # * <b>description</b>: document description that'll appear in the email
167
+ # * <b>merge_fields</b>: document merge fields, should be an array of merge_field_values in a hash with the merge_field_name.
168
+ # Ex. [{"Salary" => "$1,000,000"}]
169
+ # is equivalent to
170
+ # <merge_field merge_field_name="Salary">
171
+ # <value>$1,000,000</value>
172
+ # </merge_field>
173
+ # * <b>expires_in</b>: number of days before expiring the document. API only allows 2,5,15, or 30.
174
+ # * <b>tags</b>: document tags, an array of {:name => 'tag_name'} (for simple tag) or {:name => 'tag_name', :value => 'value'} (for tuples pairs)
175
+ # Ex. [{:name => 'sent_from_api'}, {:name => "user_id", :value => "32"}]
176
+ # * <b>callback_location</b>: A URI encoded URL that specifies the location for API to POST a callback notification to when the document has been created and signed.
177
+ # Ex. "http://yoursite/callback"
178
+ #
179
+ # Ex. call with all options used
180
+ # @rs_connection.send_template(
181
+ # "a_1_zcfdidf8fi23",
182
+ # "Your Employee Handbook",
183
+ # [{"employee" => {:name => "John Employee", :email => "john@employee.com"}}],
184
+ # {
185
+ # :description => "Please read over the handbook and sign it.",
186
+ # :merge_fields => [
187
+ # { "Department" => "Fun and games" },
188
+ # { "Salary" => "$1,000,000" }
189
+ # ],
190
+ # :expires_in => 5,
191
+ # :tags => [
192
+ # {:name => 'sent_from_api'},
193
+ # {:name => 'user_id', :value => '32'}
194
+ # ],
195
+ # :callback_location => "http://yoursite/callback"
196
+ # })
197
+ def send_template(guid, subject, roles, options={})
198
+ prefill(guid, subject, roles, options.merge({:action => 'send'}))
199
+ end
200
+
201
+ # Creates a URL that give person ability to create a template in your account.
202
+ # * <b>options</b>: optional options for redirected person
203
+ # * <b>callback_location</b>: URI encoded URL that specifies the location we will POST a callback notification to when the template has been created.
204
+ # * <b>redirect_location</b>: A URI encoded URL that specifies the location we will redirect the user to, after they have created a template.
205
+ # * <b>tags</b>: tags to add to the template. an array of 'tag_name' (for simple tag) or {'tag_name' => 'value'} (for tuples pairs)
206
+ # Ex. ['created_from_api', {"user_id" => "123"}]
207
+ # * <b>acceptable_role_names</b>: The user creating the Template will be forced to select one of the values provided.
208
+ # There will be no free-form name entry when adding roles to the Template. An array of strings.
209
+ # Ex. ["Employee", "Employeer"]
210
+ # * <b>acceptable_merge_field_names</b>: The user creating the Template will be forced to select one of the values provided.
211
+ # There will be no free-form name entry when adding merge fields to the Template.
212
+ # Ex. ["Location", "Tax ID", "Company Name"]
213
+ def generate_build_url(options={})
214
+ xml_hash = {:template => options}
215
+ xml_hash[:template][:tags] = TagsHelper.array_to_xml_hash(options[:tags]) if options[:tags]
216
+
217
+ [:acceptable_merge_field_names, :acceptable_role_names].each do |option|
218
+ xml_hash[:template][option] = array_to_acceptable_names_hash(options[option]) if options[option]
219
+ end
220
+
221
+ response = post "/api/templates/generate_build_token.xml", xml_hash
222
+
223
+ redirect_token = response["token"]["redirect_token"]
224
+
225
+ "#{site}/builder/new?rt=#{redirect_token}"
226
+ end
227
+
228
+ # Sends template with all roles as embedded signers and returns an array of hashes with :name and :url for each signer link.
229
+ # * <b>guid</b>: templates guid. Ex. a_1_zcfdidf8fi23
230
+ # * <b>roles</b>: Recipients of the document, should be an array of role names in a hash with keys as role_names.
231
+ # Ex. [{"Employee" => {:name => "John Employee"}]
232
+ # is equivalent to
233
+ # <role role_name="Employee">
234
+ # <name>John Employee</name>
235
+ # <email>noemail@rightsignature.com</email>
236
+ # </role>
237
+ # * <b>options</b>: other optional values
238
+ # * <b>subject</b>: subject of the document that'll appear in email. Defaults to Template's subject
239
+ # * <b>description</b>: document description that'll appear in the email
240
+ # * <b>merge_fields</b>: document merge fields, should be an array of merge_field_values in a hash with the merge_field_name.
241
+ # Ex. [{"Salary" => "$1,000,000"}]
242
+ # is equivalent to
243
+ # <merge_field merge_field_name="Salary">
244
+ # <value>$1,000,000</value>
245
+ # </merge_field>
246
+ # * <b>expires_in</b>: number of days before expiring the document. API only allows 2,5,15, or 30.
247
+ # * <b>tags</b>: document tags, an array of {:name => 'tag_name'} (for simple tag) or {:name => 'tag_name', :value => 'value'} (for tuples pairs)
248
+ # Ex. [{:name => 'sent_from_api'}, {:name => "user_id", :value => "32"}]
249
+ # * <b>callback_location</b>: A URI encoded URL that specifies the location for API to POST a callback notification to when the document has been created and signed.
250
+ # Ex. "http://yoursite/callback"
251
+ # * <b>redirect_location</b>: A URI encoded URL that specifies the location for the signing widget to redirect the user to after it is signed.
252
+ # Ex. "http://yoursite/thanks_for_signing"
253
+ #
254
+ # Ex. call with all options used
255
+ # @rs_connection.send_as_embedded_signers(
256
+ # "a_1_zcfdidf8fi23",
257
+ # "Your Employee Handbook",
258
+ # [{"employee" => {:name => "John Employee", :email => "john@employee.com"}}],
259
+ # {
260
+ # :description => "Please read over the handbook and sign it.",
261
+ # :merge_fields => [
262
+ # { "Department" => "Fun and games" },
263
+ # { "Salary" => "$1,000,000" }
264
+ # ],
265
+ # :expires_in => 5,
266
+ # :tags => [
267
+ # {:name => 'sent_from_api'},
268
+ # {:name => 'user_id', :value => '32'}
269
+ # ],
270
+ # :redirect_location => "http://yoursite/redirect_from_signing"
271
+ # })
272
+ def send_as_embedded_signers(guid, recipients, options={})
273
+ redirect_location = options.delete(:redirect_location)
274
+
275
+ response = prepackage(guid)
276
+ template = response["template"]
277
+
278
+ recipients.each do |role_hash|
279
+ key, value = role_hash.first
280
+ role_hash[key][:email] = "noemail@rightsignature.com" unless role_hash[key]["email"] || role_hash[key][:email]
281
+ end
282
+
283
+ response = send_template(template["guid"], options[:subject] || template["subject"], recipients, options)
284
+ document_guid = response["document"]["guid"]
285
+
286
+ get_document_signer_links_for(document_guid, redirect_location)
287
+ end
288
+
289
+ # Deletes a template
290
+ # * <b>guid</b>: Document GUID
291
+ #
292
+ # Ex. Delete template GUID123
293
+ # @rs_connection.trash_template("GUID123")
294
+ #
295
+ def trash_template(guid)
296
+ delete "/api/templates/#{guid}.xml"
297
+ end
298
+ end
299
+ end