<?PHP

/**
 * CWIS plugin that provides foldering functionality for resources.
 */
class Folders extends Plugin
{

    /**
    * Register information about this plugin.
    * @return void
    */
    public function Register()
    {
        $this->Name = "Folders";
        $this->Version = "1.0.8";
        $this->Description = "Functionality for adding resources into folders.";
        $this->Author = "Internet Scout";
        $this->Url = "http://scout.wisc.edu/cwis/";
        $this->Email = "scout@scout.wisc.edu";
        $this->Requires = array("CWISCore" => "2.3.1");
        $this->InitializeAfter = array("DBCleanUp", "AccountPruner");
        $this->EnabledByDefault = TRUE;

        $this->CfgSetup["DisplayInSidebar"] = array(
            "Type" => "Flag",
            "Label" => "Display In Sidebar",
            "Help" => "Whether to automatically add a display of the current"
                    ." folder to the sidebar.",
            "Default" => TRUE,
            );
        $this->CfgSetup["NumDisplayedResources"] = array(
            "Type" => "Number",
            "Label" => "Number to Display",
            "Help" => "The number of resources to display in the sidebar for"
                     ." the selected folder",
            "MaxVal" => 20);
        $this->CfgSetup["PrivsToTransferFolders"] = array(
            "Type" => "Privileges",
            "AllowMultiple" => TRUE,
            "Label" => "Privilege needed to transfer folders",
            "Help" => "Only users with any of the selected privilege flags "
                     ."will be able to perform a folder transfer.",
            "Default" => array(
                    PRIV_SYSADMIN,
                    PRIV_NEWSADMIN,
                    PRIV_RESOURCEADMIN,
                    PRIV_FORUMADMIN,
                    PRIV_CLASSADMIN,
                    PRIV_NAMEADMIN,
                    PRIV_RELEASEADMIN,
                    PRIV_USERADMIN,
                    PRIV_MYRESOURCEADMIN,
                    PRIV_COLLECTIONADMIN),
            );
    }

    /**
    * Startup initialization for plugin.
    * @return NULL if initialization was successful, otherwise a string containing
    *       an error message indicating why initialization failed.
    */
    public function Initialize()
    {
        # set up clean URL mapping for folders (folders/folder_id/normalized_folder_name)
        $GLOBALS["AF"]->AddCleanUrl(
            "%^folders/0*([1-9][0-9]*)%",
            "P_Folders_ViewFolder",
            array("FolderId" => "\$1"),
            array($this, "CleanUrlTemplate"));

        # add extra function dirs that we need
        # (must be added explicitly because our files get loaded
        #       on pages other than ours)
        $DirsToAdd = array(
            "local/plugins/".__CLASS__."/interface/%ACTIVEUI%/include/",
            "plugins/".__CLASS__."/interface/%ACTIVEUI%/include/",
            "local/plugins/".__CLASS__."/interface/default/include/",
            "plugins/".__CLASS__."/interface/default/include/");
        $GLOBALS["AF"]->AddFunctionDirectories($DirsToAdd);
        $GLOBALS["AF"]->AddIncludeDirectories($DirsToAdd);

        # report success
        return NULL;
    }

