PageRenderTime 53ms CodeModel.GetById 21ms RepoModel.GetById 1ms app.codeStats 0ms

/lib/Lampcms/Controllers/Loginlinkedin.php

https://github.com/snytkine/LampCMS
PHP | 838 lines | 364 code | 141 blank | 333 comment | 55 complexity | 31d6a82761854f8025e1e1b6a8e940d0 MD5 | raw file
Possible License(s): LGPL-3.0
  1. <?php
  2. /**
  3. *
  4. * License, TERMS and CONDITIONS
  5. *
  6. * This software is licensed under the GNU LESSER GENERAL PUBLIC LICENSE (LGPL) version 3
  7. * Please read the license here : http://www.gnu.org/licenses/lgpl-3.0.txt
  8. *
  9. * Redistribution and use in source and binary forms, with or without
  10. * modification, are permitted provided that the following conditions are met:
  11. * 1. Redistributions of source code must retain the above copyright
  12. * notice, this list of conditions and the following disclaimer.
  13. * 2. Redistributions in binary form must reproduce the above copyright
  14. * notice, this list of conditions and the following disclaimer in the
  15. * documentation and/or other materials provided with the distribution.
  16. * 3. The name of the author may not be used to endorse or promote products
  17. * derived from this software without specific prior written permission.
  18. *
  19. * ATTRIBUTION REQUIRED
  20. * 4. All web pages generated by the use of this software, or at least
  21. * the page that lists the recent questions (usually home page) must include
  22. * a link to the http://www.lampcms.com and text of the link must indicate that
  23. * the website\'s Questions/Answers functionality is powered by lampcms.com
  24. * An example of acceptable link would be "Powered by <a href="http://www.lampcms.com">LampCMS</a>"
  25. * The location of the link is not important, it can be in the footer of the page
  26. * but it must not be hidden by style attributes
  27. *
  28. * THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR IMPLIED
  29. * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
  30. * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
  31. * IN NO EVENT SHALL THE FREEBSD PROJECT OR CONTRIBUTORS BE LIABLE FOR ANY
  32. * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  33. * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  34. * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  35. * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  36. * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  37. * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  38. *
  39. * This product includes GeoLite data created by MaxMind,
  40. * available from http://www.maxmind.com/
  41. *
  42. *
  43. * @author Dmitri Snytkine <cms@lampcms.com>
  44. * @copyright 2005-2012 (or current year) Dmitri Snytkine
  45. * @license http://www.gnu.org/licenses/lgpl-3.0.txt GNU LESSER GENERAL PUBLIC LICENSE (LGPL) version 3
  46. * @link http://www.lampcms.com Lampcms.com project
  47. * @version Release: @package_version@
  48. *
  49. *
  50. */
  51. namespace Lampcms\Controllers;
  52. use \Lampcms\WebPage;
  53. use \Lampcms\Responder;
  54. use \Lampcms\Request;
  55. use \Lampcms\Cookie;
  56. use \Lampcms\Mongo\Schema\User as Schema;
  57. use \Lampcms\Acl\Role;
  58. class Loginlinkedin extends WebPage
  59. {
  60. const REQUEST_TOKEN_URL = 'https://api.linkedin.com/uas/oauth/requestToken?scope=r_basicprofile+r_emailaddress+rw_nus';
  61. const ACCESS_TOKEN_URL = 'https://api.linkedin.com/uas/oauth/accessToken';
  62. const AUTHORIZE_URL = 'https://www.linkedin.com/uas/oauth/authenticate';
  63. //,location:(name) cannot be used together with location:(country:(code)) it generates duplicate field exception
  64. const PROFILE_URL = 'http://api.linkedin.com/v1/people/~:(id,first-name,last-name,picture-url,public-profile-url,location,summary,interests,date-of-birth,phone-numbers,skills,educations,certifications,languages)';
  65. //const PROFILE_URL = 'http://api.linkedin.com/v1/people/~';
  66. /**
  67. * URL TO GET email address of user
  68. */
  69. const EMAIL_URL = 'http://api.linkedin.com/v1/people/~/email-address';
  70. protected $callback = '{_WEB_ROOT_}/{_loginlinkedin_}';
  71. /**
  72. * Array of Tumblr's
  73. * oauth_token and oauth_token_secret
  74. *
  75. * @var array
  76. */
  77. protected $aAccessToken = array();
  78. /**
  79. * Object php OAuth
  80. *
  81. * @var object of type php OAuth
  82. * must have oauth extension for this
  83. */
  84. protected $oAuth;
  85. protected $bInitPageDoc = false;
  86. /**
  87. * Configuration of LinkedIn API
  88. * this is array of values LINKEDIN section
  89. * in !config.ini
  90. *
  91. * @var array
  92. */
  93. protected $aTM = array();
  94. /**
  95. * Flag indicates that this is the
  96. * request to connect Twitter account
  97. * with existing user account.
  98. *
  99. * @var bool
  100. */
  101. protected $bConnect = false;
  102. /**
  103. *
  104. * @var array of data from LinkedIn API,
  105. * created from the parsed XML response
  106. */
  107. protected $aData;
  108. /**
  109. * Email address from LinkedIn profile
  110. *
  111. * @var string
  112. */
  113. protected $email = '';
  114. /**
  115. * @var object object type User (may be LinkedinUser or just plain User)
  116. */
  117. protected $User;
  118. /**
  119. * The main purpose of this class is to
  120. * generate the oAuth token
  121. * and then redirect browser to twitter url with
  122. * this unique token
  123. *
  124. * No actual page generation will take place
  125. *
  126. * @see classes/WebPage#main()
  127. */
  128. protected function main()
  129. {
  130. $routerCallback = $this->Registry->Router->getCallback();
  131. $this->callback = $routerCallback($this->callback);
  132. d('$this->callback' . $this->callback);
  133. if (!extension_loaded('oauth')) {
  134. throw new \Exception('@@Unable to use LinkedIn API because OAuth extension is not available@@');
  135. }
  136. /**
  137. * If user is logged in then this is
  138. * a request to connect LinedIN Account
  139. * with existing account.
  140. */
  141. if ($this->isLoggedIn()) {
  142. $this->bConnect = true;
  143. }
  144. d('$this->bConnect: ' . $this->bConnect);
  145. $this->callback = $this->Registry->Ini->SITE_URL . $this->callback;
  146. d('$this->callback: ' . $this->callback);
  147. $this->aTm = $this->Registry->Ini['LINKEDIN'];
  148. try {
  149. $this->oAuth = new \OAuth($this->aTm['OAUTH_KEY'], $this->aTm['OAUTH_SECRET']);
  150. $this->oAuth->disableSSLChecks();
  151. $this->oAuth->enableDebug();
  152. } catch ( \OAuthException $e ) {
  153. e('OAuthException: ' . $e->getMessage());
  154. $aDebug = $this->oAuth->getLastResponseInfo();
  155. d('debug: ' . print_r($aDebug, 1));
  156. throw new \Exception('@@Something went wrong during authorization. Please try again later@@' . $e->getMessage());
  157. }
  158. /**
  159. * If this is start of dance then
  160. * generate token, secret and store them
  161. * in session and redirect to linkedin authorization page
  162. */
  163. if (empty($_SESSION['linkedin_oauth']) || empty($this->Request['oauth_token'])) {
  164. $this->step1();
  165. } else {
  166. $this->step2();
  167. }
  168. }
  169. /**
  170. * Generate oAuth request token
  171. * and redirect to Linkedin for authentication
  172. *
  173. * @throws \Exception in case something goes wrong during
  174. * this stage
  175. * @return object $this
  176. */
  177. protected function step1()
  178. {
  179. d('cp');
  180. try {
  181. $_SESSION['linkedin_oauth'] = $this->oAuth->getRequestToken(self::REQUEST_TOKEN_URL, $this->callback);
  182. $aDebug = $this->oAuth->getLastResponseInfo();
  183. d('debug: ' . print_r($aDebug, 1));
  184. d('$_SESSION[\'linkedin_oauth\']: ' . \print_r($_SESSION['linkedin_oauth'], 1));
  185. if (!empty($_SESSION['linkedin_oauth']) && !empty($_SESSION['linkedin_oauth']['oauth_token'])) {
  186. d('cp');
  187. Responder::redirectToPage(self::AUTHORIZE_URL . '?oauth_token=' . $_SESSION['linkedin_oauth']['oauth_token']);
  188. } else {
  189. /**
  190. * Here throw regular Exception, not Lampcms\Exception
  191. * so that it will be caught ONLY by the index.php and formatted
  192. * on a clean page, without any template
  193. */
  194. throw new \Exception("Failed fetching request token, response was: " . $this->oAuth->getLastResponse());
  195. }
  196. } catch ( \OAuthException $e ) {
  197. e('OAuthException: ' . $e->getMessage());
  198. $aDebug = $this->oAuth->getLastResponseInfo();
  199. d('debug: ' . print_r($aDebug, 1));
  200. throw new \Exception('Something went wrong during authorization. Please try again later' . $e->getMessage());
  201. }
  202. return $this;
  203. }
  204. /**
  205. * Step 2 in oAuth process
  206. * this is when linkedin redirected the user back
  207. * to our callback url, which calls this controller
  208. *
  209. * @throws \Exception in case something goes wrong with oAuth class
  210. * @return object $this
  211. */
  212. protected function step2()
  213. {
  214. try {
  215. /**
  216. * This is a callback (redirected back from linkedin page
  217. * after user authorized us)
  218. * In this case we must: create account or update account
  219. * in USER table
  220. * Re-create oViewer object
  221. * send cookie to remember user
  222. * and then send out HTML with js instruction to close the popup window
  223. */
  224. d('We are at step 2 of authentication. $_REQUEST: ' . print_r($_REQUEST, 1));
  225. $token = $this->Request['oauth_token'];
  226. d('$token: ' . $token);
  227. /**
  228. * @todo check first to make sure we do have oauth_token
  229. * on REQUEST, else close the window
  230. */
  231. $this->oAuth->setToken($token, $_SESSION['linkedin_oauth']['oauth_token_secret']);
  232. /**
  233. * Get 'oauth_verifier' request param which was sent from LinkedIn
  234. */
  235. $ver = $this->Registry->Request->get('oauth_verifier', 's', '');
  236. d('$ver: ' . $ver);
  237. if (empty($ver)) {
  238. $ver = null;
  239. }
  240. $url = self::ACCESS_TOKEN_URL;
  241. d('url: ' . $url);
  242. $this->aAccessToken = $this->oAuth->getAccessToken($url, null, $ver);
  243. d('$this->aAccessToken: ' . \print_r($this->aAccessToken, 1));
  244. $this->setTokenExpirationTime();
  245. unset($_SESSION['linkedin_oauth']);
  246. $this->oAuth->setToken($this->aAccessToken['oauth_token'], $this->aAccessToken['oauth_token_secret']);
  247. d('getting profile from PROFILE_URL');
  248. $this->oAuth->fetch(self::PROFILE_URL, null, OAUTH_HTTP_METHOD_GET, array('Connection'=> 'close'));
  249. $aDebug = $this->oAuth->getLastResponseInfo();
  250. d('debug: ' . \print_r($aDebug, 1));
  251. $resp = $this->oAuth->getLastResponse();
  252. $this->parseXML($resp);
  253. $this->getEmailAddress();
  254. $this->createOrUpdate();
  255. if (!$this->bConnect) {
  256. \Lampcms\Cookie::sendLoginCookie($this->Registry->Viewer->getUid(), $this->User->rs);
  257. } else {
  258. /**
  259. * The b_li flag in Viewer is necessary
  260. * for the social checkboxes to set
  261. * the checkbox to 'checked' state
  262. *
  263. */
  264. $this->Registry->Viewer['b_li'] = true;
  265. }
  266. $this->closeWindow();
  267. } catch ( \OAuthException $e ) {
  268. e('OAuthException: ' . $e->getMessage());
  269. $aDebug = $this->oAuth->getLastResponseInfo();
  270. d('debug: ' . print_r($aDebug, 1));
  271. $err = '@@Something went wrong during authorization. Please try again later@@ ' . $e->getMessage();
  272. throw new \Exception($err);
  273. }
  274. return $this;
  275. }
  276. /**
  277. * LinkedIn returns expiration times in the number of seconds from now format
  278. * Must convert these values into unix timestamp now.
  279. */
  280. protected function setTokenExpirationTime()
  281. {
  282. if (isset($this->aAccessToken['oauth_expires_in'])) {
  283. $this->aAccessToken['oauth_expires_in'] = time() + $this->aAccessToken['oauth_expires_in'];
  284. }
  285. if (isset($this->aAccessToken['oauth_authorization_expires_in'])) {
  286. $this->aAccessToken['oauth_authorization_expires_in'] = time() + $this->aAccessToken['oauth_authorization_expires_in'];
  287. }
  288. }
  289. /**
  290. * Parses the XML returned from LinkedIn API
  291. * and creates array of $this->aData from it
  292. *
  293. * @param $xml
  294. *
  295. * @throws \Lampcms\DevException
  296. * @throws \Lampcms\Exception if xml could not
  297. * be parsed for any reason
  298. * @internal param \Lampcms\Controllers\xml $string xml string received from LinkedIn API
  299. *
  300. * @return object $this
  301. */
  302. protected function parseXML($xml)
  303. {
  304. d('xml: ' . $xml);
  305. $oXML = new \Lampcms\Dom\Document();
  306. if (false === $oXML->loadXML($xml)) {
  307. $err = 'Unexpected Error parsing response XML';
  308. throw new \Lampcms\DevException($err);
  309. }
  310. $lid = $oXML->evaluate('string(/person/id[1])'); // it will be string!
  311. if (!$lid) {
  312. throw new \Lampcms\DevException('Unable to get LinkedIn ID from xml: ' . $xml);
  313. }
  314. $this->aData['linkedin_id'] = (string)$lid;
  315. if ('' !== $industry = $oXML->evaluate('string(/person/industry[1])')) {
  316. $this->aData['industry'] = $industry;
  317. }
  318. if ('' !== $summary = $oXML->evaluate('string(/person/summary[1])')) {
  319. $this->aData['description'] = $summary;
  320. }
  321. if ('' !== $city = $oXML->evaluate('string(/person/location/name[1])')) {
  322. $this->aData['city'] = $city;
  323. }
  324. if ('' !== $cc = $oXML->evaluate('string(/person/location/country/code[1])')) {
  325. $this->aData['cc'] = \strtoupper($cc);
  326. }
  327. if ('' !== $avtr = $oXML->evaluate('string(/person/picture-url[1])')) {
  328. $this->aData['avatar_external'] = $avtr;
  329. }
  330. if ('' !== $fn = $oXML->evaluate('string(/person/first-name[1])')) {
  331. $this->aData['fn'] = $fn;
  332. }
  333. if ('' !== $ln = $oXML->evaluate('string(/person/last-name[1])')) {
  334. $this->aData['ln'] = $ln;
  335. }
  336. $this->aData['linkedin'] = array(
  337. 'tokens' => $this->aAccessToken
  338. );
  339. if ('' !== $url = $oXML->evaluate('string(/person/public-profile-url[1])')) {
  340. d('profile url: ' . $url);
  341. $this->aData['linkedin']['url'] = $url;
  342. }
  343. d('$this->aData: ' . print_r($this->aData, 1));
  344. return $this;
  345. }
  346. /**
  347. * Get email address from LinkedIN API
  348. *
  349. */
  350. protected function getEmailAddress()
  351. {
  352. d('cp');
  353. try {
  354. $this->oAuth->fetch(self::EMAIL_URL, null, OAUTH_HTTP_METHOD_GET, array('Connection'=> 'close'));
  355. $resp = $this->oAuth->getLastResponse();
  356. /**
  357. * May return empty element
  358. *
  359. * $resp: <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
  360. * <email-address />
  361. *
  362. */
  363. d('EMAIL ADDRESS RESPONSE: ' . $resp);
  364. ;
  365. $oXML = new \Lampcms\Dom\Document();
  366. if (false === $oXML->loadXML($resp)) {
  367. $err = 'Unexpected Error parsing email address response XML';
  368. e($err);
  369. return;
  370. }
  371. $email = $oXML->evaluate('string(/email-address[1])');
  372. d('email: ' . $email);
  373. if (!empty($email)) {
  374. $this->email = \mb_strtolower($email);
  375. }
  376. } catch ( \OAuthException $e ) {
  377. e('Unable to fetch email address. OAuthException: ' . $e->getMessage());
  378. $aDebug = $this->oAuth->getLastResponseInfo();
  379. d('debug: ' . print_r($aDebug, 1));
  380. }
  381. }
  382. protected function createOrUpdate()
  383. {
  384. $aUser = $this->getUserByLinkedInId($this->aData['linkedin_id']);
  385. if (!empty($this->bConnect)) {
  386. d('this is connect action');
  387. $this->User = $this->Registry->Viewer;
  388. $this->updateUser();
  389. } elseif (!empty($aUser)) {
  390. /**
  391. * This means user previously joined with LInkedIn
  392. * and now logged back in with Linked in button again
  393. */
  394. $this->User = \Lampcms\UserLinkedin::userFactory($this->Registry, $aUser);
  395. $this->updateUser(); // only update token, secret, linkedin url
  396. } else {
  397. /**
  398. * Try to find user by email address from
  399. * LinkedIN profile
  400. */
  401. $User = $this->findUserByEmail();
  402. if (is_object($User) && ($User instanceof \Lampcms\User)) {
  403. /**
  404. * set $this->bConnect to true because this is a connection
  405. * of existing user with the new linkedin credentials
  406. */
  407. $this->bConnect = true;
  408. $this->User = $User;
  409. $this->updateUser();
  410. } else {
  411. d('User not found by email address');
  412. $this->isNewAccount = true;
  413. $this->createNewUser();
  414. }
  415. }
  416. try {
  417. $this->processLogin($this->User);
  418. } catch ( \Lampcms\LoginException $e ) {
  419. /**
  420. * re-throw as regular exception
  421. * so that it can be caught and show in popup window
  422. */
  423. e('Unable to process login: ' . $e->getMessage());
  424. throw new \Exception($e->getMessage());
  425. }
  426. $this->Registry->Dispatcher->post($this, 'onLinkedinLogin');
  427. return $this;
  428. }
  429. /**
  430. * Attempt to find existing user by email address
  431. * Search in EMAILS collection first, then if not found, in USERS collection
  432. * If record is found creates User object and returns it
  433. *
  434. * @return mixed null | User object
  435. */
  436. protected function findUserByEmail()
  437. {
  438. $User = null;
  439. /**
  440. * Search EMAILS collection
  441. * try to find user that has this email address
  442. */
  443. $res = $this->Registry->Mongo->EMAILS->findOne(array(Schema::EMAIL => $this->email), array('i_uid' => true));
  444. if (!empty($res) && !empty($res['i_uid'])) {
  445. d('found user id by email address. uid: ' . $res['i_uid']);
  446. $aUser = $this->Registry->Mongo->USERS->findOne(array(Schema::PRIMARY => $res['i_uid']));
  447. $User = \Lampcms\User::userFactory($this->Registry, $aUser);
  448. }
  449. /**
  450. * Was Not able to find user by search EMAILS collection
  451. * Search USERS collection by email address
  452. */
  453. if (null === $User) {
  454. $a = $this->Registry->Mongo->USERS->findOne(array(Schema::EMAIL => $this->email));
  455. if (!empty($a)) {
  456. d('found user id by email address. uid: ' . $a['_id']);
  457. $User = \Lampcms\User::userFactory($this->Registry, $a);
  458. }
  459. }
  460. d('User not found by email: ' . $this->email);
  461. return $User;
  462. }
  463. /**
  464. * Create new record in the USERS collection
  465. * also set the $this->User to the newly created
  466. * instance of UserLinkedin object
  467. *
  468. *
  469. */
  470. protected function createNewUser()
  471. {
  472. d('creating new user');
  473. /**
  474. * Need to call /people/~/email-address to get email address
  475. * and /people/~ to get data that includes avatar among other things
  476. */
  477. if (false !== $tzn = Cookie::get('tzn')) {
  478. $timezone = $tzn;
  479. } else {
  480. $timezone = $this->Registry->Ini->SERVER_TIMEZONE;
  481. }
  482. $ln = (!empty($this->aData['ln'])) ? $this->aData['ln'] : '';
  483. $oEA = \Lampcms\ExternalAuth::factory($this->Registry);
  484. $u = $this->aData['fn'] . ' ' . $ln;
  485. d('$u: ' . $u);
  486. $username = $oEA->makeUsername($u);
  487. $sid = \Lampcms\Cookie::getSidCookie();
  488. d('sid is: ' . $sid);
  489. $this->aData[Schema::USERNAME] = $username;
  490. $this->aData[Schema::USERNAME_LOWERCASE] = \mb_strtolower($username, 'utf-8');
  491. $this->aData[Schema::REGISTRATION_TIMESTAMP] = time();
  492. $this->aData[Schema::REGISTRATION_TIME] = date('r');
  493. $this->aData[Schema::ROLE] = Role::EXTERNAL_USER;
  494. $this->aData[Schema::SID] = (false !== $sid) ? $sid : \Lampcms\String::makeSid();
  495. $this->aData[Schema::REPUTATION] = 1;
  496. $this->aData[Schema::LANG] = $this->Registry->getCurrentLang();
  497. $this->aData[Schema::LOCALE] = $this->Registry->Locale->getLocale();
  498. $this->aData[Schema::TIMEZONE] = $timezone;
  499. if (!empty($this->email)) {
  500. $this->aData[Schema::EMAIL] = $this->email;
  501. }
  502. if (empty($this->aData['cc']) && empty($this->aData['city'])) {
  503. $this->aData = array_merge($this->Registry->Geo->Location->data, $this->aData);
  504. }
  505. $this->User = \Lampcms\UserLinkedin::userFactory($this->Registry, $this->aData);
  506. /**
  507. * This will mark this user object is new user
  508. * and will be persistent for the duration of this session ONLY
  509. * This way we can know it's a newly registered user
  510. * and ask the user to provide email address but only
  511. * during the same session
  512. */
  513. $this->User->setNewUser();
  514. d('isNewUser: ' . $this->User->isNewUser());
  515. $this->User->save();
  516. \Lampcms\PostRegistration::createReferrerRecord($this->Registry, $this->User);
  517. $this->Registry->Dispatcher->post($this->User, 'onNewUser');
  518. return $this;
  519. }
  520. /**
  521. *
  522. * Adds data from LinkedIn API, including
  523. * oauth token, secret to the
  524. * User object
  525. * avatar from LinkedIn, Country Code, City
  526. * and 'about' are added ONLY if they
  527. * don't already exist in User
  528. *
  529. * @post-condition: $this->User object is updated
  530. * with the valued from $this->aData AND $this->aAccessToken
  531. * and then saved using save()
  532. *
  533. * @return $this
  534. */
  535. protected function updateUser()
  536. {
  537. $avtr = $this->User['avatar_external'];
  538. /**
  539. * Special case:
  540. * if connecting user and another user
  541. * already exists with the same Linkedin_id
  542. * then we will still allow to add Linkedin key
  543. * to this Viewer's profile
  544. * but will NOT add the Linkedin_id to the Viewer object
  545. * This is because otherwise we will have 2 users
  546. * with the same value of Linkedin_id and then
  547. * when logging in with LinkedIN we will not know
  548. * which user to login. This is why we will enforce uniqueness
  549. * of Linkedin_id key here
  550. */
  551. if ($this->bConnect) {
  552. /**
  553. * Do we have another user with the same linkedin_id?
  554. */
  555. $a = $this->Registry->Mongo->USERS->findOne(array('linkedin_id' => $this->aData['linkedin_id']), array('_id' => 1));
  556. /**
  557. * If not then add linkedin_id to user being connected (which is current Viewer object)
  558. */
  559. if (empty($a)) {
  560. $this->User['linkedin_id'] = $this->aData['linkedin_id'];
  561. } else {
  562. /**
  563. * If found another user with the same linkedin_id
  564. * DO NOT add linkedin_id to user being connected!
  565. * This is because we cannot have 2 users with the same linkedin_id
  566. * The connected user will still get the ['linkedin'] element
  567. * added to user object and it will contain oath token and secret
  568. */
  569. }
  570. /**
  571. *
  572. * If user does not have email address or email
  573. * is not activated
  574. * then add value of $this->email
  575. * and switch unactivated status to activated!
  576. */
  577. $currentEmail = $this->User[Schema::EMAIL];
  578. $currentStatus = $this->User[Schema::ROLE];
  579. d('currentEmail: ' . $currentEmail . ' currentStatus: ' . $currentStatus);
  580. /**
  581. * If Linking in existing user and user does not have email address
  582. * for any reason then add email address from LinkedIN profile
  583. *
  584. * If User has email address but status in unactivated for any reason
  585. * then change status to Role::REGISTERED or Role::EXTERNAL_USER
  586. */
  587. if (empty($currentEmail)) {
  588. if (!empty($this->email)) {
  589. d('User did not have email address. Adding one from LinkedIn account');
  590. $this->User[Schema::EMAIL] = $this->email;
  591. }
  592. } elseif ($currentStatus === Role::UNACTIVATED && $currentEmail === $this->email) {
  593. d('User had unactivated email address. Setting status to REGISTERED');
  594. $this->User->setRoleId(Role::REGISTERED);
  595. } elseif ($currentStatus === Role::UNACTIVATED_EXTERNAL && $currentEmail === $this->email) {
  596. d('User had unactivated email address. Setting status to EXTERNAL_USER');
  597. $this->User->setRoleId(Role::EXTERNAL_USER);
  598. }
  599. } else {
  600. /**
  601. * This is not a connect situation - this is a new user
  602. * OR it may be a case of existing linkedIN user logging in
  603. * with LinkedIN login button in which case
  604. * this->User already has the same linkedin_id so setting it again
  605. * to the same value is not a problem
  606. */
  607. $this->User['linkedin_id'] = $this->aData['linkedin_id'];
  608. }
  609. /**
  610. * Update the following field ONLY
  611. * if they DON'T already exists in this user's record!
  612. *
  613. * This means that if record exists and is an empty
  614. * string - don't update this because it usually means
  615. * that user did have this field before and then removed
  616. * the value by editing profile.
  617. */
  618. if (empty($avtr) && !empty($this->aData['avatar_external'])) {
  619. $this->User[Schema::EXTERNAL_AVATAR] = $this->aData['avatar_external'];
  620. }
  621. if (null === $this->User[Schema::DESCRIPTION] && !empty($this->aData['description'])) {
  622. $this->User[Schema::DESCRIPTION] = $this->aData['description'];
  623. }
  624. if (null === $this->User[Schema::COUNTRY_CODE] && !empty($this->aData['cc'])) {
  625. $this->User[Schema::COUNTRY_CODE] = $this->aData['cc'];
  626. }
  627. if (null === $this->User[Schema::CITY] && !empty($this->aData['city'])) {
  628. $this->User[Schema::CITY] = $this->aData['city'];
  629. }
  630. /**
  631. * Always update the 'linkedin' element
  632. * of user record. It contains 2 keys: tokens
  633. * with is array holding oauth tokens
  634. * and optionally 'url' with Linkedin profile url
  635. *
  636. */
  637. $this->User['linkedin'] = $this->aData['linkedin'];
  638. $this->User->save();
  639. return $this;
  640. }
  641. /**
  642. * Find user data in USERS collection
  643. * using linkedin_id key
  644. *
  645. * @todo make linkedin_id a unique index
  646. * This will add extra protection against
  647. * allowing more than one user to have same
  648. * linked-in account
  649. *
  650. * @param string $lid LinkedIn id - from LinkedIn website
  651. * It is a string, not an integer!
  652. *
  653. * @return mixed null | array of user data
  654. *
  655. */
  656. protected function getUserByLinkedInId($lid)
  657. {
  658. $coll = $this->Registry->Mongo->USERS;
  659. $coll->ensureIndex(array('linkedin_id' => 1));
  660. $aUser = $coll->findOne(array('linkedin_id' => (string)$lid));
  661. d('aUser: ' . print_r($aUser, 1));
  662. return $aUser;
  663. }
  664. /**
  665. * Return html that contains JS window.close code and nothing else
  666. *
  667. * @param array $a
  668. *
  669. * @return void
  670. */
  671. protected function closeWindow(array $a = array())
  672. {
  673. d('cp a: ' . print_r($a, 1));
  674. $js = '';
  675. $tpl = '
  676. var myclose = function(){
  677. window.close();
  678. }
  679. if(window.opener){
  680. %s
  681. setTimeout(myclose, 100); // give opener window time to process login and cancell intervals
  682. }else{
  683. alert("This is not a popup window or opener window gone away");
  684. }';
  685. d('cp');
  686. $script = \sprintf($tpl, $js);
  687. $s = Responder::PAGE_OPEN . Responder::JS_OPEN .
  688. $script .
  689. Responder::JS_CLOSE .
  690. '<h2>You have successfully connected your LinkedIn account. You should close this window now</h2>' .
  691. Responder::PAGE_CLOSE;
  692. d('cp s: ' . $s);
  693. echo $s;
  694. fastcgi_finish_request();
  695. exit;
  696. }
  697. }