mxit-rails 0.2.5 → 0.2.6

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.
@@ -8,7 +8,7 @@ module MxitRailsHelper
8
8
  str = ''
9
9
  if @_mxit.has_table
10
10
  # Close the previous row if there is one
11
- str += "</td></tr>"
11
+ str += "<br /></td></tr>"
12
12
  else
13
13
  # Start a new table
14
14
  str += '<table title="mxit:table:full" style="width:100%" name="main_table" cellspacing="0" cellpadding="0">'
@@ -17,7 +17,7 @@ module MxitRailsHelper
17
17
  @_mxit.has_table = true
18
18
 
19
19
  # Start the new row
20
- style = mxit_style *styles
20
+ style = styles.empty? ? mxit_style(:body) : mxit_style(*styles)
21
21
  str += "<tr><td style=\"#{ style }\">"
22
22
  str.html_safe
23
23
  end
@@ -43,11 +43,11 @@ module MxitRailsHelper
43
43
  end
44
44
 
45
45
  def mxit_nav_link target, label
46
- "<p style=\"#{ mxit_style :right }\">#{ mxit_link target, label }</p>".html_safe
46
+ "#{ mxit_link target, label }<br /><br />".html_safe
47
47
  end
48
48
 
49
49
  def mxit_proceed content
50
- "<p><b style=\"#{ mxit_style :link }\">&gt; #{content}</b></p>".html_safe
50
+ "<br /><b style=\"#{ mxit_style :link }\"> &raquo; #{content}</b><br />".html_safe
51
51
  end
52
52
 
53
53
  end
@@ -27,10 +27,16 @@
27
27
 
28
28
  <% if @_mxit.has_table %>
29
29
  <!-- Close the table if there is one -->
30
- </td></tr>
30
+ <br /></td></tr>
31
31
  </table>
32
32
  <% end %>
33
33
 
34
+ <% if mxit_validation_message %>
35
+ <b><%= mxit_validation_message %></b><br />
36
+ <% elsif flash[:notice] %>
37
+ <b><%= flash[:notice] %></b><br />
38
+ <% end %>
39
+
34
40
  <% if @_mxit.input %>
35
41
  <%= mxit_proceed @_mxit.input_label %>
36
42
  <form method="POST" action="<%= request.path %>">
data/lib/mxit-rails.rb CHANGED
@@ -10,3 +10,11 @@ require "mxit_rails/page"
10
10
 
11
11
  module MxitRails
12
12
  end
13
+
14
+ require "mxit_rails/mxit_api/api_client"
15
+ require "mxit_rails/mxit_api/auth_token"
16
+ require "mxit_rails/mxit_api/mxit_api_exception"
17
+ require "mxit_rails/mxit_api/request_exception"
18
+
19
+ module MxitRails::MXitApi
20
+ end
@@ -18,7 +18,7 @@ module MxitRails
18
18
  attr_accessor :action
19
19
  attr_accessor :type
20
20
 
21
- descr_accessor :proceed
21
+ attr_accessor :proceed
22
22
 
23
23
  attr_accessor :input
24
24
  attr_accessor :input_label
@@ -29,6 +29,9 @@ module MxitRails
29
29
 
30
30
  attr_accessor :has_table
31
31
 
32
+ attr_accessor :validations_failed
33
+ attr_accessor :validated
34
+
32
35
  def initialize name, action, parent=nil
33
36
  @parent_descriptor = parent
34
37
  @name = name.to_sym
@@ -0,0 +1,260 @@
1
+ require 'net/http'
2
+ require 'uri'
3
+
4
+ module MxitRails::MxitApi
5
+ class Client
6
+ MXIT_AUTH_BASE_URI = 'https://auth.mxit.com'
7
+ MXIT_AUTH_TOKEN_URI = MXIT_AUTH_BASE_URI + '/token'
8
+ MXIT_AUTH_CODE_URI = MXIT_AUTH_BASE_URI + '/authorize'
9
+
10
+ MXIT_API_URI = 'http://api.mxit.com'
11
+
12
+ attr_accessor :app_name, :client_id, :client_secret
13
+ attr_accessor :auth_token
14
+
15
+ def initialize(app_name, client_id, client_secret)
16
+ @app_name = app_name
17
+ @client_id = client_id
18
+ @client_secret = client_secret
19
+ end
20
+
21
+ def request_app_auth(scopes)
22
+ if scopes.empty?
23
+ raise MxitRails::MxitApi::Exception.new("No scopes were provided.")
24
+ end
25
+
26
+ response = http_client(MXIT_AUTH_TOKEN_URI) do |http, path|
27
+
28
+ request = new_post_request(path, {
29
+ "grant_type" => "client_credentials",
30
+ "scope" => scopes.join(" ")
31
+ })
32
+
33
+ http.request(request)
34
+ end
35
+
36
+ case response
37
+ when Net::HTTPSuccess then
38
+ @auth_token = AuthToken.new(JSON.parse(response.body))
39
+
40
+ else
41
+ raise MxitRails::MxitApi::RequestException.new(response.message, response.code)
42
+ end
43
+ end
44
+
45
+ # The user's response to the authorisation code request will be redirected to `redirect_uri`. If
46
+ # the request was successful there will be a `code` request parameter; otherwise `error`.
47
+ #
48
+ # redirect_uri - absolute URI to which the user will be redirected after authorisation
49
+ # state - passed back to `redirect_uri` as a request parameter
50
+ # scopes - list of scopes to which access is required
51
+ def user_code_request_uri(redirect_uri, state, scopes)
52
+ if scopes.empty?
53
+ raise MxitRails::MxitApi::Exception.new("No scopes were provided.")
54
+ end
55
+
56
+ # build parameters
57
+ parameters = {
58
+ :response_type => "code",
59
+ :client_id => @client_id,
60
+ :redirect_uri => redirect_uri,
61
+ :state => state,
62
+ :scope => scopes.join(' ')
63
+ }
64
+
65
+ path = MXIT_AUTH_CODE_URI + "?#{URI.encode_www_form(parameters)}"
66
+ end
67
+
68
+ # NOTE: `user_code_request_uri` must be used before `request_user_auth` because it provides the
69
+ # `code` argument. `redirect_uri` must match the one used in the `user_code_request_uri` call
70
+ def request_user_auth(code, redirect_uri)
71
+ response = http_client(MXIT_AUTH_TOKEN_URI) do |http, path|
72
+
73
+ request = new_post_request(path, {
74
+ "grant_type" => "authorization_code",
75
+ "code" => code,
76
+ "redirect_uri" => redirect_uri
77
+ })
78
+
79
+ http.request(request)
80
+ end
81
+
82
+ case response
83
+ when Net::HTTPSuccess then
84
+ @auth_token = AuthToken.new(JSON.parse(response.body))
85
+
86
+ else
87
+ raise MxitRails::MxitApi::RequestException.new(response.message, response.code)
88
+ end
89
+ end
90
+
91
+ def revoke_token(auth_token)
92
+ response = http_client(MXIT_AUTH_BASE_URI + "/revoke") do |http, path|
93
+
94
+ request = new_post_request(path, {
95
+ "token" => auth_token.access_token
96
+ })
97
+
98
+ http.request(request)
99
+ end
100
+
101
+ if response.code != '200'
102
+ raise MxitRails::MxitApi::RequestException.new(response.message, response.code)
103
+ end
104
+ end
105
+
106
+ def refresh_token(auth_token)
107
+ if auth_token.refresh_token.nil?
108
+ raise MxitRails::MxitApi::Exception.new("The provided auth token doesn't have a refresh " +
109
+ "token.")
110
+ end
111
+
112
+ response = http_client(MXIT_AUTH_TOKEN_URI) do |http, path|
113
+
114
+ request = new_post_request(path, {
115
+ "grant_type" => "refresh_token",
116
+ "refresh_token" => auth_token.refresh_token
117
+ })
118
+
119
+ http.request(request)
120
+ end
121
+
122
+ case response
123
+ when Net::HTTPSuccess then
124
+ auth_token = AuthToken.new(JSON.parse(response.body))
125
+
126
+ else
127
+ raise MxitRails::MxitApi::RequestException.new(response.message, response.code)
128
+ end
129
+ end
130
+
131
+ ### API methods requiring authorisation.
132
+
133
+ # When sending as the app the `message/send` scope is required otherwise `message/user`
134
+ def send_message(from, to, body, contains_markup, auth_token=nil)
135
+ auth_token = auth_token || @auth_token
136
+
137
+ if from == @app_name
138
+ check_auth_token(auth_token, ["message/send"])
139
+ else
140
+ check_auth_token(auth_token, ["message/user"])
141
+ end
142
+
143
+ response = http_client(MXIT_API_URI + "/message/send/") do |http, path|
144
+
145
+ request = Net::HTTP::Post.new(path)
146
+ set_api_headers(request, auth_token.access_token)
147
+
148
+ request.body = {
149
+ "Body" => body,
150
+ "ContainsMarkup" => contains_markup,
151
+ "From" => from,
152
+ "To" => to
153
+ # "Spool" => default(true)
154
+ # "SpoolTimeOut" => default(60*60*24*7)
155
+ }.to_json
156
+
157
+ http.request(request)
158
+ end
159
+
160
+ if response.code != '200'
161
+ raise MxitRails::MxitApi::RequestException.new(response.message, response.code)
162
+ end
163
+ end
164
+
165
+ # The following filter parameters are available (only one can be specified at a time):
166
+ # @All - Return all roster entries
167
+ # @Friends - Return only friends
168
+ # @Apps - Return only applications
169
+ # @Invites - Return all entries that is in an invite state
170
+ # @Connections - Return all entries that has been accepted
171
+ # @Rejected - Return all entries that has been rejected
172
+ # @Pending - Return all entries that is waiting to be accepted by the other party
173
+ # @Deleted - Return all entries that was deleted
174
+ # @Blocked - Return all entries that was blocked
175
+ def get_contact_list(filter, options={ :skip => nil, :count => nil, :auth_token => nil })
176
+ auth_token = options[:auth_token] || @auth_token
177
+ check_auth_token(auth_token, ["graph/read"])
178
+
179
+ response = http_client(MXIT_API_URI + "/user/socialgraph/contactlist") do |http, path|
180
+
181
+ parameters = { :filter => filter }
182
+ # skip and count are optional
183
+ parameters[:skip] = skip if options[:skip]
184
+ parameters[:count] = count if options[:count]
185
+
186
+ request = Net::HTTP::Get.new(path + "?#{URI.encode_www_form(parameters)}")
187
+ set_api_headers(request, auth_token.access_token)
188
+
189
+ http.request(request)
190
+ end
191
+
192
+ case response
193
+ when Net::HTTPSuccess then
194
+ data = JSON.parse(response.body)
195
+
196
+ else
197
+ raise MxitRails::MxitApi::RequestException.new(response.message, response.code)
198
+ end
199
+ end
200
+
201
+ def batch_notify_users(mxit_ids, message, contains_markup)
202
+ Rails.logger.info('Requesting MXit API auth...')
203
+ request_app_auth(["message/send"])
204
+ Rails.logger.info('Finished MXit API auth.')
205
+
206
+ batch_size = 50
207
+ Rails.logger.info('Starting to notify users in batches of ' + batch_size.to_s + '...')
208
+ i = 0
209
+ while i < mxit_ids.count
210
+ current_batch = mxit_ids[i, batch_size]
211
+ i += batch_size
212
+
213
+ to = current_batch.join(',')
214
+ send_message(@app_name, to, message, contains_markup)
215
+
216
+ Rails.logger.info("Total users notified: " + current_batch.count.to_s)
217
+ end
218
+ Rails.logger.info('Finished notifying!')
219
+ end
220
+
221
+ private
222
+
223
+ def http_client(url)
224
+ uri = URI(url)
225
+
226
+ use_ssl = uri.scheme == 'https'
227
+ response = Net::HTTP.start(uri.host, uri.port, :use_ssl => use_ssl) do |http|
228
+ yield(http, uri.path)
229
+ end
230
+ end
231
+
232
+ def new_post_request(path, form_data)
233
+ request = Net::HTTP::Post.new(path)
234
+ request.basic_auth(@client_id, @client_secret)
235
+ request.set_form_data(form_data)
236
+ return request
237
+ end
238
+
239
+ def set_request_auth(request, access_token)
240
+ request["Authorization"] = "Bearer " + access_token
241
+ end
242
+
243
+ def set_api_headers(request, access_token, format="application/json")
244
+ set_request_auth(request, access_token)
245
+ request["Accept"] = format
246
+ request.content_type = format
247
+ end
248
+
249
+ def check_auth_token(auth_token, scopes)
250
+ if auth_token.nil?
251
+ raise MxitRails::MxitApi::Exception.new("No auth token has been set/provided.")
252
+ elsif not auth_token.has_scopes? scopes
253
+ raise MxitRails::MxitApi::Exception.new("The auth token doesn't have the required " +
254
+ "scope(s).")
255
+ end
256
+ end
257
+
258
+ end
259
+
260
+ end
@@ -0,0 +1,35 @@
1
+ module MxitRails::MxitApi
2
+ class AuthToken
3
+ attr_reader :access_token, :type, :expires_in, :refresh_token, :expires_at,
4
+ :refresh_token_expires_at
5
+
6
+ def initialize(token_response)
7
+ @access_token = token_response['access_token']
8
+ @type = token_response['type']
9
+ @expires_in = token_response['expires_in']
10
+ @refresh_token = token_response['refresh_token']
11
+ @scope = token_response['scope'].split
12
+
13
+ @expires_at = Time.now + expires_in.seconds
14
+ # If there isn't a refresh token `has_refresh_token_expired?` must always return true.
15
+ @refresh_token_expires_at = @refresh_token ? Time.now + 24.hours : Time.now
16
+ end
17
+
18
+ def scope
19
+ @scope.join(' ')
20
+ end
21
+
22
+ def has_expired?
23
+ # For extreme latency check within 3 seconds.
24
+ @expires_at - Time.now <= 3.0
25
+ end
26
+
27
+ def has_refresh_token_expired?
28
+ @refresh_token_expires_at - Time.now <= 3.0
29
+ end
30
+
31
+ def has_scopes?(scopes)
32
+ (scopes - @scope).empty?
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,8 @@
1
+ module MxitRails::MxitApi
2
+ class Exception < Exception
3
+ attr_reader :message
4
+ def initialize(message)
5
+ @message = message
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,9 @@
1
+ module MxitRails::MxitApi
2
+ class RequestException < MxitRails::MxitApi::Exception
3
+ attr_reader :code
4
+ def initialize(message, code)
5
+ super(message)
6
+ @code = code
7
+ end
8
+ end
9
+ end
@@ -70,6 +70,7 @@ module MxitRails
70
70
 