    /**
    * Create the database tables necessary to use this plugin.
    * @return NULL on success or an error message otherwise
    */
    public function Install()
    {
        $Database = new Database();

        # selected folders table
        if (FALSE === $Database->Query("
            CREATE TABLE IF NOT EXISTS Folders_SelectedFolders (
                OwnerId      INT,
                FolderId     INT,
                PRIMARY KEY  (OwnerId)
            );"))
        { return "Could not create the selected folders table"; }

        $this->ConfigSetting("NumDisplayedResources", 5);
    }

    /**
    * Uninstall the plugin.
    * @return NULL|string NULL if successful or an error message otherwise
    */
    public function Uninstall()
    {
        $Database = new Database();

        # selected folders table
        if (FALSE === $Database->Query("DROP TABLE Folders_SelectedFolders;"))
        { return "Could not remove the selected folders table"; }
    }
    /**
    * Declare the events this plugin provides to the application framework.
    * @return array the events this plugin provides
    * Deprecated for new interfaces, please use the provided insertion points.
    */
    public function DeclareEvents()
    {
        return array(
            "FOLDERS_GET_SELECTED_FOLDER" =>
                    ApplicationFramework::EVENTTYPE_FIRST,
            "FOLDERS_RESOURCE_IN_SELECTED_FOLDER" =>
                    ApplicationFramework::EVENTTYPE_FIRST);
    }

    /**
    * Hook the events into the application framework.
    * @return array the events to be hooked into the application framework
    */
    public function HookEvents()
    {
        $Events = array(
            "EVENT_IN_HTML_HEADER" => "InHtmlHeader",

            # deprecated for new interfaces, please use insertion points
            "FOLDERS_GET_SELECTED_FOLDER" => "GetSelectedFolder",

            # deprecated for new interfaces, please use insertion points
            "FOLDERS_RESOURCE_IN_SELECTED_FOLDER" => "ResourceInSelectedFolder",

            "EVENT_HTML_INSERTION_POINT" => array("InsertButtonHTML",
                "InsertAllButtonHTML", "InsertResourceNote", "PrintSidebarContent"),
          );

        # add hook for the Database Clean Up plugin if it's enabled
        if ($GLOBALS["G_PluginManager"]->PluginEnabled("DBCleanUp"))
        {
            $Events["DBCleanUp_EXTERNAL_CLEAN"] = "DatabaseCleanUp";
        }

        # add hook for the Account Pruner plugin if it's enabled
        if ($GLOBALS["G_PluginManager"]->PluginEnabled("AccountPruner"))
        {
            $Events["AccountPruner_EVENT_DO_NOT_PRUNE_USER"] = "PreventAccountPruning";
        }

        return $Events;
    }

    /**
    * Print HTML list of resources in the currently selected folder.
    * Deprecated use the 'Sidebar Content' insertion point in new interfaces.
    */
    public function RequestSidebarContent()
    {
        $this->PrintSidebarContent($GLOBALS['AF']->GetPageName(),
                "Folders::RequestSidebarContent");
    }

    /**
    * Retrieve the maximum number of resources that can be added to
    * a folder in one batch.  This is maintained for backwards compatibility.
    * Older versions of CWIS limited this number for performance reasons.
    * @return int max number to add.
    */
    public function GetMaxResourcesPerAdd()
    {
        return PHP_INT_MAX;
    }

    /**
    * Print HTML intended for the document head element.
    * @return void
    */
    public function InHtmlHeader()
    {
        $GLOBALS["AF"]->RequireUIFile("jquery-ui.js");
        ?>
        <link rel="stylesheet" type="text/css" href="<?PHP
                $GLOBALS["AF"]->PUIFile("Folders_Main.css");  ?>" />
        <script type="text/javascript" src="<?PHP
                $GLOBALS["AF"]->PUIFile("Folders_Support.js");  ?>"></script>
        <script type="text/javascript" src="<?PHP
                $GLOBALS["AF"]->PUIFile("Folders_Main.js");  ?>"></script>
        <?PHP
    }

    /**
    * Print a HTML list of resources in the currently selected folder;
    * calls out to an external function and depends upon an insertion
    * point.
    * @param string $PageName The name of the page that signaled the event.
    * @param string $Location Describes the location on the page where the
    *      insertion point occurs.
    */
    public function PrintSidebarContent($PageName, $Location)
    {
        if ( ($Location == "Sidebar Content" &&
              $this->ConfigSetting("DisplayInSidebar")) ||
            $Location == "Folders::RequestSidebarContent")
        {
            # do not return content if the user is not logged in
            if ($GLOBALS["G_User"]->IsLoggedIn())
            {
                # call display function
                $GLOBALS["AF"]->LoadFunction("Folders_PrintFolderSidebarContent");
                Folders_PrintFolderSidebarContent();
            }
        }
    }

    /**
    * Perform database cleanup operations when signaled by the DBCleanUp plugin.
    */
    public function DatabaseCleanUp()
    {
        $Database = new Database();

        # remove folder items in folders that no longer exist
        $Database->Query("
            DELETE FII FROM FolderItemInts FII
            LEFT JOIN Folders F ON FII.FolderId = F.FolderId
            WHERE F.FolderId IS NULL");

        # remove selected folders for folders that no longer exist
        $Database->Query("
            DELETE FSF FROM Folders_SelectedFolders FSF
            LEFT JOIN Folders F ON FSF.FolderId = F.FolderId
            WHERE F.FolderId IS NULL");

        # find folder items for resources that no longer exist
        $Database->Query("
            SELECT FII.FolderId, FII.ItemId AS ResourceId FROM FolderItemInts FII
            LEFT JOIN Folders F ON FII.FolderId = F.FolderId
            LEFT JOIN Resources R ON FII.ItemId = R.ResourceId
            WHERE F.ContentType = '".intval(Folder::GetItemTypeId("Resource"))."'
            AND R.ResourceId IS NULL");

        # remove the resources from the folders they belong to
        while (FALSE !== ($Row = $Database->FetchRow()))
        {
            $Folder = new Folder($Row["FolderId"]);
            $ResourceId = $Row["ResourceId"];

            # mixed item type folder
            if ($Folder->ContainsItem($ResourceId, "Resource"))
            {
                $Folder->RemoveItem($ResourceId, "Resource");
            }

            # single item type folder
            else
            {
                $Folder->RemoveItem($ResourceId);
            }
        }
    }

    /**
    * Get the selected folder for the given owner.
    * @param mixed $Owner User object, user ID, or NULL to use the global user object.
    * @return Folder|null selected folder or NULL if it can't be retrieved.
    */
    public function GetSelectedFolder($Owner=NULL)
    {
        global $G_User;

        # look for passed in user object, user ID, and then the global user
        # object
        $IsLoggedIn = $G_User->IsLoggedIn();
        $Owner = $Owner instanceof User ? $Owner->Id() : $Owner;
        $Owner = !$Owner && $IsLoggedIn ? $G_User->Id() : $Owner;

        $FolderFactory = new Folders_FolderFactory($Owner);

        try
        {
            $SelectedFolder = $FolderFactory->GetSelectedFolder();
        }

        catch (Exception $Exception)
        {
            return NULL;
        }

        return $SelectedFolder;
    }

    /**
    * Get the resource folder for the given owner.
    * @param mixed $Owner User object, user ID, or NULL to use the global user object.
    * @return Folder|null resource folder that contains folders of resources or
    *   NULL if it can't be retrieved.
    */
    public function GetResourceFolder($Owner=NULL)
    {
        global $G_User;

        # look for passed in user object, user ID, and then the global user
        # object
        $IsLoggedIn = $G_User->IsLoggedIn();
        $Owner = $Owner instanceof User ? $Owner->Id() : $Owner;
        $Owner = !$Owner && $IsLoggedIn ? $G_User->Id() : $Owner;

        $FolderFactory = new Folders_FolderFactory($Owner);

        try
        {
            $ResourceFolder = $FolderFactory->GetResourceFolder();
        }

        catch (Exception $Exception)
        {
            return NULL;
        }

        return $ResourceFolder;
    }

    /**
    * Determine whether the given resource is in the currently selected folder.
    * @param Resource $Resource Resource to check.
    * @param User|integer $Owner User object or user ID for whose
    *     folder we will be retrieving.
    * @return boolean TRUE if the resource is in the folder, FALSE otherwise.
    */
    public function ResourceInSelectedFolder(Resource $Resource, $Owner=NULL)
    {
        $Folder = $this->GetSelectedFolder($Owner);
        if (!is_null($Folder))
        {
            return $this->GetSelectedFolder($Owner)->ContainsItem($Resource->Id());
        }
        else
        {
            return FALSE;
        }
    }

    /**
    * Insert the button "Add All to Folder" into HTML,
    * so that pages don't have to contain Folder-specific html.
    * @param string $PageName The name of the page that signaled the event.
    * @param string $Location Describes the location on the page where the
    *      insertion point occurs.
    * @param array $Context Specific info (e.g. "ReturnTo" address) that are needed to
    *      generate HTML.
    *      Context must include:
    *        $Context["ReturnToString"] the return to string for the button;
    *        $Context["SearchParametersForUrl"] the search groups that the search results
    *          that the button adds to the folder;
    *        $Context["SortParamsForUrl"] the sorting parameters for the search results
    *          that the button adds to the folder;
    *        $Context["NumberSearchResults"] the number of results returned by the
    *          initial search. If this number is too large, the add to folder button
    *          is greyed out
    */
    public function InsertAllButtonHTML($PageName, $Location, $Context=NULL)
    {
        if (($GLOBALS["G_User"]->IsLoggedIn())
            && ($PageName == "SearchResults")
            && ($Location == "Search Results Buttons"))
        {
            # We need several context variables for the button to function,
            # so we do not continue if there is no set context.
            if (isset($Context))
            {
                $TooManyResources = $this->GetMaxResourcesPerAdd();
                $IsLoggedIn = $GLOBALS["G_User"]->IsLoggedIn();
                $ReturnToString = $Context["ReturnToString"];
                $SearchParameters = $Context["SearchParameters"];
                $SortParamsForUrl = $Context["SortParamsForUrl"];
                $NumberSearchResults = $Context["NumberSearchResults"];
                if (is_array($NumberSearchResults))
                {
                    $NewNumberSearchResults = 0;
                    foreach ($NumberSearchResults as $ResultCount)
                    {
                        $NewNumberSearchResults += $ResultCount;
                    }
                    $NumberSearchResults = $NewNumberSearchResults;
                }

                $SearchParametersForUrl = $SearchParameters->UrlParameterString();

                if ($IsLoggedIn && $Context["NumberSearchResults"])
                {
                    $TooManySearchResults = FALSE;

                    if ($NumberSearchResults > $TooManyResources)
                    {
                        $TooManySearchResults = TRUE;
                    }

                    $AddAllURL = "index.php?P=P_Folders_AddSearchResults&amp;RF=1";

                    if ($SearchParametersForUrl)
                    {
                        $AddAllURL= $AddAllURL."&amp;".$SearchParametersForUrl;
                    }

                    if ($SortParamsForUrl)
                    {
                        $AddAllURL = $AddAllURL.$SortParamsForUrl;
                    }

                    // add item type
                    if (isset($Context["ItemType"]))
                    {
                        $AddAllURL = $AddAllURL."&amp;ItemType=".$Context["ItemType"];
                    }

                    $AddAllURL = $AddAllURL."&amp;ReturnTo=".$ReturnToString;

                    $MaxResources = $this->GetMaxResourcesPerAdd();

                    # call out to the external display function to hand off processing
                    $GLOBALS["AF"]->LoadFunction("Folders_InsertAllButtonHTML");
                    Folders_InsertAllButtonHTML($TooManySearchResults,
                        $MaxResources, $AddAllURL);

                }
            }
        }
    }

    /**
    * Prevent accounts of users from removal if they meet one of the following
    * conditions:
    * @li The user has created a new folder or modified the
    *      automatically-created one.
    * @li The user has added at least one resource to at least one folder.
    * @param int $UserId User identifier.
    * @return Returns TRUE if the user account shouldn't be removed and FALSE if
    *      the account can be removed as far as the Folders plugin is concerned.
    */
    public function PreventAccountPruning($UserId)
    {
        $UserId = intval($UserId);
        $Database = new Database();

        # query for the number of folders for the user that are not the
        # automatically-created folder or are that folder but it has been
        # changed
        $NumFolders = $Database->Query("
            SELECT COUNT(*) AS NumFolders
            FROM Folders
            WHERE OwnerId = '".addslashes($UserId)."'
            AND FolderName != 'ResourceFolderRoot'
            AND FolderName != 'Main Folder'", "NumFolders");

        # the user has manually created a folder or has modified the name of the
        # automatically-created one and shouldn't be removed
        if ($NumFolders > 0)
        {
            return TRUE;
        }

        $ResourceItemTypeId = Folder::GetItemTypeId("Resource");

        # query for the number of resources the user has put into folders
        $NumResources = $Database->Query("
            SELECT COUNT(*) AS NumResources
            FROM Folders F
            LEFT JOIN FolderItemInts FII on F.FolderId = FII.Folderid
            WHERE FII.FolderId IS NOT NULL
            AND F.OwnerId = '".addslashes($UserId)."'
            AND F.ContentType = '".addslashes($ResourceItemTypeId)."'", "NumResources");

        # the user has put at least one resource into a folder and shouldn't be
        # removed
        if ($NumResources > 0)
        {
            return TRUE;
        }

        # don't determine whether or not the user should be removed
        return FALSE;
    }

    /**
    * 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 CleanUrlTemplate($Matches, $Pattern, $Page, $SearchPattern)
    {
        if ($Page == "P_Folders_ViewFolder")
        {
            # if no ID found
            if (count($Matches) <= 2)
            {
                # return match unchanged
                return $Matches[0];
            }

            # get the URL for the folder
            $Url = Folders_Common::GetShareUrl(new Folders_Folder($Matches[2]));

            return "href=\"".defaulthtmlentities($Url)."\"";
        }

        # return match unchanged
        return $Matches[0];
    }


    /**
    * Generates "add" and "remove" urls for the current folder buttons for a resource
    * and passes them to an external display function.
    * @param string $PageName The name of the page that signaled the event.
    * @param string $Location Describes the location on the page where the
    *      insertion point occurs.
    * @param array $Context Specific info (e.g. "ReturnTo" address) that are needed to
    *      generate HTML.
    *      For the buttons to work, $Context must be set and include:
    *         $Context["Resource"], the resource for which we are
    *         printing the button
    */
    public function InsertButtonHTML($PageName, $Location, $Context=NULL)
    {

        if (isset($Context) && ($Location == "Resource Summary Buttons"
                || $Location == "Buttons Top")
                || $Location == "Resource Display Buttons")
        {
            $IsLoggedIn = $GLOBALS["G_User"]->IsLoggedIn();
            if ($IsLoggedIn && isset($Context["Resource"]) )
            {
                $ResourceId = $Context["Resource"]->Id();

                $Folder = $this->GetSelectedFolder();

                $FolderId = $Folder->Id();

                $ReturnToString = urlencode($GLOBALS["AF"]->GetCleanUrl());
                $InFolder = $Folder->ContainsItem($ResourceId);
                $RemoveActionURL= OurBaseUrl()
                        ."index.php?P=P_Folders_RemoveItem&amp;FolderId="
                        .urlencode($FolderId)."&amp;ItemId="
                        .urlencode($ResourceId)."&amp;ReturnTo=".$ReturnToString;
                $AddActionURL= OurBaseUrl() . "index.php?P=P_Folders_AddItem&amp;ItemId=".
                      urlencode($ResourceId)."&amp;FolderId=".
                      urlencode($FolderId)."&amp;ReturnTo=".$ReturnToString;

                # call out to the external display function to hand off processing
                $GLOBALS["AF"]->LoadFunction("Folders_InsertButtonHTML");
                Folders_InsertButtonHTML($InFolder, $AddActionURL, $RemoveActionURL,
                      $FolderId, $ResourceId, $Location);
            }
        }
    }

    /**
    * Retrieves folder note for a particular resource when called if there
    * is a note and inserts it via an external function call.
    * @param string $PageName The name of the page that signaled the event.
    * @param string $Location Describes the location on the page where the
    *      insertion point occurs.
    * @param string $Context Specific info (e.g. "ReturnTo" address) that are needed to
    *      generate HTML.
    *      For the note to work, $Context must be set and include:
    *        $Context["ResourceId"], the id of the resource for which we are
    *        printing the note.
    */
    public function InsertResourceNote($PageName, $Location, $Context=NULL)
    {
        if (isset($Context) && $Location == "After Resource Description"
            && $PageName== "P_Folders_ViewFolder")
        {
            $PublicFolder = FALSE;
            if (array_key_exists("FolderId", $Context))
            {
                $Folder = new Folders_Folder($Context["FolderId"]);
                $PublicFolder = $Folder->IsShared();
            }

            if ($GLOBALS["G_User"]->IsLoggedIn() || $PublicFolder)
            {
                $ResourceId = $Context["ResourceId"];
                $Folder = (isset($Folder)) ? $Folder : $this->GetSelectedFolder();

                $ResourceInFolder = $Folder->ContainsItem($ResourceId);

                if ($ResourceInFolder)
                {
                    $ResourceNote = $Folder->NoteForItem($ResourceId);
                    $ReturnToString = urlencode($GLOBALS["AF"]->GetCleanUrl());
                    $EditResourceNoteURL =
                            "index.php?P=P_Folders_ChangeResourceNote&amp;FolderId=".
                            $Folder->Id()."&amp;ItemId=".$ResourceId."&amp;ReturnTo=".
                            $ReturnToString;

                    # call out to the external display function to hand off processing
                    $GLOBALS["AF"]->LoadFunction("Folders_InsertResourceNote");
                    Folders_InsertResourceNote($ResourceNote, urlencode($Folder->Id()),
                            $ResourceId, $EditResourceNoteURL);
                }
                # else we do not insert the resource note
            }
        }
    }

    /* HELPER FUNCTIONS FOR ITEM MANIPULATION */

    /**
    * Takes a ReturnTo array that's set by the calling page and strips old Folders
    * errors from the URL so they don't pile up.
    * @param array $ReturnTo The array set based on how the calling page was reached.
    * @return array the ReturnTo array passed in with the old
    *     folders errors removed.
    */
    public static function ClearPreviousFoldersErrors($ReturnTo)
    {
        $ReturnQuery = array();
        if (array_key_exists('query', $ReturnTo))
        {
            parse_str($ReturnTo['query'], $ReturnQuery);

            foreach($ReturnQuery as $key=>$value)
            {
                if (is_string($value) &&
                    "E_FOLDERS" == substr($value, 0, 9))
                {
                    unset($ReturnQuery[$key]);
                }
            }

            $ReturnTo['query'] = http_build_query($ReturnQuery);
        }

        return $ReturnTo;
    }

    /**
    * Executes the page's result response based on how the page was reached,
    * delivering success/error via a JsonHelper (reached with AJAX) or via the standard
    * jump pages (not reached with AJAX)
    * @param array $Errors Error codes incurred that are relevant to processing, it is
    *     empty on success.
    * @param bool $ReachedWithAjax TRUE if reached w/ ajax
    */
    public static function ProcessPageResponse($Errors, $ReachedWithAjax)
    {
        if ($ReachedWithAjax)
        {
            BeginAjaxResponse();
            $JsonHelper = new JsonHelper();

            # take success as an empty $Errors array
            if (empty($Errors))
            {
                $JsonHelper->Success();
            }
            # else there was a failure and we see it in the error message
            else
            {
                $ErrorMessages = array();

                # get the messages for each error we incurred; if we get an array key
                # not found it's a desired failure -- developers are responsible
                # for defining error messages for Folders within the plugin
                foreach ($Errors as $Error)
                {
                    array_push($ErrorMessages, self::$ErrorsMaster[$Error]);
                }

                $JsonHelper->Error(implode(', ', $ErrorMessages));
            }
        }
        else
        {
            # Set up the response return address:
            # if "ReturnTo" is set, then we should return to that address
            if (GetArrayValue($_GET, "ReturnTo", FALSE))
            {
                $ReturnTo = parse_url($_GET['ReturnTo']);
            }

            # else we should return to the page which this page is being directed from
            else if (isset($_SERVER["HTTP_REFERER"]))
            {
                $ReturnTo = parse_url($_SERVER["HTTP_REFERER"]);
            }

            # return to ManageFolders page if nothing is set
            else
            {
                $ReturnTo = parse_url(
                      $GLOBALS['AF']->GetCleanUrlForPath(
                          "index.php?P=P_Folders_ManageFolders"));
            }

            # Process success or failure:
            # take success as an empty $Errors array
            if (empty($Errors))
            {
                # clear out any previous folder errors in the params
                $ReturnTo = self::ClearPreviousFoldersErrors($ReturnTo);

                if(array_key_exists('query', $ReturnTo))
                {
                    $JumpToPage = $ReturnTo['path']."?".$ReturnTo['query'];
                }
                else
                {
                    $JumpToPage = $ReturnTo['path'];
                }
                $GLOBALS['AF']->SetJumpToPage($JumpToPage);
            }
            # else there was a failure and we see it in the error message
            else
            {
                # clear out any previous folder errors in the params
                $ReturnTo = self::ClearPreviousFoldersErrors($ReturnTo);

                # we expect the messages to be retrieved on the page if we are
                # not using Ajax, as the messages are too cumbersome for URL params
                $ErrorsForUrl = array();
                $Count = 0;

                foreach ($Errors as $Error)
                {
                    $ErrorContainer = array();
                    $Index = 'ER'.$Count;
                    $ErrorsForUrl[$Index] = $Error;
                    $Count++;
                }

                $JumpToPage = $ReturnTo['path']."?".$ReturnTo['query']."&".
                      http_build_query($ErrorsForUrl);
                $GLOBALS['AF']->SetJumpToPage($JumpToPage);
            }
        }
    }

    # define the error messages we use throughout the Folders plugin
    public static $ErrorsMaster = array(
    "E_FOLDERS_NOSUCHITEM" => "That is not a valid item id.",
    "E_FOLDERS_RESOURCENOTFOUND" => "Resource not found.",
    "E_FOLDERS_NOSUCHFOLDER" => "Not a valid folder.",
    "E_FOLDERS_NOTFOLDEROWNER" => "You do not own the selected folder.");
}
