<?PHP


# utility func to find mail template file
function FindTemplateFile($FileName)
{
    $SearchPaths = array(
            "local/data/",
            "local/data/SPT--",
            "",
            "SPT--",
            "data/",
            );
    foreach ($SearchPaths as $Path)
    {
        if (is_readable($Path.$FileName))
        {
            return $Path.$FileName;
        }
    }
    return NULL;
}

# callback for generating replacement text for keywords in e-mail message templates
function KeywordReplacementCallback($Matches)
{
    global $SysConfig;
    global $RCResourceIds, $RCUser, $RCSearch;

    static $CurrentResourceId;
    static $Resources;
    static $FieldNameMappings;
    static $InSearchResults = FALSE;
    static $ResultNumber;

    # if local keyword replacement function is defined
    if (LocalFunctionExists("SavedSearchKeywordReplacement"))
    {
        # if currently in result list
        if ($InSearchResults)
        {
            # pass keyword to local function (including result info)
            $Replacement = Local_SavedSearchKeywordReplacement($Matches[0],
                    $RCUser, $RCSearch, $CurrentResourceId, $ResultNumber);
        }
        else
        {
            # pass keyword to local function
            $Replacement = Local_SavedSearchKeywordReplacement($Matches[0],
                    $RCUser, $RCSearch);
        }

        # if local function returned replacement
        if ($Replacement !== NULL)
        {
            # return replacement to caller
            return $Replacement;
        }
    }

    # load record template (if not already loaded)
    static $EmailResourceTemplate;
    if (!isset($EmailResourceTemplate))
    {
        $EmailResourceTemplate = file(
                FindTemplateFile("SearchMailTemplate--Resource.txt"));
    }

    # start out with assumption that no replacement text will be found
    $Replacement = $Matches[0];

    # switch on match string
    switch ($Matches[0])
    {
        case "X-PORTALNAME-X":
            $Replacement = $SysConfig->PortalName();
            break;

        case "X-ADMINEMAIL-X":
            $Replacement = $SysConfig->AdminEmail();
            break;

        case "X-LEGALNOTICE-X":
            $Replacement = $SysConfig->LegalNotice();
            break;

        case "X-USERLOGIN-X":
            $Replacement = $RCUser->Get("UserName");
            break;

        case "X-USERREALNAME-X":
            $Replacement = $RCUser->Get("RealName");
            if (!strlen(trim($Replacement))) {  $Replacement = $RCUser->Get("UserName");  }
            break;

        case "X-USEREMAIL-X":
            $Replacement = $RCUser->Get("EMail");
            break;

        case "X-RESULTCOUNT-X":
            $Replacement = count($RCResourceIds);
            break;

        case "X-SEARCHNAME-X":
            $Replacement = $RCSearch->SearchName();
            break;

        case "X-SEARCHCRITERIA-X":
            $Replacement = $RCSearch->GetSearchGroupsAsTextDescription(NULL, FALSE, FALSE);
            break;

        case "X-SEARCHRESULTS-X":
            $Replacement = "";
            if ($InSearchResults == FALSE)
            {
                $InSearchResults = TRUE;
                $ResultNumber = 1;
                foreach ($RCResourceIds as $ResourceId)
                {
                    $CurrentResourceId = $ResourceId;
                    if (!isset($Resources[$ResourceId]))
                    {
                         $Resources[$ResourceId] = new Resource($ResourceId);
                    }
                    foreach ($EmailResourceTemplate as $ResourceTemplateLine)
                    {
                        if (!preg_match("/^#/", $ResourceTemplateLine))
                        {
                            $Replacement .= preg_replace_callback("/X-[A-Z0-9:]+-X/",
                                    "KeywordReplacementCallback", $ResourceTemplateLine);
                        }
                    }
                    $ResultNumber++;
                }
                $InSearchResults = FALSE;
            }
            break;

        case "X-RESULTNUMBER-X":
            $Replacement = $ResultNumber;
            break;

        case "X-RESOURCEID-X":
            $Replacement = $CurrentResourceId;
            break;

        default:
            # map to date/time value if appropriate
            $DateFormats = array(
                    "X-DATE-X"            => "M j Y",
                    "X-TIME-X"            => "g:ia T",
                    "X-YEAR-X"            => "Y",
                    "X-YEARABBREV-X"      => "y",
                    "X-MONTH-X"           => "n",
                    "X-MONTHNAME-X"       => "F",
                    "X-MONTHABBREV-X"     => "M",
                    "X-MONTHZERO-X"       => "m",
                    "X-DAY-X"             => "j",
                    "X-DAYZERO-X"         => "d",
                    "X-DAYWITHSUFFIX-X"   => "jS",
                    "X-WEEKDAYNAME-X"     => "l",
                    "X-WEEKDAYABBREV-X"   => "D",
                    "X-HOUR-X"            => "g",
                    "X-HOURZERO-X"        => "h",
                    "X-MINUTE-X"          => "i",
                    "X-TIMEZONE-X"        => "T",
                    "X-AMPMLOWER-X"       => "a",
                    "X-AMPMUPPER-X"       => "A",
                    );
            if (isset($DateFormats[$Matches[0]]))
            {
                $Replacement = date($DateFormats[$Matches[0]]);
            }
            else
            {
                # load field name mappings (if not already loaded)
                if (!isset($FieldNameMappings))
                {
                    $Schema = new MetadataSchema();
                    $Fields = $Schema->GetFields();
                    foreach ($Fields as $Field)
                    {
                        $NormalizedName = strtoupper(preg_replace("/[^A-Za-z0-9]/", "", $Field->Name()));
                        $FieldNameMappings[$NormalizedName] = $Field->Name();
                    }
                }

                # if keyword refers to known field and we have a current resource
                $KeywordIsField = preg_match("/X-FIELD:([A-Z0-9]+)-X/", $Matches[0], $SubMatches);
                if ($KeywordIsField && isset($FieldNameMappings[$SubMatches[1]])
                        && isset($Resources[$CurrentResourceId]))
                {
                    # replacement is value from current resource
                    $Replacement = $Resources[$CurrentResourceId]->Get($FieldNameMappings[$SubMatches[1]]);
                    if (is_array($Replacement))
                    {
                        foreach ($Replacement as $ReplacementEntry)
                        {
                            if (!isset($RebuiltReplacement))
                            {
                                $RebuiltReplacement = $ReplacementEntry;
                            }
                            else
                            {
                                $RebuiltReplacement .= ", ".$ReplacementEntry;
                            }
                        }
                        $Replacement = isset($RebuiltReplacement) ? $RebuiltReplacement : "";
                    }
                    $Replacement = wordwrap(strip_tags($Replacement), 78);
                }
            }
            break;
    }

    # return replacement string to caller
    return $Replacement;
}

