<?php
/*
 * $RCSfile: GalleryUser.class,v $
 *
 * Gallery - a web based photo album viewer and editor
 * Copyright (C) 2000-2005 Bharat Mediratta
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or (at
 * your option) any later version.
 *
 * This program 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
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA  02110-1301, USA.
 */
/**
 * @version $Revision: 1.40 $ $Date: 2005/08/23 03:49:03 $
 * @package GalleryCore
 * @author Bharat Mediratta <bharat@menalto.com>
 */

/**
 * Load the parent class
 */
GalleryCoreApi::relativeRequireOnce('modules/core/classes/GalleryEntity.class');

/**
 * Representation of a single user
 *
 * This class is the container for information about Gallery
 * users.  Each instance of User contains a unique user id. It
 * must be implemented by a class that has a persistent store for
 * the relevant user data.
 *
 * @g2 <class-name>GalleryUser</class-name>
 * @g2 <parent-class-name>GalleryEntity</parent-class-name>
 * @g2 <schema>
 * @g2   <schema-major>1</schema-major>
 * @g2   <schema-minor>0</schema-minor>
 * @g2 </schema>
 * @g2 <requires-id/>
 *
 * @package GalleryCore
 * @subpackage Classes
 */
class GalleryUser_core extends GalleryEntity {

    /*
     * ****************************************
     *                 Members
     * ****************************************
     */

    /**
     * The User's username
     *
     * @g2 <member>
     * @g2   <member-name>userName</member-name>
     * @g2   <member-type>STRING</member-type>
     * @g2   <member-size>SMALL</member-size>
     * @g2   <unique/>
     * @g2   <required/>
     * @g2 </member>
     *
     * @var string $_userName
     * @access private
     */
    var $_userName;

    /**
     * The User's full name
     *
     * @g2 <member>
     * @g2   <member-name>fullName</member-name>
     * @g2   <member-type>STRING</member-type>
     * @g2   <member-size>MEDIUM</member-size>
     * @g2 </member>
     *
     * @var string $_fullName
     * @access private
     */
    var $_fullName;

    /**
     * The User's password in a hashed form.
     *
     * @g2 <member>
     * @g2   <member-name>hashedPassword</member-name>
     * @g2   <member-type>STRING</member-type>
     * @g2   <member-size>MEDIUM</member-size>
     * @g2 </member>
     *
     * @var string $_hashedPassword
     * @access private
     */
    var $_hashedPassword;

    /**
     * The User's email address.
     *
     * @g2 <member>
     * @g2   <member-name>email</member-name>
     * @g2   <member-type>STRING</member-type>
     * @g2   <member-size>MEDIUM</member-size>
     * @g2 </member>
     *
     * @var string $_email
     * @access private
     */
    var $_email;

    /**
     * The User's language preference
     *
     * @g2 <member>
     * @g2   <member-name>language</member-name>
     * @g2   <member-type>STRING</member-type>
     * @g2   <member-size>MEDIUM</member-size>
     * @g2 </member>
     *
     * @var string $_language
     * @access private
     */
    var $_language;

    /*
     * ****************************************
     *                 Methods
     * ****************************************
     */

    /**
     * Create a new instance of this user in the persistent store
     *
     * @return object GalleryStatus a status code
     */
    function create($userName) {
	global $gallery;

	$query = '
	SELECT
	  [GalleryUser::id]
	FROM
	  [GalleryUser]
	WHERE
	  [GalleryUser::userName] = ?
	';

	/* Check to see if we have a collision */
	list($ret, $results) =
	    $gallery->search($query, array($userName),
			     array('limit' => array('count' => 1)));

	if ($ret->isError()) {
	    return $ret->wrap(__FILE__, __LINE__);
	}

	$result = $results->nextResult();
	if ($result[0] > 0) {
	    return GalleryStatus::error(ERROR_COLLISION, __FILE__, __LINE__);
	}

	$ret = parent::create();
	if ($ret->isError()) {
	    return $ret->wrap(__FILE__, __LINE__);
	}

	$this->setUserName($userName);
	return GalleryStatus::success();
    }

    /**
     * Is the password provided correct?
     *
     * @param string a plaintext password
     * @return boolean true if the password is correct
     */
    function isCorrectPassword($password) {
	$valid = $this->getHashedPassword();
	$salt = substr($valid, 0, 4);
	/* Support both old (G1 thru 1.4.0; G2 thru alpha-4) and new password schemes: */
	$guess = (strlen($valid) == 32) ? md5($password) : ($salt . md5($salt . $password));
	return !strcmp($guess, $valid);
    }

    /**
     * Change the user's password to the new value provided.
     *
     * @param string a plaintext password
     */
    function changePassword($newPassword) {
	$salt = '';
	for ($i = 0; $i < 4; $i++) {
	    $char = mt_rand(48, 109);
	    $char += ($char > 90) ? 13 : ($char > 57) ? 7 : 0;
	    $salt .= chr($char);
	}
	$this->setHashedPassword($salt . md5($salt . $newPassword));
    }

