<?php

/*
 +--------------------------------------------------------------------+
 | CiviCRM version 3.0                                                |
 +--------------------------------------------------------------------+
 | Copyright CiviCRM LLC (c) 2004-2009                                |
 +--------------------------------------------------------------------+
 | This file is a part of CiviCRM.                                    |
 |                                                                    |
 | CiviCRM is free software; you can copy, modify, and distribute it  |
 | under the terms of the GNU Affero General Public License           |
 | Version 3, 19 November 2007.                                       |
 |                                                                    |
 | CiviCRM is distributed in the hope that it will be useful, but     |
 | WITHOUT ANY WARRANTY; without even the implied warranty of         |
 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.               |
 | See the GNU Affero General Public License for more details.        |
 |                                                                    |
 | You should have received a copy of the GNU Affero General Public   |
 | License along with this program; if not, contact CiviCRM LLC       |
 | at info[AT]civicrm[DOT]org. If you have questions about the        |
 | GNU Affero General Public License or the licensing of CiviCRM,     |
 | see the CiviCRM license FAQ at http://civicrm.org/licensing        |
 +--------------------------------------------------------------------+
*/

/**
 *
 * @package CRM
 * @copyright CiviCRM LLC (c) 2004-2009
 * $Id$
 *
 */

require_once 'CRM/Core/Session.php';
require_once 'CRM/Core/DAO/UFMatch.php';

/**
 * The basic class that interfaces with the external user framework
 */
class CRM_Core_BAO_UFMatch extends CRM_Core_DAO_UFMatch {
    /**
     * Given a UF user object, make sure there is a contact
     * object for this user. If the user has new values, we need
     * to update the CRM DB with the new values
     *
     * @param Object  $user    the drupal user object
     * @param boolean $update  has the user object been edited
     * @param         $uf
     * 
     * @return void
     * @access public
     * @static
     */
  static function synchronize( &$user, $update, $uf, $ctype ) {
        $session =& CRM_Core_Session::singleton( );
        if ( ! is_object( $session ) ) {
            CRM_Core_Error::fatal( 'wow, session is not an object?' );
            return;
        }
        
        //print "synchronize called with uniq_id " . $user->identity_url . "<br/>";

        if ( $uf == 'Drupal' ) {
            $key   = 'uid';
            $login = 'name';
            $mail  = 'mail';
        } else if ( $uf == 'Joomla' ) {
            $key   = 'id';
            $login = 'username';
            $mail  = 'email';
        } else if ( $uf == 'Standalone' ) {
            $key = 'id';
            $mail = 'email';
            $uniqId = $user->identity_url;
            $query = "
SELECT    uf_id
FROM      civicrm_uf_match 
LEFT JOIN civicrm_openid ON ( civicrm_uf_match.contact_id = civicrm_openid.contact_id ) 
WHERE     openid = %1";
            $p = array( 1 => array( $uniqId, 'String' ) );
            $dao = CRM_Core_DAO::executeQuery( $query, $p );
            if ( $dao->fetch() ) {
                $user->$key = $dao->uf_id;
            }

            if ( ! $user->$key ) {
                // Let's get the next uf_id since we don't actually have one
                $user->$key = self::getNextUfIdValue( );
            }
        } else {
            CRM_Core_Error::statusBounce(ts('Please set the user framework variable'));
        }
        
        // make sure we load the joomla object to get valid information
        if ( $uf == 'Joomla' ) {
            if ( ! isset( $user->id ) || ! isset( $user->email ) ) {
                $user =& JFactory::getUser( );
            }
        }

        // if the id of the object is zero (true for anon users in drupal)
        // have we already processed this user, if so early
        // return.
        $userID = $session->get( 'userID' );
        $ufID   = $session->get( 'ufID'   );

        if ( ! $update && $ufID == $user->$key ) {
            //print "Already processed this user<br/>";
            return;
        }

        // reset the session if we are a different user
        if ( $ufID && $ufID != $user->$key ) {
            $session->reset( );
        }

        // return early
        if ( $user->$key == 0 ) {
            return;
        }

        if ( ! isset( $uniqId ) ||
             ! $uniqId ) {
            $uniqId = $user->$mail;
        }

        //print "Calling synchronizeUFMatch...<br/>";
        $ufmatch =& self::synchronizeUFMatch( $user, $user->$key, $uniqId, $uf, null, $ctype );
        if ( ! $ufmatch ) {
            return;
        }

        $session->set( 'ufID'    , $ufmatch->uf_id          );
        $session->set( 'userID'  , $ufmatch->contact_id     );
        $session->set( 'ufUniqID', isset($ufmatch->user_unique_id) ? $ufmatch->user_unique_id : "");

        if ( $update ) {
            // the only information we care about is uniqId, so lets check that
            if ( ! isset( $ufmatch->user_unique_id ) ||
                 $uniqId != $ufmatch->user_unique_id ) {
                // uniqId has changed, so we need to update that everywhere
                $ufmatch->user_unique_id = $uniqId;
                $ufmatch->save( );
            }
        }
    }

    /**
     * Synchronize the object with the UF Match entry. Can be called stand-alone from
     * the drupalUsers script
     *
     * @param Object  $user    the drupal user object
     * @param string  $userKey the id of the user from the uf object
     * @param string  $uniqId    the OpenID of the user
     * @param string  $uf      the name of the user framework
     * @param integer $status  returns the status if user created or already exits (used for CMS sync)
     *
     * @return the ufmatch object that was found or created
     * @access public
     * @static
     */
    static function &synchronizeUFMatch( &$user, $userKey, $uniqId, $uf, $status = null, $ctype = null ) 
    {
        // validate that uniqId is a valid url. it will either be
        // an OpenID (which should always be a valid url) or a
        // http://uf_username/ construction (so that it can
        // be used as an OpenID in the future)
        require_once 'CRM/Utils/Rule.php';
        if ( $uf == 'Standalone' ) {
            if ( ! CRM_Utils_Rule::url( $uniqId ) ) {
                return $status ? null : false;
            }
        } else if ( ! CRM_Utils_Rule::email( $uniqId ) ) {
            return $status ? null : false;
        }
        
        $newContact   = false;

        // make sure that a contact id exists for this user id
        $ufmatch =& new CRM_Core_DAO_UFMatch( );
        if ( CRM_Core_DAO::checkFieldExists('civicrm_uf_match', 'domain_id') ) {
            // FIXME: if() condition check was required especially for upgrade cases (2.2.x -> 3.0.x), 
            // where folks if happen to logout, would encounter a column not found fatal error  
            $ufmatch->domain_id = CRM_Core_Config::domainID( );
        }
        $ufmatch->uf_id = $userKey;
        if ( ! $ufmatch->find( true ) ) {
            require_once 'CRM/Core/Transaction.php';
            $transaction = new CRM_Core_Transaction( );

            require_once 'CRM/Contact/BAO/Contact.php';
            $dao =& CRM_Contact_BAO_Contact::matchContactOnEmail( $uniqId, $ctype );
            
            if ( ! $dao && ( $uf == 'Standalone' ) ) {
                $dao =& CRM_Contact_BAO_Contact::matchContactOnOpenId( $uniqId, $ctype );
            }
            if ( $dao ) {
                //print "Found contact with uniqId $uniqId<br/>";
                $ufmatch->contact_id     = $dao->contact_id;
                $ufmatch->uf_name        = $uniqId;
            } else {
                if ( $uf == 'Drupal' ) {
                    $mail = 'mail';
                } else {
                    $mail = 'email';
                }
                
                if ( is_Object($user) ) {
                    $params = array( 'email-Primary'  => $user->$mail );
                }
                
                if ( $ctype == 'Organization' ) {
                    $params['organization_name'] = $uniqId;
                } else if ( $ctype == 'Household' ) {
                    $params['household_name'] = $uniqId;
                }
                if ( ! $ctype ) {
                    $ctype = "Individual";
                }
                $params['contact_type'] = $ctype;

                // extract first / middle / last name
                // for joomla
                if ( $uf == 'Joomla' && $user->name ) {
                    require_once 'CRM/Utils/String.php';
                    CRM_Utils_String::extractName( $user->name, $params );
                }

                if ( $uf == 'Standalone' ) {
                    $params['openid-Primary'] = $uniqId;

                    //need to delete below code once profile is
                    //exposed on signup page
                    if ( ( ! empty( $user->first_name ) ) || ( ! empty( $user->last_name ) ) ) {
                        $params['first_name'] = $user->first_name;
                        $params['last_name'] = $user->last_name;
                    } elseif ( ! empty( $user->name ) ) {
                        require_once 'CRM/Utils/String.php';
                        CRM_Utils_String::extractName( $user->name, $params );
                    }
                }

                $contactId = CRM_Contact_BAO_Contact::createProfileContact( $params, CRM_Core_DAO::$_nullArray );
                $ufmatch->contact_id     = $contactId;
                $ufmatch->uf_name        = $uniqId;
            }

            $ufmatch->save( );
            $ufmatch->free();
            $newContact   = true;
            
            $transaction->commit();
        }

        if ( $status ) {
            return $newContact;
        } else {
            return $ufmatch;
        }
    }

    /**
     * update the uf_name in the user object
     *
     * @param int    $contactId id of the contact to update
     *
     * @return void
     * @access public
     * @static
     */
    static function updateUFName( $contactId ) {
        $config =& CRM_Core_Config::singleton( );
        if ( $config->userFramework == 'Standalone' ) {
            $ufName = CRM_Contact_BAO_Contact::getPrimaryOpenId( $contactId );
        } else {
            $ufName = CRM_Contact_BAO_Contact::getPrimaryEmail( $contactId );
        }

        if ( ! $ufName ) {
            return;
        }

        $ufmatch =& new CRM_Core_DAO_UFMatch( );
        $ufmatch->contact_id = $contactId;
        $ufmatch->domain_id  = CRM_Core_Config::domainID( );
        if ( ! $ufmatch->find( true ) ||
             $ufmatch->uf_name == $ufName ) {
            // if object does not exist or the OpenID has not changed
            return;
        }

        // save the updated ufmatch object
        $ufmatch->uf_name = $ufName;
        $ufmatch->save( );

        require_once 'CRM/Core/BAO/CMSUser.php';
        CRM_Core_BAO_CMSUser::updateUFName( $ufmatch->uf_id, $ufName );

    }
    
    /**
     * Update the email value for the contact and user profile
     *  
     * @param  $contactId  Int     Contact ID of the user
     * @param  $email      String  email to be modified for the user
     *
     * @return void
     * @access public
     * @static
     */
    static function updateContactEmail($contactId, $emailAddress) 
    {
        $emailAddress = strtolower( $emailAddress );

        $ufmatch =& new CRM_Core_DAO_UFMatch( );
        $ufmatch->contact_id = $contactId;
        $ufmatch->domain_id  = CRM_Core_Config::domainID( );
        if ( $ufmatch->find( true ) ) {
            // Save the email in UF Match table
            $ufmatch->uf_name = $emailAddress;
            $ufmatch->save( );
            
            //check if the primary email for the contact exists 
            //$contactDetails[1] - email 
            //$contactDetails[3] - email id
            require_once 'CRM/Contact/BAO/Contact/Location.php';
            $contactDetails = CRM_Contact_BAO_Contact_Location::getEmailDetails( $contactId );
            
            if ( trim($contactDetails[1]) ) {
                $emailID = $contactDetails[3];
                //update if record is found
                $query ="UPDATE  civicrm_email
                     SET email = %1
                     WHERE id =  %2";
                $p = array( 1 => array( $emailAddress, 'String'  ),
                            2 => array( $emailID, 'Integer' ) );
                $dao =& CRM_Core_DAO::executeQuery( $query, $p );
            } else {
                //else insert a new email record
                $email =& new CRM_Core_DAO_Email();
                $email->contact_id  = $contactId;
                $email->is_primary  = 1;
                $email->email       = $emailAddress; 
                $email->save( );
                $emailID = $email->id;
            }

            require_once 'CRM/Core/BAO/Log.php';
            CRM_Core_BAO_Log::register( $contactId,
                                        'civicrm_email',
                                        $emailID  );
        }
    }
    