71
71
  @_mxit = descriptor
72
72
  @_mxit_validated = true
73
+ @_mxit_validation_types = []
73
74
  @_mxit_validation_messages = []
74
75
 
75
76
  clean_session
@@ -118,6 +119,7 @@ module MxitRails
118
119
 
119
120
  valid = true
120
121
  input = descriptor.input.to_sym
122
+ validation_type = arguments.first
121
123
 
122
124
  if block.nil?
123
125
  parameter = arguments[1..-2][0] # Will return nil if there isn't an argument
@@ -127,30 +129,36 @@ module MxitRails
127
129
  end
128
130
 
129
131
  else
132
+ validation_type = :custom
130
133
  valid = yield(params[input])
131
134
  end
132
135
 
133
136
  if !valid
134
137
  @_mxit_validated = false
138
+ @_mxit_validation_types << validation_type
135
139
  @_mxit_validation_messages << arguments.last
136
140
  end
137
141
 
138
142
  end
139
143
 
144
+ def validations_failed &block
145
+ descriptor.validations_failed = block
146
+ end
147
+ def validated &block
148
+ descriptor.validated = block
149
+ end
150
+
140
151
  def submit &block
141
152
  set_descriptor :default
142
153
 
143
154
  if descriptor.form? && next_step?
144
155
  yield
145
156
 
146
- elsif params.include?(:_mxit_rails_submit)
147
- if @_mxit_validated
148
- yield
149
- end
157
+ elsif (params.include?(:_mxit_rails_submit) && @_mxit_validated) || (self.current_step == :_submit)
158
+ yield
150
159
  end
151
160
  end
152
161
 
153
-
154
162
  def current_step
155
163
  return nil if session[:_mxit_rails_step].nil?
156
164
  session[:_mxit_rails_step].to_sym
@@ -176,6 +184,12 @@ module MxitRails
176
184
 
177
185
  if params.include?(:_mxit_rails_submit)
178
186
  if @_mxit_validated
187
+ # Validated hook
188
+ unless descriptor.validated.nil?
189
+ descriptor.validated.call
190
+ end
191
+
192
+ # Store input in session
179
193
  if descriptor.input
180
194
  input = descriptor.input.to_sym
181
195
  mxit_form_session[input] = params[input]
@@ -185,9 +199,15 @@ module MxitRails
185
199
  mxit_form_session[select] = params[select]
186
200
  end
187
201
 
202
+ # Clear submission flag from params and go to next step
188
203
  params.delete :_mxit_rails_submit
189
204
  @next_step = true
190
205
  return
206
+ else
207
+ # Validations_failed hook
208
+ unless descriptor.validations_failed.nil?
209
+ descriptor.validations_failed.call(@_mxit_validation_types, @_mxit_validation_messages)
210
+ end
191
211
  end
192
212
  end
193
213
 
@@ -203,6 +223,12 @@ module MxitRails
203
223
  redirect_to request.path
204
224
  end
205
225
 
226
+ def submit!
227
+ self.current_step = :_submit
228
+ # A redirect might not be absolutely required, but it makes things much simpler
229
+ redirect_to request.path
230
+ end
231
+
206
232
  def form &block
207
233
  descriptor.type = :form
208
234
  session[:_mxit_rails_params] ||= {}