Created
May 28, 2012 18:44
-
-
Save brianpattison/2820601 to your computer and use it in GitHub Desktop.
ACS Users Controller Work in Progress
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var Ember = require('/lib/em_ti/ember-runtime'); | |
Keychain = require('com.obscure.keychain'); | |
/** | |
@class | |
An Appcelerator Cloud Services User. | |
@extends Ember.Object | |
*/ | |
var User = Ember.Object.extend({ | |
// .......................................................... | |
// Properties | |
// | |
/** | |
Unique identifier for the user from ACS. | |
@property {String} | |
*/ | |
id: null, | |
/** | |
Email used to log in to ACS. | |
@property {String} | |
*/ | |
email: null, | |
/** | |
Access token used to sign in to ACS using Facebook. | |
@property {String} | |
*/ | |
facebookAccessToken: null, | |
/** | |
Unique ID for user's Facebook account. | |
@property {Number} | |
*/ | |
facebookId: null, | |
/** | |
Full name of the ACS user. | |
@property {String} | |
*/ | |
name: null, | |
/** | |
Password used to log in to ACS. | |
@property {String} | |
*/ | |
password: null, | |
/** | |
OAuth access token key for authorized Twitter user. | |
@property {String} | |
*/ | |
twitterAccessTokenKey: null, | |
/** | |
OAuth access token secret for authorized Twitter user. | |
@property {String} | |
*/ | |
twitterAccessTokenSecret: null, | |
/** | |
Unique ID for user's Twitter account. | |
@property {Number} | |
*/ | |
twitterId: null, | |
/** | |
The name of the user as obtained from Facebook or Twitter or the username if name is not defined. | |
@property {String} | |
*/ | |
displayName: function() { | |
if (!Ember.none(this.get('name'))) { | |
return this.get('name'); | |
} else { | |
return this.get('username'); | |
} | |
}.property('name', 'username'), | |
// .......................................................... | |
// Methods | |
// | |
/** | |
Save the User to the local db. | |
@returns {User} The saved version of the object. | |
*/ | |
save: function() { | |
var db = Titanium.Database.open('mydb'); | |
Ti.API.info(this.toString()); | |
db.execute('CREATE TABLE IF NOT EXISTS users (id TEXT, json TEXT)'); | |
if (Ember.none(User.find(this.get('id')))) { | |
db.execute('INSERT INTO users (id, json) VALUES (?, ?)', this.get('id'), this.toString()); | |
} else { | |
db.execute('UPDATE users SET json = ? WHERE id = ?', this.toString(), this.get('id')); | |
} | |
db.close(); | |
if (!Ember.none(this.get('password'))) { | |
var keychainItem = Keychain.createItem({ identifier: 'login' }); | |
keychainItem.account = this.get('username'); | |
keychainItem.valueData = this.get('password'); | |
} | |
return this; | |
}, | |
/** | |
Returns the User object as a JSON object. | |
@returns {Hash} | |
*/ | |
toJSON: function() { | |
return this.getProperties('id', 'email', 'facebookAccessToken', 'facebookId', 'name', 'twitterAccessTokenKey', 'twitterAccessTokenSecret', 'twitterId'); | |
}, | |
/** | |
Returns the User object as a stringified JSON object. | |
@returns {String} | |
*/ | |
toString: function() { | |
return JSON.stringify(this.toJSON()); | |
} | |
}); | |
User.reopenClass({ | |
/** | |
Override the default create method to handle creating from stringified JSON and arrays. | |
@param {Object} options | |
A JSON object, an Array of JSON objects, or a stringified JSON object to create the User object(s). | |
@returns {Object} The created User object or an Array of User objects. | |
*/ | |
create: function(options) { | |
if (Ember.none(options)) return this._super(); | |
if (typeof options === 'string') { | |
try { | |
var parsedOptions = JSON.parse(options); | |
return this.create(parsedOptions); | |
} catch(e) { | |
Ti.API.warn('User#create could not parse the options string.'); | |
} | |
} else if (options.constructor.toString().indexOf('Array') > -1) { | |
var objArray = []; | |
for (var i = 0; i < options.length; i++) { | |
objArray.push(this.create(options[i])); | |
} | |
return objArray; | |
} else { | |
return this._super(options); | |
} | |
}, | |
/** | |
Search the local db for a User for a specific id. | |
@param {String} id | |
The unique id for the User you're looking up as a string. | |
@returns {Object} The User object or null if no User is found. | |
*/ | |
find: function(id) { | |
var db = Titanium.Database.open('mydb'), user = null; | |
db.execute('CREATE TABLE IF NOT EXISTS users (id TEXT, json TEXT)'); | |
var row = db.execute('SELECT * FROM users WHERE id = ?', id); | |
if (row.isValidRow()) { | |
user = this.create(row.fieldByName('json')); | |
} | |
row.close(); | |
db.close(); | |
return user; | |
}, | |
/** | |
Convert one or many User objects to JSON objects. | |
@param {Object} user | |
A User object or an array of User objects. | |
@returns {Object} The JSON object or an Array of JSON objects. | |
*/ | |
toJSON: function(user) { | |
if (!Ember.none(user) && user.constructor.toString().indexOf('Array') > -1) { | |
var jsonArray = []; | |
for (var i = 0; i < user.length; i++) { | |
jsonArray.push(user[i].toJSON()); | |
} | |
return jsonArray; | |
} else { | |
return user.toJSON(); | |
} | |
} | |
}); | |
module.exports = User; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var Cloud = require('ti.cloud'), | |
Ember = require('/lib/em_ti/ember-runtime'), | |
Keychain = require('com.obscure.keychain'), | |
User = require('/models/user'); | |
/** | |
@class | |
Manage Appcelerator Cloud Services, Facebook, and Twitter accounts. | |
@extends Ember.Object | |
*/ | |
var UsersController = Ember.Object.extend({ | |
/** | |
The current logged in user. | |
@property {User} | |
*/ | |
content: User.create(), | |
/** | |
Array of Users found via Facebook. | |
@property {Array} | |
*/ | |
facebookFriends: [], | |
/** | |
Array of Users found via Twitter. | |
@property {Array} | |
*/ | |
twitterFriends: [], | |
/** | |
Initialize the controller with the Facebook and Twitter keys, | |
and listen for when the user authorizes Facebook or Twitter. | |
*/ | |
init: function() { | |
this._super(); | |
var facebookAppId = Ti.App.Properties.getString('facebook-app-id', null), | |
twitterConsumerKey = Ti.App.Properties.getString('twitter-consumer-key', null), | |
twitterConsumerSecret = Ti.App.Properties.getString('twitter-consumer-secret', null), | |
self = this; | |
if (facebookAppId === null) { | |
Ti.API.warn('The property `facebook-app-id` must be set in tiapp.xml to log in with Facebook.'); | |
Ti.API.warn('Example: <property name="facebook-app-id" type="string">[YOUR KEY]</property>'); | |
} else { | |
Ti.Facebook.appid = facebookAppId; | |
Ti.Facebook.addEventListener('login', function(e) { | |
if (e.success) { | |
var user = self.get('content'); | |
user.set('facebookId', Ti.Facebook.uid); | |
user.set('facebookAccessToken', Ti.Facebook.accessToken); | |
if (Ember.none(user.get('name')) && !Ember.none(e.data.name)) { | |
user.set('name', e.data.name); | |
} | |
if (Ember.none(user.get('id'))) { | |
self.signInWithFacebook(); | |
} else { | |
self.linkFacebookAccount(); | |
} | |
} else if (e.error) { | |
self.handleError(e.error); | |
} else if (e.cancelled) { | |
Ti.API.debug('Cancelled Facebook login.'); | |
} | |
}); | |
} | |
if (twitterConsumerKey === null || twitterConsumerSecret === null) { | |
Ti.API.warn('The properties `twitter-consumer-key` and `twitter-consumer-secret` must be set in tiapp.xml to log in with Twitter.'); | |
Ti.API.warn('Example: <property name="twitter-consumer-key" type="string">[YOUR KEY]</property>'); | |
Ti.API.warn('Example: <property name="twitter-consumer-secret" type="string">[YOUR KEY]</property>'); | |
} else { | |
var twitter = require('/lib/ti_platform_connect/twitter').Twitter({ | |
consumerKey: twitterConsumerKey, | |
consumerSecret: twitterConsumerSecret, | |
accessTokenKey: Ti.App.Properties.getString('twitterAccessTokenKey', ''), | |
accessTokenSecret: Ti.App.Properties.getString('twitterAccessTokenSecret', '') | |
}); | |
this.set('twitter', twitter); | |
twitter.addEventListener('login', function(e) { | |
if (e.success) { | |
Ti.App.Properties.setString('twitterAccessTokenKey', e.accessTokenKey); | |
Ti.App.Properties.setString('twitterAccessTokenSecret', e.accessTokenSecret); | |
var user = self.get('content'); | |
user.set('twitterAccessTokenKey', e.accessTokenKey); | |
user.set('twitterAccessTokenSecret', e.accessTokenSecret); | |
twitter.request('1/account/verify_credentials.json', {}, {}, 'GET', function(e) { | |
if (e.success) { | |
var json = JSON.parse(e.result.text); | |
user.set('twitterId', json.id); | |
if (Ember.none(user.get('name')) && !Ember.none(json.name)) { | |
user.set('name', json.name); | |
} | |
if (Ember.none(user.get('id'))) { | |
self.signInWithTwitter(); | |
} else { | |
self.linkTwitterAccount(); | |
} | |
} else { | |
self.handleError(e); | |
} | |
}); | |
} else { | |
self.handleError(e); | |
} | |
}); | |
} | |
}, | |
/** | |
Authorize using Facebook for logging in to ACS. | |
*/ | |
authorizeFacebook: function() { | |
var user = this.get('content'); | |
if (Ti.Facebook.getLoggedIn()) { | |
user.set('facebookId', Ti.Facebook.uid); | |
user.set('facebookAccessToken', Ti.Facebook.accessToken); | |
if (Ember.none(user.get('id'))) { | |
this.signInWithFacebook(); | |
} else { | |
this.linkFacebookAccount(); | |
} | |
} else { | |
Ti.Facebook.authorize(); | |
} | |
}, | |
/** | |
Authorize using Twitter for logging in to ACS. | |
*/ | |
authorizeTwitter: function() { | |
var twitter = this.get('twitter'); | |
twitter.authorize(); | |
}, | |
/** | |
Search for other users who have signed in with Facebook. | |
@param {Hash} options | |
Hash of options including button, and callback. | |
*/ | |
findFacebookFriends: function(options) { | |
var self = this; | |
Cloud.SocialIntegrations.searchFacebookFriends(function (e) { | |
if (e.success) { | |
Ti.API.info('Success!'); | |
Ti.API.info(e.users); | |
} else { | |
self.handleError(e); | |
} | |
self.handleCallbacks(options); | |
}); | |
}, | |
/** | |
Search for other users who have signed in with Twitter. | |
@param {Hash} options | |
Hash of options including button, and callback. | |
*/ | |
findTwitterFriends: function(options) { | |
var twitter = this.get('twitter'), self = this; | |
twitter.request('1/friends/ids.json', {}, {}, 'GET', function(e) { | |
if (e.success) { | |
var json = JSON.parse(e.result.text), twitterIds = json.ids; | |
// ACS requires that the Twitter IDs are strings for searching users. | |
for (var i = 0; i < twitterIds.length; i++) { | |
twitterIds[i] = twitterIds[i] + ''; | |
} | |
// There also needs to be a limit on the number of ids sent. | |
var page = 1, perPage = 400, start = 0, end = 0, users = []; | |
var getTwitterUsers = function() { | |
start = (page - 1) * perPage; | |
end = twitterIds.length; | |
if (twitterIds.length > page * perPage) { | |
end = page * perPage; | |
} | |
var searchIds = twitterIds.slice(start, end); | |
Cloud.Users.query({ | |
where: { | |
'external_accounts.external_type': 'twitter', | |
'external_accounts.external_id': { $in: searchIds } | |
} | |
}, function (e) { | |
if (e.success) { | |
users = users.concat(e.users); | |
if (end === twitterIds.length) { | |
// Last of the ids to search on ACS. Time to grab the user info from twitter. | |
var twitterFriends = []; | |
for (var i = 0; i < users.length; i++) { | |
for (var x = 0; x < users[i].external_accounts.length; x++) { | |
if (users[i].external_accounts[x].external_type === 'twitter') { | |
twitterFriends.push(User.create({ | |
id: users[i].id, | |
twitterId: parseInt(users[i].external_accounts[x].external_id, 10) | |
})); | |
break; | |
} | |
} | |
} | |
var lookupIds = twitterFriends.mapProperty('twitterId'); | |
twitter.request('1/users/lookup.json', { user_id: lookupIds.join(',') }, {}, 'POST', function(e) { | |
if (e.success) { | |
var json = JSON.parse(e.result.text); | |
for (var i = 0; i < json.length; i++) { | |
var user = twitterFriends.findProperty('twitterId', json[i].id); | |
user.setProperties({ | |
name: json[i].name, | |
avatarUrl: json[i].profile_image_url.replace('_normal', '') | |
}); | |
} | |
self.set('twitterFriends', twitterFriends); | |
self.handleCallbacks(options); | |
} else { | |
self.handleError(e); | |
self.handleCallbacks(options); | |
} | |
}); | |
} else { | |
page++; | |
getTwitterUsers(users); | |
} | |
} else { | |
self.handleError(e); | |
self.handleCallbacks(options); | |
} | |
}); | |
}; | |
getTwitterUsers(); | |
} else { | |
self.handleError(e); | |
self.handleCallbacks(options); | |
} | |
}); | |
}, | |
/** | |
Used to handle any callbacks required when passing | |
options to any of UsersController's methods | |
@param {Hash} options | |
Hash of options including button and callback. | |
*/ | |
handleCallbacks: function(options) { | |
if (Ember.none(options)) return this; | |
if (!Ember.none(options.button)) options.button.unclick(); | |
if (!Ember.none(options.callback)) options.callback(); | |
return this; | |
}, | |
/** | |
Handle any errors thrown when registering a user or signing in. | |
@param {Object} error | |
*/ | |
handleError: function(error) { | |
if (error.message) { | |
alert(error.message); | |
} | |
Ti.API.warn(JSON.stringify(error)); | |
}, | |
/** | |
Link Facebook account to existing ACS user account. | |
*/ | |
linkFacebookAccount: function() { | |
var user = this.get('content'), self = this; | |
Cloud.SocialIntegrations.externalAccountLink({ | |
id: user.get('facebookId'), | |
type: 'facebook', | |
token: user.get('facebookAccessToken') | |
}, function (e) { | |
if (e.success) { | |
var user = e.users[0]; | |
self.saveUser(user); | |
} else { | |
self.handleError(e); | |
} | |
}); | |
}, | |
/** | |
Link Twitter account to existing ACS user account. | |
*/ | |
linkTwitterAccount: function() { | |
var user = this.get('content'), self = this; | |
Cloud.SocialIntegrations.externalAccountLink({ | |
id: user.get('twitterId'), | |
type: 'twitter', | |
token: user.get('twitterAccessTokenKey') | |
}, function (e) { | |
if (e.success) { | |
var user = e.users[0]; | |
self.saveUser(user); | |
} else { | |
self.handleError(e); | |
} | |
}); | |
}, | |
/** | |
Create an ACS user account. | |
@param {Hash} options | |
Hash of options including button, and callback. | |
*/ | |
register: function(options) { | |
var user = this.get('content'), self = this; | |
Cloud.Users.create({ | |
first_name: user.get('name'), | |
email: user.get('email'), | |
password: user.get('password'), | |
password_confirmation: user.get('passwordConfirmation') | |
}, function (e) { | |
if (e.success) { | |
var user = e.users[0]; | |
self.saveUser(user); | |
options.callback(); | |
} else { | |
self.handleError(e); | |
} | |
options.button.unclick(); | |
}); | |
}, | |
/** | |
Save the logged in user. | |
@param {Object} userData | |
ACS user object | |
*/ | |
saveUser: function(userData) { | |
var user = this.get('content'); | |
user.set('id', userData.id); | |
user.save(); | |
Ti.App.Properties.setString('acs-user-id', userData.id); | |
}, | |
/** | |
Sign in to ACS. | |
The method for signing in is determined by the `type` option. | |
@param {Hash} options | |
Hash of options including type, button, and callback. | |
*/ | |
signIn: function(options) { | |
var user = this.get('content'), self = this; | |
if (Ember.none(options)) { | |
options = {}; | |
} | |
switch(options.type) { | |
case 'acs': | |
this.signInWithACS(); | |
break; | |
case 'facebook': | |
this.authorizeFacebook(); | |
break; | |
case 'twitter': | |
this.authorizeTwitter(); | |
break; | |
default: | |
// Try signing in with saved login info. | |
var id = Ti.App.Properties.getString('acs-user-id', null); | |
user = User.find(id); | |
if (!Ember.none(user)) { | |
this.set('content', user); | |
if (!Ember.none(user.get('facebookId'))) { | |
this.signInWithFacebook(); | |
} else if (!Ember.none(user.get('twitterId'))) { | |
this.signInWithTwitter(); | |
} else if (!Ember.none(user.get('email'))) { | |
var keychainItem = Keychain.createItem({ identifier: 'login' }); | |
user.set('password', keychainItem.valueData); | |
this.signInWithACS(); | |
} | |
} | |
} | |
this.handleCallbacks(options); | |
}, | |
/** | |
Sign out of ACS, Facebook, and Twitter. | |
*/ | |
signOut: function(options) { | |
Ti.API.warn('UsersController#signOut not implemented yet.'); | |
}, | |
/** | |
Sign in to ACS user account using email and password. | |
*/ | |
signInWithACS: function() { | |
var user = this.get('content'), self = this; | |
Cloud.Users.login({ | |
login: user.get('email'), | |
password: user.get('password') | |
}, function (e) { | |
if (e.success) { | |
var user = e.users[0]; | |
self.saveUser(user); | |
} else { | |
self.handleError(e); | |
} | |
}); | |
}, | |
/** | |
Sign in to ACS user account using Facebook. | |
*/ | |
signInWithFacebook: function() { | |
var user = this.get('content'), self = this; | |
Cloud.SocialIntegrations.externalAccountLogin({ | |
id: user.get('facebookId'), | |
type: 'facebook', | |
token: user.get('facebookAccessToken') | |
}, function (e) { | |
if (e.success) { | |
var user = e.users[0]; | |
self.saveUser(user); | |
} else { | |
self.handleError(e); | |
} | |
}); | |
}, | |
/** | |
Sign in to ACS user account using Twitter. | |
*/ | |
signInWithTwitter: function() { | |
var user = this.get('content'), self = this; | |
Cloud.SocialIntegrations.externalAccountLogin({ | |
id: user.get('twitterId'), | |
type: 'twitter', | |
token: user.get('twitterAccessTokenKey') | |
}, function (e) { | |
if (e.success) { | |
var user = e.users[0]; | |
self.saveUser(user); | |
} else { | |
self.handleError(e); | |
} | |
}); | |
}, | |
/** | |
Unlink Facebook account to existing ACS user account. | |
@param {Hash} options | |
Hash of options including button and callback. | |
*/ | |
unlinkFacebook: function(options) { | |
var user = this.get('content'), self = this; | |
Cloud.SocialIntegrations.externalAccountUnlink({ | |
id: user.get('facebookId'), | |
type: 'facebook' | |
}, function (e) { | |
if (e.success) { | |
Ti.Facebook.logout(); | |
user.setProperties({ facebookId: null, facebookAccessToken: null }); | |
user.save(); | |
} else { | |
self.handleError(e); | |
} | |
self.handleCallbacks(options); | |
}); | |
}, | |
/** | |
Unlink Twitter account to existing ACS user account. | |
@param {Hash} options | |
Hash of options including button and callback. | |
*/ | |
unlinkTwitter: function(options) { | |
var user = this.get('content'), self = this; | |
Cloud.SocialIntegrations.externalAccountUnlink({ | |
id: user.get('twitterId'), | |
type: 'twitter' | |
}, function (e) { | |
if (e.success) { | |
Ti.App.Properties.removeProperty('twitterAccessTokenKey'); | |
Ti.App.Properties.removeProperty('twitterAccessTokenSecret'); | |
user.setProperties({ twitterId: null, twitterAccessTokenKey: null, twitterAccessTokenSecret: null }); | |
user.save(); | |
} else { | |
self.handleError(e); | |
} | |
self.handleCallbacks(options); | |
}); | |
} | |
}); | |
module.exports = UsersController; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment