PageRenderTime 987ms CodeModel.GetById 345ms app.highlight 533ms RepoModel.GetById 91ms 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

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

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