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

/**
* Plugin to support exporting resource metadata.
*/
class ResourceExporter extends Plugin
{
    # ---- STANDARD PLUGIN INTERFACE -----------------------------------------

    /**
    * Set plugin attributes.
    */
    public function Register()
    {
        $this->Name = "Resource Exporter";
        $this->Version = "1.0.0";
        $this->Description = "Supports exporting resource metadata in"
                ." various formats.";
        $this->Author = "Internet Scout";
        $this->Url = "http://scout.wisc.edu/cwis/";
        $this->Email = "scout@scout.wisc.edu";
        $this->Requires = array(
                "CWISCore" => "3.0.0");
        $this->EnabledByDefault = TRUE;
    }

    /**
    * Hook the events into the application framework.
    * @return Returns an array of events to be hooked into the application
    *      framework.
    */
    public function HookEvents()
    {
        return array(
                "EVENT_COLLECTION_ADMINISTRATION_MENU" => "AddCollectionAdminMenuItems",
                "EVENT_HOURLY" => "CleanOutOldExports",
                );
    }

    /**
    * Perform one-time plugin setup tasks.
    * @return NULL on success or an error string on failure.
    */
    public function Install()
    {
        $this->ConfigSetting("FormatParameterValues", array());
        return NULL;
    }

    # ---- HOOKED METHODS ----------------------------------------------------

    /**
    * Add entries to the Collection Administration menu.
    * @return array List of entries to add, with the label as the value and
    *       the page to link to as the index.
    */
    public function AddCollectionAdminMenuItems()
    {
        return array(
                "ExportResources" => "Export Resources",
                );
    }

    /**
    * Clean out old export files.
    */
    public function CleanOutOldExports()
    {
        # retrieve list of exported files
        $ExportedFiles = $this->ConfigSetting("ExportedFiles");

        # for each known exported file
        $NewExportedFiles = array();
        foreach ($ExportedFiles as $Secret => $Info)
        {
            # if file was exported more than a day ago
            if ($Info["ExportTimestamp"] < strtotime("-1 day"))
            {
                # delete file
                unlink($Info["LocalFileName"]);
            }
            else
            {
                # keep file in list of exported files
                $NewExportedFiles[$Secret] = $Info;
            }
        }

        # save new list of exported files
        $this->ConfigSetting("ExportedFiles", $NewExportedFiles);
    }


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

    /**
    * Register format as available for export.
    * @param string $FormatName Human-readable name of format.
    * @param string $FileNameExtension Extension for file name (e.g. "xml"),
    *       without the leading "."..
    * @param callable $ExportFunc Function or method to call to export.
    * @param array $ExportedDataTypes Metadata field types exported.
    * @param array $Params Export parameter setup, in the format
    *       supported by the ConfigSettingsUI class.  (OPTIONAL)
    */
    public function RegisterFormat($FormatName, $FileNameExtension, $ExportFunc,
            $ExportedDataTypes, $Params = NULL)
    {
        # check to make sure format name is not a duplicate
        if (array_key_exists($FormatName, $this->ExportFuncs))
        {
            throw new Exception("Duplicate format name registered: ".$FormatName);
        }

        # check to make sure export function is callable
        if (!is_callable($ExportFunc))
        {
            throw new Exception("Uncallable export function for format ".$FormatName);
        }

        # save format information
        $this->ExportFuncs[$FormatName] = $ExportFunc;
        $this->FileNameExtensions[$FormatName] = $FileNameExtension;
        $this->ExportedDataTypes[$FormatName] = $ExportedDataTypes;
        $this->ExportParameters[$FormatName] = $Params;
    }

    /**
    * Check whether the specified format is registered.
    * @param string $FormatName Human-readable name of format.
    * @return bool TRUE if specified format is registered, otherwise FALSE.
    */
    public function IsRegisteredFormat($FormatName)
    {
        return array_key_exists($FormatName, $this->ExportFuncs)
                ? TRUE : FALSE;
    }

    /**
    * Get list of registered formats.
    * @return array Array of names of registered formats.
    */
    public function GetFormats()
    {
        $Formats = array();
        foreach ($this->ExportFuncs as $FormatName => $Func)
        {
            $Formats[] = $FormatName;
        }
        return $Formats;
    }

    /**
    * Get list of metadata field types supported by each registered format.
    * @return array Array with names of registered formats for the index and
    *       arrays of metadata field types for the values.
    */
    public function GetExportedDataTypes()
    {
        return $this->ExportedDataTypes;
    }

    /**
    * Get set of export parameters (if any) for each registered format.
    * @return array Array with names of registered formats for the index and
    *       export parameter sets for the values.
    */
    public function GetExportParameters()
    {
        return $this->ExportParameters;
    }

