mxit-rails 0.2.5 → 0.2.6

Sign up to get free protection for your applications and to get access to all the features.
@@ -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] ||= {}