rightsignature-railstyle 1.1.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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