    /**
     * Save the changes to this GalleryUser
     *
     * Do some bookkeeping, like adding the user to the all user and admin
     * groups.
     *
     * @access public
     * @return object GalleryStatus a status code
     */
    function save() {
	global $gallery;

	if ($this->testPersistentFlag(STORAGE_FLAG_NEWLY_CREATED)) {
	    $newlyCreated = 1;
	} else {
	    $newlyCreated = 0;
	}

	$ret = parent::save();
	if ($ret->isError()) {
	    return $ret->wrap(__FILE__, __LINE__);
	}

	if ($newlyCreated) {
	    /* Add her to the various groups */
	    foreach (array('id.allUserGroup', 'id.everybodyGroup') as $groupKey) {
		list($ret, $groupId) =
		    GalleryCoreApi::getPluginParameter('module', 'core', $groupKey);
		if ($ret->isError()) {
		    return $ret->wrap(__FILE__, __LINE__);
		}

		$ret = GalleryCoreApi::addUserToGroup($this->getId(), $groupId);
		if ($ret->isError()) {
		    return $ret->wrap(__FILE__, __LINE__);
		}
	    }
	}

	return GalleryStatus::success();
    }

    /**
     * Delete this GalleryUser
     *
     * Do some bookkeeping, like removing the user from all groups,
     * remapping his items to a site admin user and removing all
     * of his permissions
     *
     * @access public
     * @return object GalleryStatus a status code
     */
    function delete() {
	global $gallery;
	$activeUserId = $gallery->getActiveUserId();
	if ($activeUserId == $this->getId()) {
	    return GalleryStatus::error(ERROR_BAD_PARAMETER, __FILE__, __LINE__);
	}

	/*
	 * First assign all items of this user to another owner.
	 * You should call remapOwner() before calling $user->delete()
	 * but we call it here again, for 100% data integrity.
	 * If remapOwner() has been called before, as it is the case with
	 * the AdminDeleteUser controller, this 2nd call to remapOwner()
	 * does exactly nothing.
	 */
	/* Check if activeUser is Site Admin, if not, get any of the Site Admins */
	list($ret, $isAdmin) = GalleryCoreApi::isUserInSiteAdminGroup();
	if ($ret->isError()) {
	    return $ret->wrap(__FILE__, __LINE__);
	}
	if ($isAdmin) {
	    $newOwnerId = $activeUserId;
	} else {
	    list ($ret, $siteAdminGroupId) =
		GalleryCoreApi::getPluginParameter('module', 'core', 'id.adminGroup');
	    if ($ret->isError()) {
		return $ret->wrap(__FILE__, __LINE__);
	    }
	    list ($ret, $adminUsers) = GalleryCoreApi::fetchUsersForGroup($siteAdminGroupId, 2);
	    if ($ret->isError()) {
		return $ret->wrap(__FILE__, __LINE__);
	    }
	    if (empty($adminUsers)) {
		return GalleryStatus::error(ERROR_MISSING_VALUE, __FILE__, __LINE__);
	    }
	    $adminUsers = array_keys($adminUsers);
	    if ($adminUsers[0] == $this->getId() && count($adminUsers) == 1) {
		/* Block attempt to delete the only site admin */
		return GalleryStatus::error(ERROR_BAD_PARAMETER, __FILE__, __LINE__);
	    }
	    $newOwnerId = $adminUsers[0] != $this->getId() ? $adminUsers[0] : $adminUsers[1];
	}

	/* Now remap the owner of all of his items */
	$ret = GalleryCoreApi::remapOwnerId($this->getId(), $newOwnerId);
	if ($ret->isError()) {
	    return $ret->wrap(__FILE__, __LINE__);
	}

	/* Delete all of his permissions from the permissions map table */
	GalleryCoreApi::relativeRequireOnce('modules/core/classes/GalleryAccessMap.class');
	$ret = GalleryAccessMap::removeMapEntry(array('userId' => $this->getId()));
	if ($ret->isError()) {
	    return $ret->wrap(__FILE__, __LINE__);
	}

	/* And remove him from all groups he was a member of */
	$ret = GalleryCoreApi::removeUserFromAllGroups($this->getId());
	if ($ret->isError()) {
	    return $ret->wrap(__FILE__, __LINE__);
	}

	/* And finally delete the user from the database */
	$ret = parent::delete();
	if ($ret->isError()) {
	    return $ret->wrap(__FILE__, __LINE__);
	}

	return GalleryStatus::success();
    }

    /**
     * @see GalleryEntity::itemTypeName
     */
    function itemTypeName($localized = true) {
	global $gallery;
	if ($localized) {
	    list ($ret, $core) = GalleryCoreApi::loadPlugin('module', 'core');
	    if (! $ret->isError()) {
		return array($core->translate('User'), $core->translate('user'));
	    }
	}
	return array('User', 'user');
    }
}

include(dirname(__FILE__) . '/interfaces/GalleryUser.inc');
?>
