<?PHP
#
#   FILE:  CWUser.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 class.
*/
class CWUser extends User
{

    # ---- PUBLIC INTERFACE --------------------------------------------------

    /**
    * Load user data from the given user info or from the session if available.
    * @param mixed $UserInfo A user ID or user name. (OPTIONAL)
    */
    public function __construct($UserInfo=NULL)
    {
        static $EmailWrapperSet = FALSE;
        if (!$EmailWrapperSet)
        {
            User::SetEmailFunction(array("CWUser", "EmailWrapper"));
            $EmailWrapperSet = TRUE;
        }

        parent::__construct($UserInfo);

        # try to fetch the associated resource if the user was found
        if ($this->Result === U_OKAY)
        {
            $Resource = $this->FetchAssociatedResource($this->UserId);

            # the associated resource was successfully found
            if ($Resource instanceof Resource)
            {
                $this->Resource = $Resource;
            }

            # there was a problem finding the resource
            else
            {
                $this->Result = $Resource;
            }
        }
    }

    /**
    * THIS FUNCTION HAS BEEN DEPRECATED
    * This provides compatibility for interfaces written to use a
    * version of PrivilegeSet from CWIS 3.0.0 to 3.1.0.
    * @param PrivilegeSet $NewValue New value (OPTIONAL, default NULL)
    * @return PrivilegeSetCompatibiliityShim for use in legacy code.
    */
    public function Privileges(PrivilegeSet $NewValue = NULL)
    {
        if ($NewValue !== NULL)
        {
            throw new Exception(
                "Attempt to set user privileges with CWUser::Privileges(), "
                ."which is no longer supported");
        }

        return new PrivilegeSetCompatibilityShim($this);
    }

    /**
    * Get the ID of the user resource associated with the user.
    * @return Returns the ID of the associated user resource or NULL if it's not
    *      available, e.g., the user isn't logged in.
    */
    public function ResourceId()
    {
        return $this->IsResourceObjectSet() ? $this->Resource->Id() : NULL;
    }

    /**
    * Get the associated user resource for this user.
    * @return Returns the associated user resource or NULL if it's not
    *      available, e.g., the user isn't logged in.
    */
    public function GetResource()
    {
        return $this->IsResourceObjectSet() ? $this->Resource : NULL;
    }


    /**
    * Determine if a user has a given privilege, or satisfies the
    * conditions specified by a given privilege set.  Calling this
    * function with a PrivilegeSet as the first argument is supported
    * only for backwards compatibility.  New code should not do this.
    * @param mixed $Privilege Privilige number or Privilige set to check
    * @param mixed $Privileges Additional privileges (as in
    *      parent::HasPriv()), or a Resource to use if the first arg was a
    *      PrivilegeSet.
    * @return TRUE if the user has the specified privilege (or satisfies
    *      the requirements of the specified privilege set.
    */
    public function HasPriv($Privilege, $Privileges = NULL)
    {
        if ($Privilege instanceof PrivilegeSet)
        {
            if ($Privileges instanceof Resource)
            {
                return $Privilege->MeetsRequirements($this, $Privileges);
            }
            else
            {
                return $Privilege->MeetsRequirements($this);
            }
        }
        else
        {
            return call_user_func_array( "parent::HasPriv", func_get_args() );
        }
    }

    /**
    * Adapter method to bridge between AxisPHP User class and ScoutLib Email class.
    * @param string $To To line for message.
    * @param string $Subject Subject line for message.
    * @param string $Message Body of message.
    * @param string $AdditionalHeaders Other message header lines, concatenated
    *       together into a string.
    * @return bool TRUE if message successfully accepted for delivery,
    *       otherwise FALSE.
    */
    public static function EmailWrapper($To, $Subject, $Message, $AdditionalHeaders)
    {
        # extract "From" address from supplied headers if available
        if (strlen($AdditionalHeaders))
        {
            $HeaderLines = explode("\n", $AdditionalHeaders);
            $Headers = array();
            foreach ($HeaderLines as $Line)
            {
                $HeaderLine = trim($Line);
                if (preg_match("/^from:/i", $Line))
                {
                    $From = preg_replace("/^from:/i", "", $Line);
                }
                else
                {
                    $Headers[] = $HeaderLine;
                }
            }
        }

        # send message
        $Msg = new Email();
        if (isset($From)) {  $Msg->From($From);  }
        $Msg->To($To);
        $Msg->Subject($Subject);
        $Msg->AddHeaders($Headers);
        $Msg->Body($Message);
        $Result = $Msg->Send();

        # report success to caller
        return $Result;
    }