/**
* Generate the body a given saved search email.
* @param array $ResourceIds of different/new results that should be sent
* for the search.
* @param User $User the user who should receive the email.
* @param SavedSearch $Search the search whose updated results should
* be emailed.
*/
function GenerateSearchResultEmailBody($Search, $ResourceIds, $User)
{
    # pass values to callback function via global variables
    global $RCResourceIds, $RCUser, $RCSearch;
    $RCResourceIds = $ResourceIds;
    $RCUser = $User;
    $RCSearch = $Search;

    # start with empty message body
    $Body = "";

    # load search result e-mail templates
    $EmailBodyTemplate = file(FindTemplateFile("SearchMailTemplate--Body.txt"));

    # for each line of body template
    foreach ($EmailBodyTemplate as $BodyTemplateLine)
    {
        # if line does not look like a comment
        if (!preg_match("/^#/", $BodyTemplateLine))
        {
            # replace any keywords in template line with appropriate content
            #       and add line to body
            $Body .= preg_replace_callback("/X-[A-Z0-9]+-X/",
                    "KeywordReplacementCallback", $BodyTemplateLine);
        }
    }

    # return message body to caller
    return $Body;
}

# email resource entry found as a result of a user agent search
/**
* Send out a given saved search email.
* @param array $ResourceIds of different/new results that should be sent
* for the search.
* @param User $EndUser the user who should receive the email.
* @param SavedSearch $Search the search whose updated results should
* be emailed.
*/
function EmailSearchResults($ResourceIds, $EndUser, $Search)
{
    global $SysConfig;

    # generate header and body of e-mail message
    $MessageText = GenerateSearchResultEmailBody(
            $Search, $ResourceIds, $EndUser);

    # split off header lines from body
    $AllowedHeaderKeywords = array("from", "cc", "bcc", "subject",
            "content-type", "content-transfer-encoding", "mime-version",
            "auto-submitted", "precedence");
    $BodyLines = explode("\n", $MessageText);
    $Subject = "";
    $AdditionalHeaders = array();
    while (in_array(strtolower(trim(array_shift(
                explode(":", trim($BodyLines[0]))))), $AllowedHeaderKeywords)
           || in_array(strtolower(trim(array_shift(
                explode("=", trim($BodyLines[0]))))), $AllowedHeaderKeywords))
    {
        $Line = trim(array_shift($BodyLines));
        if (preg_match("/^subject:/i", $Line))
        {
            $Subject = preg_replace("/^subject:/i", "", $Line);
        }
        elseif (preg_match("/^from:/i", $Line))
        {
            $From = preg_replace("/^from:/i", "", $Line);
        }
        else
        {
            $AdditionalHeaders[] = $Line;
        }
    }
    if (!count($AdditionalHeaders) && !strlen($Subject))
    {
        $Subject = trim(array_shift($BodyLines));
    }
    $Body = implode("\n", $BodyLines);

    # use system email address if no "From" line supplied in template
    if (!isset($From))
    {
        $From = trim($SysConfig->PortalName())
                ." <".trim($SysConfig->AdminEmail()).">";
    }

    # mail report to User
    if (strlen($EndUser->Get("EMail")) > 0)
    {
        $Msg = new Email();
        $Msg->From($From);
        $Msg->To($EndUser->Get("EMail"));
        $Msg->Subject($Subject);
        $Msg->AddHeaders($AdditionalHeaders);
        $Msg->Body($Body);
        $Msg->Send();
    }
}

