PageRenderTime 62ms CodeModel.GetById 21ms RepoModel.GetById 1ms app.codeStats 1ms

/kernel/classes/datatypes/ezuser/ezuser.php

https://github.com/granitegreg/ezpublish
PHP | 2814 lines | 2017 code | 245 blank | 552 comment | 279 complexity | 6b6800fc5d1a48bc2a093bb996a441f7 MD5 | raw file
Possible License(s): GPL-2.0

Large files files are truncated, but you can click here to view the full file

  1. <?php
  2. //
  3. // Definition of eZUser class
  4. //
  5. // Created on: <10-Jun-2002 17:03:15 bf>
  6. //
  7. // ## BEGIN COPYRIGHT, LICENSE AND WARRANTY NOTICE ##
  8. // SOFTWARE NAME: eZ Publish
  9. // SOFTWARE RELEASE: 4.1.x
  10. // COPYRIGHT NOTICE: Copyright (C) 1999-2011 eZ Systems AS
  11. // SOFTWARE LICENSE: GNU General Public License v2.0
  12. // NOTICE: >
  13. // This program is free software; you can redistribute it and/or
  14. // modify it under the terms of version 2.0 of the GNU General
  15. // Public License as published by the Free Software Foundation.
  16. //
  17. // This program is distributed in the hope that it will be useful,
  18. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  19. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  20. // GNU General Public License for more details.
  21. //
  22. // You should have received a copy of version 2.0 of the GNU General
  23. // Public License along with this program; if not, write to the Free
  24. // Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
  25. // MA 02110-1301, USA.
  26. //
  27. //
  28. // ## END COPYRIGHT, LICENSE AND WARRANTY NOTICE ##
  29. //
  30. /*!
  31. \class eZUser ezuser.php
  32. \brief eZUser handles eZ Publish user accounts
  33. \ingroup eZDatatype
  34. */
  35. class eZUser extends eZPersistentObject
  36. {
  37. /// MD5 of password
  38. const PASSWORD_HASH_MD5_PASSWORD = 1;
  39. /// MD5 of user and password
  40. const PASSWORD_HASH_MD5_USER = 2;
  41. /// MD5 of site, user and password
  42. const PASSWORD_HASH_MD5_SITE = 3;
  43. /// Legacy support for mysql hashed passwords
  44. const PASSWORD_HASH_MYSQL = 4;
  45. /// Passwords in plaintext, should not be used for real sites
  46. const PASSWORD_HASH_PLAINTEXT = 5;
  47. // Crypted passwords
  48. const PASSWORD_HASH_CRYPT = 6;
  49. /// Authenticate by matching the login field
  50. const AUTHENTICATE_LOGIN = 1;
  51. /// Authenticate by matching the email field
  52. const AUTHENTICATE_EMAIL = 2;
  53. const AUTHENTICATE_ALL = 3; //EZ_USER_AUTHENTICATE_LOGIN | EZ_USER_AUTHENTICATE_EMAIL;
  54. protected static $anonymousId = null;
  55. function eZUser( $row = array() )
  56. {
  57. $this->eZPersistentObject( $row );
  58. $this->OriginalPassword = false;
  59. $this->OriginalPasswordConfirm = false;
  60. }
  61. static function definition()
  62. {
  63. static $definition = array( 'fields' => array( 'contentobject_id' => array( 'name' => 'ContentObjectID',
  64. 'datatype' => 'integer',
  65. 'default' => 0,
  66. 'required' => true,
  67. 'foreign_class' => 'eZContentObject',
  68. 'foreign_attribute' => 'id',
  69. 'multiplicity' => '0..1' ),
  70. 'login' => array( 'name' => 'Login',
  71. 'datatype' => 'string',
  72. 'default' => '',
  73. 'required' => true ),
  74. 'email' => array( 'name' => 'Email',
  75. 'datatype' => 'string',
  76. 'default' => '',
  77. 'required' => true ),
  78. 'password_hash' => array( 'name' => 'PasswordHash',
  79. 'datatype' => 'string',
  80. 'default' => '',
  81. 'required' => true ),
  82. 'password_hash_type' => array( 'name' => 'PasswordHashType',
  83. 'datatype' => 'integer',
  84. 'default' => 1,
  85. 'required' => true ) ),
  86. 'keys' => array( 'contentobject_id' ),
  87. 'sort' => array( 'contentobject_id' => 'asc' ),
  88. 'function_attributes' => array( 'contentobject' => 'contentObject',
  89. 'groups' => 'groups',
  90. 'has_stored_login' => 'hasStoredLogin',
  91. 'original_password' => 'originalPassword',
  92. 'original_password_confirm' => 'originalPasswordConfirm',
  93. 'roles' => 'roles',
  94. 'role_id_list' => 'roleIDList',
  95. 'limited_assignment_value_list' => 'limitValueList',
  96. 'is_logged_in' => 'isLoggedIn',
  97. 'is_enabled' => 'isEnabled',
  98. 'is_locked' => 'isLocked',
  99. 'last_visit' => 'lastVisit',
  100. 'login_count' => 'loginCount',
  101. 'has_manage_locations' => 'hasManageLocations' ),
  102. 'relations' => array( 'contentobject_id' => array( 'class' => 'ezcontentobject',
  103. 'field' => 'id' ) ),
  104. 'class_name' => 'eZUser',
  105. 'name' => 'ezuser' );
  106. return $definition;
  107. }
  108. /*!
  109. \return a textual identifier for the hash type $id
  110. */
  111. static function passwordHashTypeName( $id )
  112. {
  113. switch ( $id )
  114. {
  115. case self::PASSWORD_HASH_MD5_PASSWORD:
  116. {
  117. return 'md5_password';
  118. } break;
  119. case self::PASSWORD_HASH_MD5_USER:
  120. {
  121. return 'md5_user';
  122. } break;
  123. case self::PASSWORD_HASH_MD5_SITE:
  124. {
  125. return 'md5_site';
  126. } break;
  127. case self::PASSWORD_HASH_MYSQL:
  128. {
  129. return 'mysql';
  130. } break;
  131. case self::PASSWORD_HASH_PLAINTEXT:
  132. {
  133. return 'plaintext';
  134. } break;
  135. case self::PASSWORD_HASH_CRYPT:
  136. {
  137. return 'crypt';
  138. } break;
  139. }
  140. }
  141. /*!
  142. \return the hash type for the textual identifier $identifier
  143. */
  144. static function passwordHashTypeID( $identifier )
  145. {
  146. switch ( $identifier )
  147. {
  148. case 'md5_password':
  149. {
  150. return self::PASSWORD_HASH_MD5_PASSWORD;
  151. } break;
  152. default:
  153. case 'md5_user':
  154. {
  155. return self::PASSWORD_HASH_MD5_USER;
  156. } break;
  157. case 'md5_site':
  158. {
  159. return self::PASSWORD_HASH_MD5_SITE;
  160. } break;
  161. case 'mysql':
  162. {
  163. return self::PASSWORD_HASH_MYSQL;
  164. } break;
  165. case 'plaintext':
  166. {
  167. return self::PASSWORD_HASH_PLAINTEXT;
  168. } break;
  169. case 'crypt':
  170. {
  171. return self::PASSWORD_HASH_CRYPT;
  172. } break;
  173. }
  174. }
  175. /*!
  176. Check if current user has "content/manage_locations" access
  177. */
  178. function hasManageLocations()
  179. {
  180. $accessResult = $this->hasAccessTo( 'content', 'manage_locations' );
  181. if ( $accessResult['accessWord'] != 'no' )
  182. {
  183. return true;
  184. }
  185. return false;
  186. }
  187. static function create( $contentObjectID )
  188. {
  189. $row = array(
  190. 'contentobject_id' => $contentObjectID,
  191. 'login' => null,
  192. 'email' => null,
  193. 'password_hash' => null,
  194. 'password_hash_type' => null
  195. );
  196. return new eZUser( $row );
  197. }
  198. function store( $fieldFilters = null )
  199. {
  200. $this->Email = trim( $this->Email );
  201. $userID = $this->attribute( 'contentobject_id' );
  202. // Clear memory cache
  203. unset( $GLOBALS['eZUserObject_' . $userID] );
  204. $GLOBALS['eZUserObject_' . $userID] = $this;
  205. self::purgeUserCacheByUserId( $userID );
  206. eZPersistentObject::store( $fieldFilters );
  207. }
  208. function originalPassword()
  209. {
  210. return $this->OriginalPassword;
  211. }
  212. function setOriginalPassword( $password )
  213. {
  214. $this->OriginalPassword = $password;
  215. }
  216. function originalPasswordConfirm()
  217. {
  218. return $this->OriginalPasswordConfirm;
  219. }
  220. function setOriginalPasswordConfirm( $password )
  221. {
  222. $this->OriginalPasswordConfirm = $password;
  223. }
  224. function hasStoredLogin()
  225. {
  226. $db = eZDB::instance();
  227. $contentObjectID = $this->attribute( 'contentobject_id' );
  228. $sql = "SELECT * FROM ezuser WHERE contentobject_id='$contentObjectID' AND LENGTH( login ) > 0";
  229. $rows = $db->arrayQuery( $sql );
  230. return !empty( $rows );
  231. }
  232. /*!
  233. Fills in the \a $id, \a $login, \a $email and \a $password for the user
  234. and creates the proper password hash.
  235. */
  236. function setInformation( $id, $login, $email, $password, $passwordConfirm = false )
  237. {
  238. $this->setAttribute( "contentobject_id", $id );
  239. $this->setAttribute( "email", $email );
  240. $this->setAttribute( "login", $login );
  241. if ( eZUser::validatePassword( $password ) and
  242. $password == $passwordConfirm ) // Cannot change login or password_hash without login and password
  243. {
  244. $this->setAttribute( "password_hash", eZUser::createHash( $login, $password, eZUser::site(),
  245. eZUser::hashType() ) );
  246. $this->setAttribute( "password_hash_type", eZUser::hashType() );
  247. }
  248. else
  249. {
  250. $this->setOriginalPassword( $password );
  251. $this->setOriginalPasswordConfirm( $passwordConfirm );
  252. }
  253. }
  254. static function fetch( $id, $asObject = true )
  255. {
  256. if ( !$id )
  257. return null;
  258. return eZPersistentObject::fetchObject( eZUser::definition(),
  259. null,
  260. array( 'contentobject_id' => $id ),
  261. $asObject );
  262. }
  263. static function fetchByName( $login, $asObject = true )
  264. {
  265. return eZPersistentObject::fetchObject( eZUser::definition(),
  266. null,
  267. array( 'LOWER( login )' => strtolower( $login ) ),
  268. $asObject );
  269. }
  270. static function fetchByEmail( $email, $asObject = true )
  271. {
  272. return eZPersistentObject::fetchObject( eZUser::definition(),
  273. null,
  274. array( 'LOWER( email )' => strtolower( $email ) ),
  275. $asObject );
  276. }
  277. /*!
  278. \static
  279. \return a list of the logged in users.
  280. \param $asObject If false it will return a list with only the names of the users as elements and user ID as key,
  281. otherwise each entry is a eZUser object.
  282. \sa fetchLoggedInCount
  283. */
  284. static function fetchLoggedInList( $asObject = false, $offset = false, $limit = false, $sortBy = false )
  285. {
  286. $db = eZDB::instance();
  287. $time = time() - eZINI::instance()->variable( 'Session', 'ActivityTimeout' );
  288. $parameters = array();
  289. if ( $offset )
  290. $parameters['offset'] =(int) $offset;
  291. if ( $limit )
  292. $parameters['limit'] =(int) $limit;
  293. $sortText = '';
  294. if ( $asObject )
  295. {
  296. $selectArray = array( "distinct ezuser.*" );
  297. }
  298. else
  299. {
  300. $selectArray = array( "ezuser.contentobject_id as user_id", "ezcontentobject.name" );
  301. }
  302. if ( $sortBy !== false )
  303. {
  304. $sortElements = array();
  305. if ( !is_array( $sortBy ) )
  306. {
  307. $sortBy = array( array( $sortBy, true ) );
  308. }
  309. else if ( !is_array( $sortBy[0] ) )
  310. $sortBy = array( $sortBy );
  311. $sortColumns = array();
  312. foreach ( $sortBy as $sortElements )
  313. {
  314. $sortColumn = $sortElements[0];
  315. $sortOrder = $sortElements[1];
  316. $orderText = $sortOrder ? 'asc' : 'desc';
  317. switch ( $sortColumn )
  318. {
  319. case 'user_id':
  320. {
  321. $sortColumn = "ezuser.contentobject_id $orderText";
  322. } break;
  323. case 'login':
  324. {
  325. $sortColumn = "ezuser.login $orderText";
  326. } break;
  327. case 'activity':
  328. {
  329. $selectArray[] = "ezuservisit.current_visit_timestamp AS activity";
  330. $sortColumn = "activity $orderText";
  331. } break;
  332. case 'email':
  333. {
  334. $sortColumn = "ezuser.email $orderText";
  335. } break;
  336. default:
  337. {
  338. eZDebug::writeError( "Unkown sort column '$sortColumn'", __METHOD__ );
  339. $sortColumn = false;
  340. } break;
  341. }
  342. if ( $sortColumn )
  343. $sortColumns[] = $sortColumn;
  344. }
  345. if ( !empty( $sortColumns ) )
  346. $sortText = "ORDER BY " . implode( ', ', $sortColumns );
  347. }
  348. if ( $asObject )
  349. {
  350. $selectText = implode( ', ', $selectArray );
  351. $sql = "SELECT $selectText
  352. FROM ezuservisit, ezuser
  353. WHERE ezuservisit.user_id != '" . self::anonymousId() . "' AND
  354. ezuservisit.current_visit_timestamp > '$time' AND
  355. ezuser.contentobject_id = ezuservisit.user_id
  356. $sortText";
  357. $rows = $db->arrayQuery( $sql, $parameters );
  358. $list = array();
  359. foreach ( $rows as $row )
  360. {
  361. $list[] = new eZUser( $row );
  362. }
  363. }
  364. else
  365. {
  366. $selectText = implode( ', ', $selectArray );
  367. $sql = "SELECT $selectText
  368. FROM ezuservisit, ezuser, ezcontentobject
  369. WHERE ezuservisit.user_id != '" . self::anonymousId() . "' AND
  370. ezuservisit.current_visit_timestamp > '$time' AND
  371. ezuser.contentobject_id = ezuservisit.user_id AND
  372. ezcontentobject.id = ezuser.contentobject_id
  373. $sortText";
  374. $rows = $db->arrayQuery( $sql, $parameters );
  375. $list = array();
  376. foreach ( $rows as $row )
  377. {
  378. $list[$row['user_id']] = $row['name'];
  379. }
  380. }
  381. return $list;
  382. }
  383. /*!
  384. \return the number of logged in users in the system.
  385. \note The count will be cached for the current page if caching is allowed.
  386. \sa fetchAnonymousCount
  387. */
  388. static function fetchLoggedInCount()
  389. {
  390. if ( isset( $GLOBALS['eZSiteBasics']['no-cache-adviced'] ) and
  391. !$GLOBALS['eZSiteBasics']['no-cache-adviced'] and
  392. isset( $GLOBALS['eZUserLoggedInCount'] ) )
  393. return $GLOBALS['eZUserLoggedInCount'];
  394. $db = eZDB::instance();
  395. $time = time() - eZINI::instance()->variable( 'Session', 'ActivityTimeout' );
  396. $sql = "SELECT count( DISTINCT user_id ) as count
  397. FROM ezuservisit
  398. WHERE user_id != '" . self::anonymousId() . "' AND
  399. user_id > 0 AND
  400. current_visit_timestamp > '$time'";
  401. $rows = $db->arrayQuery( $sql );
  402. $count = isset( $rows[0] ) ? $rows[0]['count'] : 0;
  403. $GLOBALS['eZUserLoggedInCount'] = $count;
  404. return $count;
  405. }
  406. /**
  407. * Return the number of anonymous users in the system.
  408. *
  409. * @deprecated As of 4.4 since default session handler does not support this.
  410. * @return int
  411. */
  412. static function fetchAnonymousCount()
  413. {
  414. if ( isset( $GLOBALS['eZSiteBasics']['no-cache-adviced'] ) and
  415. !$GLOBALS['eZSiteBasics']['no-cache-adviced'] and
  416. isset( $GLOBALS['eZUserAnonymousCount'] ) )
  417. return $GLOBALS['eZUserAnonymousCount'];
  418. $db = eZDB::instance();
  419. $time = time();
  420. $ini = eZINI::instance();
  421. $activityTimeout = $ini->variable( 'Session', 'ActivityTimeout' );
  422. $sessionTimeout = $ini->variable( 'Session', 'SessionTimeout' );
  423. $time = $time + $sessionTimeout - $activityTimeout;
  424. $sql = "SELECT count( session_key ) as count
  425. FROM ezsession
  426. WHERE user_id = '" . self::anonymousId() . "' AND
  427. expiration_time > '$time'";
  428. $rows = $db->arrayQuery( $sql );
  429. $count = isset( $rows[0] ) ? $rows[0]['count'] : 0;
  430. $GLOBALS['eZUserAnonymousCount'] = $count;
  431. return $count;
  432. }
  433. /*!
  434. \static
  435. \return true if the user with ID $userID is currently logged into the system.
  436. \note The information will be cached for the current page if caching is allowed.
  437. \sa fetchLoggedInList
  438. */
  439. static function isUserLoggedIn( $userID )
  440. {
  441. $userID = (int)$userID;
  442. if ( isset( $GLOBALS['eZSiteBasics']['no-cache-adviced'] ) and
  443. !$GLOBALS['eZSiteBasics']['no-cache-adviced'] and
  444. isset( $GLOBALS['eZUserLoggedInMap'][$userID] ) )
  445. return $GLOBALS['eZUserLoggedInMap'][$userID];
  446. $db = eZDB::instance();
  447. $time = time() - eZINI::instance()->variable( 'Session', 'ActivityTimeout' );
  448. $sql = "SELECT DISTINCT user_id
  449. FROM ezuservisit
  450. WHERE user_id = '" . $userID . "' AND
  451. current_visit_timestamp > '$time'";
  452. $rows = $db->arrayQuery( $sql, array( 'limit' => 2 ) );
  453. $isLoggedIn = isset( $rows[0] );
  454. $GLOBALS['eZUserLoggedInMap'][$userID] = $isLoggedIn;
  455. return $isLoggedIn;
  456. }
  457. /*!
  458. \static
  459. Removes any cached session information, this is:
  460. - logged in user count
  461. - anonymous user count
  462. - logged in user map
  463. */
  464. static function clearSessionCache()
  465. {
  466. unset( $GLOBALS['eZUserLoggedInCount'] );
  467. unset( $GLOBALS['eZUserAnonymousCount'] );
  468. unset( $GLOBALS['eZUserLoggedInMap'] );
  469. }
  470. /**
  471. * Remove session data for user \a $userID.
  472. * @todo should use eZSession api (needs to be created) so
  473. * callbacks (like preference / basket..) is cleared as well.
  474. *
  475. * @params int $userID
  476. */
  477. static function removeSessionData( $userID )
  478. {
  479. eZUser::clearSessionCache();
  480. eZSession::getHandlerInstance()->deleteByUserIDs( array( $userID ) );
  481. }
  482. /*!
  483. Removes the user from the ezuser table.
  484. \note Will also remove any notifications and session related to the user.
  485. */
  486. static function removeUser( $userID )
  487. {
  488. $user = eZUser::fetch( $userID );
  489. if ( !$user )
  490. {
  491. eZDebug::writeError( "unable to find user with ID $userID", __METHOD__ );
  492. return false;
  493. }
  494. eZUser::removeSessionData( $userID );
  495. eZSubtreeNotificationRule::removeByUserID( $userID );
  496. eZCollaborationNotificationRule::removeByUserID( $userID );
  497. eZUserSetting::removeByUserID( $userID );
  498. eZUserAccountKey::removeByUserID( $userID );
  499. eZForgotPassword::removeByUserID( $userID );
  500. eZWishList::removeByUserID( $userID );
  501. // only remove general digest setting if there are no other users with the same e-mail
  502. $email = $user->attribute( 'email' );
  503. $usersWithEmailCount = eZPersistentObject::count( eZUser::definition(), array( 'email' => $email ) );
  504. if ( $usersWithEmailCount == 1 )
  505. {
  506. eZGeneralDigestUserSettings::removeByAddress( $email );
  507. }
  508. eZPersistentObject::removeObject( eZUser::definition(),
  509. array( 'contentobject_id' => $userID ) );
  510. return true;
  511. }
  512. /*!
  513. \return a list of valid and enabled users, the data returned is an array
  514. with ezcontentobject database data.
  515. */
  516. static function fetchContentList()
  517. {
  518. $contentObjectStatus = eZContentObject::STATUS_PUBLISHED;
  519. $query = "SELECT ezcontentobject.*
  520. FROM ezuser, ezcontentobject, ezuser_setting
  521. WHERE ezcontentobject.status = '$contentObjectStatus' AND
  522. ezuser_setting.is_enabled = 1 AND
  523. ezcontentobject.id = ezuser.contentobject_id AND
  524. ezuser_setting.user_id = ezuser.contentobject_id";
  525. $db = eZDB::instance();
  526. $rows = $db->arrayQuery( $query );
  527. return $rows;
  528. }
  529. /*!
  530. \static
  531. \return the default hash type which is specified in UserSettings/HashType in site.ini
  532. */
  533. static function hashType()
  534. {
  535. $ini = eZINI::instance();
  536. $type = strtolower( $ini->variable( 'UserSettings', 'HashType' ) );
  537. if ( $type == 'md5_site' )
  538. return self::PASSWORD_HASH_MD5_SITE;
  539. else if ( $type == 'md5_user' )
  540. return self::PASSWORD_HASH_MD5_USER;
  541. else if ( $type == 'plaintext' )
  542. return self::PASSWORD_HASH_PLAINTEXT;
  543. else if ( $type == 'crypt' )
  544. return self::PASSWORD_HASH_CRYPT;
  545. else
  546. return self::PASSWORD_HASH_MD5_PASSWORD;
  547. }
  548. /*!
  549. \static
  550. \return the site name used in password hashing.
  551. */
  552. static function site()
  553. {
  554. $ini = eZINI::instance();
  555. return $ini->variable( 'UserSettings', 'SiteName' );
  556. }
  557. /*!
  558. Fetches a builtin user and returns it, this helps avoid special cases where
  559. user is not logged in.
  560. */
  561. static function fetchBuiltin( $id )
  562. {
  563. if ( !in_array( $id, $GLOBALS['eZUserBuiltins'] ) )
  564. $id = self::anonymousId();
  565. if ( empty( $GLOBALS["eZUserBuilitinInstance-$id"] ) )
  566. {
  567. $GLOBALS["eZUserBuilitinInstance-$id"] = eZUser::fetch( self::anonymousId() );
  568. }
  569. return $GLOBALS["eZUserBuilitinInstance-$id"];
  570. }
  571. /*!
  572. \return the user id.
  573. */
  574. function id()
  575. {
  576. return $this->ContentObjectID;
  577. }
  578. /*!
  579. \return a bitfield which decides the authenticate methods.
  580. */
  581. static function authenticationMatch()
  582. {
  583. $ini = eZINI::instance();
  584. $matchArray = $ini->variableArray( 'UserSettings', 'AuthenticateMatch' );
  585. $match = 0;
  586. foreach ( $matchArray as $matchItem )
  587. {
  588. switch ( $matchItem )
  589. {
  590. case "login":
  591. {
  592. $match = ( $match | self::AUTHENTICATE_LOGIN );
  593. } break;
  594. case "email":
  595. {
  596. $match = ( $match | self::AUTHENTICATE_EMAIL );
  597. } break;
  598. }
  599. }
  600. return $match;
  601. }
  602. /*!
  603. \return \c true if there can only be one instance of an email address on the site.
  604. */
  605. static function requireUniqueEmail()
  606. {
  607. $ini = eZINI::instance();
  608. return $ini->variable( 'UserSettings', 'RequireUniqueEmail' ) == 'true';
  609. }
  610. /**
  611. * Logs in the user if applied username and password is valid.
  612. *
  613. * @param string $login
  614. * @param string $password
  615. * @param bool $authenticationMatch
  616. * @return mixed eZUser on success, bool false on failure
  617. */
  618. public static function loginUser( $login, $password, $authenticationMatch = false )
  619. {
  620. $user = self::_loginUser( $login, $password, $authenticationMatch );
  621. if ( is_object( $user ) )
  622. {
  623. self::loginSucceeded( $user );
  624. return $user;
  625. }
  626. else
  627. {
  628. self::loginFailed( $user, $login );
  629. return false;
  630. }
  631. }
  632. /**
  633. * Does some house keeping work once a log in has succeeded.
  634. *
  635. * @param eZUser $user
  636. */
  637. protected static function loginSucceeded( $user )
  638. {
  639. $userID = $user->attribute( 'contentobject_id' );
  640. // if audit is enabled logins should be logged
  641. eZAudit::writeAudit( 'user-login', array( 'User id' => $userID, 'User login' => $user->attribute( 'login' ) ) );
  642. eZUser::updateLastVisit( $userID, true );
  643. eZUser::setCurrentlyLoggedInUser( $user, $userID );
  644. // Reset number of failed login attempts
  645. eZUser::setFailedLoginAttempts( $userID, 0 );
  646. }
  647. /**
  648. * Does some house keeping work when a log in has failed.
  649. *
  650. * @param mixed $userID
  651. * @param string $login
  652. */
  653. protected static function loginFailed( $userID = false, $login )
  654. {
  655. $loginEscaped = eZDB::instance()->escapeString( $login );
  656. // Failed login attempts should be logged
  657. eZAudit::writeAudit( 'user-failed-login', array( 'User login' => $loginEscaped,
  658. 'Comment' => 'Failed login attempt: eZUser::loginUser()' ) );
  659. // Increase number of failed login attempts.
  660. if ( $userID )
  661. eZUser::setFailedLoginAttempts( $userID );
  662. }
  663. /**
  664. * Logs in an user if applied login and password is valid.
  665. *
  666. * This method does not do any house keeping work anymore (writing audits, etc).
  667. * When you call this method make sure to call loginSucceeded() or loginFailed()
  668. * depending on the success of the login.
  669. *
  670. * @param string $login
  671. * @param string $password
  672. * @param bool $authenticationMatch
  673. * @return mixed eZUser object on log in success, int userID if the username
  674. * exists but log in failed, or false if the username doesn't exists.
  675. */
  676. protected static function _loginUser( $login, $password, $authenticationMatch = false )
  677. {
  678. $http = eZHTTPTool::instance();
  679. $db = eZDB::instance();
  680. if ( $authenticationMatch === false )
  681. $authenticationMatch = eZUser::authenticationMatch();
  682. $loginEscaped = $db->escapeString( $login );
  683. $passwordEscaped = $db->escapeString( $password );
  684. $loginArray = array();
  685. if ( $authenticationMatch & self::AUTHENTICATE_LOGIN )
  686. $loginArray[] = "login='$loginEscaped'";
  687. if ( $authenticationMatch & self::AUTHENTICATE_EMAIL )
  688. {
  689. if ( eZMail::validate( $login ) )
  690. {
  691. $loginArray[] = "email='$loginEscaped'";
  692. }
  693. }
  694. if ( empty( $loginArray ) )
  695. $loginArray[] = "login='$loginEscaped'";
  696. $loginText = implode( ' OR ', $loginArray );
  697. $contentObjectStatus = eZContentObject::STATUS_PUBLISHED;
  698. $ini = eZINI::instance();
  699. $databaseName = $db->databaseName();
  700. // if mysql
  701. if ( $databaseName === 'mysql' )
  702. {
  703. $query = "SELECT contentobject_id, password_hash, password_hash_type, email, login
  704. FROM ezuser, ezcontentobject
  705. WHERE ( $loginText ) AND
  706. ezcontentobject.status='$contentObjectStatus' AND
  707. ezcontentobject.id=contentobject_id AND
  708. ( ( password_hash_type!=4 ) OR
  709. ( password_hash_type=4 AND
  710. ( $loginText ) AND
  711. password_hash=PASSWORD('$passwordEscaped') ) )";
  712. }
  713. else
  714. {
  715. $query = "SELECT contentobject_id, password_hash,
  716. password_hash_type, email, login
  717. FROM ezuser, ezcontentobject
  718. WHERE ( $loginText )
  719. AND ezcontentobject.status='$contentObjectStatus'
  720. AND ezcontentobject.id=contentobject_id";
  721. }
  722. $users = $db->arrayQuery( $query );
  723. $exists = false;
  724. if ( $users !== false && isset( $users[0] ) )
  725. {
  726. $ini = eZINI::instance();
  727. foreach ( $users as $userRow )
  728. {
  729. $userID = $userRow['contentobject_id'];
  730. $hashType = $userRow['password_hash_type'];
  731. $hash = $userRow['password_hash'];
  732. $exists = eZUser::authenticateHash( $userRow['login'], $password, eZUser::site(),
  733. $hashType,
  734. $hash );
  735. // If hash type is MySql
  736. if ( $hashType == self::PASSWORD_HASH_MYSQL and $databaseName === 'mysql' )
  737. {
  738. $queryMysqlUser = "SELECT contentobject_id, password_hash, password_hash_type, email, login
  739. FROM ezuser, ezcontentobject
  740. WHERE ezcontentobject.status='$contentObjectStatus' AND
  741. password_hash_type=4 AND ( $loginText ) AND password_hash=PASSWORD('$passwordEscaped') ";
  742. $mysqlUsers = $db->arrayQuery( $queryMysqlUser );
  743. if ( isset( $mysqlUsers[0] ) )
  744. $exists = true;
  745. }
  746. eZDebugSetting::writeDebug( 'kernel-user', eZUser::createHash( $userRow['login'], $password, eZUser::site(),
  747. $hashType, $hash ), "check hash" );
  748. eZDebugSetting::writeDebug( 'kernel-user', $hash, "stored hash" );
  749. // If current user has been disabled after a few failed login attempts.
  750. $canLogin = eZUser::isEnabledAfterFailedLogin( $userID );
  751. if ( $exists )
  752. {
  753. // We should store userID for warning message.
  754. $GLOBALS['eZFailedLoginAttemptUserID'] = $userID;
  755. $userSetting = eZUserSetting::fetch( $userID );
  756. $isEnabled = $userSetting->attribute( "is_enabled" );
  757. if ( $hashType != eZUser::hashType() and
  758. strtolower( $ini->variable( 'UserSettings', 'UpdateHash' ) ) == 'true' )
  759. {
  760. $hashType = eZUser::hashType();
  761. $hash = eZUser::createHash( $userRow['login'], $password, eZUser::site(),
  762. $hashType );
  763. $db->query( "UPDATE ezuser SET password_hash='$hash', password_hash_type='$hashType' WHERE contentobject_id='$userID'" );
  764. }
  765. break;
  766. }
  767. }
  768. }
  769. if ( $exists and $isEnabled and $canLogin )
  770. {
  771. return new eZUser( $userRow );
  772. }
  773. else
  774. {
  775. return isset( $userID ) ? $userID : false;
  776. }
  777. }
  778. /*!
  779. \static
  780. Checks if IP address of current user is in \a $ipList.
  781. */
  782. static function isUserIPInList( $ipList )
  783. {
  784. $ipAddress = eZSys::serverVariable( 'REMOTE_ADDR', true );
  785. if ( $ipAddress )
  786. {
  787. $result = false;
  788. foreach( $ipList as $itemToMatch )
  789. {
  790. if ( preg_match("/^(([0-9]+)\.([0-9]+)\.([0-9]+)\.([0-9]+))(\/([0-9]+)$|$)/", $itemToMatch, $matches ) )
  791. {
  792. if ( $matches[6] )
  793. {
  794. if ( eZDebug::isIPInNet( $ipAddress, $matches[1], $matches[7] ) )
  795. {
  796. $result = true;
  797. break;
  798. }
  799. }
  800. else
  801. {
  802. if ( $matches[1] == $ipAddress )
  803. {
  804. $result = true;
  805. break;
  806. }
  807. }
  808. }
  809. }
  810. }
  811. else
  812. {
  813. $result = (
  814. in_array( 'commandline', $ipList ) &&
  815. ( php_sapi_name() == 'cli' )
  816. );
  817. }
  818. return $result;
  819. }
  820. /*!
  821. \static
  822. Returns true if current user is trusted user.
  823. */
  824. static function isTrusted()
  825. {
  826. $ini = eZINI::instance();
  827. // Check if current user is trusted user.
  828. $trustedIPs = $ini->hasVariable( 'UserSettings', 'TrustedIPList' ) ? $ini->variable( 'UserSettings', 'TrustedIPList' ) : array();
  829. // Check if IP address of current user is in $trustedIPs array.
  830. $trustedUser = eZUser::isUserIPInList( $trustedIPs );
  831. if ( $trustedUser )
  832. return true;
  833. return false;
  834. }
  835. /*!
  836. \static
  837. Returns max number of failed login attempts.
  838. */
  839. static function maxNumberOfFailedLogin()
  840. {
  841. $ini = eZINI::instance();
  842. $maxNumberOfFailedLogin = $ini->hasVariable( 'UserSettings', 'MaxNumberOfFailedLogin' ) ? $ini->variable( 'UserSettings', 'MaxNumberOfFailedLogin' ) : '0';
  843. return $maxNumberOfFailedLogin;
  844. }
  845. /*
  846. \static
  847. Returns true if the user can login
  848. If user has number of failed login attempts more than eZUser::maxNumberOfFailedLogin()
  849. and user is not trusted
  850. the user will not be allowed to login.
  851. */
  852. static function isEnabledAfterFailedLogin( $userID, $ignoreTrusted = false )
  853. {
  854. if ( !is_numeric( $userID ) )
  855. return true;
  856. $userObject = eZUser::fetch( $userID );
  857. if ( !$userObject )
  858. return true;
  859. $trustedUser = eZUser::isTrusted();
  860. // If user is trusted we should stop processing
  861. if ( $trustedUser and !$ignoreTrusted )
  862. return true;
  863. $maxNumberOfFailedLogin = eZUser::maxNumberOfFailedLogin();
  864. if ( $maxNumberOfFailedLogin == '0' )
  865. return true;
  866. $failedLoginAttempts = $userObject->failedLoginAttempts();
  867. if ( $failedLoginAttempts > $maxNumberOfFailedLogin )
  868. return false;
  869. return true;
  870. }
  871. /*!
  872. \protected
  873. Makes sure the user \a $user is set as the currently logged in user by
  874. updating the session and setting the necessary global variables.
  875. All login handlers should use this function to ensure that the process
  876. is executed properly.
  877. */
  878. static function setCurrentlyLoggedInUser( $user, $userID )
  879. {
  880. $GLOBALS["eZUserGlobalInstance_$userID"] = $user;
  881. // Set/overwrite the global user, this will be accessed from
  882. // instance() when there is no ID passed to the function.
  883. $GLOBALS["eZUserGlobalInstance_"] = $user;
  884. eZSession::setUserID( $userID );
  885. eZSession::set( 'eZUserLoggedInID', $userID );
  886. self::cleanup();
  887. eZSession::regenerate();
  888. }
  889. /*!
  890. \virtual
  891. Used by login handler to clean up session variables
  892. */
  893. function sessionCleanup()
  894. {
  895. }
  896. /**
  897. * Cleanup user related session values, for use by login / logout code
  898. *
  899. * @internal
  900. */
  901. static function cleanup()
  902. {
  903. $http = eZHTTPTool::instance();
  904. $http->removeSessionVariable( 'CanInstantiateClassList' );
  905. $http->removeSessionVariable( 'ClassesCachedForUser' );
  906. // Note: This must be done more generic with an internal
  907. // callback system.
  908. eZPreferences::sessionCleanup();
  909. }
  910. /*!
  911. \return logs in the current user object
  912. */
  913. function loginCurrent()
  914. {
  915. self::setCurrentlyLoggedInUser( $this, $this->ContentObjectID );
  916. }
  917. /*!
  918. \static
  919. Logs out the current user
  920. */
  921. static function logoutCurrent()
  922. {
  923. $http = eZHTTPTool::instance();
  924. $id = false;
  925. $GLOBALS["eZUserGlobalInstance_$id"] = false;
  926. $contentObjectID = $http->sessionVariable( 'eZUserLoggedInID' );
  927. // reset session data
  928. $newUserID = self::anonymousId();
  929. eZSession::setUserID( $newUserID );
  930. $http->setSessionVariable( 'eZUserLoggedInID', $newUserID );
  931. // Clear current basket if necessary
  932. $db = eZDB::instance();
  933. $db->begin();
  934. eZBasket::cleanupCurrentBasket();
  935. $db->commit();
  936. if ( $contentObjectID )
  937. self::cleanup();
  938. // give user new session id
  939. eZSession::regenerate();
  940. // set the property used to prevent SSO from running again
  941. self::$userHasLoggedOut = true;
  942. }
  943. /**
  944. * Returns a shared instance of the eZUser class pr $id value.
  945. * If user can not be fetched, then anonymous user is returned and
  946. * a warning trown, if anonymous user can not be fetched, then NoUser
  947. * is returned and another warning is thrown.
  948. *
  949. * @param int|false $id On false: Gets current user id from session
  950. * or from {@link eZUser::anonymousId()} if not set.
  951. * @return eZUser
  952. */
  953. static function instance( $id = false )
  954. {
  955. if ( !empty( $GLOBALS["eZUserGlobalInstance_$id"] ) )
  956. {
  957. return $GLOBALS["eZUserGlobalInstance_$id"];
  958. }
  959. $userId = $id;
  960. $currentUser = null;
  961. $http = eZHTTPTool::instance();
  962. $anonymousUserID = self::anonymousId();
  963. $sessionHasStarted = eZSession::hasStarted();
  964. // If not specified get the current user
  965. if ( $userId === false )
  966. {
  967. if ( $sessionHasStarted )
  968. {
  969. $userId = $http->sessionVariable( 'eZUserLoggedInID' );
  970. if ( !is_numeric( $userId ) )
  971. {
  972. $userId = $anonymousUserID;
  973. eZSession::setUserID( $userId );
  974. $http->setSessionVariable( 'eZUserLoggedInID', $userId );
  975. }
  976. }
  977. else
  978. {
  979. $userId = $anonymousUserID;
  980. eZSession::setUserID( $userId );
  981. }
  982. }
  983. // Check user cache (this effectivly fetches user from cache)
  984. // user not found if !isset( isset( $userCache['info'][$userId] ) )
  985. $userCache = self::getUserCacheByUserId( $userId );
  986. if ( isset( $userCache['info'][$userId] ) )
  987. {
  988. $userArray = $userCache['info'][$userId];
  989. if ( is_numeric( $userArray['contentobject_id'] ) )
  990. {
  991. $currentUser = new eZUser( $userArray );
  992. $currentUser->setUserCache( $userCache );
  993. }
  994. }
  995. $ini = eZINI::instance();
  996. // Check if:
  997. // - the user has not logged out,
  998. // - the user is not logged in,
  999. // - and if a automatic single sign on plugin is enabled.
  1000. if ( !self::$userHasLoggedOut and is_object( $currentUser ) and !$currentUser->isLoggedIn() )
  1001. {
  1002. $ssoHandlerArray = $ini->variable( 'UserSettings', 'SingleSignOnHandlerArray' );
  1003. if ( !empty( $ssoHandlerArray ) )
  1004. {
  1005. $ssoUser = false;
  1006. foreach ( $ssoHandlerArray as $ssoHandler )
  1007. {
  1008. // Load handler
  1009. $handlerFile = 'kernel/classes/ssohandlers/ez' . strtolower( $ssoHandler ) . 'ssohandler.php';
  1010. if ( file_exists( $handlerFile ) )
  1011. {
  1012. include_once( $handlerFile );
  1013. $className = 'eZ' . $ssoHandler . 'SSOHandler';
  1014. $impl = new $className();
  1015. $ssoUser = $impl->handleSSOLogin();
  1016. }
  1017. else // check in extensions
  1018. {
  1019. $extensionDirectories = $ini->variable( 'UserSettings', 'ExtensionDirectory' );
  1020. $directoryList = eZExtension::expandedPathList( $extensionDirectories, 'sso_handler' );
  1021. foreach( $directoryList as $directory )
  1022. {
  1023. $handlerFile = $directory . '/ez' . strtolower( $ssoHandler ) . 'ssohandler.php';
  1024. if ( file_exists( $handlerFile ) )
  1025. {
  1026. include_once( $handlerFile );
  1027. $className = 'eZ' . $ssoHandler . 'SSOHandler';
  1028. $impl = new $className();
  1029. $ssoUser = $impl->handleSSOLogin();
  1030. }
  1031. }
  1032. }
  1033. }
  1034. // If a user was found via SSO, then use it
  1035. if ( $ssoUser !== false )
  1036. {
  1037. $currentUser = $ssoUser;
  1038. $userId = $currentUser->attribute( 'contentobject_id' );
  1039. $userInfo = array();
  1040. $userInfo[$userId] = array( 'contentobject_id' => $userId,
  1041. 'login' => $currentUser->attribute( 'login' ),
  1042. 'email' => $currentUser->attribute( 'email' ),
  1043. 'password_hash' => $currentUser->attribute( 'password_hash' ),
  1044. 'password_hash_type' => $currentUser->attribute( 'password_hash_type' )
  1045. );
  1046. eZSession::setUserID( $userId );
  1047. $http->setSessionVariable( 'eZUserLoggedInID', $userId );
  1048. eZUser::updateLastVisit( $userId );
  1049. eZUser::setCurrentlyLoggedInUser( $currentUser, $userId );
  1050. eZHTTPTool::redirect( eZSys::wwwDir() . eZSys::indexFile( false ) . eZSys::requestURI(), array(), 302 );
  1051. eZExecution::cleanExit();
  1052. }
  1053. }
  1054. }
  1055. if ( $userId <> $anonymousUserID )
  1056. {
  1057. $sessionInactivityTimeout = $ini->variable( 'Session', 'ActivityTimeout' );
  1058. if ( !isset( $GLOBALS['eZSessionIdleTime'] ) )
  1059. {
  1060. eZUser::updateLastVisit( $userId );
  1061. }
  1062. else
  1063. {
  1064. $sessionIdle = $GLOBALS['eZSessionIdleTime'];
  1065. if ( $sessionIdle > $sessionInactivityTimeout )
  1066. {
  1067. eZUser::updateLastVisit( $userId );
  1068. }
  1069. }
  1070. }
  1071. if ( !$currentUser )
  1072. {
  1073. $currentUser = eZUser::fetch( self::anonymousId() );
  1074. eZDebug::writeWarning( 'User not found, returning anonymous' );
  1075. }
  1076. if ( !$currentUser )
  1077. {
  1078. $currentUser = new eZUser( array( 'id' => -1, 'login' => 'NoUser' ) );
  1079. eZDebug::writeWarning( 'Anonymous user not found, returning NoUser' );
  1080. }
  1081. $GLOBALS["eZUserGlobalInstance_$id"] = $currentUser;
  1082. return $currentUser;
  1083. }
  1084. /**
  1085. * Get User cache from cache file
  1086. *
  1087. * @since 4.4
  1088. * @return array( 'info' => array, 'groups' => array, 'roles' => array, 'role_limitations' => array, 'access_array' => array)
  1089. */
  1090. public function getUserCache()
  1091. {
  1092. if ( $this->UserCache === null )
  1093. {
  1094. $this->setUserCache( self::getUserCacheByUserId( $this->ContentObjectID ) );
  1095. }
  1096. return $this->UserCache;
  1097. }
  1098. /**
  1099. * Delete User cache from locale var and cache file for current user.
  1100. *
  1101. * @since 4.4
  1102. */
  1103. public function purgeUserCache()
  1104. {
  1105. $this->UserCache = null;
  1106. self::purgeUserCacheByUserId( $this->ContentObjectID );
  1107. }
  1108. /**
  1109. * Set User cache from cache file
  1110. * Needs to be in excact same format as {@link eZUser::getUserCache()}!
  1111. *
  1112. * @since 4.4
  1113. * @param array $userCache
  1114. */
  1115. public function setUserCache( array $userCache )
  1116. {
  1117. $this->UserCache = $userCache;
  1118. }
  1119. /**
  1120. * Delete User cache from cache file for Anonymous user(usefull for sessionless users)
  1121. *
  1122. * @since 4.4
  1123. * @see eZUser::purgeUserCacheByUserId()
  1124. */
  1125. static public function purgeUserCacheByAnonymousId()
  1126. {
  1127. self::purgeUserCacheByUserId( self::anonymousId() );
  1128. }
  1129. /**
  1130. * Delete User cache pr user
  1131. *
  1132. * @since 4.4
  1133. * @param int $userId
  1134. */
  1135. static public function purgeUserCacheByUserId( $userId )
  1136. {
  1137. $cacheFilePath = eZUser::getCacheDir( $userId ). "/user-data-{$userId}.cache.php" ;
  1138. eZClusterFileHandler::instance()->fileDelete( $cacheFilePath );
  1139. }
  1140. /**
  1141. * Get User cache from cache file for Anonymous user(usefull for sessionless users)
  1142. *
  1143. * @since 4.4
  1144. * @see eZUser::getUserCacheByUserId()
  1145. * @return array
  1146. */
  1147. static public function getUserCacheByAnonymousId()
  1148. {
  1149. return self::getUserCacheByUserId( self::anonymousId() );
  1150. }
  1151. /**
  1152. * Get User cache from cache file (usefull for sessionless users)
  1153. *
  1154. * @since 4.4
  1155. * @see eZUser::getUserCache()
  1156. * @param int $userId
  1157. * @return array
  1158. */
  1159. static protected function getUserCacheByUserId( $userId )
  1160. {
  1161. $cacheFilePath = eZUser::getCacheDir( $userId ). "/user-data-{$userId}.cache.php" ;
  1162. $cacheFile = eZClusterFileHandler::instance( $cacheFilePath );
  1163. return $cacheFile->processCache( array( 'eZUser', 'retrieveUserCacheFromFile' ),
  1164. array( 'eZUser', 'generateUserCacheForFile' ),
  1165. null,
  1166. self::userInfoExpiry(),
  1167. $userId );
  1168. }
  1169. /**
  1170. * Callback which fetches user cache from local file.
  1171. *
  1172. * @internal
  1173. * @since 4.4
  1174. * @see eZUser::getUserCacheByUserId()
  1175. */
  1176. static function retrieveUserCacheFromFile( $filePath, $mtime, $userId )
  1177. {
  1178. return include( $filePath );
  1179. }
  1180. /**
  1181. * Callback which generates user cache for user
  1182. *
  1183. * @internal
  1184. * @since 4.4
  1185. * @see eZUser::getUserCacheByUserId()
  1186. */
  1187. static function generateUserCacheForFile( $filePath, $userId )
  1188. {
  1189. $data = array( 'info' => array(),
  1190. 'groups' => array(),
  1191. 'roles' => array(),
  1192. 'role_limitations' => array(),
  1193. 'access_array' => array(),
  1194. 'discount_rules' => array() );
  1195. $user = eZUser::fetch( $userId );
  1196. if ( $user instanceOf eZUser )
  1197. {
  1198. // user info (session: eZUserInfoCache)
  1199. $data['info'][$userId] = array( 'contentobject_id' => $user->attribute( 'contentobject_id' ),
  1200. 'login' => $user->attribute( 'login' ),
  1201. 'email' => $user->attribute( 'email' ),
  1202. 'password_hash' => $user->attribute( 'password_hash' ),
  1203. 'password_hash_type' => $user->attribute( 'password_hash_type' ) );
  1204. // user groups list (session: eZUserGroupsCache)
  1205. $groups = $user->generateGroupIdList();
  1206. $data['groups'] = $groups;
  1207. // role list (session: eZRoleIDList)
  1208. $groups[] = $userId;
  1209. $data['roles'] = eZRole::fetchIDListByUser( $groups );
  1210. // role limitation list (session: eZRoleLimitationValueList)
  1211. $limitList = $user->limitList( false );
  1212. foreach ( $limitList as $limit )
  1213. {
  1214. $data['role_limitations'][] = $limit['limit_value'];
  1215. }
  1216. // access array (session: AccessArray)
  1217. $data['access_array'] = $user->generateAccessArray();
  1218. // discount rules (session: eZUserDiscountRules<userId>)
  1219. $data['discount_rules'] = eZUserDiscountRule::generateIDListByUserID( $userId );
  1220. }
  1221. return array( 'content' => $data,
  1222. 'scope' => 'user-info-cache',
  1223. 'datatype' => 'php',
  1224. 'store' => true );
  1225. }
  1226. /*!
  1227. Updates the user's last visit timestamp
  1228. Optionally updates user login count by setting $updateLoginCount to true
  1229. */
  1230. static function updateLastVisit( $userID, $updateLoginCount = false )
  1231. {
  1232. if ( isset( $GLOBALS['eZUserUpdatedLastVisit'] ) )
  1233. return;
  1234. $db = eZDB::instance();
  1235. $userID = (int) $userID;
  1236. $userVisitArray = $db->arrayQuery( "SELECT 1 FROM ezuservisit WHERE user_id=$userID" );
  1237. $time = time();
  1238. if ( isset( $userVisitArray[0] ) )
  1239. {
  1240. $loginCountSQL = $updateLoginCount ? ', login_count=login_count+1' : '';
  1241. $db->query( "UPDATE ezuservisit SET last_visit_timestamp=current_visit_timestamp, current_visit_timestamp=$time$loginCountSQL WHERE user_id=$userID" );
  1242. }
  1243. else
  1244. {
  1245. $intialLoginCount = $updateLoginCount ? 1 : 0;
  1246. $db->query( "INSERT INTO ezuservisit ( current_visit_timestamp, last_visit_timestamp, user_id, login_count ) VALUES ( $time, $time, $userID, $intialLoginCount )" );
  1247. }
  1248. $GLOBALS['eZUserUpdatedLastVisit'] = true;
  1249. }

Large files files are truncated, but you can click here to view the full file