    /**
    * Get all custom user fields.
    * @return Returns an array of the custom user fields.
    */
    public static function GetCustomUserFields()
    {
        static $CustomFields;

        if (!isset($CustomFields))
        {
            $CustomFields = array();
            $Schema = new MetadataSchema(MetadataSchema::SCHEMAID_USER);

            foreach ($Schema->GetFields() as $Field)
            {
                # they're custom if not owned by CWIS
                if ($Field->Owner() != "CWISCore")
                {
                    $CustomFields[$Field->Id()] = $Field;
                }
            }
        }

        return $CustomFields;
    }

    /**
    * Get the default user fields.
    * @return Returns an array of the default user fields.
    */
    public static function GetDefaultUserFields()
    {
        static $DefaultFields;

        if (!isset($DefaultFields))
        {
            $DefaultFields = array();
            $Schema = new MetadataSchema(MetadataSchema::SCHEMAID_USER);

            foreach ($Schema->GetFields() as $Field)
            {
                # they're default if owned by CWIS
                if ($Field->Owner() == "CWISCore")
                {
                    $DefaultFields[$Field->Id()] = $Field;
                }
            }
        }

        return $DefaultFields;
    }

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

    /**
    * Delete the user and its associated user resource. Methods should not be
    * called on the object after calling this method.
    * @return Returns the status of the deletion attempt.
    */
    public function Delete()
    {
        # delete the associated user resource if set
        if (isset($this->Resource))
        {
            $this->Resource->Delete();
            $this->Result = U_OKAY;
        }

        return parent::Delete();
    }

    /**
    * Get a value from the specified field.
    * @param string $FieldName The name of the field to get.
    * @return Returns the field value or NULL if user data isn't available,
    *      e.g., the user isn't logged in.
    */
    public function Get($FieldName)
    {
        # provide backwards-compatibility for data migrated to users fields as
        # of CWIS 3.0.0
        if (in_array($FieldName, self::$MigratedUserFields))
        {
            # return NULL if the resource object isn't set
            if (!$this->IsResourceObjectSet())
            {
                return NULL;
            }

            # return the value from the associated resource
            return $this->Resource->Get($FieldName);
        }

        # otherwise, get it from the APUsers table
        return parent::Get($FieldName);
    }

    /**
    * Set a value for the specified field.
    * @param string $FieldName The name of the field to set.
    * @param mixed $NewValue The value to which to set the field.
    * @return Returns the status of the operation.
    */
    public function Set($FieldName, $NewValue)
    {
        # provide backwards-compatibility for data migrated to users fields as
        # of CWIS 3.0.0
        if (in_array($FieldName, self::$MigratedUserFields))
        {
            # set the value only if the resource object is set
            if ($this->IsResourceObjectSet())
            {
                $this->Resource->Set($FieldName, $NewValue);
                $this->Result = U_OKAY;
            }
        }

        # transform boolean values to 1 or 0 because that's what the User
        # class expects
        if (is_bool($NewValue))
        {
            $NewValue = $NewValue ? 1 : 0;
        }

        # update the APUsers table
        return parent::Set($FieldName, $NewValue);
    }

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

    /**
    * The user resource associated with the user or NULL if the user isn't
    * logged in.
    */
    protected $Resource;

    /**
    * Fields that were previously part of the APUsers table that have been
    * migrated to the Resources table as of CWIS 3.0.0.
    */
    protected static $MigratedUserFields = array(
        "RealName", "WebSite", "AddressLineOne", "AddressLineTwo", "City",
        "State", "ZipCode", "Country");

    /**
    * Fetch the associated user resource based off of a user ID.
    * @param int $UserId The user ID for the user associated with the esource.
    * @return Returns the associated user resource or an error status if an
    *      error occurs.
    */
    protected function FetchAssociatedResource($UserId)
    {
        try
        {
            $Factory = new ResourceFactory(MetadataSchema::SCHEMAID_USER);
            $Schema = new MetadataSchema(MetadataSchema::SCHEMAID_USER);
        }

        # couldn't get the factory or schema, which probably means CWIS hasn't
        # been installed yet
        catch (Exception $Exception)
        {
            return U_ERROR;
        }

        # the UserId field doesn't exist, which probably means CWIS hasn't been
        # installed yet
        if (!$Schema->FieldExists("UserId"))
        {
            return U_ERROR;
        }

        # get matching resources, which should only be one
        $Field = $Schema->GetFieldByName("UserId");

        $DB = new Database();
        $DB->Query(
            "SELECT ResourceId FROM ResourceUserInts WHERE ".
            "FieldId=".intval($Field->Id()).
            " AND UserId=".intval($UserId) );
        $ResourceIds = $DB->FetchColumn("ResourceId");
        $ResourceIdCount = count($ResourceIds);

        # no resource found
        if ($ResourceIdCount < 1)
        {
            return U_NOSUCHUSER;
        }

        # too many resources found
        if ($ResourceIdCount > 1)
        {
            return U_ERROR;
        }

        # construct the associated resource and return it
        return new Resource(array_shift($ResourceIds));
    }