    /**
     * Delete the object records that are associated with this cms user
     *
     * @param  int  $ufID id of the user to delete
     *
     * @return void
     * @access public
     * @static
     */
    static function deleteUser( $ufID ) {
        $ufmatch =& new CRM_Core_DAO_UFMatch( );

        $ufmatch->uf_id      = $ufID;
        $ufmatch->domain_id  = CRM_Core_Config::domainID( );
        $ufmatch->delete( );
    }

    /**
     * get the contact_id given a uf_id
     *
     * @param int  $ufID  Id of UF for which related contact_id is required
     *
     * @return int    contact_id on success, null otherwise
     * @access public
     * @static
     */
    static function getContactId( $ufID ) {
        if (!isset($ufID)) {
            return null;
        }

        $ufmatch =& new CRM_Core_DAO_UFMatch( );

        $ufmatch->uf_id     = $ufID;
        $ufmatch->domain_id = CRM_Core_Config::domainID( );
        if ( $ufmatch->find( true ) ) {
            return $ufmatch->contact_id;
        }
        return null;
    }

    /** 
     * get the uf_id given a contact_id 
     * 
     * @param int  $contactID   ID of the contact for which related uf_id is required
     * 
     * @return int    uf_id of the given contact_id on success, null otherwise
     * @access public 
     * @static 
     */ 
    static function getUFId( $contactID ) { 
        if (!isset($contactID)) { 
            return null; 
        } 
        
        $ufmatch =& new CRM_Core_DAO_UFMatch( ); 
        
        $ufmatch->contact_id = $contactID;
        if ( $ufmatch->find( true ) ) {
            return $ufmatch->uf_id;
        }
        return null;
    }

    static function isEmptyTable( ) {
        $sql = "SELECT count(id) FROM civicrm_uf_match";
        return CRM_Core_DAO::singleValueQuery( $sql ) > 0 ? false : true;
    }

    /**
     * get the list of contact_id
     *
     *
     * @return int    contact_id on success, null otherwise
     * @access public
     * @static
     */
    static function getContactIDs() {
        $id = array();
        $dao =& new CRM_Core_DAO_UFMatch();
        $dao->find();
        while ($dao->fetch()) {
            $id[] = $dao->contact_id;
        }
        return $id;
    }
    
    /**
     * see if this user exists, and if so, if they're allowed to login
     *
     *
     * @return bool     true if allowed to login, false otherwise
     * @access public
     * @static
     */
    static function getAllowedToLogin( $openId ) {
        $ufmatch =& new CRM_Core_DAO_UFMatch( );
        $ufmatch->uf_name = $openId;
        $ufmatch->allowed_to_login = 1;
        if ( $ufmatch->find( true ) ) {
            return true;
        }
        return false;
    }
    
    /**
     * get the next unused uf_id value, since the standalone UF doesn't
     * have id's (it uses OpenIDs, which go in a different field)
     *
     *
     * @return int     next highest unused value for uf_id
     * @access public
     * @static
     */
    static function getNextUfIdValue( ) {
        $query = "SELECT MAX(uf_id)+1 AS next_uf_id FROM civicrm_uf_match";
        $dao   = CRM_Core_DAO::executeQuery( $query );
        if ( $dao->fetch() ) {
            $ufId = $dao->next_uf_id;
        }

        if ( ! isset($ufId) ) {
            $ufId = 1;
        }
        return $ufId;
    }

    static function isDuplicateUser( $email ) {
        $session   =& CRM_Core_Session::singleton( );
        $contactID =  $session->get( 'userID' );
        if ( ! empty( $email ) &&
             isset( $contactID ) ) {
            $dao =& new CRM_Core_DAO_UFMatch();
            $dao->uf_name = $email;
            if ( $dao->find( true ) &&
                 $contactID != $dao->contact_id ) {
                return true;
            }
        }
        return false;
    }

}
