PageRenderTime 44ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 0ms

/modules/facebook/index.php

https://github.com/whale2/users
PHP | 577 lines | 472 code | 64 blank | 41 comment | 53 complexity | 7f468ccf5219213b938d776ea368bfea MD5 | raw file
  1. <?php
  2. require_once(dirname(__FILE__).'/facebook.php');
  3. class FacebookAuthenticationModule extends AuthenticationModule
  4. {
  5. private $sdk;
  6. private $appID;
  7. private $secret;
  8. private $permissions;
  9. private $remember;
  10. private $headersLoaded = false;
  11. /**
  12. * Cretes Facebook authentication module
  13. * @param string $appID Facebook application ID
  14. * @param string $secret Facebook application secret (not key)
  15. * @param array $permissions Array of additional permissions (e.g. email)
  16. * full list can be found here: http://developers.facebook.com/docs/authentication/permissions/
  17. */
  18. public function __construct($appID, $secret, $permissions = array(), $remember = true)
  19. {
  20. parent::__construct();
  21. $this->appID= $appID;
  22. $this->secret = $secret;
  23. $this->permissions = $permissions;
  24. // TODO Replace it with immediate FB Connect call:
  25. // http://code.google.com/p/userbase/issues/detail?id=16
  26. $this->remember = $remember;
  27. $config = array(
  28. 'appId' => $this->appID,
  29. 'secret' => $this->secret
  30. );
  31. $this->sdk = new Facebook($config);
  32. }
  33. public function getID()
  34. {
  35. return "facebook";
  36. }
  37. public function getLegendColor()
  38. {
  39. return "3b5999";
  40. }
  41. public function getTitle()
  42. {
  43. return "Facebook";
  44. }
  45. public function getUserCredentials($user)
  46. {
  47. $db = UserConfig::getDB();
  48. $userid = $user->getID();
  49. if ($stmt = $db->prepare('SELECT fb_id FROM '.UserConfig::$mysql_prefix.'users WHERE id = ?'))
  50. {
  51. if (!$stmt->bind_param('i', $userid))
  52. {
  53. throw new Exception("Can't bind parameter".$stmt->error);
  54. }
  55. if (!$stmt->execute())
  56. {
  57. throw new Exception("Can't execute statement: ".$stmt->error);
  58. }
  59. if (!$stmt->bind_result($fb_id))
  60. {
  61. throw new Exception("Can't bind result: ".$stmt->error);
  62. }
  63. $stmt->fetch();
  64. $stmt->close();
  65. if (!is_null($fb_id))
  66. {
  67. return new FacebookUserCredentials($fb_id);
  68. }
  69. }
  70. else
  71. {
  72. throw new Exception("Can't prepare statement: ".$db->error);
  73. }
  74. return null;
  75. }
  76. public function getTotalConnectedUsers()
  77. {
  78. $db = UserConfig::getDB();
  79. $conns = 0;
  80. if ($stmt = $db->prepare('SELECT count(*) AS conns FROM '.UserConfig::$mysql_prefix.'users WHERE fb_id IS NOT NULL'))
  81. {
  82. if (!$stmt->execute())
  83. {
  84. throw new Exception("Can't execute statement: ".$stmt->error);
  85. }
  86. if (!$stmt->bind_result($conns))
  87. {
  88. throw new Exception("Can't bind result: ".$stmt->error);
  89. }
  90. $stmt->fetch();
  91. $stmt->close();
  92. }
  93. else
  94. {
  95. throw new Exception("Can't prepare statement: ".$db->error);
  96. }
  97. return $conns;
  98. }
  99. /*
  100. * retrieves aggregated registrations numbers
  101. */
  102. public function getDailyRegistrations()
  103. {
  104. $db = UserConfig::getDB();
  105. $dailyregs = array();
  106. if ($stmt = $db->prepare('SELECT CAST(regtime AS DATE) AS regdate, count(*) AS regs FROM '.UserConfig::$mysql_prefix.'users WHERE fb_id IS NOT NULL GROUP BY regdate'))
  107. {
  108. if (!$stmt->execute())
  109. {
  110. throw new Exception("Can't execute statement: ".$stmt->error);
  111. }
  112. if (!$stmt->bind_result($regdate, $regs))
  113. {
  114. throw new Exception("Can't bind result: ".$stmt->error);
  115. }
  116. while($stmt->fetch() === TRUE)
  117. {
  118. $dailyregs[] = array('regdate' => $regdate, 'regs' => $regs);
  119. }
  120. $stmt->close();
  121. }
  122. else
  123. {
  124. throw new Exception("Can't prepare statement: ".$db->error);
  125. }
  126. return $dailyregs;
  127. }
  128. public function renderLoginForm($action)
  129. {
  130. if (is_null($action))
  131. {
  132. $action = UserConfig::$USERSROOTURL.'/login.php?module='.$this->getID();
  133. }
  134. $this->renderForm($action, 'login');
  135. }
  136. public function renderAutoLogoutForm()
  137. {
  138. ?><html>
  139. <head><title>Logging out from Facebook...</title></head>
  140. <body>
  141. <div id="fb-root"></div>
  142. <script src="http://connect.facebook.net/en_US/all.js"></script>
  143. <script>
  144. FB.init({
  145. appId : '<?php echo $this->appID?>',
  146. status : true, // check login status
  147. cookie : true, // enable cookies to allow the server to access the session
  148. channelURL : '<?php echo UserConfig::$USERSROOTFULLURL; ?>/modules/facebook/channel.php', // channel file
  149. oauth : true // enable OAuth 2.0
  150. });
  151. FB.getLoginStatus(function(response) {
  152. if (response.authResponse) {
  153. FB.logout(function(response) {
  154. window.location.href = "<?php echo UserConfig::$USERSROOTFULLURL; ?>/logout.php?autologgedout=<?php echo $this->getID(); ?>";
  155. });
  156. } else {
  157. window.location.href = "<?php echo UserConfig::$USERSROOTFULLURL; ?>/logout.php?autologgedout=<?php echo $this->getID(); ?>";
  158. }
  159. });
  160. // force logout sequence after a long timeout of 15 seconds
  161. setTimeout('window.location.href = "<?php echo UserConfig::$USERSROOTFULLURL; ?>/logout.php?autologgedout=<?php echo $this->getID(); ?>"', 15000);
  162. </script>
  163. Logging out from Facebook...
  164. </body>
  165. </html>
  166. <?php
  167. }
  168. private function renderForm($action, $form)
  169. {
  170. if ($form == 'login') {
  171. $formsubmit = 'login';
  172. $buttonspritestyle = 'background-position: 0px -22px; width: 198px; height: 22px;';
  173. $buttontitle = 'Login with Facebook';
  174. } else if ($form == 'register') {
  175. $formsubmit = 'register';
  176. $buttonspritestyle = 'background-position: 0px 0px; width: 250px; height: 22px;';
  177. $buttontitle = 'Quick Sign-up using Facebook';
  178. } else if ($form == 'connect') {
  179. $formsubmit = 'save';
  180. $buttonspritestyle = 'background-position: 0px -44px; width: 230px; height: 22px;';
  181. $buttontitle = 'Connect to your Facebook Account';
  182. }
  183. ?><div id="fb-root"></div>
  184. <form action="<?php echo $action?>" method="POST" name="facebookconnectform">
  185. <input type="hidden" name="<?php echo $formsubmit ?>" value="Connect &gt;&gt;&gt;"/>
  186. <?php UserTools::renderCSRFNonce(); ?>
  187. </form>
  188. <a class="userbase-fb-connect" href="#" onclick="UserBaseFBConnectButtonClicked(); return false;"><span style="background-image: url(<?php echo UserConfig::$USERSROOTURL ?>/modules/facebook/facebook-sprite.png); <?php echo $buttonspritestyle ?> display: block; cursor: hand;" title="<?php echo $buttontitle ?>"></span></a>
  189. <script>
  190. var UserBaseFBConnectButtonClicked = function() {
  191. // FB is not loaded yet
  192. };
  193. window.fbAsyncInit = function() {
  194. // permissions required by this instance of UserBase
  195. var required_perms = <?php echo json_encode($this->permissions); ?>;
  196. var required_perms_string = <?php echo json_encode(implode(',', $this->permissions)); ?>;
  197. FB.init({
  198. appId : '<?php echo $this->appID?>',
  199. status : true, // check login status
  200. cookie : true, // enable cookies to allow the server to access the session
  201. oauth : true, // enable OAuth 2.0
  202. xfbml : true, // parse XFBML
  203. channelURL : '<?php echo UserConfig::$USERSROOTFULLURL; ?>/modules/facebook/channel.php' // channel file
  204. });
  205. // when button is clicked, auto-login or popu-up a dialog
  206. UserBaseFBConnectButtonClicked = function() {
  207. FB.getLoginStatus(function(r) {
  208. // TODO Also check if all permissions are set or we need more
  209. if(r.status === 'connected') {
  210. // alert('already logged in');
  211. document.facebookconnectform.submit();
  212. } else {
  213. // here perms is just a comma-separated string
  214. FB.login(function(response) {
  215. if (response.session &&
  216. (required_perms == '' || response.perms == required_perms)
  217. ) {
  218. document.facebookconnectform.submit();
  219. return;
  220. }
  221. }, {scope: required_perms_string});
  222. }
  223. });
  224. };
  225. FB.Event.subscribe('auth.login', function() {
  226. document.facebookconnectform.submit();
  227. });
  228. (function() {
  229. FB.getLoginStatus(function(response) {
  230. // getLoginStatus returns an array with 'extended' key or null
  231. if (response.session) {
  232. if (required_perms.length > 0) {
  233. // bug in API - it returns a serialized array
  234. if (typeof(response.perms) == 'string') {
  235. response.perms = JSON.parse(response.perms);
  236. }
  237. if (typeof(response.perms) == 'object'
  238. && typeof(response.perms.extended) == 'object'
  239. && (response.perms.extended instanceof Array)
  240. ) {
  241. var i = required_perms.length;
  242. while (i--) {
  243. var ex = response.perms.extended;
  244. var j = ex.length;
  245. var found = false;
  246. while (j--) {
  247. if (required_perms[i] == ex[j]) {
  248. found = true;
  249. break;
  250. }
  251. }
  252. if (!found) {
  253. return;
  254. }
  255. }
  256. } else {
  257. return; // no permissions passed
  258. }
  259. }
  260. // looks like we have enough permissions
  261. // override login button to use simple form submit
  262. UserBaseFBConnectButtonClicked = function() {
  263. document.facebookconnectform.submit();
  264. }
  265. return;
  266. } else {
  267. return;
  268. }
  269. }, {perms: required_perms_string});
  270. })(); // returning a function to run on login button click
  271. };
  272. (function() {
  273. var e = document.createElement('script');
  274. e.src = document.location.protocol + '//connect.facebook.net/en_US/all.js';
  275. e.async = true;
  276. document.getElementById('fb-root').appendChild(e);
  277. }());
  278. </script>
  279. <?php
  280. }
  281. public function renderRegistrationForm($full = false, $action = null, $errors = null, $data = null)
  282. {
  283. if (is_null($action))
  284. {
  285. $action = UserConfig::$USERSROOTURL.'/register.php?module='.$this->getID();
  286. }
  287. $this->renderForm($action, 'register');
  288. }
  289. /*
  290. * Renders user editing form
  291. *
  292. * Parameters:
  293. * $action - form action to post back to
  294. * $errors - error messages to display
  295. * $user - user object for current user that is being edited
  296. * $data - data submitted to the form
  297. */
  298. public function renderEditUserForm($action, $errors, $user, $data)
  299. {
  300. $fb_id = $user->getFacebookID();
  301. if (is_null($fb_id)) {
  302. $this->renderForm($action, 'connect');
  303. }
  304. else
  305. {
  306. try {
  307. $me = $this->sdk->api('/'.$fb_id);
  308. } catch (FacebookApiException $e) {
  309. UserTools::debug("Can't get /me API data");
  310. return null;
  311. }
  312. ?>
  313. <table><tr>
  314. <td rowspan="2"><a href="<?php echo $me['link'] ?>" target="_blank"><img src="http://graph.facebook.com/<?php echo $fb_id ?>/picture" style="border: 0; max-width: 100px; max-height: 100px" title="<?php echo UserTools::escape($me['name']) ?>"></a></td>
  315. <td><a href="<?php echo UserTools::escape($me['link']) ?>" target="_blank"><?php echo $me['name'] ?></a></td>
  316. </tr><tr>
  317. <td>
  318. <form action="<?php echo $action?>" method="POST" name="facebookusereditform">
  319. <input type="hidden" name="save" value="Save &gt;&gt;&gt;"/>
  320. <input type="submit" name="remove" value="remove" style="font-size: xx-small"/>
  321. <?php UserTools::renderCSRFNonce(); ?>
  322. </form>
  323. </td>
  324. </tr></table>
  325. <?php
  326. }
  327. }
  328. public function processAutoLogin()
  329. {
  330. UserTools::debug('Automatic login start');
  331. $remember = false;
  332. return $this->processLogin(null, $remember, true);
  333. }
  334. public function getAutoLogoutURL($return) {
  335. return UserConfig::$USERSROOTFULLURL.'/modules/facebook/logout.php';
  336. }
  337. public function processLogin($post_data, &$remember, $auto = false)
  338. {
  339. $remember = $this->remember;
  340. try {
  341. $fbuser = intval($this->sdk->getUser());
  342. } catch (FacebookApiException $e) {
  343. UserTools::debug("Can't get Facebook user");
  344. return null;
  345. }
  346. UserTools::debug('Facebook user id: '.$fbuser);
  347. if ($fbuser == 0) {
  348. // if we're trying to auto-login, just return null
  349. if ($auto) {
  350. return null;
  351. }
  352. $errors['fbuserid'][] = 'No Facebook id is passed';
  353. throw new InputValidationException('No facebook user id', 0, $errors);
  354. }
  355. $permissions = $this->sdk->api('/me/permissions');
  356. UserTools::debug('User permissions: '.var_export($permissions, true));
  357. foreach ($this->permissions as $perm) {
  358. if (!array_key_exists($perm, $permissions['data'][0]) || $permissions['data'][0][$perm] !== 1) {
  359. // looks like not all required permissions were granted
  360. UserTools::debug("Can't login - not enough permissions granted");
  361. return null;
  362. }
  363. }
  364. $user = User::getUserByFacebookID($fbuser);
  365. if (!is_null($user)) {
  366. $user->recordActivity(USERBASE_ACTIVITY_LOGIN_FB);
  367. return $user;
  368. } else if ($auto) {
  369. // do not auto-register if auto-logging-in
  370. UserTools::debug('Auto-logged-in, not registering');
  371. return null;
  372. } else {
  373. return $this->processRegistration($post_data, $remember);
  374. }
  375. }
  376. public function processRegistration($post_data, &$remember)
  377. {
  378. $remember = $this->remember;
  379. try {
  380. $fbuser = intval($this->sdk->getUser());
  381. } catch (FacebookApiException $e) {
  382. UserTools::debug("Can't get Facebook user");
  383. return null;
  384. }
  385. $errors = array();
  386. if ($fbuser == 0) {
  387. $errors['fbuserid'][] = 'No Facebook id is passed';
  388. throw new InputValidationException('No facebook user id', 0, $errors);
  389. }
  390. // checking if user with this Facebook ID already exists and if so, then logs them in
  391. $existing_user = User::getUserByFacebookID($fbuser);
  392. if (!is_null($existing_user))
  393. {
  394. $existing_user->recordActivity(USERBASE_ACTIVITY_LOGIN_FB);
  395. return $existing_user;
  396. }
  397. try {
  398. $me = $this->sdk->api('/me');
  399. } catch (FacebookApiException $e) {
  400. UserTools::debug("Can't get /me API data");
  401. return null;
  402. }
  403. if (array_key_exists('name', $me))
  404. {
  405. $name = $me['name'];
  406. }
  407. else
  408. {
  409. $errors['username'][] = "User doesn't have a name";
  410. }
  411. // ok, let's create a user
  412. try {
  413. $user = User::createNewFacebookUser($name, $fbuser, $me);
  414. } catch (UserCreationException $e) {
  415. $errors[$e->getField()][] = $e->getMessage();
  416. }
  417. if (count($errors) > 0)
  418. {
  419. throw new ExistingUserException('User already exists', 0, $errors);
  420. }
  421. $user->recordActivity(USERBASE_ACTIVITY_REGISTER_FB);
  422. return $user;
  423. }
  424. /*
  425. * Updates user information
  426. *
  427. * returns true if successful and false if unsuccessful
  428. *
  429. * throws InputValidationException if there are problems with input data
  430. */
  431. public function processEditUser($user, $data)
  432. {
  433. if (array_key_exists('remove', $data)) {
  434. $user->setFacebookID(null);
  435. $user->save();
  436. $user->recordActivity(USERBASE_ACTIVITY_REMOVED_FB);
  437. return true;
  438. }
  439. try {
  440. $fbuser = intval($this->sdk->getUser());
  441. } catch (FacebookApiException $e) {
  442. UserTools::debug("Can't get Facebook user");
  443. return null;
  444. }
  445. $errors = array();
  446. if ($fbuser == 0) {
  447. $errors['fbuserid'][] = 'No Facebook id is passed';
  448. throw new InputValidationException('No facebook user id', 0, $errors);
  449. }
  450. if (!is_null(User::getUserByFacebookID($fbuser)))
  451. {
  452. $errors['fbuserid'][] = 'Another user is already associated with your Facebook account.';
  453. }
  454. if (count($errors) > 0)
  455. {
  456. throw new InputValidationException('Validation failed', 0, $errors);
  457. }
  458. $user->setFacebookID($fbuser);
  459. // if user doesn't have email address and we required it for Facebook connection, let's save it
  460. if (!$user->getEmail()) {
  461. try {
  462. $me = $this->sdk->api('/me');
  463. } catch (FacebookApiException $e) {
  464. UserTools::debug("Can't get /me API data");
  465. return null;
  466. }
  467. if (array_key_exists('email', $me))
  468. {
  469. $user->setEmail($me['email']);
  470. }
  471. }
  472. $user->save();
  473. $user->recordActivity(USERBASE_ACTIVITY_ADDED_FB);
  474. return true;
  475. }
  476. }
  477. class FacebookUserCredentials extends UserCredentials {
  478. // Facebook user id
  479. private $fb_id;
  480. public function __construct($fb_id) {
  481. $this->fb_id = $fb_id;
  482. }
  483. public function getFacebookID() {
  484. return $this->fb_id;
  485. }
  486. public function getHTML() {
  487. return "<a href=\"http://www.facebook.com/profile.php?id=$this->fb_id\">$this->fb_id</a>";
  488. }
  489. }