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

# ----- LOCAL FUNCTIONS ------------------------------------------------------

/**
 * Get the remote address of the user agent. Attempts to get the host name if
 * possible.
 * @return string remote address
 */
function GetRemoteAddress()
{
    $Host = gethostbyaddr($_SERVER["REMOTE_ADDR"]);

    return $Host !== FALSE ? $Host : $_SERVER["REMOTE_ADDR"];
}

/**
 * Get the user name or an appropriate string if the user is not logged in.
 * @param User $User user
 * @return string user name or appropriate string if not logged in
 */
function GetUserName(User $User)
{
    return $User->IsLoggedIn() ? $User->Get("UserName") : "(not logged in)";
}

/**
 * Get the real name or an appropriate string if the user is not logged in.
 * @param User $User user
 * @return string real name or appropriate string if not logged in
 */
function GetUserRealName(User $User)
{
    if ($User->IsLoggedIn())
    {
        $RealName = trim($User->Get("RealName"));
        return $RealName ? $RealName : "(not set)";
    }

    return "(not logged in)";
}

/**
 * Get the e-mail address or an appropriate string if the user is not logged in.
 * @param User $User user
 * @param string $Secondary a secondary e-mail address to check
 * @return string e-mail address or appropriate string if not logged in
 */
function GetUserEmail(User $User, $Secondary=NULL)
{
    if ($User->IsLoggedIn())
    {
        return $User->Get("EMail");
    }

    $SecondaryLooksValid = SPTUser::IsValidLookingEMailAddress($Secondary);

    return $SecondaryLooksValid ? $Secondary : "(no e-mail address supplied)";
}

/**
 * Get the best e-mail address available to use as the sender.
 * @param User $User user
 * @param string $Secondary a secondary e-mail address to try
 * @return string the best e-mail address available to use as the sender
 */
function GetSenderEmail(User $User, $Secondary=NULL)
{
    # assemble e-mail address for user
    if ($User->IsLoggedIn())
    {
        # try to use the user's real name
        if (trim($User->Get("RealName")))
        {
            $Name = $User->Get("RealName");
        }

        # use the user name if the real name is blank
        else
        {
            $Name = $User->Get("UserName");
        }

        $Email = $User->Get("EMail");
    }

    # use the secondary address
    else if ($Secondary && SPTUser::IsValidLookingEMailAddress($Secondary))
    {
        $Name = $GLOBALS["SysConfig"]->PortalName() . " User";
        $Email = $Secondary;
    }

    # just use the admin's e-mail address
    else
    {
        $Name = "Anonymous " . $GLOBALS["SysConfig"]->PortalName() . " User";
        $Email = $GLOBALS["SysConfig"]->AdminEmail();
    }

    return trim($Name) . " <" . trim($Email) . ">";
}

/**
 * Sanitize the given value for use as an e-mail header value.
 * @param string $Value value to sanitize
 * @return string sanitized value
 */
function SanitizeEmailHeaderValue($Value)
{
    # this is taken from Mail PEAR package (_sanitizeHeaders())
    return preg_replace(
        '=((<CR>|<LF>|0x0A/%0A|0x0D/%0D|\\n|\\r)\S).*=i',
        NULL,
        $Value);
}

/**
 * Extract additional fields to add to the message body.
 * @param array $Values array of values, a subset of which are the field values
 * @return array extracted additional fields
 */
function ExtractAdditionalFields(array $Values)
{
    $CustomFields = array();

    foreach ($Values as $Key => $Value)
    {
        if (substr($Key, 0, 9) == "F_Custom_")
        {
            $CustomFields[substr($Key, 9)] = $Value;
        }
    }

    return $CustomFields;
}

# ----- EXPORTED FUNCTIONS ---------------------------------------------------

/**
 * Get the header for the page based on the feedback type, optionally passing in
 * additional headers or headers used to override the defaults.
 * @param string $FeedbackType the type of feedback
 * @param array $AdditionalHeaders additional or overriding headers
 * @return string the appropriate page header
 */
function GetPageHeader($FeedbackType, array $AdditionalHeaders=array())
{
    $Headers = $AdditionalHeaders + array(
        "ResourceSuggestion" => "Suggest a Resource",
        "ResourceFeedback" => "Resource Feedback");

    return GetArrayValue($Headers, $FeedbackType, "Leave Feedback");
}

# ----- MAIN -----------------------------------------------------------------

global $H_FeedbackSent;
global $H_FormErrors;
global $H_FeedbackType;
global $H_ParameterOne;
global $H_ParameterTwo;
global $H_ParameterThree;
global $H_ReturnTo;

$H_FeedbackType = GetArrayValue($_GET, "FT", "Feedback");
$H_ParameterOne = GetArrayValue($_GET, "P1");
$H_ParameterTwo = GetArrayValue($_GET, "P2");
$H_ParameterThree = GetArrayValue($_GET, "P3");
$H_ReturnTo = GetArrayValue($_POST, "F_ReturnTo", GetArrayValue($_GET, "ReturnTo", GetArrayValue($_SERVER, "HTTP_REFERER")));
$H_FeedbackSent = FALSE;
$H_FormErrors = array();
$H_EmailRequired = $GLOBALS["SysConfig"]->RequireEmailWithFeedback();