    /**
    * Determine if the resource object for this object is set.
    * @return Returns TRUE if the resource object for this objects is set.
    */
    protected function IsResourceObjectSet()
    {
        # there must be a user ID, which is what the User class assumes, and the
        # resource must be set
        return isset($this->UserId) && isset($this->Resource);
    }


    # ---- MAINTAINED FOR BACKWARD COMPATIBILITY IN INTERFACES (BEGIN)

    # ---- user interface preference mnemonics
    # color avoidance flags
    const UIPREF_AVOID_RED =           1;
    const UIPREF_AVOID_REDGREEN =      2;
    const UIPREF_AVOID_BLUEYELLOW =    4;
    const UIPREF_AVOID_GREENYELLOW =   8;
    const UIPREF_AVOID_ORANGE =        16;
    const UIPREF_AVOID_REDBLACK =      32;
    const UIPREF_AVOID_PURPLEGREY =    64;
    const UIPREF_AVOID_USEMAXMONOCHR = 128;

    # content display options
    const UIPREF_CONTENTDENSITY_NOPREFERENCE =  0;
    const UIPREF_CONTENTDENSITY_DETAILED =  1;
    const UIPREF_CONTENTDENSITY_OVERVIEW =  2;

    # content view options
    const UIPREF_CONTENTVIEW_NOPREFERENCE =  0;
    const UIPREF_CONTENTVIEW_TEXTINTENSIVE =  1;
    const UIPREF_CONTENTVIEW_IMAGEINTENSIVE =  2;

    # audio description options
    const UIPREF_AUDIODESCRIPTION_NONE =  0;
    const UIPREF_AUDIODESCRIPTION_STANDARD =  1;
    const UIPREF_AUDIODESCRIPTION_EXPANDED =  2;

    # caption type options
    const UIPREF_CAPTIONTYPE_NONE =  0;
    const UIPREF_CAPTIONTYPE_VERBATIM =  1;
    const UIPREF_CAPTIONTYPE_REDUCEDREADINGLEVEL =  2;

    // @codingStandardsIgnoreStart

    # user interface / accessibility preferences
    function PrefFontSize($NewValue = DB_NOVALUE)
    {  return 0;  }

    function PrefFontTypeFace($NewValue = DB_NOVALUE)
    {  return 0;  }

    function PrefFontColor($NewValue = DB_NOVALUE)
    {  return 0;  }

    function PrefBackgroundColor($NewValue = DB_NOVALUE)
    {  return 0;  }

    function PrefColorAvoidanceFlags($NewValue = DB_NOVALUE)
    {  return 0;  }

    function PrefContentDensity($NewValue = DB_NOVALUE)
    {  return 0;  }

    function PrefContentView($NewValue = DB_NOVALUE)
    {  return 0;  }

    function PrefAudioDescriptionLevel($NewValue = DB_NOVALUE)
    {  return 0;  }

    function PrefAudioDescriptionLanguage($NewValue = DB_NOVALUE)
    { return 0;  }

    function PrefVisualDescriptionLanguage($NewValue = DB_NOVALUE)
    { return 0;  }

    function PrefImageDescriptionLanguage($NewValue = DB_NOVALUE)
    {  return 0;  }

    function PrefUseGraphicAlternatives($NewValue = DB_NOVALUE)
    {  return 0;  }

    function PrefSignLanguage($NewValue = DB_NOVALUE)
    {  return 0;  }

    function PrefCaptionType($NewValue = DB_NOVALUE)
    {  return 0;  }

    function PrefCaptionRate($NewValue = DB_NOVALUE)
    {  return 0;  }

    // @codingStandardsIgnoreEnd
    # ---- MAINTAINED FOR BACKWARD COMPATIBILITY IN INTERFACES (END)
}
