<?PHP
#
#   FILE:  CWUserFactory.php
#
#   Part of the Collection Workflow Integration System (CWIS)
#   Copyright 2013 Edward Almasy and Internet Scout Research Group
#   http://scout.wisc.edu/cwis/
#

/**
* CWIS-specific user factory class.
*/
class CWUserFactory extends UserFactory
{
    # ---- PUBLIC INTERFACE --------------------------------------------------

    /**
    * Construct the user factory object.
    */
    public function __construct()
    {
        parent::__construct();
        $this->ResourceFactory = new ResourceFactory(MetadataSchema::SCHEMAID_USER);
    }

    /**
    * Get a list of users sorted by how many resources they have added or
    * edited, starting with those who have added/edited the most.
    * @param int $Limit The maximum number of users to return.  (OPTIONAL,
    *       defaults to 5.)
    * @return array List of users, with user IDs for the index and CWUser
    *       objects for the values.
    */
    public function GetTopContributors($Limit = 5)
    {
        # assume no users will be found
        $Users = array();

        $Schema = new MetadataSchema();
        $LastModField = $Schema->GetFieldByName("Last Modified By Id");

        # fetch the top contributors
        $this->DB->Query(
            "SELECT UserId FROM ResourceUserInts "
            ." WHERE FieldId = ".$LastModField->Id()
            ." GROUP BY UserId"
            ." ORDER BY COUNT(*) DESC"
            ." LIMIT ".intval($Limit));
        $UserIds = $this->DB->FetchColumn("UserId");

        # for each user id found
        foreach ($UserIds as $UserId)
        {
            $Users[$UserId] = new CWUser($UserId);
        }

        # return the newest users
        return $Users;
    }

    /**
    * Get the users sorted by when they last added or edited a resource
    * starting with those who added/edited a resource most recently.
    * @param int $Limit The maximum number of users to return.  (OPTIONAL,
    *       defaults to 5.)
    * @return array List of users, with user IDs for the index and CWUser
    *       objects for the values.
    */
    public function GetMostRecentContributors($Limit = 5)
    {
        # assume no users will be found
        $Users = array();

        $Schema = new MetadataSchema();
        $LastModField = $Schema->GetFieldByName("Last Modified By Id");

        # fetch the top contributors
        $this->DB->Query(
            "SELECT UserId FROM ResourceUserInts RU, Resources R "
            ." WHERE RU.FieldId = ".$LastModField->Id()
            ." AND R.ResourceId = RU.ResourceId "
            ." GROUP BY RU.UserId"
            ." ORDER BY MAX(R.DateLastModified) DESC"
            ." LIMIT ".intval($Limit));
        $UserIds = $this->DB->FetchColumn("UserId");

        # for each user id found
        foreach ($UserIds as $UserId)
        {
            $Users[$UserId] = new CWUser($UserId);
        }

        # return the newest users
        return $Users;
    }

    /**
    * Derive a unique username from an email address.
    * @param string $Email Source email address.
    * @return string Unique username.
    */
    public static function GenerateUniqueUsernameFromEmail($Email)
    {
        $TrialName = explode('@', $Email);
        $TrialName = array_shift($TrialName);
        $TrialName = preg_replace("/[^A-Za-z0-9]/", "", $TrialName);
        $TrialName = strtolower($TrialName);

        # if this email address is very short, we'll pad it with some random
        # characters
        if (strlen($TrialName) < 2)
        {
            $TrialName .= GetRandomCharacters(2, "/[^a-hj-np-z0-9]/");
        }

        # see if the specified name exists
        $UFactory = new UserFactory();

        $Name = self::AppendSuffix($TrialName, '');

        while ($UFactory->UserNameExists($Name))
        {
            $Name = self::AppendSuffix(
                $TrialName, GetRandomCharacters(2) );
        }

        return $Name;
    }

    # ---- OVERRIDDEN METHODS ------------------------------------------------

    /**
    * Create a new user. The second password and e-mail address parameters are
    * intended for second copies of each entered by the user.
    * @param string $UserName Login name for new user.
    * @param string $Password Password for new user.
    * @param string $PasswordAgain Second copy of password entered by user.
    * @param string $EMail E-mail address for new user.
    * @param string $EMailAgain Second copy of e-mail address entered by user.
    * @param bool $IgnoreErrorCodes Array containing any error codes that should
    *       be ignored.  (OPTIONAL)
    * @return CWUser object or array of error codes.
    */
    public function CreateNewUser(
            $UserName, $Password, $PasswordAgain, $EMail, $EMailAgain,
            $IgnoreErrorCodes = NULL)
    {
        # add the user to the APUsers table
        $User = parent::CreateNewUser(
            $UserName,
            $Password,
            $PasswordAgain,
            $EMail,
            $EMailAgain,
            $IgnoreErrorCodes);

        # user account creation did not succeed, so return the error codes
        if (!($User instanceof User))
        {
            return $User;
        }

        # create the user resource
        $Resource = Resource::Create(MetadataSchema::SCHEMAID_USER);

        # set the user ID for the resource
        $Resource->Set("UserId", $User->Id());

        $Schema = new MetadataSchema(MetadataSchema::SCHEMAID_USER);
        $TimestampFields = $Schema->GetFields(MetadataSchema::MDFTYPE_TIMESTAMP);

        # update timestamps as required
        foreach ($TimestampFields as $Field)
        {
            if ($Field->UpdateMethod()
                == MetadataField::UPDATEMETHOD_ONRECORDEDIT)
            {
                $Resource->Set($Field, "now");
            }
        }

        # make the user resource permanent
        $Resource->IsTempResource(FALSE);

        # get the CWUser object for the user
        $CWUser = new CWUser(intval($User->Id()));

        # couldn't get the CWUser object
        if ($CWUser->Status() != U_OKAY)
        {
            return array($CWUser->Status());
        }

        # set up initial UI setting
        $CWUser->Set("ActiveUI",
            $GLOBALS["G_SysConfig"]->DefaultActiveUI());

        # set up initial privileges
        foreach ($GLOBALS["G_SysConfig"]->DefaultUserPrivs() as $Privilege)
        {
            $CWUser->GivePriv($Privilege);
        }

        # return new user object to caller
        return $CWUser;
    }

    # ---- PRIVATE INTERFACE -------------------------------------------------

    /**
    * Append a suffix to a string (typically a username), truncating
    *   to keep the total length less than 24 characters.
    * @param string $TrialName Base string.
    * @param string $Suffix Suffix to append.
    * @param int $MaxLength Maximum length of the result (OPTIONAL,
    *   default 24 matching IsValidUsername() from Axis--User)
    * @return string Constructed string.
    */
    private static function AppendSuffix($TrialName, $Suffix, $MaxLength=24)
    {
        if (strlen($TrialName.$Suffix)>$MaxLength)
        {
            $TrialName = substr(
                $TrialName, 0, $MaxLength - strlen($Suffix));
        }

        return $TrialName.$Suffix;
    }

    /**
    * The resource factory for user resources.
    */
    protected $ResourceFactory;
}