    /**
    * Export data to file(s).
    * @param string $FormatName Name of format in which to export.
    * @param int $SourceFolderId ID of folder containing resources, or
    *     (-$SchemaId - 1) to export all resources for a specified schema.
    * @param array $FieldIds Array of IDs of metadata fields to export, or
    *       NULL to export all enabled fields.
    * @param array $ParamSettings Export parameter settings.
    * @return int Number of resources exported, or NULL if export failed.
    */
    public function ExportData(
        $FormatName, $SourceFolderId, $FieldIds, $ParamSettings)
    {
        # retrieve ResourceIds, either all for a specified schema or
        # all contained in a folder
        if ($SourceFolderId < 0)
        {
            $RFactory = new ResourceFactory( -($SourceFolderId+1) );
            $ResourceIds = $RFactory->GetItemIds();
        }
        else
        {
            # extract all the ids from the target folder
            $Folder = new Folder($SourceFolderId);
            $ResourceIds = $Folder->GetItemIds();
        }

        # generate secret string for local file name
        $this->LastExportFileSecret = sprintf(
                "%05X%01X", (time() % 0xFFFFF), rand(0, 0xF));

        # construct file name
        $this->LastExportFileName = "tmp/"
                ."ResourceExport-".sprintf("%04d", $GLOBALS["G_User"]->Id())."-"
                .date("ymd-His").".".$this->FileNameExtensions[$FormatName];

        # construct local file name
        $this->LastExportLocalFileName = "tmp/".$this->LastExportFileSecret."."
                ."ResourceExport-".sprintf("%04d", $GLOBALS["G_User"]->Id())."-"
                .date("ymd-His").".".$this->FileNameExtensions[$FormatName];

        # attempt to export data
        $this->LastExportErrors = array();
        try
        {
            $ResourceCount = call_user_func($this->ExportFuncs[$FormatName],
                    $ResourceIds, $FieldIds, $this->LastExportLocalFileName,
                    $ParamSettings);
        }
        catch (Exception $Exception)
        {
            $this->LastExportErrors[] = $Exception->getMessage();
            $ResourceCount = NULL;
        }

        # save export values if export succeeded
        if ($ResourceCount !== NULL)
        {
            $ExportedFiles = $this->ConfigSetting("ExportedFiles");
            if (!is_array($ExportedFiles)) {  $ExportedFiles = array();  }
            $ExportedFiles[$this->LastExportFileSecret] = array(
                    "FileName" => $this->LastExportFileName,
                    "LocalFileName" => $this->LastExportLocalFileName,
                    "ExportTimestamp" => time(),
                    "ExporterId" => $GLOBALS["G_User"]->Id(),
                    "ResourceCount" => $ResourceCount);
            $this->ConfigSetting("ExportedFiles", $ExportedFiles);
        }

        # return number of resources exported to caller
        return $ResourceCount;
    }

    /**
    * Retrieve name of last exported file as stored locally (includes
    * leading path and secret hash value in name).
    * @return string Local file name or NULL if no last exported file.
    */
    public function LastExportLocalFileName()
    {
        return $this->LastExportLocalFileName;
    }

    /**
    * Retrieve name of last exported file as intended to be downloaded by
    * user, with no leading path.  (Not the name of the file stored locally.)
    * @return string File name or NULL if no last exported file.
    */
    public function LastExportFileName()
    {
        return $this->LastExportFileName;
    }

    /**
    * Retrieve secret string used in local file name for last export.
    * @return string Secret string.
    */
    public function LastExportFileSecret()
    {
        return $this->LastExportFileSecret;
    }

    /**
    * Retrieve error messages (if any) from last export.
    * @return array Array of error messages strings.
    */
    public function LastExportErrorMessages()
    {
        return $this->LastExportErrors;
    }

    /**
    * Retrieve info about exported file.
    * @param string $Secret Secret string that identifies file.
    * @return array Associative array with "FileName", "LocalFileName",
    *       "ExportTimestamp", "ExporterId", and "ResourceCount" entries,
    *       or NULL if no exported file found with specified secret.
    */
    public function GetExportedFileInfo($Secret)
    {
        $ExportedFiles = $this->ConfigSetting("ExportedFiles");
        return (!is_array($ExportedFiles)
                || !array_key_exists($Secret, $ExportedFiles))
                ? NULL : $ExportedFiles[$Secret];
    }


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

    private $ExportedDataTypes = array();
    private $ExportFuncs = array();
    private $ExportParameters = array();
    private $FileNameExtensions = array();
    private $LastExportErrors = array();
    private $LastExportFileName = NULL;
    private $LastExportFileSecret = NULL;
    private $LastExportLocalFileName = NULL;
}
