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

/**
* Plugin for adding and maintaining clean (human- and SEO-friendly) URLs.
*/
class CleanURLs extends Plugin
{

    # ---- STANDARD PLUGIN INTERFACE -----------------------------------------

    /**
    * Called by the PluginManager to register the plugin.
    */
    public function Register()
    {
        $this->Name = "Clean URL Manager";
        $this->Version = "1.0.2";
        $this->Description = "Provides SEO-friendly clean URLs for resource"
                ." browsing and full record pages.";
        $this->Author = "Internet Scout";
        $this->Url = "http://scout.wisc.edu/cwis/";
        $this->Email = "scout@scout.wisc.edu";
        $this->Requires = array("CWISCore" => "2.4.1");
        $this->EnabledByDefault = TRUE;
    }

    /**
    * Initialize the plugin.  This is called after all plugins have been
    * loaded but before any methods for this plugin (other than Register()
    * or Initialize()) have been called.
    * @return NULL if initialization was successful, otherwise a string
    *       containing an error message indicating why initialization failed.
    */
    public function Initialize()
    {
        # bail out if no clean URL support in .htaccess file
        if (!isset($_SERVER["CLEANURL_SUPPORT"]))
        {
            return "Clean URL support rewrites not available in .htaccess file.";
        }

        # set up default CWIS clean URLs
        $UrlMappings = array(
                # full record page
                array("Page" => "FullRecord",
                        "Pattern" => "%^r([0-9]+)%i",
                        "GetVars" => array("ID" => "\$1"),
                        "Template" => "r\$ID",
                        "UseCallback" => TRUE,
                        "Enabled" => TRUE),
                # browse page
                array("Page" => "BrowseResources",
                        "Pattern" => "%^b([0-9]+)%i",
                        "GetVars" => array("ID" => "\$1"),
                        "Template" => "b\$ID",
                        "UseCallback" => TRUE,
                        "Enabled" => TRUE),
                # file download
                array("Page" => "DownloadFile",
                        "Pattern" => "%^downloads/([0-9]+)/.*%",
                        "GetVars" => array("ID" => "\$1"),
                        "Template" => "downloads/\$ID",
                        "Enabled" => TRUE),
                # image view
                array("Page" => "ViewImage",
                      "Pattern" =>
                          "%^viewimage/([0-9]+)_([0-9]+)_([0-9]+)_([fpt])(\.[a-z]+)?%",
                      "GetVars" => array(
                          "RI" => "\$1",
                          "FI" => "\$2",
                          "IX" => "\$3",
                          "T"  => "\$4"),
                      "Template" => "viewimage/\$RI_\$FI_\$IX_\$T",
                      "Enabled" => TRUE),
                # Url field click
                array("Page" => "GoTo",
                        "Pattern" => "%^g([0-9]+)/f([0-9]+)%i",
                        "GetVars" => array(
                                "ID" => "$1",
                                "MF" => "$2"),
                        "Template" => "g\$ID/f\$MF",
                        "Enabled" => TRUE),
                # keyword search
                array("Page" => "AdvancedSearch",
                        "Pattern" => "%^s=(.+)%i",
                        "GetVars" => array("FK" => "\$1"),
                        "Template" => "s=\$FK",
                        "Enabled" => TRUE),
                # explicit background task execution
                array("Page" => "RunBackgroundTasks",
                        "Pattern" => "%^runtasks\$%i",
                        "GetVars" => array("FK" => "\$1"),
                        "Template" => "s=\$FK",
                        "Enabled" => TRUE),
                );

        # add clean URL for going to resource URL if standard URL field mapped
        $Schema = new MetadataSchema();
        if ($Schema->StdNameToFieldMapping("Url") !== NULL)
        {
            $StdUrlFieldId = $Schema->StdNameToFieldMapping("Url");
            $UrlMappings[]= array("Pattern" => "%^g([0-9]+)%i",
                    "Page" => "GoTo",
                    "GetVars" => array(
                            "ID" => "$1",
                            "MF" => $StdUrlFieldId),
                    "Template" => "g\$ID",
                    "Enabled" => TRUE);
        }

        # for each defined clean URL mapping
        foreach ($UrlMappings as $Mapping)
        {
            # if mapping is enabled
            if ($Mapping["Enabled"])
            {
                # set clean URL in application framework
                $GLOBALS["AF"]->AddCleanUrl(
                        $Mapping["Pattern"],
                        $Mapping["Page"],
                        (isset($Mapping["GetVars"])
                                ? $Mapping["GetVars"] : NULL),
                        (isset($Mapping["UseCallback"])
                                ? array($this, "ReplacementCallback")
                                : (isset($Mapping["Template"])
                                        ? $Mapping["Template"] : NULL)));
            }
        }

        # report successful initialization
        return NULL;
    }