function RunSavedSearchMailings()
{
    $Template = $GLOBALS["G_SysConfig"]->SavedSearchMailTemplate();
    $UsingMailer = FALSE;

    # just return if saved search mailings aren't enabled
    if (!$GLOBALS["G_SysConfig"]->UserAgentsEnabled())
    {
        return;
    }

    # use the default template if the Mailer plugin isn't enabled
    if (!$GLOBALS["G_PluginManager"]->PluginEnabled("Mailer"))
    {
        $Template = -1;
    }
    else
    {
        $MailerPlugin = $GLOBALS["G_PluginManager"]->GetPlugin("Mailer");
    }

    # using the default template
    if ($Template == -1)
    {
        # return if the template can't be found
        if (!FindTemplateFile("SearchMailTemplate--Body.txt"))
        {
            return;
        }
    }

    # using a template from the Mailer plugin
    else
    {
        $UsingMailer = TRUE;
        $AllTemplates = $MailerPlugin->GetTemplateList();

        # return if the template is invalid
        if (!isset($AllTemplates[$Template]))
        {
            return;
        }
    }

    # get searches that need to be run
    $SSFactory = new SavedSearchFactory();
    $Searches = $SSFactory->GetSearchesDueToRun();

    # for each search
    foreach ($Searches as $Search)
    {
        # get objects that we'll need
        if (!isset($SearchEngine)) { $SearchEngine = new SPTSearchEngine();  }
        if (!isset($RFactory))     { $RFactory = new ResourceFactory();      }

        # retrieve search criteria and target user
        $SearchParameters = $Search->SearchParameters();
        $EndUser = new CWUser($Search->UserId());

        # signal event to allow modification of search parameters
        $SignalResult = $GLOBALS["AF"]->SignalEvent(
            "EVENT_FIELDED_SEARCH", array(
                "SearchParameters" => $SearchParameters,
                "User" => $EndUser,
                "SavedSearch" => $Search));
        $SearchParameters = $SignalResult["SearchParameters"];

        # perform search
        $SearchResults = $SearchEngine->Search($SearchParameters, 0, PHP_INT_MAX);
        if (isset($SearchResults[MetadataSchema::SCHEMAID_DEFAULT]))
        {
            $SearchResults = $SearchResults[MetadataSchema::SCHEMAID_DEFAULT];
        }
        else
        {
            $SearchResults = array();
        }

        $TotResults = count($SearchResults);

        # remove resources that the user cannot view
        $ViewableResourceIds = $RFactory->FilterNonViewableResources(
            array_keys($SearchResults), $EndUser);
        $SearchResults = array_intersect_key(
            $SearchResults, array_flip($ViewableResourceIds));

        # if any results remain
        if (count($SearchResults))
        {
            # get the list of resources that are new since last time
            $NewResourceIds = array_diff(
                array_keys($SearchResults), $Search->LastMatches() );

            # save these matches for future comparisons
            $Search->SaveLastMatches(array_keys($SearchResults));

            # if we had any new resources
            if(count($NewResourceIds))
            {
                # use the Mailer plugin to send the template, if necessary
                if ($UsingMailer)
                {
                    # additional values to replace in the template
                    $ExtraValues = array(
                        "SEARCHNAME" => $Search->SearchName(),
                        "TOTALNEWMATCHES" => count($NewResourceIds),
                        "TRUNCATIONMSG" => "",
                        "SEARCHCRITERIA" => $Search->GetSearchGroupsAsTextDescription(
                            NULL, FALSE, FALSE));

                    # truncate longer lists of results
                    if(count($NewResourceIds) > 100)
                    {
                        $ExtraValues["TRUNCATIONMSG"] =
                            "Listing the first 100 resources of ".count($NewResourceIds);
                        $NewResourceIds = array_slice($NewResourceIds, 0, 100);
                    }

                    # send the e-mail
                    $MailerPlugin->SendEmail(
                        $Template,
                        $EndUser,
                        $NewResourceIds,
                        $ExtraValues);
                }

                # otherwise, use the default template
                else
                {
                    # generate results e-mail and send to user
                    EmailSearchResults($NewResourceIds, $EndUser, $Search);
                }
            }
        }

        # update timestamp that indicates when search was last run
        $Search->UpdateDateLastRun();
    }
}
