package controllers import play.api.mvc._ import play.api.Play import play.api.Play.current import play.api.libs.ws.WS import scala.util.matching.Regex import scala.concurrent.{Promise, Future, ExecutionContext} import ExecutionContext.Implicits.global import org.apache.commons.lang3.{RandomStringUtils, StringUtils} import jp.t2v.lab.play2.auth.LoginLogout import jp.t2v.lab.play2.stackc.{StackableController, RequestWithAttributes} import controllers.element.{TransactionElement, AuthConfigImpl} import com.restfb.DefaultFacebookClient import models.{Users, User} import scala.util.{Failure, Success} import utils.HttpUtil /** * The Class FacebookCtr. * * @author Nguyen Duc Dung * @since 1/12/14 6:23 PM * */ class FacebookCtr extends Controller with StackableController with LoginLogout with AuthConfigImpl with TransactionElement { lazy val app_id = Play.configuration.getString("facebook.app.id").getOrElse(throw new Exception("Please add facebook.app.id to application.conf")) lazy val app_secret = Play.configuration.getString("facebook.app.secret").getOrElse(throw new Exception("Please add facebook.app.secret to application.conf")) lazy val redirect_url = Play.configuration.getString("application.facebook.redirect.url").getOrElse(throw new Exception("Please add application.facebook.redirect.url to application.conf")) lazy val regex = new Regex("access_token=(.*)&expires=(.*)") def profilePictureUrl(facebookId: String) = s"http://graph.facebook.com/$facebookId/picture?type=normal" def login = Action(implicit request => { val url = s"https://www.facebook.com/dialog/oauth?client_id=$app_id&redirect_uri=$redirect_url&scope=email,public_profile,user_friends" Redirect(url) }) def auth = AsyncStack(implicit request => { request.getQueryString("code").map(code => { val accessTokenUrl = s"https://graph.facebook.com/oauth/access_token?client_id=$app_id&client_secret=$app_secret&code=$code&redirect_uri=$redirect_url" WS.url(accessTokenUrl).get().flatMap(response => response.body match { case regex(accessToken, expires) => processToken(accessToken) case _ => Future.successful(BadRequest) }) }).getOrElse(Future.successful(BadRequest)) }) def authorize(accessToken: String) = AsyncStack(implicit request => { processToken(accessToken) }) private def processToken(accessToken: String)(implicit request: RequestWithAttributes[_]): Future[SimpleResult] = { val promise = Promise[SimpleResult]() def getUserProfile = Future { val facebookClient = new DefaultFacebookClient(accessToken) val fbUser = facebookClient.fetchObject("me", classOf[com.restfb.types.User]) fbUser } def getUserAvatar(fbId: String) = HttpUtil.safeDownload(profilePictureUrl(fbId)) getUserProfile.onComplete { case Success(fbUser) => //Make sure email is not null if (fbUser != null && StringUtils.isNotBlank(fbUser.getEmail)) { Users.findByEmail(fbUser.getEmail) .map(user => promise.completeWith(gotoLoginSucceeded(user.email))) .getOrElse { val newPassword = RandomStringUtils.randomAlphanumeric(6) val fullName = if (StringUtils.isNotBlank(fbUser.getName)) Some(fbUser.getName) else None getUserAvatar(fbUser.getId).onComplete { case Success(avatar) => val newUser = User( email = fbUser.getEmail, fullName = fullName, avatar = avatar.getOrElse(Array.empty), password = newPassword ) Users.save(newUser) promise.completeWith(gotoLoginSucceeded(newUser.email)) case Failure(ex) => promise.failure(ex) } } } else { promise.success(Redirect("/user/register")) } case Failure(ex) => promise.failure(ex) } promise.future } }