Skip to content

Instantly share code, notes, and snippets.

@brianpattison
Created May 28, 2012 18:44
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save brianpattison/2820601 to your computer and use it in GitHub Desktop.
Save brianpattison/2820601 to your computer and use it in GitHub Desktop.
ACS Users Controller Work in Progress
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;
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