# go to the home page after form submission if the redirect URL is blank or
# appears malicious
if (!$H_ReturnTo || !IsSafeRedirectUrl($H_ReturnTo))
{
    $H_ReturnTo = "index.php?P=Home";
}

# set the page title based on the feedback type
PageTitle(GetPageHeader($H_FeedbackType));

# the form was submitted
if (!empty($_POST["Submit"]))
{
    # extract various data from the POST variables
    $Submit = GetArrayValue($_POST, "Submit", "Cancel");
    $EmailAddress = GetArrayValue($_POST, "F_EmailAddress");
    $Spambot = GetArrayValue($_POST, "F_Spmbt");
    $ReturnTo = GetArrayValue($_POST, "F_ReturnTo", "Home");
    $ReturnImmediately = GetArrayValue($_POST, "F_ReturnImmediately", FALSE);
    $FeedbackType = GetArrayValue($_POST, "F_FeedbackType");
    $CustomFields = ExtractAdditionalFields($_POST);

    # the user canceled submission or is probably a spambot
    if ($Submit == "Cancel" || $Spambot != "2001ASO")
    {
        $AF->SetJumpToPage($ReturnTo);
        return;
    }

    # if a valid e-mail address wasn't submitted
    if (!$G_User->IsLoggedIn() && !SPTUser::IsValidLookingEMailAddress($EmailAddress))
    {
        # add an error message
        if ($H_EmailRequired)
        {
            $H_FormErrors["F_EmailAddress"] = "A valid e-mail address is required.";
        }

        # otherwise unset the e-mail address
        else
        {
            $EMailAddress = NULL;
        }
    }

    # lodge the feedback submission if there were no errors
    if (!count($H_FormErrors))
    {
        # start out with a blank subject and body
        $Subject = "";
        $Body = "";

        # new resource suggestion
        if ($FeedbackType == "ResourceSuggestion")
        {
            $Subject .= "New Resource Suggestion";
            $Body .= "Title: " . trim(GetArrayValue($_POST, "F_Title")) . "\n";
            $Body .= "URL: " . trim(GetArrayValue($_POST, "F_Url")) . "\n";
            $Body .= "Description: " . trim(GetArrayValue($_POST, "F_Description")) . "\n";
        }

        # resource feedback
        else if ($FeedbackType == "ResourceFeedback")
        {
            $Resource = new Resource(GetArrayValue($_POST, "F_ResourceId"));

            # the resource is invalid, so just redirect
            if ($Resource->Status() !== 1)
            {
                $AF->SetJumpToPage($ReturnTo);
                return;
            }

            $Subject .= "Resource Feedback";
            $Body .= "Title: " . trim($Resource->GetMapped("Title")) . "\n";
            $Body .= "URL: " . trim($Resource->GetMapped("Url")) . "\n";
            $Body .= "View: " . OurBaseUrl() . "?P=FullRecord&ID=" . $Resource->Id() . "\n";
            $Body .= "Edit: " . OurBaseUrl() . "?P=EditResource&ID=" . $Resource->Id() . "\n";
            $Body .= "Problem Description: " . trim(GetArrayValue($_POST, "F_Description")) . "\n";
        }

        # generic feedback
        else if ($FeedbackType == "Feedback")
        {
            $Subject .= trim(GetArrayValue($_POST, "F_Subject", "User Feedback"));
            $Body .= "Message: " . trim(GetArrayValue($_POST, "F_Body")) . "\n";
        }

        # a custom form
        else
        {
            $Subject .= trim(GetArrayValue($_POST, "F_Subject", "User Feedback"));
        }

        # add additional fields to the body if any are specified
        foreach ($CustomFields as $Key => $Value)
        {
            $Body .= trim($Key) . ": " . trim($Value) . "\n";
        }

        # go to the redirect page if there is nothing to send
        if (!$Subject || !$Body)
        {
            $AF->SetJumpToPage($ReturnTo);
            return;
        }

        # validate the message
        $Status = $AF->SignalEvent(
            "EVENT_VALIDATE_USER_COMMENT",
            array($Subject, $Body));

        # validation failed, so don't send the message
        if ($Status[0] === FALSE)
        {
            $H_FormErrors []= $Status[1];
        }
        else
        {
            # add portal name to the subject
            $Subject = "[" . $SysConfig->PortalName() . "] " . $Subject;

            # append context info to the mail body
            $Body .= "\n";
            $Body .= "User: " . GetUserName($G_User) . "\n";
            $Body .= "Name: " . GetUserRealName($G_User) . "\n";
            $Body .= "E-mail: " . GetUserEmail($G_User, GetArrayValue($_POST, "F_EmailAddress")) . "\n";
            $Body .= "Address: " . GetRemoteAddress() . "\n";

            # get final information necessary to send the message
            $Msg = new Email();
            $Msg->To($SysConfig->AdminEmail());
            $Sender = GetSenderEmail($User, GetArrayValue($_POST, "F_EmailAddress"));
            $Msg->From($Sender);

            # send the e-mail message
            $Msg->Subject($Subject);
            $Msg->Body($Body);
            $Msg->Send();

            # signal that the feedback has been lodged
            $H_FeedbackSent = TRUE;

            # if specified to return immediately
            if ($ReturnImmediately)
            {
                $AF->SetJumpToPage($ReturnTo);
                return;
            }
        }
    }
}