    /**
    * See if the current page should be redirected to a Canonical CleanURL.
    * @param string $PageName The currently loading page.
    * @return array Page to load, which may be different.
    */
    public function CheckForRedirect($PageName)
    {
        # only GET and HEAD requests can be automatically redirected
        #  by a 301, so only check those
        if ($_SERVER["REQUEST_METHOD"] == "GET" ||
            $_SERVER["REQUEST_METHOD"] == "HEAD")
        {
            # get the basepath-relateive URL if the current page
            $ThisUrl = str_replace(
                OurBasePath(), "", $_SERVER["REQUEST_URI"]);

            # and see if we had a corresponding CleanUrl
            $CleanUrl = $GLOBALS["AF"]->GetCleanUrlForPath($ThisUrl);

            # if this wasn't the clean url
            if ($ThisUrl != $CleanUrl)
            {
                # stash the tgt CleanUrl for the Redirect page
                global $H_CleanUrl;
                $H_CleanUrl = $CleanUrl;

                # and go to the redirect page
                return array("PageName" => "P_CleanURLs_Redirect");
            }
        }

        return array("PageName" => $PageName);
    }

    /**
    * Set up plugin events.
    * @return array of events to hook.
    */
    public function HookEvents()
    {
        return array("EVENT_PHP_FILE_LOAD" => "CheckForRedirect");
    }

    # ---- CALLABLE METHODS --------------------------------------------------

    /**
    * Callback for constructing clean URLs to be inserted by the application
    * framework when more than regular expression replacement is required.
    * This method is passed to ApplicationFramework::AddCleanURL().
    * @param array $Matches Array of matches from preg_replace().
    * @param string $Pattern Original pattern for clean URL mapping.
    * @param string $Page Page name for clean URL mapping.
    * @param string $SearchPattern Full pattern passed to preg_replace().
    * @return string Replacement to be inserted in place of match.
    */
    public function ReplacementCallback($Matches, $Pattern, $Page, $SearchPattern)
    {
        switch ($Page)
        {
            case "FullRecord":
            case "BrowseResources":
                # if no resource/classification ID found
                if (count($Matches) <= 2)
                {
                    # return match unchanged
                    $Replacement = $Matches[0];
                }
                else
                {
                    # if target is a resource
                    $Id = $Matches[2];
                    if ($Page == "BrowseResources")
                    {
                        # set clean URL prefix
                        $Prefix = "b";

                        # if classification ID was valid
                        $CFactory = new ClassificationFactory();
                        if ($CFactory->ItemExists($Id))
                        {
                            # set title to full classification name
                            $Classification = new Classification($Id);
                            $Title = $Classification->Name();
                        }
                    }
                    else
                    {
                        # set clean URL prefix
                        $Prefix = "r";

                        # if resource ID was valid
                        if (Resource::ItemExists($Id))
                        {
                            # set title to resource title
                            $Resource = new Resource($Id);
                            $Title = $Resource->GetMapped("Title");
                        }
                    }

                    # set title for use in URL if found
                    $UrlTitle = isset($Title)
                            ? "/".strtolower(preg_replace(
                                    array("% -- %",
                                            "%\\s+%",
                                            "%[^a-zA-Z0-9_-]+%"),
                                    array("--",
                                            "_",
                                            ""),
                                    trim($Title))) : "";

                    # assemble replacement
                    $Replacement = "href=\"".$Prefix.$Id.$UrlTitle."\"";
                }
                break;
        }
        return $Replacement;
    }
}
