/// <summary> Method returns the table of results for the browse indicated </summary>
        /// <param name = "ItemAggr">Object with all the information about the browse</param>
        /// <param name = "Page"> Page of results requested for the indicated browse </param>
        /// <param name = "Sort"> Sort applied to the results before being returned </param>
        /// <param name="Potentially_Include_Facets"> Flag indicates if facets could be included in this browse results </param>
        /// <param name = "Need_Browse_Statistics"> Flag indicates if the browse statistics (facets and total counts) are required for this browse as well </param>
        /// <param name = "Tracer">Trace object keeps a list of each method executed and important milestones in rendering</param>
        /// <param name="Results_Per_Page"> Number of results to retrieve per page</param>
        /// <returns> Resutls for the browse or info in table form </returns>
        public static Multiple_Paged_Results_Args Gat_All_Browse(Complete_Item_Aggregation ItemAggr,
	        int Page, int Sort, int Results_Per_Page,
	        bool Potentially_Include_Facets, bool Need_Browse_Statistics,
	        Custom_Tracer Tracer)
        {
            if (Tracer != null)
            {
                Tracer.Add_Trace("Item_Aggregation_Utilities.Get_Browse_Results", String.Empty);
            }

            // Get the list of facets first
            List<short> facetsList = ItemAggr.Facets;
            if (!Potentially_Include_Facets)
                facetsList = null;

            // Pull data from the database if necessary

            // Get this browse from the database
            if ((ItemAggr.ID < 0) || (ItemAggr.Code.ToUpper() == "ALL"))
            {
                return Engine_Database.Get_All_Browse_Paged(false, false, Results_Per_Page, Page, Sort, Need_Browse_Statistics, facetsList, Need_Browse_Statistics, Tracer);
            }

            return Engine_Database.Get_Item_Aggregation_Browse_Paged(ItemAggr.Code, false, false, Results_Per_Page, Page, Sort, Need_Browse_Statistics, facetsList, Need_Browse_Statistics, Tracer);
        }
        /// <summary> Reads the item aggregation configuration file and populates the new data into the
        /// item aggregation object </summary>
        /// <param name="HierarchyObject"> Item aggregation object to populate</param>
        /// <param name="FileLocation"> Full name of the item aggregation configuration XML file </param>
        public void Add_Info_From_XML_File(Complete_Item_Aggregation HierarchyObject, string FileLocation )
        {
            // Get the directory from the file location
            string directory = (new FileInfo(FileLocation)).DirectoryName;

            // Load this XML file
            XmlDocument hierarchyXml = new XmlDocument();
            hierarchyXml.Load(FileLocation);

            // create the node reader
            XmlNodeReader nodeReader = new XmlNodeReader(hierarchyXml);

            // Read all the nodes
            while (nodeReader.Read())
            {
                // If this is the beginning tag for an element, assign the next values accordingly
                if (nodeReader.NodeType == XmlNodeType.Element)
                {
                    // Get the node name, trimmed and to upper
                    string nodeName = nodeReader.Name.Trim().ToUpper();

                    // switch the rest based on the tag name
                    switch (nodeName)
                    {
                        case "HI:SETTINGS":
                            read_settings(nodeReader, HierarchyObject);
                            break;

                        case "HI:HOME":
                            read_home(nodeReader, HierarchyObject);
                            break;

                        case "HI:BANNER":
                            read_banners(nodeReader, HierarchyObject);
                            break;

                        case "HI:DIRECTIVES":
                            read_directives(nodeReader, HierarchyObject, directory);
                            break;

                        case "HI:HIGHLIGHTS":
                            read_highlights(nodeReader, HierarchyObject);
                            break;

                        case "HI:BROWSE":
                            read_browse(true, nodeReader, HierarchyObject);
                            break;

                        case "HI:INFO":
                            read_browse(false, nodeReader, HierarchyObject);
                            break;

                        case "HI:RESULTS":
                            read_results_specs(nodeReader, HierarchyObject);
                            break;
                    }
                }
            }
        }
        /// <summary> Finds the home page source file and banner images or html for this item aggregation </summary>
        /// <param name="ThisObject"> Item aggregation to add the home page link and banner html </param>
        /// <remarks>This method is only called if the item aggregation does not have an existing XML configuration file. </remarks>
        protected static void Add_HTML(Complete_Item_Aggregation ThisObject)
        {
            // Just use the standard home text
            if ( File.Exists(Engine_ApplicationCache_Gateway.Settings.Servers.Base_Design_Location + ThisObject.ObjDirectory + "html/home/text.html"))
                ThisObject.Add_Home_Page_File(  "html/home/text.html", Engine_ApplicationCache_Gateway.Settings.System.Default_UI_Language, false );
            if (File.Exists(Engine_ApplicationCache_Gateway.Settings.Servers.Base_Design_Location + ThisObject.ObjDirectory + "html/home/text_en.html"))
                ThisObject.Add_Home_Page_File("html/home/text_en.html",  Web_Language_Enum.English, false );
            if (File.Exists(Engine_ApplicationCache_Gateway.Settings.Servers.Base_Design_Location + ThisObject.ObjDirectory + "html/home/text_fr.html"))
                ThisObject.Add_Home_Page_File("html/home/text_fr.html", Web_Language_Enum.French, false);
            if (File.Exists(Engine_ApplicationCache_Gateway.Settings.Servers.Base_Design_Location + ThisObject.ObjDirectory + "html/home/text_es.html"))
                ThisObject.Add_Home_Page_File("html/home/text_es.html", Web_Language_Enum.Spanish, false);
            if (File.Exists(Engine_ApplicationCache_Gateway.Settings.Servers.Base_Design_Location + ThisObject.ObjDirectory + "html/home/text_sp.html"))
                ThisObject.Add_Home_Page_File("html/home/text_sp.html", Web_Language_Enum.Spanish, false);

            // Just use the standard banner image
            if (File.Exists(Engine_ApplicationCache_Gateway.Settings.Servers.Base_Design_Location + ThisObject.ObjDirectory + "images/banners/coll.jpg"))
                ThisObject.Add_Banner_Image("images/banners/coll.jpg", Engine_ApplicationCache_Gateway.Settings.System.Default_UI_Language);
            if (File.Exists(Engine_ApplicationCache_Gateway.Settings.Servers.Base_Design_Location + ThisObject.ObjDirectory + "images/banners/coll_en.jpg"))
                ThisObject.Add_Banner_Image("images/banners/coll_en.jpg", Web_Language_Enum.English);
            if (File.Exists(Engine_ApplicationCache_Gateway.Settings.Servers.Base_Design_Location + ThisObject.ObjDirectory + "images/banners/coll_fr.jpg"))
                ThisObject.Add_Banner_Image("images/banners/coll_fr.jpg", Web_Language_Enum.French);
            if (File.Exists(Engine_ApplicationCache_Gateway.Settings.Servers.Base_Design_Location + ThisObject.ObjDirectory + "images/banners/coll_es.jpg"))
                ThisObject.Add_Banner_Image("images/banners/coll_es.jpg", Web_Language_Enum.Spanish);
            if (File.Exists(Engine_ApplicationCache_Gateway.Settings.Servers.Base_Design_Location + ThisObject.ObjDirectory + "images/banners/coll_sp.jpg"))
                ThisObject.Add_Banner_Image("images/banners/coll_sp.jpg", Web_Language_Enum.Spanish);
        }
        /// <summary> Checks the appropriate design folders to add any existing browse or info pages to the item aggregation </summary>
        /// <param name="ThisObject"> Item aggregation object to add the browse and info pages to</param>
        /// <param name="Tracer"> Trace object keeps a list of each method executed and important milestones in rendering</param>
        /// <remarks>This method is only called if the item aggregation does not have an existing XML configuration file.</remarks>
        protected static void Add_Browse_Files(Complete_Item_Aggregation ThisObject, Custom_Tracer Tracer)
        {
            // Collect the list of items in the browse folder
            if (Directory.Exists(Engine_ApplicationCache_Gateway.Settings.Servers.Base_Design_Location + ThisObject.ObjDirectory + "html/browse"))
            {
                string[] files = Directory.GetFiles(Engine_ApplicationCache_Gateway.Settings.Servers.Base_Design_Location + ThisObject.ObjDirectory + "html/browse", "*.htm*");
                foreach (string thisFile in files)
                {
                    // Get the new browse info object
                    Complete_Item_Aggregation_Child_Page newBrowse = Get_Item_Aggregation_Browse_Info(thisFile, Item_Aggregation_Child_Visibility_Enum.Main_Menu, Tracer);
                    if (newBrowse != null)
                    {
                        ThisObject.Add_Child_Page(newBrowse);
                    }
                }
            }

            // Collect the list of items in the info folder
            if (Directory.Exists(Engine_ApplicationCache_Gateway.Settings.Servers.Base_Design_Location + ThisObject.ObjDirectory + "html/info"))
            {
                string[] files = Directory.GetFiles(Engine_ApplicationCache_Gateway.Settings.Servers.Base_Design_Location + ThisObject.ObjDirectory + "html/info", "*.htm*");
                foreach (string thisFile in files)
                {
                    // Get the title for this file
                    // Get the new browse info object
                    Complete_Item_Aggregation_Child_Page newInfo = Get_Item_Aggregation_Browse_Info(thisFile, Item_Aggregation_Child_Visibility_Enum.None, Tracer);
                    if (newInfo != null)
                    {
                        ThisObject.Add_Child_Page(newInfo);
                    }
                }
            }
        }
        /// <summary> Adds the search terms to display under advanced search from the datatable extracted from the database 
        /// and also the list of browseable fields for this collection </summary>
        /// <param name="AggrInfo">Partially built item aggregation object</param>
        /// <param name="SearchTermsTable"> Table of all advanced search values </param>
        private static void add_advanced_terms(Complete_Item_Aggregation AggrInfo, DataTable SearchTermsTable)
        {
            // Add ANYWHERE first
            AggrInfo.Search_Fields.Add(new Complete_Item_Aggregation_Metadata_Type(-1, "Anywhere", "ZZ"));

            // Add values either default values or from the table
            if ((SearchTermsTable != null) && (SearchTermsTable.Rows.Count > 0))
            {
                foreach (DataRow thisRow in SearchTermsTable.Rows)
                {
                    short thisTypeId = Convert.ToInt16(thisRow[0]);
                    bool canBrowse = Convert.ToBoolean(thisRow[1]);
                    string displayTerm = thisRow[2].ToString();
                    string sobekCode = thisRow[3].ToString();
                    string solrCode = thisRow[4].ToString();

                    Complete_Item_Aggregation_Metadata_Type metadataType = new Complete_Item_Aggregation_Metadata_Type(thisTypeId, displayTerm, sobekCode) {SolrCode = solrCode};

                    if (!AggrInfo.Search_Fields.Contains(metadataType))
                    {
                        AggrInfo.Search_Fields.Add(metadataType);
                    }

                    if ((canBrowse) && (!AggrInfo.Browseable_Fields.Contains(metadataType)))
                    {
                        AggrInfo.Browseable_Fields.Add(metadataType);
                    }
                }
            }
        }
        private static void read_banners(XmlNodeReader NodeReader, Complete_Item_Aggregation HierarchyObject)
        {
            while (NodeReader.Read())
            {
                // If this is the beginning tag for an element, assign the next values accordingly
                if (NodeReader.NodeType == XmlNodeType.Element)
                {
                    // Get the node name, trimmed and to upper
                    string nodeName = NodeReader.Name.Trim().ToUpper();

                    // switch the rest based on the tag name
                    switch (nodeName)
                    {
                        case "HI:SOURCE":
                            // Check for any attributes to this banner node
                            string lang = String.Empty;
                            bool special = false;
                            Item_Aggregation_Front_Banner_Type_Enum type = Item_Aggregation_Front_Banner_Type_Enum.Left;
                            ushort width = 550;
                            ushort height = 230;

                            if (NodeReader.HasAttributes)
                            {

                                if (NodeReader.MoveToAttribute("lang"))
                                {
                                    lang = NodeReader.Value.Trim().ToUpper();
                                }
                                if (NodeReader.MoveToAttribute("type"))
                                {
                                    if ((NodeReader.Value.Trim().ToUpper() == "HIGHLIGHT") || ( NodeReader.Value.Trim().ToUpper() == "FRONT"))
                                        special = true;
                                }
                                if (NodeReader.MoveToAttribute("side"))
                                {
                                    switch (NodeReader.Value.Trim().ToUpper())
                                    {
                                        case "RIGHT":
                                            type = Item_Aggregation_Front_Banner_Type_Enum.Right;
                                            break;

                                        case "LEFT":
                                            type = Item_Aggregation_Front_Banner_Type_Enum.Left;
                                            break;

                                        case "FULL":
                                            type = Item_Aggregation_Front_Banner_Type_Enum.Full;
                                            break;
                                    }
                                }
                                if (NodeReader.MoveToAttribute("width"))
                                {
                                    ushort.TryParse(NodeReader.Value, out width);

                                }
                                if (NodeReader.MoveToAttribute("height"))
                                {
                                    ushort.TryParse(NodeReader.Value, out height);
                                }
                            }

                            // Now read the banner information and add to the aggregation object
                            NodeReader.Read();
                            if (special)
                            {
                                Item_Aggregation_Front_Banner bannerObj = HierarchyObject.Add_Front_Banner_Image(NodeReader.Value, Web_Language_Enum_Converter.Code_To_Enum( lang));
                                bannerObj.Width = width;
                                bannerObj.Height = height;
                                bannerObj.Type = type;
                            }
                            else
                            {
                                HierarchyObject.Add_Banner_Image(NodeReader.Value, Web_Language_Enum_Converter.Code_To_Enum(lang));
                            }

                            break;
                    }
                }

                if ((NodeReader.NodeType == XmlNodeType.EndElement) && (NodeReader.Name.Trim().ToUpper() == "HI:BANNER"))
                {
                    return;
                }
            }
        }
        private static void read_results_specs(XmlNodeReader NodeReader, Complete_Item_Aggregation HierarchyObject)
        {
            bool inViews = false;
            while (NodeReader.Read())
            {
                // If this is the beginning tag for an element, assign the next values accordingly
                string nodeName;
                if (NodeReader.NodeType == XmlNodeType.Element)
                {
                    // Get the node name, trimmed and to upper
                    nodeName = NodeReader.Name.Trim().ToUpper();

                    switch (nodeName)
                    {
                        case "HI:VIEWS":
                            inViews = true;
                            break;

                        case "HI:ADD":
                            if ( inViews )
                            {
                                bool isDefault = false;
                                string type = String.Empty;
                                if (NodeReader.MoveToAttribute("default"))
                                {
                                    isDefault = true;
                                }
                                if (NodeReader.MoveToAttribute("type"))
                                {
                                    type = NodeReader.Value.ToUpper();
                                }
                                if (type.Length > 0)
                                {
                                    Result_Display_Type_Enum displayType = Result_Display_Type_Enum.Default;
                                    switch (type)
                                    {
                                        case "BRIEF":
                                            displayType = Result_Display_Type_Enum.Brief;
                                            break;

                                        case "FULL":
                                            displayType = Result_Display_Type_Enum.Full_Citation;
                                            break;

                                        case "THUMBNAIL":
                                            displayType = Result_Display_Type_Enum.Thumbnails;
                                            break;

                                        case "TABLE":
                                            displayType = Result_Display_Type_Enum.Table;
                                            break;

                                        case "MAP":
                                            displayType = Result_Display_Type_Enum.Map;
                                            break;
                                    }
                                    if (displayType != Result_Display_Type_Enum.Default)
                                    {
                                        if (!HierarchyObject.Result_Views.Contains(displayType))
                                        {
                                            HierarchyObject.Result_Views.Add(displayType);
                                        }
                                        if (isDefault)
                                        {
                                            HierarchyObject.Default_Result_View = displayType;
                                        }
                                    }
                                }
                            }
                            break;

                        case "HI:REMOVE":
                            if (inViews)
                            {
                                string type = String.Empty;
                                if (NodeReader.MoveToAttribute("type"))
                                {
                                    type = NodeReader.Value.ToUpper();
                                }
                                if (type.Length > 0)
                                {
                                    Result_Display_Type_Enum displayType = Result_Display_Type_Enum.Default;
                                    switch (type)
                                    {
                                        case "BRIEF":
                                            displayType = Result_Display_Type_Enum.Brief;
                                            break;

                                        case "FULL":
                                            displayType = Result_Display_Type_Enum.Full_Citation;
                                            break;

                                        case "THUMBNAIL":
                                            displayType = Result_Display_Type_Enum.Thumbnails;
                                            break;

                                        case "TABLE":
                                            displayType = Result_Display_Type_Enum.Table;
                                            break;

                                        case "MAP":
                                            displayType = Result_Display_Type_Enum.Map;
                                            break;
                                    }
                                    if (displayType != Result_Display_Type_Enum.Default)
                                    {
                                        if (HierarchyObject.Result_Views.Contains(displayType))
                                        {
                                            HierarchyObject.Result_Views.Remove(displayType);
                                        }
                                    }
                                }
                            }
                            break;
                    }
                }

                // If this is not an end element, continue
                if (NodeReader.NodeType != XmlNodeType.EndElement) continue;

                // Get the node name, trimmed and to upper
                nodeName = NodeReader.Name.Trim().ToUpper();

                switch ( nodeName )
                {
                    case "HI:VIEWS":
                        inViews = false;
                        break;

                    case "HI:RESULTS":
                        return;
                }
            }
        }
        private static void read_highlights(XmlNodeReader NodeReader, Complete_Item_Aggregation HierarchyObject)
        {
            Complete_Item_Aggregation_Highlights highlight = new Complete_Item_Aggregation_Highlights();

            // Determine if this is a rotating type of highlight or not
            if (NodeReader.HasAttributes)
            {
                if (NodeReader.MoveToAttribute("type"))
                {
                    if (NodeReader.Value == "ROTATING")
                        HierarchyObject.Rotating_Highlights = true;
                }

                if (HierarchyObject.Front_Banner_Dictionary != null)
                {
                    // The following three values are for reading legacy XML files.  These
                    // data fields have been moved to be attached to the actual banner
                    if (NodeReader.MoveToAttribute("bannerSide"))
                    {

                        if (NodeReader.Value.Trim().ToUpper() == "RIGHT")
                        {
                            foreach (KeyValuePair<Web_Language_Enum, Item_Aggregation_Front_Banner> banners in HierarchyObject.Front_Banner_Dictionary)
                                banners.Value.Type = Item_Aggregation_Front_Banner_Type_Enum.Right;
                        }
                        else
                        {
                            foreach (KeyValuePair<Web_Language_Enum, Item_Aggregation_Front_Banner> banners in HierarchyObject.Front_Banner_Dictionary)
                                banners.Value.Type = Item_Aggregation_Front_Banner_Type_Enum.Left;
                        }
                    }
                    if (NodeReader.MoveToAttribute("bannerHeight"))
                    {
                        foreach (KeyValuePair<Web_Language_Enum, Item_Aggregation_Front_Banner> banners in HierarchyObject.Front_Banner_Dictionary)
                            banners.Value.Height = Convert.ToUInt16(NodeReader.Value);
                    }
                    if (NodeReader.MoveToAttribute("bannerWidth"))
                    {
                        foreach (KeyValuePair<Web_Language_Enum, Item_Aggregation_Front_Banner> banners in HierarchyObject.Front_Banner_Dictionary)
                            banners.Value.Width = Convert.ToUInt16(NodeReader.Value);
                    }
                }
            }

            while (NodeReader.Read())
            {
                // If this is the beginning tag for an element, assign the next values accordingly
                if (NodeReader.NodeType == XmlNodeType.Element)
                {
                    // Get the node name, trimmed and to upper
                    string nodeName = NodeReader.Name.Trim().ToUpper();

                    // switch the rest based on the tag name
                    string languageText;
                    switch (nodeName)
                    {
                        case "HI:SOURCE":
                            NodeReader.Read();
                            highlight.Image = NodeReader.Value.ToLower();
                            break;

                        case "HI:LINK":
                            NodeReader.Read();
                            highlight.Link = NodeReader.Value.ToLower();
                            break;

                        case "HI:TOOLTIP":
                            languageText = String.Empty;
                            if ((NodeReader.HasAttributes) && (NodeReader.MoveToAttribute("lang")))
                                languageText = NodeReader.Value.ToUpper();
                            NodeReader.Read();
                            highlight.Add_Tooltip( Web_Language_Enum_Converter.Code_To_Enum(languageText), NodeReader.Value );
                            break;

                        case "HI:TEXT":
                            languageText = String.Empty;
                            if ((NodeReader.HasAttributes) && (NodeReader.MoveToAttribute("lang")))
                                languageText = NodeReader.Value.ToUpper();
                            NodeReader.Read();
                            highlight.Add_Text(Web_Language_Enum_Converter.Code_To_Enum(languageText), NodeReader.Value);
                            break;
                    }
                }

                if (NodeReader.NodeType == XmlNodeType.EndElement)
                {
                    if (NodeReader.Name.Trim().ToUpper() == "HI:HIGHLIGHT" )
                    {
                        if (HierarchyObject.Highlights == null)
                            HierarchyObject.Highlights = new List<Complete_Item_Aggregation_Highlights>();
                        HierarchyObject.Highlights.Add(highlight);
                        highlight = new Complete_Item_Aggregation_Highlights();
                    }

                    if (NodeReader.Name.Trim().ToUpper() == "HI:HIGHLIGHTS")
                    {
                        // Done with all the highlights so return
                        return;
                    }
                }
            }
        }
        private void Perform_Database_Search(Custom_Tracer Tracer, List<string> Terms, List<string> Web_Fields, long Date1, long Date2, int ActualCount, Results_Arguments Current_Mode, int Current_Sort, Complete_Item_Aggregation Aggregation_Object, int Results_Per_Page, bool Potentially_Include_Facets, out Search_Results_Statistics Complete_Result_Set_Info, out List<List<iSearch_Title_Result>> Paged_Results, bool Need_Search_Statistics)
        {
            if (Tracer != null)
            {
                Tracer.Add_Trace("SobekCM_Assistant.Perform_Database_Search", "Query the database for search results");
            }

            // Get the list of facets first
            List<short> facetsList = Aggregation_Object.Facets;
            if (!Potentially_Include_Facets)
                facetsList.Clear();

            // Set the return values to NULL initially
            Complete_Result_Set_Info = null;

            const bool INCLUDE_PRIVATE = false;

            List<short> links = new List<short>();
            List<short> db_fields = new List<short>();
            List<string> db_terms = Terms.ToList();

            // Step through all the web fields and convert to db fields
            for (int i = 0; i < ActualCount; i++)
            {
                if (Web_Fields[i].Length > 1)
                {
                    // Find the joiner
                    if ((Web_Fields[i][0] == '+') || (Web_Fields[i][0] == '=') || (Web_Fields[i][0] == '-'))
                    {
                        if (Web_Fields[i][0] == '+')
                            links.Add(0);
                        if (Web_Fields[i][0] == '=')
                            links.Add(1);
                        if (Web_Fields[i][0] == '-')
                            links.Add(2);

                        Web_Fields[i] = Web_Fields[i].Substring(1);
                    }
                    else
                    {
                        links.Add(0);
                    }

                    // Find the db field number
                    db_fields.Add(Metadata_Field_Number(Web_Fields[i]));
                }

                // Also add starting and ending quotes to all the valid searches
                if (db_terms[i].Length > 0)
                {
                    if ((db_terms[i].IndexOf("\"") < 0) && (db_terms[i].IndexOf(" ") < 0))
                    {
                        // Since this is a single word, see what type of special codes to include
                        switch (Current_Mode.Search_Precision)
                        {
                            case Search_Precision_Type_Enum.Contains:
                                db_terms[i] = "\"" + db_terms[i] + "\"";
                                break;

                            case Search_Precision_Type_Enum.Inflectional_Form:
                                // If there are any non-characters, don't use inflectional for this term
                                bool inflectional = db_terms[i].All(Char.IsLetter);
                                if (inflectional)
                                {
                                    db_terms[i] = "FORMSOF(inflectional," + db_terms[i] + ")";
                                }
                                else
                                {
                                    db_terms[i] = "\"" + db_terms[i] + "\"";
                                }
                                break;

                            case Search_Precision_Type_Enum.Synonmic_Form:
                                // If there are any non-characters, don't use thesaurus for this term
                                bool thesaurus = db_terms[i].All(Char.IsLetter);
                                if (thesaurus)
                                {
                                    db_terms[i] = "FORMSOF(thesaurus," + db_terms[i] + ")";
                                }
                                else
                                {
                                    db_terms[i] = "\"" + db_terms[i] + "\"";
                                }
                                break;
                        }
                    }
                    else
                    {
                        if (Current_Mode.Search_Precision != Search_Precision_Type_Enum.Exact_Match)
                        {
                            db_terms[i] = "\"" + db_terms[i] + "\"";
                        }
                    }
                }
            }

            // Get the page count in the results
            int current_page_index =Current_Mode.Page;

            // If this is an exact match, just do the search
            if (Current_Mode.Search_Precision == Search_Precision_Type_Enum.Exact_Match)
            {
                Multiple_Paged_Results_Args returnArgs = Engine_Database.Perform_Metadata_Exact_Search_Paged(db_terms[0], db_fields[0], INCLUDE_PRIVATE, Current_Mode.Aggregation, Date1, Date2, Results_Per_Page, current_page_index, Current_Sort, Need_Search_Statistics, facetsList, Need_Search_Statistics, Tracer);
                if (Need_Search_Statistics)
                    Complete_Result_Set_Info = returnArgs.Statistics;
                Paged_Results = returnArgs.Paged_Results;
            }
            else
            {
                // Finish filling up the fields and links
                while (links.Count < 10)
                    links.Add(0);
                while (db_fields.Count < 10)
                    db_fields.Add(-1);
                while (db_terms.Count < 10)
                    db_terms.Add(String.Empty);

                // See if this is a simple search, which can use a more optimized search routine
                bool simplified_search = db_fields.All(Field => (Field <= 0));

                // Perform either the simpler metadata search, or the more complex
                if (simplified_search)
                {
                    StringBuilder searchBuilder = new StringBuilder();
                    for (int i = 0; i < db_terms.Count; i++)
                    {
                        if (db_terms[i].Length > 0)
                        {
                            if (i > 0)
                            {
                                if (i > links.Count)
                                {
                                    searchBuilder.Append(" AND ");
                                }
                                else
                                {
                                    switch (links[i - 1])
                                    {
                                        case 0:
                                            searchBuilder.Append(" AND ");
                                            break;

                                        case 1:
                                            searchBuilder.Append(" OR ");
                                            break;

                                        case 2:
                                            searchBuilder.Append(" AND NOT ");
                                            break;
                                    }
                                }
                            }

                            searchBuilder.Append(db_terms[i]);
                        }
                    }

                    Multiple_Paged_Results_Args returnArgs = Engine_Database.Perform_Metadata_Search_Paged(searchBuilder.ToString(), INCLUDE_PRIVATE, Current_Mode.Aggregation, Date1, Date2, Results_Per_Page, current_page_index, Current_Sort, Need_Search_Statistics, facetsList, Need_Search_Statistics, Tracer);
                    if (Need_Search_Statistics)
                        Complete_Result_Set_Info = returnArgs.Statistics;
                    Paged_Results = returnArgs.Paged_Results;
                }
                else
                {
                    // Perform search in the database
                    Multiple_Paged_Results_Args returnArgs = Engine_Database.Perform_Metadata_Search_Paged(links[0], db_terms[0], db_fields[0], links[1], db_terms[1], db_fields[1], links[2], db_terms[2], db_fields[2], links[3], db_terms[3],
                                                                                                            db_fields[3], links[4], db_terms[4], db_fields[4], links[5], db_terms[5], db_fields[5], links[6], db_terms[6], db_fields[6], links[7], db_terms[7], db_fields[7], links[8], db_terms[8], db_fields[8],
                                                                                                            links[9], db_terms[9], db_fields[9], INCLUDE_PRIVATE, Current_Mode.Aggregation, Date1, Date2, Results_Per_Page, current_page_index, Current_Sort, Need_Search_Statistics, facetsList, Need_Search_Statistics, Tracer);
                    if (Need_Search_Statistics)
                        Complete_Result_Set_Info = returnArgs.Statistics;
                    Paged_Results = returnArgs.Paged_Results;
                }
            }
        }
        /// <summary> Stores the copmlete item aggregation object to the cache </summary>
        /// <param name="Aggregation_Code"> Code for the item aggregation to store </param>
        /// <param name="StoreObject"> Item aggregation object to store for later retrieval </param>
        /// <param name="Tracer"> Trace object keeps a list of each method executed and important milestones in rendering</param>
        public void Store_Complete_Item_Aggregation(string Aggregation_Code, Complete_Item_Aggregation StoreObject, Custom_Tracer Tracer)
        {
            if (Tracer != null)
            {
                Tracer.Add_Trace("CachedDataManager.Store_Item_Aggregation", "Entering Store_Item_Aggregation method");
            }

            // If the cache is disabled, just return before even tracing
            if (settings.Disabled)
            {
                if ( Tracer != null ) Tracer.Add_Trace("CachedDataManager.Store_Item_Aggregation", "Caching is disabled");
                return;
            }

            // Determine the key
            string key = "AGGR|" + Aggregation_Code.ToUpper() + "|COMPLETE";

            // Check the number of item aggregationPermissions currently locally cached
            //int items_cached = 0;
            const int LOCAL_EXPIRATION = 15;

            // Locally cache if this doesn't exceed the limit
            if (Tracer != null)
            {
                Tracer.Add_Trace("CachedDataManager.Store_Item_Aggregation", "Adding object '" + key + "' to the local cache with expiration of " + LOCAL_EXPIRATION + " minute(s)");
            }

            HttpContext.Current.Cache.Insert(key, StoreObject, null, Cache.NoAbsoluteExpiration, TimeSpan.FromMinutes(LOCAL_EXPIRATION));
        }
        /// <summary> Constructor for a new instance of the Aggregation_Single_AdminViewer class </summary>
        /// <param name="RequestSpecificValues"> All the necessary, non-global data specific to the current request </param>
        /// <remarks> Postback from handling an edit or new aggregation is handled here in the constructor </remarks>
        public Aggregation_Single_AdminViewer(RequestCache RequestSpecificValues)
            : base(RequestSpecificValues)
        {
            RequestSpecificValues.Tracer.Add_Trace("Aggregation_Single_AdminViewer.Constructor", String.Empty);

            // Set some defaults
            actionMessage = String.Empty;
            string code = RequestSpecificValues.Current_Mode.Aggregation;

            // If the RequestSpecificValues.Current_User cannot edit this, go back
            if (!RequestSpecificValues.Current_User.Is_Aggregation_Curator(code))
            {
                RequestSpecificValues.Current_Mode.Mode = Display_Mode_Enum.My_Sobek;
                RequestSpecificValues.Current_Mode.My_Sobek_Type = My_Sobek_Type_Enum.Home;
                UrlWriterHelper.Redirect(RequestSpecificValues.Current_Mode);
                return;
            }

            // Load the item aggregation, either currenlty from the session (if already editing this aggregation )
            // or by reading all the appropriate XML and reading data from the database
            object possibleEditAggregation = HttpContext.Current.Session["Edit_Aggregation_" + code];
            Complete_Item_Aggregation cachedInstance = possibleEditAggregation as Complete_Item_Aggregation;
            if (cachedInstance != null)
            {
                itemAggregation = cachedInstance;
            }
            else
            {
                itemAggregation = SobekEngineClient.Aggregations.Get_Complete_Aggregation(code, false, RequestSpecificValues.Tracer);
            }

            // If unable to retrieve this aggregation, send to home
            if (itemAggregation == null)
            {
                RequestSpecificValues.Current_Mode.My_Sobek_Type = My_Sobek_Type_Enum.Home;
                UrlWriterHelper.Redirect(RequestSpecificValues.Current_Mode);
                return;
            }

            // Get the aggregation directory and ensure it exists
            aggregationDirectory = HttpContext.Current.Server.MapPath("design/aggregations/" + itemAggregation.Code );
            if (!Directory.Exists(aggregationDirectory))
                Directory.CreateDirectory(aggregationDirectory);

            // Determine the page
            page = 1;
            if (!String.IsNullOrEmpty(RequestSpecificValues.Current_Mode.My_Sobek_SubMode))
            {
                if (RequestSpecificValues.Current_Mode.My_Sobek_SubMode == "b")
                    page = 2;
                else if (RequestSpecificValues.Current_Mode.My_Sobek_SubMode == "c")
                    page = 3;
                else if (RequestSpecificValues.Current_Mode.My_Sobek_SubMode == "d")
                    page = 4;
                else if (RequestSpecificValues.Current_Mode.My_Sobek_SubMode == "e")
                    page = 5;
                else if (RequestSpecificValues.Current_Mode.My_Sobek_SubMode == "f")
                    page = 6;
                else if (RequestSpecificValues.Current_Mode.My_Sobek_SubMode == "g")
                    page = 7;
                else if (RequestSpecificValues.Current_Mode.My_Sobek_SubMode == "h")
                    page = 8;
                else if (RequestSpecificValues.Current_Mode.My_Sobek_SubMode == "i")
                    page = 9;
                else if (RequestSpecificValues.Current_Mode.My_Sobek_SubMode == "j")
                    page = 10;
                else if (RequestSpecificValues.Current_Mode.My_Sobek_SubMode == "k")
                    page = 11;
                else if (RequestSpecificValues.Current_Mode.My_Sobek_SubMode == "y")
                    page = 12;
                else if (RequestSpecificValues.Current_Mode.My_Sobek_SubMode.IndexOf("g_") == 0)
                    page = 13;
            }

            // If this is a postback, handle any events first
            if (RequestSpecificValues.Current_Mode.isPostBack)
            {
                try
                {
                    // Pull the standard values
                    NameValueCollection form = HttpContext.Current.Request.Form;

                    // Get the curret action
                    string action = form["admin_aggr_save"];

                    // If no action, then we should return to the current tab page
                    if (action.Length == 0)
                        action = RequestSpecificValues.Current_Mode.My_Sobek_SubMode;

                    // If this is to cancel, handle that here; no need to handle post-back from the
                    // editing form page first
                    if (action == "z")
                    {
                        // Clear the aggregation from the sessions
                        HttpContext.Current.Session["Edit_Aggregation_" + itemAggregation.Code] = null;
                        HttpContext.Current.Session["Item_Aggr_Edit_" + itemAggregation.Code + "_NewLanguages"] = null;

                        // Redirect the RequestSpecificValues.Current_User
                        RequestSpecificValues.Current_Mode.Mode = Display_Mode_Enum.Aggregation;
                        RequestSpecificValues.Current_Mode.Aggregation_Type = Aggregation_Type_Enum.Home;
                        UrlWriterHelper.Redirect(RequestSpecificValues.Current_Mode);
                        return;
                    }

                    // Save the returned values, depending on the page
                    switch (page)
                    {
                        case 1:
                            Save_Page_1_Postback(form);
                            break;

                        case 2:
                            Save_Page_2_Postback(form);
                            break;

                        case 3:
                            Save_Page_3_Postback(form);
                            break;

                        case 4:
                            Save_Page_4_Postback(form);
                            break;

                        case 5:
                            Save_Page_Appearance_Postback(form);
                            break;

                        case 6:
                            Save_Page_6_Postback();
                            break;

                        case 7:
                            Save_Page_7_Postback(form);
                            break;

                        case 8:
                            Save_Page_8_Postback(form);
                            break;

                        case 9:
                            Save_Page_Uploads_Postback(form);
                            break;

                        case 12:
                            Save_Page_CSS_Postback(form);
                            break;

                        case 13:
                            Save_Child_Page_Postback(form);
                            break;
                    }

                    // Should this be saved to the database?
                    if ((action == "save") || (action == "save_exit") || (action == "save_wizard"))
                    {
                        // Get the current aggrgeation information, for comparison
                        Complete_Item_Aggregation currentAggregation = SobekEngineClient.Aggregations.Get_Complete_Aggregation(code, true, RequestSpecificValues.Tracer);

                        // Backup the old aggregation info
                        string backup_folder = UI_ApplicationCache_Gateway.Settings.Servers.Base_Design_Location + itemAggregation.ObjDirectory.Replace("/","\\") + "backup\\configs";
                        if (!Directory.Exists(backup_folder))
                            Directory.CreateDirectory(backup_folder);
                        string current_config = UI_ApplicationCache_Gateway.Settings.Servers.Base_Design_Location + itemAggregation.ObjDirectory + "\\" + itemAggregation.Code + ".xml";
                        if (File.Exists(current_config))
                        {
                            // Use the last modified date as the name of the backup
                            DateTime lastModifiedDate = (new FileInfo(current_config)).LastWriteTime;
                            string backup_name = itemAggregation.Code + lastModifiedDate.Year + lastModifiedDate.Month.ToString().PadLeft(2, '0') + lastModifiedDate.Day.ToString().PadLeft(2, '0') + lastModifiedDate.Hour.ToString().PadLeft(2, '0') + lastModifiedDate.Minute.ToString().PadLeft(2, '0') + ".xml";
                            if (!File.Exists(backup_folder + "\\" + backup_name))
                                File.Copy(current_config, backup_folder + "\\" + backup_name, false );
                        }

                        // Save the new configuration file
                        string save_error = String.Empty;
                        bool successful_save = true;
                        if (!itemAggregation.Write_Configuration_File(UI_ApplicationCache_Gateway.Settings.Servers.Base_Design_Location + itemAggregation.ObjDirectory))
                        {
                            successful_save = false;
                            save_error = "<br /><br />Error saving the configuration file";
                        }

                        // Save to the database
                        if (!Item_Aggregation_Utilities.Save_To_Database(itemAggregation, RequestSpecificValues.Current_User.Full_Name, null))
                        {
                            successful_save = false;
                            save_error = "<br /><br />Error saving to the database.";

                            if (Engine_Database.Last_Exception != null)
                            {
                                save_error = save_error + "<br /><br />" + Engine_Database.Last_Exception.Message;
                            }
                        }

                        // Save the link between this item and the thematic heading
                        int thematicHeadingId = -1;
                        if (itemAggregation.Thematic_Heading != null)
                            thematicHeadingId = itemAggregation.Thematic_Heading.ID;
                        UI_ApplicationCache_Gateway.Aggregations.Set_Aggregation_Thematic_Heading(itemAggregation.Code, thematicHeadingId);

                        // Clear the aggregation from the cache
                        CachedDataManager.Aggregations.Remove_Item_Aggregation(itemAggregation.Code, null);
                        CachedDataManager.Aggregations.Clear_Aggregation_Hierarchy();
                        Engine_ApplicationCache_Gateway.RefreshCodes();
                        Engine_ApplicationCache_Gateway.RefreshThematicHeadings();

                        // Forward back to the aggregation home page, if this was successful
                        if (successful_save)
                        {
                            // Also, update the information that was changed
                            try
                            {
                                List<string> changes = Complete_Item_Aggregation_Comparer.Compare(currentAggregation, itemAggregation);
                                if ((changes != null) && (changes.Count > 0))
                                {
                                    StringBuilder builder = new StringBuilder(changes[0]);
                                    for (int i = 1; i < changes.Count; i++)
                                    {
                                        builder.Append("\n" + changes[i]);
                                    }
                                    SobekCM_Database.Save_Item_Aggregation_Milestone(itemAggregation.Code, builder.ToString(), RequestSpecificValues.Current_User.Full_Name);

                                }
                                else
                                {
                                    SobekCM_Database.Save_Item_Aggregation_Milestone(itemAggregation.Code, "Configuration edited", RequestSpecificValues.Current_User.Full_Name);
                                }
                            }
                            catch
                            {
                                SobekCM_Database.Save_Item_Aggregation_Milestone(itemAggregation.Code, "Configuration edited", RequestSpecificValues.Current_User.Full_Name);
                            }

                            // Clear the aggregation from the sessions
                            HttpContext.Current.Session["Edit_Aggregation_" + itemAggregation.Code] = null;
                            HttpContext.Current.Session["Item_Aggr_Edit_" + itemAggregation.Code + "_NewLanguages"] = null;

                            // Redirect the RequestSpecificValues.Current_User
                            if (action == "save_exit")
                            {
                                RequestSpecificValues.Current_Mode.Mode = Display_Mode_Enum.Aggregation;
                                RequestSpecificValues.Current_Mode.Aggregation_Type = Aggregation_Type_Enum.Home;
                                UrlWriterHelper.Redirect(RequestSpecificValues.Current_Mode);
                            }
                            else if (action == "save_wizard")
                            {

                                RequestSpecificValues.Current_Mode.Admin_Type = Admin_Type_Enum.Add_Collection_Wizard;
                                string wizard_url = UrlWriterHelper.Redirect_URL(RequestSpecificValues.Current_Mode);
                                RequestSpecificValues.Current_Mode.Admin_Type = Admin_Type_Enum.Aggregations_Mgmt;

                                if (wizard_url.IndexOf("?") < 0)
                                    wizard_url = wizard_url + "?parent=" + itemAggregation.Code;
                                else
                                    wizard_url = wizard_url + "&parent=" + itemAggregation.Code;

                                RequestSpecificValues.Current_Mode.Request_Completed = true;
                                HttpContext.Current.Response.Redirect(wizard_url, false);
                                HttpContext.Current.ApplicationInstance.CompleteRequest();

                            }
                            else
                            {
                                UrlWriterHelper.Redirect(RequestSpecificValues.Current_Mode);
                            }
                        }
                        else
                        {
                            actionMessage = "Error saving aggregation information!" + save_error;
                        }
                    }
                    else
                    {
                        // In some cases, skip this part
                        if (((page == 8) && (action == "h")) || ((page == 7) && (action == "g")))
                            return;

                        // Save to the admins session
                        HttpContext.Current.Session["Edit_Aggregation_" + itemAggregation.Code] = itemAggregation;
                        RequestSpecificValues.Current_Mode.My_Sobek_SubMode = action;
                        HttpContext.Current.Response.Redirect(UrlWriterHelper.Redirect_URL(RequestSpecificValues.Current_Mode), false);
                        HttpContext.Current.ApplicationInstance.CompleteRequest();
                        RequestSpecificValues.Current_Mode.Request_Completed = true;
                    }
                }
                catch
                {
                    actionMessage = "Unable to correctly parse postback data.";
                }
            }
        }
        /// <summary> Creates the item aggregation object from the datatable extracted from the database </summary>
        /// <param name="BasicInfo">Datatable from database calls ( either SobekCM_Get_Item_Aggregation or SobekCM_Get_All_Groups )</param>
        /// <returns>Minimally built aggregation object</returns>
        /// <remarks>The child and parent information is not yet added to the returned object </remarks>
        private static Complete_Item_Aggregation create_basic_aggregation_from_datatable(DataTable BasicInfo)
        {
            // Pull out this row
            DataRow thisRow = BasicInfo.Rows[0];

            string displayOptions = thisRow[15].ToString();
            DateTime lastAdded = new DateTime(2000, 1, 1);
            if (thisRow[16] != DBNull.Value)
                lastAdded = Convert.ToDateTime(thisRow[16]);

            // Build the collection group object
            Complete_Item_Aggregation aggrInfo = new Complete_Item_Aggregation(Engine_ApplicationCache_Gateway.Settings.System.Default_UI_Language,
                thisRow[1].ToString().ToLower(), thisRow[4].ToString(), Convert.ToInt32(thisRow[0]), displayOptions, lastAdded)
            {
                Name = thisRow[2].ToString(),
                ShortName = thisRow[3].ToString(),
                Active = Convert.ToBoolean(thisRow[5]),
                Hidden = Convert.ToBoolean(thisRow[6]),
                Has_New_Items = Convert.ToBoolean(thisRow[7]),
                //Map_Display = Convert.ToUInt16(thisRow[11]),
                //Map_Search = Convert.ToUInt16(thisRow[12]),
                OAI_Enabled = Convert.ToBoolean(thisRow[13]),
                Items_Can_Be_Described = Convert.ToInt16(thisRow[18]),
            };

            if ((thisRow[8] != DBNull.Value) && (thisRow[8].ToString().Length > 0))
                aggrInfo.Contact_Email = thisRow[8].ToString();
            if ((thisRow[9] != DBNull.Value) && (thisRow[9].ToString().Length > 0))
                aggrInfo.Default_Skin = thisRow[9].ToString();
            if ((thisRow[10] != DBNull.Value) && (thisRow[10].ToString().Length > 0))
                aggrInfo.Description = thisRow[10].ToString();
            if ((thisRow[14] != DBNull.Value) && (thisRow[14].ToString().Length > 0))
                aggrInfo.OAI_Metadata = thisRow[14].ToString();
            if ((thisRow[19] != DBNull.Value) && (thisRow[19].ToString().Length > 0))
                aggrInfo.External_Link = thisRow[19].ToString();

            if (BasicInfo.Columns.Contains("ThematicHeadingID"))
            {
                if (thisRow["ThematicHeadingID"] != DBNull.Value)
                {
                    int thematicHeadingId = Convert.ToInt32(thisRow["ThematicHeadingID"]);

                    if (thematicHeadingId > 0)
                    {
                        string thematicHeading = thisRow["ThemeName"].ToString();
                        aggrInfo.Thematic_Heading = new Thematic_Heading(thematicHeadingId, thematicHeading);
                    }
                }
            }

            // return the built object
            return aggrInfo;
        }
 /// <summary> Adds the child information to the item aggregation object from the datatable extracted from the database </summary>
 /// <param name="AggrInfo">Partially built item aggregation object</param>
 /// <param name="ParentInfo">Datatable from database calls with parent item aggregation information ( from  SobekCM_Get_Item_Aggregation only )</param>
 private static void add_parents(Complete_Item_Aggregation AggrInfo, DataTable ParentInfo)
 {
     foreach (DataRow parentRow in ParentInfo.Rows)
     {
         Item_Aggregation_Related_Aggregations parentObject = new Item_Aggregation_Related_Aggregations(parentRow[0].ToString(), parentRow[1].ToString(), parentRow[3].ToString(), Convert.ToBoolean(parentRow[4]), false);
         AggrInfo.Add_Parent_Aggregation(parentObject);
     }
 }
 /// <summary> Adds the page count, item count, and title count to the item aggregation object from the datatable extracted from the database </summary>
 /// <param name="AggrInfo">Partially built item aggregation object</param>
 /// <param name="CountInfo">Datatable from database calls with page count, item count, and title count ( from either SobekCM_Get_Item_Aggregation or SobekCM_Get_All_Groups )</param>
 private static void add_counts(Complete_Item_Aggregation AggrInfo, DataTable CountInfo)
 {
     if (CountInfo.Rows.Count > 0)
     {
         AggrInfo.Statistics = new Item_Aggregation_Statistics
         {
             Page_Count = Convert.ToInt32(CountInfo.Rows[0]["Page_Count"]),
             Item_Count = Convert.ToInt32(CountInfo.Rows[0]["Item_Count"]),
             Title_Count = Convert.ToInt32(CountInfo.Rows[0]["Title_Count"])
         };
     }
 }
        /// <summary> Adds the child information to the item aggregation object from the datatable extracted from the database </summary>
        /// <param name="AggrInfo">Partially built item aggregation object</param>
        /// <param name="ChildInfo">Datatable from database calls with child item aggregation information ( either SobekCM_Get_Item_Aggregation or SobekCM_Get_All_Groups )</param>
        private static void add_children(Complete_Item_Aggregation AggrInfo, DataTable ChildInfo)
        {
            if (ChildInfo.Rows.Count == 0)
                return;

            string childTypes = String.Empty;

            // Build a dictionary of nodes while building this tree
            Dictionary<string, Item_Aggregation_Related_Aggregations> nodes = new Dictionary<string, Item_Aggregation_Related_Aggregations>(ChildInfo.Rows.Count);

            // Step through each row of children
            foreach (DataRow thisRow in ChildInfo.Rows)
            {
                // pull some of the basic data out
                int hierarchyLevel = Convert.ToInt16(thisRow[5]);
                string code = thisRow[0].ToString().ToLower();
                string parentCode = thisRow[1].ToString().ToLower();

                // If this does not already exist, create it
                if (!nodes.ContainsKey(code))
                {
                    // Create the object
                    Item_Aggregation_Related_Aggregations childObject = new Item_Aggregation_Related_Aggregations(code, thisRow[2].ToString(), thisRow[4].ToString(), Convert.ToBoolean(thisRow[6]), Convert.ToBoolean(thisRow[7]));

                    // Add this object to the node dictionary
                    nodes.Add(code, childObject);

                    // If this is not ALL, no need to add the full hierarchy
                    if ((AggrInfo.Code == "all") || (hierarchyLevel == -1))
                    {
                        // Check for parent in the node list
                        if ((parentCode.Length > 0) && (AggrInfo.Code != parentCode) && (nodes.ContainsKey(parentCode)))
                        {
                            nodes[parentCode].Add_Child_Aggregation(childObject);
                        }
                    }

                    // If this is the first hierarchy, add to the main item aggregation object
                    if (hierarchyLevel == -1)
                    {
                        AggrInfo.Add_Child_Aggregation(childObject);

                        // If this is active and not hidden, check the type and save to list
                        if ((!childObject.Hidden) && (childObject.Active))
                        {
                            if (childTypes.Length == 0)
                                childTypes = childObject.Type + "s";
                            else if (childTypes != childObject.Type)
                                childTypes = "SubCollections";
                        }
                    }
                }
            }

            // Save the type for the child collections
            AggrInfo.Child_Types = childTypes;
        }
        /// <summary> Method gets the HOME PAGE html for the appropriate UI settings </summary>
        /// <param name="CompAggr"> Complete item aggregation object </param>
        /// <param name = "Language"> Current language of the user interface </param>
        /// <param name = "Tracer">Trace object keeps a list of each method executed and important milestones in rendering</param>
        /// <returns>Home page HTML</returns>
        private static HTML_Based_Content Get_Home_HTML(Complete_Item_Aggregation CompAggr, Web_Language_Enum Language, Custom_Tracer Tracer)
        {
            if (Tracer != null)
            {
                Tracer.Add_Trace("Item_Aggregation_Utilities.Get_Home_HTML", "Reading home text source file");
            }

            string homeFileSource = "";
            // Get the home file source
            if(CompAggr.Home_Page_File(Language) != null)
               homeFileSource = Path.Combine(Engine_ApplicationCache_Gateway.Settings.Servers.Base_Design_Location, CompAggr.ObjDirectory, CompAggr.Home_Page_File(Language).Source);

            // If no home file source even found, return a message to that affect
            if (homeFileSource.Length == 0)
            {
                return new HTML_Based_Content("<div class=\"error_div\">NO HOME PAGE SOURCE FILE FOUND</div>", null, homeFileSource);
            }

            // Do the rest in a try/catch
            try
            {
                // Does the file exist?
                if (!File.Exists(homeFileSource))
                {
                    return new HTML_Based_Content("<div class=\"error_div\">HOME PAGE SOURCE FILE '" + homeFileSource + "' DOES NOT EXIST.</div>", null, homeFileSource);
                }

                HTML_Based_Content content = HTML_Based_Content_Reader.Read_HTML_File(homeFileSource, true, Tracer);
                content.Source = homeFileSource;

                return content;
            }
            catch (Exception ee)
            {
                return new HTML_Based_Content("<div class=\"error_div\">EXCEPTION CAUGHT WHILE TRYING TO READ THE HOME PAGE SOURCE FILE '" + homeFileSource + "'.<br /><br />ERROR: " + ee.Message + "</div>", null, homeFileSource);
            }
        }
        private static void read_browse(bool Browse, XmlNodeReader NodeReader, Complete_Item_Aggregation HierarchyObject)
        {
            // Create a new browse/info object
            Complete_Item_Aggregation_Child_Page newBrowse = new Complete_Item_Aggregation_Child_Page
                                {
                                    Browse_Type = Item_Aggregation_Child_Visibility_Enum.Main_Menu,
                                    Source_Data_Type = Item_Aggregation_Child_Source_Data_Enum.Static_HTML
                                };

            bool isDefault = false;

            // Determine which XML node name to look for and set browse v. info
            string lastName = "HI:BROWSE";
            if (!Browse)
            {
                lastName = "HI:INFO";
                newBrowse.Browse_Type = Item_Aggregation_Child_Visibility_Enum.None;
            }

            // Check for the attributes
            if (NodeReader.HasAttributes)
            {
                if (NodeReader.MoveToAttribute("location"))
                {
                    if (NodeReader.Value == "BROWSEBY")
                        newBrowse.Browse_Type = Item_Aggregation_Child_Visibility_Enum.Metadata_Browse_By;
                }
                if (NodeReader.MoveToAttribute("default"))
                {
                    if (NodeReader.Value == "DEFAULT")
                        isDefault = true;
                }
                if (NodeReader.MoveToAttribute("visibility"))
                {
                    switch (NodeReader.Value)
                    {
                        case "NONE":
                            newBrowse.Browse_Type = Item_Aggregation_Child_Visibility_Enum.None;
                            break;

                        case "MAIN_MENU":
                            newBrowse.Browse_Type = Item_Aggregation_Child_Visibility_Enum.Main_Menu;
                            break;

                        case "BROWSEBY":
                            newBrowse.Browse_Type = Item_Aggregation_Child_Visibility_Enum.Metadata_Browse_By;
                            break;
                    }
                }
                if (NodeReader.MoveToAttribute("parent"))
                {
                    newBrowse.Parent_Code = NodeReader.Value;
                }
            }

            // Step through the XML and build this browse/info object
            while (NodeReader.Read())
            {
                // If this is the beginning tag for an element, assign the next values accordingly
                if (NodeReader.NodeType == XmlNodeType.Element)
                {
                    // Get the node name, trimmed and to upper
                    string nodeName = NodeReader.Name.Trim().ToUpper();

                    // switch the rest based on the tag name
                    switch (nodeName)
                    {
                        case "HI:METADATA":
                            NodeReader.Read();
                            newBrowse.Code = NodeReader.Value.ToLower();
                            newBrowse.Source_Data_Type = Item_Aggregation_Child_Source_Data_Enum.Database_Table;
                            break;

                        case "HI:CODE":
                            NodeReader.Read();
                            newBrowse.Code = NodeReader.Value.ToLower();
                            break;

                        case "HI:TITLE":
                            // Look for a language attached to this title
                            string titleLanguage = String.Empty;
                            if ((NodeReader.HasAttributes) && ( NodeReader.MoveToAttribute("lang")))
                            {
                                titleLanguage = NodeReader.GetAttribute("lang");
                            }

                            // read and save the title
                            NodeReader.Read();
                            newBrowse.Add_Label( NodeReader.Value, Web_Language_Enum_Converter.Code_To_Enum(titleLanguage));
                            break;

                        case "HI:BODY":
                            // Look for a language attached to this title
                            string bodyLanguage = String.Empty;
                            if ((NodeReader.HasAttributes) && (NodeReader.MoveToAttribute("lang")))
                            {
                                bodyLanguage = NodeReader.GetAttribute("lang");
                            }

                            // read and save the title
                            NodeReader.Read();
                            string bodySource = NodeReader.Value;
                            newBrowse.Add_Static_HTML_Source(bodySource, Web_Language_Enum_Converter.Code_To_Enum(bodyLanguage));
                            break;
                    }
                }

                if (NodeReader.NodeType == XmlNodeType.EndElement)
                {
                    if (NodeReader.Name.Trim().ToUpper() == lastName )
                    {
                        // Don't add ALL or NEW here
                        if ((String.Compare(newBrowse.Code, "all", StringComparison.InvariantCultureIgnoreCase) != 0) && (String.Compare(newBrowse.Code, "new", StringComparison.InvariantCultureIgnoreCase) != 0))
                        {
                            HierarchyObject.Add_Child_Page(newBrowse);
                            //HierarchyObject.Add

                            // If this set the default browse by save that information
                            if ((newBrowse.Browse_Type == Item_Aggregation_Child_Visibility_Enum.Metadata_Browse_By) && (isDefault))
                            {
                                HierarchyObject.Default_BrowseBy = newBrowse.Code;
                            }
                        }

                        return;
                    }
                }
            }
        }
        /// <summary> Performs a search ( or retrieves the search results from the cache ) and outputs the results and search url used  </summary>
        /// <param name="Current_Mode"> Mode / navigation information for the current request</param>
        /// <param name="Aggregation_Object"> Object for the current aggregation object, against which this search is performed </param>
        /// <param name="Tracer"> Trace object keeps a list of each method executed and important milestones in rendering </param>
        /// <param name="Complete_Result_Set_Info"> [OUT] Information about the entire set of results </param>
        /// <param name="Paged_Results"> [OUT] List of search results for the requested page of results </param>
        public ResultsEndpointErrorEnum Get_Search_Results(Results_Arguments Current_Mode,
                                       Complete_Item_Aggregation Aggregation_Object, 
                                       Custom_Tracer Tracer,
                                       out Search_Results_Statistics Complete_Result_Set_Info,
                                       out List<iSearch_Title_Result> Paged_Results)
        {
            Tracer.Add_Trace("SobekCM_Assistant.Get_Search_Results", String.Empty);

            // Set output initially to null
            Paged_Results = null;
            Complete_Result_Set_Info = null;

            // Get the sort
            int sort = Current_Mode.Sort.HasValue ? Math.Max(Current_Mode.Sort.Value, ((ushort)1)) : 0;
            if ((sort != 0) && (sort != 1) && (sort != 2) && (sort != 10) && (sort != 11))
                sort = 0;

            // If there was no search, it is a browse
            if (String.IsNullOrEmpty(Current_Mode.Search_String))
            {
                // Get the page count in the results
                int current_page_index = Current_Mode.Page;

                // Set the flags for how much data is needed.  (i.e., do we need to pull ANYTHING?  or
                // perhaps just the next page of results ( as opposed to pulling facets again).
                bool need_browse_statistics = true;
                bool need_paged_results = true;
                if (Current_Mode.Use_Cache)
                {
                    // Look to see if the browse statistics are available on any cache for this browse
                    Complete_Result_Set_Info = CachedDataManager.Retrieve_Browse_Result_Statistics(Aggregation_Object.Code, "all", Tracer);
                    if (Complete_Result_Set_Info != null)
                        need_browse_statistics = false;

                    // Look to see if the paged results are available on any cache..
                    Paged_Results = CachedDataManager.Retrieve_Browse_Results(Aggregation_Object.Code, "all", current_page_index, sort, Tracer);
                    if (Paged_Results != null)
                        need_paged_results = false;
                }

                // Was a copy found in the cache?
                if ((!need_browse_statistics) && (!need_paged_results))
                {
                    if (Tracer != null)
                    {
                        Tracer.Add_Trace("SobekCM_Assistant.Get_Browse_Info", "Browse statistics and paged results retrieved from cache");
                    }
                }
                else
                {
                    if (Tracer != null)
                    {
                        Tracer.Add_Trace("SobekCM_Assistant.Get_Browse_Info", "Building results information");
                    }

                    // Try to pull more than one page, so we can cache the next page or so
                    List<List<iSearch_Title_Result>> pagesOfResults;

                    // Get from the hierarchy object
                        Multiple_Paged_Results_Args returnArgs = Item_Aggregation_Utilities.Gat_All_Browse(Aggregation_Object, current_page_index, sort, (int) Current_Mode.ResultsPerPage, Current_Mode.Use_Cache, need_browse_statistics, Tracer);
                        if (need_browse_statistics)
                        {
                            Complete_Result_Set_Info = returnArgs.Statistics;
                            foreach (Search_Facet_Collection thisFacet in Complete_Result_Set_Info.Facet_Collections)
                            {
                                Metadata_Search_Field field = Engine_ApplicationCache_Gateway.Settings.Metadata_Search_Field_By_ID(thisFacet.MetadataTypeID);
                                thisFacet.MetadataTerm = field.Facet_Term;
                            }
                        }
                        pagesOfResults = returnArgs.Paged_Results;
                        if ((pagesOfResults != null) && (pagesOfResults.Count > 0))
                            Paged_Results = pagesOfResults[0];

                    // Save the overall result set statistics to the cache if something was pulled
                    if (Current_Mode.Use_Cache)
                    {
                        if ((need_browse_statistics) && (Complete_Result_Set_Info != null))
                        {
                            CachedDataManager.Store_Browse_Result_Statistics(Aggregation_Object.Code, "all", Complete_Result_Set_Info, Tracer);
                        }

                        // Save the overall result set statistics to the cache if something was pulled
                        if ((need_paged_results) && (Paged_Results != null))
                        {
                            CachedDataManager.Store_Browse_Results(Aggregation_Object.Code, "all", current_page_index, sort, pagesOfResults, Tracer);
                        }
                    }
                }

                return ResultsEndpointErrorEnum.NONE;
            }

            // Depending on type of search, either go to database or Greenstone
            if (Current_Mode.Search_Type == Search_Type_Enum.Map)
            {
                try
                {
                    double lat1 = 1000;
                    double long1 = 1000;
                    double lat2 = 1000;
                    double long2 = 1000;
                    string[] terms = Current_Mode.Coordinates.Split(",".ToCharArray());
                    if (terms.Length < 4)
                    {
                        lat1 = Convert.ToDouble(terms[0]);
                        lat2 = lat1;
                        long1 = Convert.ToDouble(terms[1]);
                        long2 = long1;
                    }
                    if (terms.Length >= 4)
                    {
                        if (terms[0].Length > 0)
                            lat1 = Convert.ToDouble(terms[0]);
                        if (terms[1].Length > 0)
                            long1 = Convert.ToDouble(terms[1]);
                        if (terms[2].Length > 0)
                            lat2 = Convert.ToDouble(terms[2]);
                        if (terms[3].Length > 0)
                            long2 = Convert.ToDouble(terms[3]);
                    }

                    // If just the first point is valid, use that
                    if ((lat2 == 1000) || (long2 == 1000))
                    {
                        lat2 = lat1;
                        long2 = long1;
                    }

                    // If just the second point is valid, use that
                    if ((lat1 == 1000) || (long1 == 1000))
                    {
                        lat1 = lat2;
                        long1 = long2;
                    }

                    // Perform the search against the database
                    try
                    {
                        // Get the page count in the results
                        int current_page_index = Current_Mode.Page;

                        // Try to pull more than one page, so we can cache the next page or so
                        Multiple_Paged_Results_Args returnArgs = Engine_Database.Get_Items_By_Coordinates(Current_Mode.Aggregation, lat1, long1, lat2, long2, false, 20, current_page_index, sort, false, new List<short>(), true, Tracer);
                        List<List<iSearch_Title_Result>> pagesOfResults = returnArgs.Paged_Results;
                        Complete_Result_Set_Info = returnArgs.Statistics;

                        if ((pagesOfResults != null) && (pagesOfResults.Count > 0))
                            Paged_Results = pagesOfResults[0];
                    }
                    catch (Exception ee)
                    {
                        // Next, show the message to the user
                        Tracer.Add_Trace("ResultServices.Get_Search_Results", "Exception caught while performing database coordinate search");
                        Tracer.Add_Trace("ResultServices.Get_Search_Results", "Exception: " + ee.Message);
                        Tracer.Add_Trace("ResultServices.Get_Search_Results", ee.StackTrace);

                        return ee.Message.ToUpper().IndexOf("TIMEOUT") >= 0 ? ResultsEndpointErrorEnum.Database_Timeout_Exception : ResultsEndpointErrorEnum.Database_Exception;
                    }
                }
                catch (Exception ee )
                {
                    // Next, show the message to the user
                    Tracer.Add_Trace("ResultServices.Get_Search_Results", "Exception: " + ee.Message);
                    Tracer.Add_Trace("ResultServices.Get_Search_Results", ee.StackTrace);
                    return ResultsEndpointErrorEnum.Unknown;
                }
            }
            else
            {
                List<string> terms = new List<string>();
                List<string> web_fields = new List<string>();

                // Split the terms correctly ( only use the database stop words for the split if this will go to the database ultimately)
                if ((Current_Mode.Search_Type == Search_Type_Enum.Full_Text) || (Current_Mode.Search_Fields.IndexOf("TX") >= 0))
                {
                    Split_Clean_Search_Terms_Fields(Current_Mode.Search_String, Current_Mode.Search_Fields, Current_Mode.Search_Type, terms, web_fields, null, Current_Mode.Search_Precision, ',');
                }
                else
                {
                    Split_Clean_Search_Terms_Fields(Current_Mode.Search_String, Current_Mode.Search_Fields, Current_Mode.Search_Type, terms, web_fields, Engine_ApplicationCache_Gateway.StopWords, Current_Mode.Search_Precision, ',');
                }

                // Get the count that will be used
                int actualCount = Math.Min(terms.Count, web_fields.Count);

                // Determine if this is a special search type which returns more rows and is not cached.
                // This is used to return the results as XML and DATASET
                int results_per_page = (int) Current_Mode.ResultsPerPage;

                // Determine if a date range was provided
                long date1 = -1;
                long date2 = -1;
                if (Current_Mode.DateRange_Date1.HasValue)
                {
                    date1 = Current_Mode.DateRange_Date1.Value;
                    if (Current_Mode.DateRange_Date2.HasValue)
                    {
                        if (Current_Mode.DateRange_Date2.Value >= Current_Mode.DateRange_Date1.Value)
                            date2 = Current_Mode.DateRange_Date2.Value;
                        else
                        {
                            date1 = Current_Mode.DateRange_Date2.Value;
                            date2 = Current_Mode.DateRange_Date1.Value;
                        }
                    }
                    else
                    {
                        date2 = date1;
                    }
                }
                if (date1 < 0)
                {
                    if ((Current_Mode.DateRange_Year1.HasValue) && (Current_Mode.DateRange_Year1.Value > 0))
                    {
                        DateTime startDate = new DateTime(Current_Mode.DateRange_Year1.Value, 1, 1);
                        TimeSpan timeElapsed = startDate.Subtract(new DateTime(1, 1, 1));
                        date1 = (long)timeElapsed.TotalDays;
                        if ((Current_Mode.DateRange_Year2.HasValue) && (Current_Mode.DateRange_Year2.Value > 0))
                        {
                            startDate = new DateTime(Current_Mode.DateRange_Year2.Value, 12, 31);
                            timeElapsed = startDate.Subtract(new DateTime(1, 1, 1));
                            date2 = (long)timeElapsed.TotalDays;
                        }
                        else
                        {
                            startDate = new DateTime(Current_Mode.DateRange_Year1.Value, 12, 31);
                            timeElapsed = startDate.Subtract(new DateTime(1, 1, 1));
                            date2 = (long)timeElapsed.TotalDays;
                        }
                    }
                }

                // Set the flags for how much data is needed.  (i.e., do we need to pull ANYTHING?  or
                // perhaps just the next page of results ( as opposed to pulling facets again).
                bool need_search_statistics = true;
                bool need_paged_results = true;
                if (Current_Mode.Use_Cache)
                {
                    // Look to see if the search statistics are available on any cache..
                    Complete_Result_Set_Info = CachedDataManager.Retrieve_Search_Result_Statistics(Current_Mode, actualCount, web_fields, terms, date1, date2, Tracer);
                    if (Complete_Result_Set_Info != null)
                        need_search_statistics = false;

                    // Look to see if the paged results are available on any cache..
                    Paged_Results = CachedDataManager.Retrieve_Search_Results(Current_Mode, sort, actualCount, web_fields, terms, date1, date2, Tracer);
                    if (Paged_Results != null)
                        need_paged_results = false;
                }

                // If both were retrieved, do nothing else
                if ((need_paged_results) || (need_search_statistics))
                {
                    // Should this pull the search from the database, or from greenstone?
                    if ((Current_Mode.Search_Type == Search_Type_Enum.Full_Text) || (Current_Mode.Search_Fields.IndexOf("TX") >= 0))
                    {
                        try
                        {
                            // Get the page count in the results
                            int current_page_index = Current_Mode.Page;

                            // Perform the search against greenstone
                            Search_Results_Statistics recomputed_search_statistics;
                            Perform_Solr_Search(Tracer, terms, web_fields, actualCount, Current_Mode.Aggregation, current_page_index, sort, results_per_page, out recomputed_search_statistics, out Paged_Results);
                            if (need_search_statistics)
                            {
                                Complete_Result_Set_Info = recomputed_search_statistics;

                                foreach (Search_Facet_Collection thisFacet in Complete_Result_Set_Info.Facet_Collections)
                                {
                                    Metadata_Search_Field field = Engine_ApplicationCache_Gateway.Settings.Metadata_Search_Field_By_ID(thisFacet.MetadataTypeID);
                                    thisFacet.MetadataTerm = field.Facet_Term;
                                }
                            }
                        }
                        catch (Exception ee)
                        {
                            // Next, show the message to the user
                            Tracer.Add_Trace("ResultServices.Get_Search_Results", "Exception caught while performing solr/lucene search");
                            Tracer.Add_Trace("ResultServices.Get_Search_Results", "Exception: " + ee.Message);
                            Tracer.Add_Trace("ResultServices.Get_Search_Results", ee.StackTrace);
                            return ResultsEndpointErrorEnum.Unknown;
                        }

                        // If this was a special search, don't cache this
                        if (!Current_Mode.Use_Cache)
                        {
                            // Cache the search statistics, if it was needed
                            if ((need_search_statistics) && (Complete_Result_Set_Info != null))
                            {
                                CachedDataManager.Store_Search_Result_Statistics(Current_Mode, actualCount, web_fields, terms, date1, date2, Complete_Result_Set_Info, Tracer);
                            }

                            // Cache the search results
                            if ((need_paged_results) && (Paged_Results != null))
                            {
                                CachedDataManager.Store_Search_Results(Current_Mode, sort, actualCount, web_fields, terms, date1, date2, Paged_Results, Tracer);
                            }
                        }
                    }
                    else
                    {
                        // Try to pull more than one page, so we can cache the next page or so
                        List<List<iSearch_Title_Result>> pagesOfResults;

                        // Perform the search against the database
                        try
                        {
                            Search_Results_Statistics recomputed_search_statistics;
                            Perform_Database_Search(Tracer, terms, web_fields, date1, date2, actualCount, Current_Mode, sort, Aggregation_Object, results_per_page, Current_Mode.Use_Cache, out recomputed_search_statistics, out pagesOfResults, need_search_statistics);
                            if (need_search_statistics)
                            {
                                Complete_Result_Set_Info = recomputed_search_statistics;

                                foreach (Search_Facet_Collection thisFacet in Complete_Result_Set_Info.Facet_Collections)
                                {
                                    Metadata_Search_Field field = Engine_ApplicationCache_Gateway.Settings.Metadata_Search_Field_By_ID(thisFacet.MetadataTypeID);
                                    thisFacet.MetadataTerm = field.Facet_Term;
                                }
                            }

                            if ((pagesOfResults != null) && (pagesOfResults.Count > 0))
                                Paged_Results = pagesOfResults[0];
                        }
                        catch (Exception ee)
                        {
                            // Next, show the message to the user
                            Tracer.Add_Trace("ResultServices.Get_Search_Results", "Exception caught while performing database coordinate search");
                            Tracer.Add_Trace("ResultServices.Get_Search_Results", "Exception: " + ee.Message);
                            Tracer.Add_Trace("ResultServices.Get_Search_Results", ee.StackTrace);

                            return ee.Message.ToUpper().IndexOf("TIMEOUT") >= 0 ? ResultsEndpointErrorEnum.Database_Timeout_Exception : ResultsEndpointErrorEnum.Database_Exception;
                        }

                        // If this was a special search, don't cache this
                        if (!Current_Mode.Use_Cache)
                        {
                            // Cache the search statistics, if it was needed
                            if ((need_search_statistics) && (Complete_Result_Set_Info != null))
                            {
                                CachedDataManager.Store_Search_Result_Statistics(Current_Mode, actualCount, web_fields, terms, date1, date2, Complete_Result_Set_Info, Tracer);
                            }

                            // Cache the search results
                            if ((need_paged_results) && (pagesOfResults != null))
                            {
                                CachedDataManager.Store_Search_Results(Current_Mode, sort, actualCount, web_fields, terms, date1, date2, pagesOfResults, Tracer);
                            }
                        }
                    }
                }
            }

            return ResultsEndpointErrorEnum.NONE;
        }
        private static void read_directives(XmlNodeReader NodeReader, Complete_Item_Aggregation HierarchyObject, string Directory)
        {
            string directiveCode = String.Empty;
            string directiveFile = String.Empty;
            while (NodeReader.Read())
            {
                // If this is the beginning tag for an element, assign the next values accordingly
                if (NodeReader.NodeType == XmlNodeType.Element)
                {
                    // Get the node name, trimmed and to upper
                    string nodeName = NodeReader.Name.Trim().ToUpper();

                    // switch the rest based on the tag name
                    switch (nodeName)
                    {
                        case "HI:CODE":
                            NodeReader.Read();
                            directiveCode = NodeReader.Value.Replace("<%","").Replace("%>","");
                            break;

                        case "HI:SOURCE":
                            NodeReader.Read();
                            directiveFile = NodeReader.Value;
                            break;
                    }
                }

                if (NodeReader.NodeType == XmlNodeType.EndElement)
                {
                    if (NodeReader.Name.Trim().ToUpper() == "HI:DIRECTIVE" )
                    {
                        if ((directiveCode.Length > 0) && (directiveFile.Length > 0))
                        {
                            string contents;
                            try
                            {
                                // Look for the matching file
                                if (File.Exists(Directory + "\\" + directiveFile))
                                {
                                    StreamReader reader = new StreamReader(Directory + "\\" + directiveFile);
                                    contents = reader.ReadToEnd();
                                    reader.Close();

                                }
                                else
                                {
                                    contents = "MISSING DIRECTIVE SOURCE FILE ('" + directiveFile + "')";
                                }
                            }
                            catch
                            {
                                contents = "EXCEPTION WHILE READING DIRECTIVE SOURCE FILE ('" + directiveFile + "')";
                            }

                            // Create the custom derivative object
                            Item_Aggregation_Custom_Directive newDirective = new Item_Aggregation_Custom_Directive(directiveCode, directiveFile, contents);
                            if ( HierarchyObject.Custom_Directives == null )
                                HierarchyObject.Custom_Directives = new Dictionary<string, Item_Aggregation_Custom_Directive>()
                            ;
                            HierarchyObject.Custom_Directives["<%" + directiveCode.ToUpper() + "%>"] = newDirective;

                        }
                        directiveCode = String.Empty;
                        directiveFile = String.Empty;
                    }

                    if (NodeReader.Name.Trim().ToUpper() == "HI:DIRECTIVES")
                    {
                        // Done with all the directives so return
                        return;
                    }
                }
            }
        }
        /// <summary> Get the language specific item aggregation, from the complete item aggregation object </summary>
        /// <param name="CompAggr"> Copmlete item aggregation object </param>
        /// <param name="RequestedLanguage"> Language version requested </param>
        /// <param name="Tracer"></param>
        /// <returns> The language-specific aggregation, built from the complete aggregation object, or NULL if an error occurred </returns>
        public static Item_Aggregation Get_Item_Aggregation(Complete_Item_Aggregation CompAggr, Web_Language_Enum RequestedLanguage, Custom_Tracer Tracer)
        {
            // If the complete aggregation was null, return null
            if (CompAggr == null)
            {
                if (Tracer != null)
                {
                    Tracer.Add_Trace("Item_Aggregation_Utilities.Get_Item_Aggregation", "Complete item aggregation was NULL.. aborting and returning NULL");
                }

                return null;
            }

            if (Tracer != null)
            {
                Tracer.Add_Trace("Item_Aggregation_Utilities.Get_Item_Aggregation", "Building language-specific item aggregation from the complete object");
            }

            // Build the item aggregation
            Item_Aggregation returnValue = new Item_Aggregation(RequestedLanguage, CompAggr.ID, CompAggr.Code)
            {
                Active = CompAggr.Active,
                BannerImage = CompAggr.Banner_Image(RequestedLanguage, null ),
                Child_Types = CompAggr.Child_Types,
                Contact_Email = CompAggr.Contact_Email,
                ContactForm = CompAggr.ContactForm,
                CSS_File = CompAggr.CSS_File,
                Default_BrowseBy = CompAggr.Default_BrowseBy,
                Default_Result_View = CompAggr.Default_Result_View,
                Default_Skin = CompAggr.Default_Skin,
                Description = CompAggr.Description,
                Display_Options = CompAggr.Display_Options,
                FrontBannerObj = CompAggr.Front_Banner_Image(RequestedLanguage),
                Hidden = CompAggr.Hidden,
                Last_Item_Added = CompAggr.Last_Item_Added,
                Name = CompAggr.Name,
                Rotating_Highlights = CompAggr.Rotating_Highlights,
                ShortName = CompAggr.ShortName,
                Statistics = CompAggr.Statistics,
                Type = CompAggr.Type
            };

            // Copy the map search and browse information
            if (CompAggr.Map_Search_Display != null) returnValue.Map_Search_Display = CompAggr.Map_Search_Display.Copy();
            if (CompAggr.Map_Browse_Display != null) returnValue.Map_Browse_Display = CompAggr.Map_Browse_Display.Copy();

            // Copy any children aggregations over
            if (CompAggr.Active_Children_Count > 0)
            {
                if (Tracer != null)
                {
                    Tracer.Add_Trace("Item_Aggregation_Utilities.Get_Item_Aggregation", "...Copying children objects");
                }

                returnValue.Children = new List<Item_Aggregation_Related_Aggregations>();
                foreach (Item_Aggregation_Related_Aggregations thisAggr in CompAggr.Children)
                {
                    returnValue.Children.Add(thisAggr);
                }
            }

            // Copy any parent aggregations over
            if (CompAggr.Parent_Count > 0)
            {
                if (Tracer != null)
                {
                    Tracer.Add_Trace("Item_Aggregation_Utilities.Get_Item_Aggregation", "...Copying parent objects");
                }

                returnValue.Parents = new List<Item_Aggregation_Related_Aggregations>();
                foreach (Item_Aggregation_Related_Aggregations thisAggr in CompAggr.Parents)
                {
                    returnValue.Parents.Add(thisAggr);
                }
            }

            // Copy all the facet information over
            if (Tracer != null)
            {
                Tracer.Add_Trace("Item_Aggregation_Utilities.Get_Item_Aggregation", "...Copying facets");
            }
            foreach (short thisFacet in CompAggr.Facets)
            {
                returnValue.Facets.Add(thisFacet);
            }

            // Copy over all the results views
            if (Tracer != null)
            {
                Tracer.Add_Trace("Item_Aggregation_Utilities.Get_Item_Aggregation", "...Copying result views");
            }
            foreach (Result_Display_Type_Enum display in CompAggr.Result_Views)
            {
                returnValue.Result_Views.Add(display);
            }

            // Copy all the views and searches over
            if (Tracer != null)
            {
                Tracer.Add_Trace("Item_Aggregation_Utilities.Get_Item_Aggregation", "...Copying views and searches");
            }
            if (CompAggr.Views_And_Searches != null)
            {
                foreach (Item_Aggregation_Views_Searches_Enum viewsSearches in CompAggr.Views_And_Searches)
                {
                    returnValue.Views_And_Searches.Add(viewsSearches);
                }
            }

            // Copy all the setting values
            if ((CompAggr.Settings != null) && (CompAggr.Settings.Count > 0))
            {
                foreach (StringKeyValuePair setting in CompAggr.Settings)
                {
                    returnValue.Add_Setting(setting.Key, setting.Value );
                }
            }

            // Copy over any web skin limitations
            if ((CompAggr.Web_Skins != null) && (CompAggr.Web_Skins.Count > 0))
            {
                if (Tracer != null)
                {
                    Tracer.Add_Trace("Item_Aggregation_Utilities.Get_Item_Aggregation", "...Copying web skins");
                }

                returnValue.Web_Skins = new List<string>();
                foreach (string thisSkin in CompAggr.Web_Skins)
                {
                    returnValue.Web_Skins.Add(thisSkin);
                }
            }

            // Language-specific (and simplified) metadata type info
            if (Tracer != null)
            {
                Tracer.Add_Trace("Item_Aggregation_Utilities.Get_Item_Aggregation", "...Copying search anbd browseable fields");
            }
            foreach (Complete_Item_Aggregation_Metadata_Type thisAdvSearchField in CompAggr.Search_Fields)
            {
                returnValue.Search_Fields.Add(new Item_Aggregation_Metadata_Type(thisAdvSearchField.DisplayTerm, thisAdvSearchField.SobekCode));
            }
            foreach (Complete_Item_Aggregation_Metadata_Type thisAdvSearchField in CompAggr.Browseable_Fields)
            {
                returnValue.Browseable_Fields.Add(new Item_Aggregation_Metadata_Type(thisAdvSearchField.DisplayTerm, thisAdvSearchField.SobekCode));
            }

            // Language-specific (and simplified) child pages information
            if ((CompAggr.Child_Pages != null) && (CompAggr.Child_Pages.Count > 0))
            {
                if (Tracer != null)
                {
                    Tracer.Add_Trace("Item_Aggregation_Utilities.Get_Item_Aggregation", "...Copying child pages");
                }

                returnValue.Child_Pages = new List<Item_Aggregation_Child_Page>();
                foreach (Complete_Item_Aggregation_Child_Page fullPage in CompAggr.Child_Pages)
                {
                    Item_Aggregation_Child_Page newPage = new Item_Aggregation_Child_Page
                    {
                        Browse_Type = fullPage.Browse_Type,
                        Code = fullPage.Code,
                        Parent_Code = fullPage.Parent_Code,
                        Source_Data_Type = fullPage.Source_Data_Type
                    };

                    string label = fullPage.Get_Label(RequestedLanguage);
                    if (!String.IsNullOrEmpty(label))
                        newPage.Label = label;

                    string source = fullPage.Get_Static_HTML_Source(RequestedLanguage);
                    if (!String.IsNullOrEmpty(label))
                        newPage.Source = source;

                    returnValue.Child_Pages.Add(newPage);
                }
            }

            // Language-specific (and simplified) highlight information
            if ((CompAggr.Highlights != null) && (CompAggr.Highlights.Count > 0))
            {
                if (Tracer != null)
                {
                    Tracer.Add_Trace("Item_Aggregation_Utilities.Get_Item_Aggregation", "...Copying relevant highlights");
                }

                returnValue.Highlights = new List<Item_Aggregation_Highlights>();
                int day_integer = DateTime.Now.DayOfYear + 1;
                int highlight_to_use = day_integer % CompAggr.Highlights.Count;

                // If this is for rotating highlights, show up to eight
                if ((CompAggr.Rotating_Highlights.HasValue ) && ( CompAggr.Rotating_Highlights.Value ))
                {
                    // Copy over just the eight highlights we should use
                    int number = Math.Min(8, CompAggr.Highlights.Count);
                    for (int i = 0; i < number; i++)
                    {
                        Complete_Item_Aggregation_Highlights thisHighlight = CompAggr.Highlights[highlight_to_use];

                        Item_Aggregation_Highlights newHighlight = new Item_Aggregation_Highlights
                        {
                            Image = thisHighlight.Image,
                            Link = thisHighlight.Link
                        };

                        string text = thisHighlight.Get_Text(RequestedLanguage);
                        if (!String.IsNullOrEmpty(text))
                            newHighlight.Text = text;

                        string tooltip = thisHighlight.Get_Tooltip(RequestedLanguage);
                        if (!String.IsNullOrEmpty(tooltip))
                            newHighlight.Tooltip = tooltip;

                        returnValue.Highlights.Add(newHighlight);

                        highlight_to_use++;
                        if (highlight_to_use >= CompAggr.Highlights.Count)
                            highlight_to_use = 0;
                    }
                }
                else
                {
                    Complete_Item_Aggregation_Highlights thisHighlight = CompAggr.Highlights[highlight_to_use];

                    Item_Aggregation_Highlights newHighlight = new Item_Aggregation_Highlights
                    {
                        Image = thisHighlight.Image,
                        Link = thisHighlight.Link
                    };

                    string text = thisHighlight.Get_Text(RequestedLanguage);
                    if (!String.IsNullOrEmpty(text))
                        newHighlight.Text = text;

                    string tooltip = thisHighlight.Get_Tooltip(RequestedLanguage);
                    if (!String.IsNullOrEmpty(tooltip))
                        newHighlight.Tooltip = tooltip;

                    returnValue.Highlights.Add(newHighlight);
                }
            }

            // Language-specific source page
            if (Tracer != null)
            {
                Tracer.Add_Trace("Item_Aggregation_Utilities.Get_Item_Aggregation", "Getting the home page source");
            }
            returnValue.HomePageSource = String.Empty;
            HTML_Based_Content homeHtml = Get_Home_HTML(CompAggr, RequestedLanguage, null);
            returnValue.HomePageHtml = homeHtml;
            returnValue.Custom_Home_Page = (CompAggr.Home_Page_File(RequestedLanguage) != null) && (CompAggr.Home_Page_File(RequestedLanguage).isCustomHome);

            if (Tracer != null)
            {
                Tracer.Add_Trace("Item_Aggregation_Utilities.Get_Item_Aggregation", "Returning fully built item aggregation object");
            }
            return returnValue;
        }
        private static void read_home(XmlNodeReader NodeReader, Complete_Item_Aggregation HierarchyObject)
        {
            while (NodeReader.Read())
            {
                // If this is the beginning tag for an element, assign the next values accordingly
                if (NodeReader.NodeType == XmlNodeType.Element)
                {
                    // Get the node name, trimmed and to upper
                    string nodeName = NodeReader.Name.Trim().ToUpper();

                    // switch the rest based on the tag name
                    switch (nodeName)
                    {
                        case "HI:BODY":
                            Web_Language_Enum langEnum = Web_Language_Enum.DEFAULT;
                            bool isCustom = false;
                            if ((NodeReader.HasAttributes) && (NodeReader.MoveToAttribute("lang")))
                            {
                                string bodyLanguage = NodeReader.GetAttribute("lang");
                                langEnum = Web_Language_Enum_Converter.Code_To_Enum(bodyLanguage);
                            }
                            if ((NodeReader.HasAttributes) && (NodeReader.MoveToAttribute("isCustom")))
                            {
                                string attribute = NodeReader.GetAttribute("isCustom");
                                if (attribute != null && attribute.ToLower() == "true")
                                    isCustom = true;
                            }

                            NodeReader.Read();
                            HierarchyObject.Add_Home_Page_File(NodeReader.Value, langEnum, isCustom );
                            break;
                    }
                }

                if ((NodeReader.NodeType == XmlNodeType.EndElement) && (NodeReader.Name.Trim().ToUpper() == "HI:HOME"))
                {
                    return;
                }
            }
        }
        /// <summary> Saves the information about this item aggregation to the database </summary>
        /// <param name="ItemAggr"> Item aggregation object with all the information to be saved </param>
        /// <param name="Username"> Name of the user performing this save, for the item aggregation milestones</param>
        /// <param name="Tracer">Trace object keeps a list of each method executed and important milestones in rendering</param>
        /// <returns>TRUE if successful, otherwise FALSE </returns>
        public static bool Save_To_Database(Complete_Item_Aggregation ItemAggr, string Username, Custom_Tracer Tracer)
        {
            // Build the list of language variants
            List<string> languageVariants = new List<string>
            {
                Web_Language_Enum_Converter.Enum_To_Code(Engine_ApplicationCache_Gateway.Settings.System.Default_UI_Language)
            };
            if (ItemAggr.Home_Page_File_Dictionary != null)
            {
                foreach (Web_Language_Enum language in ItemAggr.Home_Page_File_Dictionary.Keys)
                {
                    string code = Web_Language_Enum_Converter.Enum_To_Code(language);
                    if (!languageVariants.Contains(code))
                        languageVariants.Add(code);
                }
            }
            if (ItemAggr.Banner_Dictionary != null)
            {
                foreach (Web_Language_Enum language in ItemAggr.Banner_Dictionary.Keys)
                {
                    string code = Web_Language_Enum_Converter.Enum_To_Code(language);
                    if (!languageVariants.Contains(code))
                        languageVariants.Add(code);
                }
            }
            if (ItemAggr.Child_Pages != null)
            {
                foreach (Complete_Item_Aggregation_Child_Page childPage in ItemAggr.Child_Pages)
                {
                    if (childPage.Label_Dictionary != null)
                    {
                        foreach (Web_Language_Enum language in childPage.Label_Dictionary.Keys)
                        {
                            string code2 = Web_Language_Enum_Converter.Enum_To_Code(language);
                            if (!languageVariants.Contains(code2))
                                languageVariants.Add(code2);
                        }
                    }
                    if (childPage.Source_Dictionary != null)
                    {
                        foreach (Web_Language_Enum language in childPage.Source_Dictionary.Keys)
                        {
                            string code2 = Web_Language_Enum_Converter.Enum_To_Code(language);
                            if (!languageVariants.Contains(code2))
                                languageVariants.Add(code2);
                        }
                    }
                }
            }
            StringBuilder languageVariantsBuilder = new StringBuilder();
            foreach (string language in languageVariants)
            {
                if (language.Length > 0)
                {
                    if (languageVariantsBuilder.Length > 0)
                        languageVariantsBuilder.Append("|" + language);
                    else
                        languageVariantsBuilder.Append(language);
                }
            }

            return Engine_Database.Save_Item_Aggregation(ItemAggr.ID, ItemAggr.Code, ItemAggr.Name, ItemAggr.ShortName,
                ItemAggr.Description, ItemAggr.Thematic_Heading, ItemAggr.Type, ItemAggr.Active, ItemAggr.Hidden,
                ItemAggr.Display_Options, 0, ItemAggr.Map_Search_Beta, 0, ItemAggr.Map_Display_Beta,
                ItemAggr.OAI_Enabled, ItemAggr.OAI_Metadata, ItemAggr.Contact_Email, String.Empty, ItemAggr.External_Link, -1, Username,
                languageVariantsBuilder.ToString(), Tracer);
        }
        private static void read_settings(XmlNodeReader NodeReader, Complete_Item_Aggregation HierarchyObject)
        {
            while (NodeReader.Read())
            {
                // If this is the beginning tag for an element, assign the next values accordingly
                if (NodeReader.NodeType == XmlNodeType.Element)
                {
                    // Get the node name, trimmed and to upper
                    string nodeName = NodeReader.Name.Trim().ToUpper();

                    // switch the rest based on the tag name
                    switch (nodeName)
                    {
                        case "HI:WEBSKINS":
                            NodeReader.Read();
                            string webskins = NodeReader.Value;
                            string[] splitter = webskins.Split(",".ToCharArray());
                            foreach (string thisSplitter in splitter)
                            {
                                if ( thisSplitter.Length > 0 )
                                    HierarchyObject.Add_Web_Skin(thisSplitter.ToLower());
                            }
                            break;

                        case "HI:CSS":
                            NodeReader.Read();
                            HierarchyObject.CSS_File = NodeReader.Value.Trim();
                            break;

                        case "HI:CUSTOMHOME":
                            NodeReader.Read();
                            // No longer do anything with this tag
                            // HierarchyObject.Custom_Home_Page_Source_File = NodeReader.Value.Trim();
                            break;

                        case "HI:FACETS":
                            NodeReader.Read();
                            string facets = NodeReader.Value;
                            string[] splitter2 = facets.Split(",".ToCharArray());
                            HierarchyObject.Clear_Facets();
                            foreach (string thisSplitter2 in splitter2)
                            {
                                    HierarchyObject.Add_Facet(Convert.ToInt16(thisSplitter2));
                            }
                            break;

                        case "HI:MAPSEARCH":
                            if (NodeReader.MoveToAttribute("type"))
                            {
                                switch (NodeReader.GetAttribute("type").ToLower())
                                {
                                    case "extent":
                                    case "computed":
                                        // This should already be set, assuming there were values to be added
                                        if (HierarchyObject.Map_Search_Display == null)
                                        {
                                            HierarchyObject.Map_Search_Display = new Item_Aggregation_Map_Coverage_Info(Item_Aggregation_Map_Coverage_Type_Enum.COMPUTED);
                                        }
                                        break;

                                    case "fixed":
                                        decimal latitude = 999;
                                        decimal longitude = 999;
                                        int zoom = 999;

                                        if (NodeReader.MoveToAttribute("latitude"))
                                        {
                                            Decimal.TryParse(NodeReader.GetAttribute("latitude"), out latitude);
                                        }
                                        if (NodeReader.MoveToAttribute("longitude"))
                                        {
                                            Decimal.TryParse(NodeReader.GetAttribute("longitude"), out longitude);
                                        }
                                        if (NodeReader.MoveToAttribute("zoom"))
                                        {
                                            Int32.TryParse(NodeReader.GetAttribute("zoom"), out zoom);
                                        }

                                        if ((latitude != 999) && (longitude != 999))
                                        {
                                            HierarchyObject.Map_Search_Display = new Item_Aggregation_Map_Coverage_Info(Item_Aggregation_Map_Coverage_Type_Enum.FIXED, zoom, longitude, latitude );
                                        }
                                        break;
                                }
                            }
                            break;

                        case "HI:MAPBROWSE":
                            if (NodeReader.MoveToAttribute("type"))
                            {
                                switch (NodeReader.GetAttribute("type").ToLower())
                                {
                                    case "extent":
                                        HierarchyObject.Map_Browse_Display = new Item_Aggregation_Map_Coverage_Info(Item_Aggregation_Map_Coverage_Type_Enum.EXTENT);
                                        break;

                                    case "computed":
                                        HierarchyObject.Map_Browse_Display = new Item_Aggregation_Map_Coverage_Info(Item_Aggregation_Map_Coverage_Type_Enum.COMPUTED);
                                        break;

                                    case "fixed":
                                        decimal latitude = 999;
                                        decimal longitude = 999;
                                        int zoom = 999;

                                        if (NodeReader.MoveToAttribute("latitude"))
                                        {
                                            Decimal.TryParse(NodeReader.GetAttribute("latitude"), out latitude);
                                        }
                                        if (NodeReader.MoveToAttribute("longitude"))
                                        {
                                            Decimal.TryParse(NodeReader.GetAttribute("longitude"), out longitude);
                                        }
                                        if (NodeReader.MoveToAttribute("zoom"))
                                        {
                                            Int32.TryParse(NodeReader.GetAttribute("zoom"), out zoom);
                                        }

                                        if ((latitude != 999) && (longitude != 999))
                                        {
                                            HierarchyObject.Map_Browse_Display = new Item_Aggregation_Map_Coverage_Info(Item_Aggregation_Map_Coverage_Type_Enum.FIXED, zoom, longitude, latitude);
                                        }
                                        break;
                                }
                            }
                            break;

                    }
                }

                if (NodeReader.NodeType == XmlNodeType.EndElement)
                {
                    if (NodeReader.Name.Trim().ToUpper() == "HI:SETTINGS")
                    {
                        return;
                    }
                }
            }
        }
        /// <summary> Adds the ALL ITEMS and NEW ITEMS browses to the item aggregation, if the display options and last added
        /// item date call for it </summary>
        /// <param name="ThisObject"> Item aggregation to which to add the ALL ITEMS and NEW ITEMS browse</param>
        /// <remarks>This method is always called while building an item aggregation, irregardless of whether there is an
        /// item aggregation configuration XML file or not.</remarks>
        protected static void Add_All_New_Browses(Complete_Item_Aggregation ThisObject)
        {
            // If this is the main home page for this site, do not show ALL since we cannot browse ALL items
            if (!ThisObject.Can_Browse_Items )
                return;

            // If this is in the display options, and the item browses
            if ((ThisObject.Display_Options.Length == 0) || (ThisObject.Display_Options.IndexOf("I") >= 0))
            {
                // Add the ALL browse, if there should be one
                ThisObject.Add_Child_Page(Item_Aggregation_Child_Visibility_Enum.Main_Menu, "all", String.Empty, "All Items");

                // Add the NEW search, if the ALL search exists
                if ((ThisObject.Get_Browse_Info_Object("all") != null) && (ThisObject.Show_New_Item_Browse))
                {
                    ThisObject.Add_Child_Page(Item_Aggregation_Child_Visibility_Enum.Main_Menu, "new", String.Empty, "Recently Added Items");
                }
            }
            else
            {
                // Add the ALL browse as an info
                ThisObject.Add_Child_Page(Item_Aggregation_Child_Visibility_Enum.None, "all", String.Empty, "All Items");
            }
        }
        /// <summary> Compares the two aggregation objects and returns a list of differences for saving as milestones
        /// during aggregation curatorial or administrative work </summary>
        /// <param name="Base"> Base item aggregation </param>
        /// <param name="Compared"> New item aggregation object to compare to the base </param>
        /// <returns> List of changes between the two aggregation objects </returns>
        public static List<string> Compare(Complete_Item_Aggregation Base, Complete_Item_Aggregation Compared )
        {
            // TODO: Facet comparison below needs to look up the name of the facet,
            // TODO: rather than just showing the primary key to the facet

            List<string> changes = new List<string>();

            // code
            if (!Base.Code.Equals(Compared.Code))
            {
                changes.Add("Changed code ( '" + Base.Code + "' --> '" + Compared.Code + "' )");
            }

            // parents
            List<string> base_parents = new List<string>();
            List<string> compared_parents = new List<string>();
            if (Base.Parents != null)
            {
                foreach (Item_Aggregation_Related_Aggregations parentAggr in Base.Parents)
                {
                    // Look in compared for a match
                    if ((Compared.Parents == null) || (Compared.Parents.All(CompareAggr => String.Compare(parentAggr.Code, CompareAggr.Code, StringComparison.InvariantCultureIgnoreCase) != 0)))
                    {
                        base_parents.Add(parentAggr.Code);
                    }

                }
            }
            if (Compared.Parents != null)
            {
                foreach (Item_Aggregation_Related_Aggregations parentAggr in Compared.Parents)
                {
                    // Look in base for a match
                    if ((Base.Parents == null) || (Base.Parents.All(CompareAggr => String.Compare(parentAggr.Code, CompareAggr.Code, StringComparison.InvariantCultureIgnoreCase) != 0)))
                    {
                        compared_parents.Add(parentAggr.Code);
                    }
                }
            }
            if (base_parents.Count > 0)
            {
                if (base_parents.Count == 1)
                {
                    changes.Add("Removed parent " + base_parents[0] );
                }
                else
                {
                    StringBuilder builder = new StringBuilder("Removed parents " + base_parents[0]);
                    for (int i = 1; i < base_parents.Count; i++)
                        builder.Append(", " + base_parents[i]);
                    changes.Add(builder.ToString());
                }
            }
            if (compared_parents.Count > 0)
            {
                if (compared_parents.Count == 1)
                {
                    changes.Add("Added parent " + compared_parents[0]);
                }
                else
                {
                    StringBuilder builder = new StringBuilder("Added parents " + compared_parents[0]);
                    for (int i = 1; i < compared_parents.Count; i++)
                        builder.Append(", " + compared_parents[i]);
                    changes.Add(builder.ToString());
                }
            }

            // name
            if (!Base.Name.Equals(Compared.Name))
            {
                changes.Add("Changed name ( '" + Base.Name + "' --> '" + Compared.Name + "' )");
            }

            // short name
            if (!Base.ShortName.Equals(Compared.ShortName))
            {
                changes.Add("Changed short name ( '" + Base.ShortName + "' --> '" + Compared.ShortName + "' )");
            }

            // description
            if (!Base.Description.Equals(Compared.Description))
            {
                changes.Add("Changed description");
            }

            // type
            if (!Base.Type.Equals(Compared.Type))
            {
                changes.Add("Changed type ( '" + Base.Type + "' --> '" + Compared.Type + "' )");
            }

            // email address
            compare_nullable_strings(Base.Contact_Email, Compared.Contact_Email, "contact email", changes);

            // external link
            compare_nullable_strings(Base.External_Link, Compared.External_Link, "external link", changes);

            // Hidden flag
            if ( Base.Hidden != Compared.Hidden )
            {
                if ( Compared.Hidden )
                    changes.Add("Removed from parent home page");
                else
                    changes.Add("Added to parent home page");
            }

            // active flag
            if (Base.Active != Compared.Active)
            {
                if (Compared.Active)
                    changes.Add("Aggregation activated");
                else
                    changes.Add("Aggregation deactivated");
            }

            // thematic headings
            if ( Base.Thematic_Heading == null )
            {
                if ( Compared.Thematic_Heading != null )
                {
                    changes.Add("Added to thematic heading '" + Compared.Thematic_Heading.Text + "'");
                }
            }
            else
            {
                if ( Compared.Thematic_Heading == null )
                {
                    changes.Add("Removed from thematic heading '" + Base.Thematic_Heading.Text + "'");
                }
                else
                {
                    if (Base.Thematic_Heading.ID != Compared.Thematic_Heading.ID)
                    {
                        changes.Add("Changed thematic heading ( '" + Base.Thematic_Heading.Text + "' --> '" + Compared.Thematic_Heading.Text + "' )");
                    }
                }
            }

            // web skin
            List<string> base_skins = new List<string>();
            List<string> compared_skins = new List<string>();
            if (Base.Web_Skins != null)
            {
                foreach( string thisSkin in Base.Web_Skins)
                {
                    // Look in compared for a match
                    if ((Compared.Web_Skins == null) || (Compared.Web_Skins.All(CompareSkin => String.Compare(thisSkin, CompareSkin, StringComparison.InvariantCultureIgnoreCase) != 0)))
                    {
                        base_skins.Add(thisSkin);
                    }
                }
            }
            if (Compared.Web_Skins != null)
            {
                foreach ( string thisSkin in Compared.Web_Skins)
                {
                    // Look in base for a match
                    if ((Base.Web_Skins == null) || (Base.Web_Skins.All(CompareSkin => String.Compare(thisSkin, CompareSkin, StringComparison.InvariantCultureIgnoreCase) != 0)))
                    {
                        compared_skins.Add(thisSkin);
                    }
                }
            }
            if (base_skins.Count > 0)
            {
                if (base_skins.Count == 1)
                {
                    changes.Add("Removed web skin " + base_skins[0]);
                }
                else
                {
                    StringBuilder builder = new StringBuilder("Removed web skins " + base_skins[0]);
                    for (int i = 1; i < base_skins.Count; i++)
                        builder.Append(", " + base_skins[i]);
                    changes.Add(builder.ToString());
                }
            }
            if (compared_skins.Count > 0)
            {
                if (compared_skins.Count == 1)
                {
                    changes.Add("Added web skin " + compared_skins[0]);
                }
                else
                {
                    StringBuilder builder = new StringBuilder("Added web skins " + compared_skins[0]);
                    for (int i = 1; i < compared_skins.Count; i++)
                        builder.Append(", " + compared_skins[i]);
                    changes.Add(builder.ToString());
                }
            }

            // default web skin
            compare_nullable_strings(Base.Default_Skin, Compared.Default_Skin, "default web skin", changes);

            // custom css
            if (String.IsNullOrEmpty(Base.CSS_File))
            {
                if (!String.IsNullOrEmpty(Compared.CSS_File))
                {
                    changes.Add("Enabled the aggregation-level stylesheet");
                }
            }
            else
            {
                if (String.IsNullOrEmpty(Compared.CSS_File))
                {
                    changes.Add("Disabled the aggregation-level stylesheet");
                }
            }

            // home pages (multilingual)
            List<Web_Language_Enum> removedLanguages = new List<Web_Language_Enum>();
            List<Web_Language_Enum> addedLanguages = new List<Web_Language_Enum>();
            if (Base.Home_Page_File_Dictionary != null)
            {
                foreach ( KeyValuePair<Web_Language_Enum, Complete_Item_Aggregation_Home_Page> thisHomePage in Base.Home_Page_File_Dictionary)
                {
                    // Look in compared for a match
                    if ((Compared.Home_Page_File_Dictionary == null) || (Compared.Home_Page_File_Dictionary.All(CompareHomePage => thisHomePage.Key != CompareHomePage.Key)))
                    {
                        removedLanguages.Add(thisHomePage.Key);
                    }
                }
            }
            if (Compared.Home_Page_File_Dictionary != null)
            {
                foreach (KeyValuePair<Web_Language_Enum, Complete_Item_Aggregation_Home_Page> thisHomePage in Compared.Home_Page_File_Dictionary)
                {
                    // Look in base for a match
                    if ((Base.Home_Page_File_Dictionary == null) || (Base.Home_Page_File_Dictionary.All(CompareHomePage => thisHomePage.Key != CompareHomePage.Key)))
                    {
                        addedLanguages.Add(thisHomePage.Key);
                    }
                }
            }
            if (removedLanguages.Count > 0)
            {
                if (removedLanguages.Count == 1)
                {
                    changes.Add("Removed " + Web_Language_Enum_Converter.Enum_To_Name(removedLanguages[0]) + " home page");
                }
                else
                {
                    StringBuilder builder = new StringBuilder("Removed " + Web_Language_Enum_Converter.Enum_To_Name(removedLanguages[0]));
                    for (int i = 1; i < removedLanguages.Count; i++)
                        builder.Append(", " + removedLanguages[i]);
                    changes.Add(builder + " home pages");
                }
            }
            if (addedLanguages.Count > 0)
            {
                if (addedLanguages.Count == 1)
                {
                    changes.Add("Added " + Web_Language_Enum_Converter.Enum_To_Name(addedLanguages[0]) + " home page");
                }
                else
                {
                    StringBuilder builder = new StringBuilder("Added " + Web_Language_Enum_Converter.Enum_To_Name(removedLanguages[0]));
                    for (int i = 1; i < addedLanguages.Count; i++)
                        builder.Append(", " + addedLanguages[i]);
                    changes.Add(builder + " home pages");
                }
            }

            // banners (multilingual)
            removedLanguages.Clear();
            addedLanguages.Clear();
            if (Base.Banner_Dictionary != null)
            {
                foreach (KeyValuePair<Web_Language_Enum, string> thisBanner in Base.Banner_Dictionary)
                {
                    // Look in compared for a match
                    bool match = false;
                    if (Compared.Banner_Dictionary != null)
                    {
                        foreach (KeyValuePair<Web_Language_Enum, string> compareBanner in Compared.Banner_Dictionary)
                        {
                            if (thisBanner.Key == compareBanner.Key)
                            {
                                match = true;

                                // Now, compare the source file as well
                                if (String.Compare(thisBanner.Value, compareBanner.Value, StringComparison.InvariantCultureIgnoreCase) != 0)
                                {
                                    changes.Add("Changed " + Web_Language_Enum_Converter.Enum_To_Name(thisBanner.Key) + " banner source file (" + thisBanner.Value + " --> " + compareBanner.Value + ")");
                                }

                                break;
                            }
                        }
                    }
                    if (!match)
                        removedLanguages.Add(thisBanner.Key);
                }
            }
            if (Compared.Banner_Dictionary != null)
            {
                foreach (KeyValuePair<Web_Language_Enum, string> thisBanner in Compared.Banner_Dictionary)
                {
                    // Look in base for a match
                    if ((Base.Banner_Dictionary == null) || (Base.Banner_Dictionary.All(CompareBanner => thisBanner.Key != CompareBanner.Key)))
                    {
                        addedLanguages.Add(thisBanner.Key);
                    }
                }
            }
            if (removedLanguages.Count > 0)
            {
                if (removedLanguages.Count == 1)
                {
                    changes.Add("Removed " + Web_Language_Enum_Converter.Enum_To_Name(removedLanguages[0]) + " banner");
                }
                else
                {
                    StringBuilder builder = new StringBuilder("Removed " + Web_Language_Enum_Converter.Enum_To_Name(removedLanguages[0]));
                    for (int i = 1; i < removedLanguages.Count; i++)
                        builder.Append(", " + removedLanguages[i]);
                    changes.Add(builder + " banners");
                }
            }
            if (addedLanguages.Count > 0)
            {
                if (addedLanguages.Count == 1)
                {
                    changes.Add("Added " + Web_Language_Enum_Converter.Enum_To_Name(addedLanguages[0]) + " banner");
                }
                else
                {
                    StringBuilder builder = new StringBuilder("Added " + Web_Language_Enum_Converter.Enum_To_Name(addedLanguages[0]));
                    for (int i = 1; i < addedLanguages.Count; i++)
                        builder.Append(", " + addedLanguages[i]);
                    changes.Add(builder + " banners");
                }
            }

            // search types
            List<Search_Type_Enum> removedSearches = new List<Search_Type_Enum>();
            List<Search_Type_Enum> addedSearches = new List<Search_Type_Enum>();
            if (Base.Search_Types != null)
            {
                foreach (Search_Type_Enum thisSearch in Base.Search_Types)
                {
                    // Look in compared for a match
                    if ((Compared.Search_Types == null) || (Compared.Search_Types.All(CompareSearch => thisSearch != CompareSearch)))
                    {
                        removedSearches.Add(thisSearch);
                    }
                }
            }
            if (Compared.Search_Types != null)
            {
                foreach (Search_Type_Enum thisSearch in Compared.Search_Types)
                {
                    // Look in base for a match
                    if ((Base.Search_Types == null) || (Base.Search_Types.All(CompareSearch => thisSearch != CompareSearch)))
                    {
                        addedSearches.Add(thisSearch);
                    }
                }
            }
            if (removedSearches.Count > 0)
            {
                if (removedSearches.Count == 1)
                {
                    changes.Add("Removed " + removedSearches[0].ToString() + " search");
                }
                else
                {
                    StringBuilder builder = new StringBuilder("Removed " + removedSearches[0].ToString());
                    for (int i = 1; i < removedSearches.Count; i++)
                        builder.Append(", " + removedSearches[i].ToString());
                    changes.Add(builder + " searches");
                }
            }
            if (addedSearches.Count > 0)
            {
                if (addedSearches.Count == 1)
                {
                    changes.Add("Added " + addedSearches[0] + " search");
                }
                else
                {
                    StringBuilder builder = new StringBuilder("Added " + addedSearches[0]);
                    for (int i = 1; i < addedSearches.Count; i++)
                        builder.Append(", " + addedSearches[i]);
                    changes.Add(builder + " searches");
                }
            }

            // other display types? ( all/new item browse, map browse )

            // map search default area
            if ((Base.Map_Search_Display != null ) || ( Compared.Map_Search_Display != null ))
            {
                if (Base.Map_Search_Display == null)
                {
                    changes.Add("Added default area for map search display");
                }
                else if (Compared.Map_Search_Display == null)
                {
                    changes.Add("Removed default area information for map search display");

                }
                else
                {
                    // Are these different?
                    bool different = false;
                    if (Base.Map_Search_Display.Type != Compared.Map_Search_Display.Type)
                    {
                        different = true;
                    }
                    else
                    {
                        if ((Base.Map_Search_Display.ZoomLevel.HasValue != Compared.Map_Search_Display.ZoomLevel.HasValue) ||
                            ((Base.Map_Search_Display.ZoomLevel.HasValue) && (Base.Map_Search_Display.ZoomLevel.Value != Compared.Map_Search_Display.ZoomLevel.Value)))
                            different = true;
                        if ((Base.Map_Search_Display.Latitude.HasValue != Compared.Map_Search_Display.Latitude.HasValue) ||
                            ((Base.Map_Search_Display.Latitude.HasValue) && (Base.Map_Search_Display.Latitude.Value != Compared.Map_Search_Display.Latitude.Value)))
                            different = true;
                        if ((Base.Map_Search_Display.Longitude.HasValue != Compared.Map_Search_Display.Longitude.HasValue) ||
                            ((Base.Map_Search_Display.Longitude.HasValue) && (Base.Map_Search_Display.Longitude.Value != Compared.Map_Search_Display.Longitude.Value)))
                            different = true;
                    }

                    if ( different )
                        changes.Add("Changed default area for map search display");
                }
            }

            // map browse default area
            if ((Base.Map_Browse_Display != null) || (Compared.Map_Browse_Display != null))
            {
                if (Base.Map_Browse_Display == null)
                {
                    changes.Add("Added default area for map browse display");
                }
                else if (Compared.Map_Browse_Display == null)
                {
                    changes.Add("Removed default area information for map browse display");

                }
                else
                {
                    // Are these different?
                    bool different = false;
                    if (Base.Map_Browse_Display.Type != Compared.Map_Browse_Display.Type)
                    {
                        different = true;
                    }
                    else
                    {
                        if ((Base.Map_Browse_Display.ZoomLevel.HasValue != Compared.Map_Browse_Display.ZoomLevel.HasValue) ||
                            ((Base.Map_Browse_Display.ZoomLevel.HasValue) && (Base.Map_Browse_Display.ZoomLevel.Value != Compared.Map_Browse_Display.ZoomLevel.Value)))
                            different = true;
                        if ((Base.Map_Browse_Display.Latitude.HasValue != Compared.Map_Browse_Display.Latitude.HasValue) ||
                            ((Base.Map_Browse_Display.Latitude.HasValue) && (Base.Map_Browse_Display.Latitude.Value != Compared.Map_Browse_Display.Latitude.Value)))
                            different = true;
                        if ((Base.Map_Browse_Display.Longitude.HasValue != Compared.Map_Browse_Display.Longitude.HasValue) ||
                            ((Base.Map_Browse_Display.Longitude.HasValue) && (Base.Map_Browse_Display.Longitude.Value != Compared.Map_Browse_Display.Longitude.Value)))
                            different = true;
                    }

                    if (different)
                        changes.Add("Changed default area for map browse display");
                }
            }

            // facets
            List<short> addedFacets = new List<short>();
            List<short> removedFacets = new List<short>();
            if (Base.Facets != null)
            {
                foreach (short thisFacet in Base.Facets)
                {
                    // Look in compared for a match
                    if ((Compared.Facets == null) || (Compared.Facets.All(CompareFacet => thisFacet != CompareFacet)))
                    {
                        removedFacets.Add(thisFacet);
                    }
                }
            }
            if (Compared.Facets != null)
            {
                foreach (short thisFacet in Compared.Facets)
                {
                    // Look in base for a match
                    if ((Base.Facets == null) || (Base.Facets.All(CompareFacet => thisFacet != CompareFacet)))
                    {
                        addedFacets.Add(thisFacet);
                    }
                }
            }
            if (removedFacets.Count > 0)
            {
                if (removedFacets.Count == 1)
                {
                    changes.Add("Removed facet " + removedFacets[0]);
                }
                else
                {
                    StringBuilder builder = new StringBuilder("Removed facets " + removedFacets[0]);
                    for (int i = 1; i < removedFacets.Count; i++)
                        builder.Append(", " + removedFacets[i]);
                    changes.Add(builder.ToString());
                }
            }
            if (addedFacets.Count > 0)
            {
                if (addedFacets.Count == 1)
                {
                    changes.Add("Added facet " + addedFacets[0]);
                }
                else
                {
                    StringBuilder builder = new StringBuilder("Added facets " + addedFacets[0]);
                    for (int i = 1; i < addedFacets.Count; i++)
                        builder.Append(", " + addedFacets[i]);
                    changes.Add(builder.ToString());
                }
            }

            // result views
            List<Result_Display_Type_Enum> removedResultsDisplay = new List<Result_Display_Type_Enum>();
            List<Result_Display_Type_Enum> addedResultsDisplays = new List<Result_Display_Type_Enum>();
            if (Base.Result_Views != null)
            {
                foreach (Result_Display_Type_Enum thisSearch in Base.Result_Views)
                {
                    // Look in compared for a match
                    if ((Compared.Result_Views == null) || (Compared.Result_Views.All(CompareSearch => thisSearch != CompareSearch)))
                    {
                        removedResultsDisplay.Add(thisSearch);
                    }
                }
            }
            if (Compared.Search_Types != null)
            {
                foreach (Result_Display_Type_Enum thisSearch in Compared.Result_Views)
                {
                    // Look in base for a match
                    if ((Base.Result_Views == null) || (Base.Result_Views.All(CompareSearch => thisSearch != CompareSearch)))
                    {
                        addedResultsDisplays.Add(thisSearch);
                    }
                }
            }
            if (removedResultsDisplay.Count > 0)
            {
                if (removedResultsDisplay.Count == 1)
                {
                    changes.Add("Removed " + removedResultsDisplay[0].ToString() + " result display type");
                }
                else
                {
                    StringBuilder builder = new StringBuilder("Removed " + removedResultsDisplay[0].ToString());
                    for (int i = 1; i < removedResultsDisplay.Count; i++)
                        builder.Append(", " + removedResultsDisplay[i].ToString());
                    changes.Add(builder + " result display types");
                }
            }
            if (addedResultsDisplays.Count > 0)
            {
                if (addedResultsDisplays.Count == 1)
                {
                    changes.Add("Added " + addedResultsDisplays[0] + " result display type");
                }
                else
                {
                    StringBuilder builder = new StringBuilder("Added " + addedResultsDisplays[0]);
                    for (int i = 1; i < addedResultsDisplays.Count; i++)
                        builder.Append(", " + addedResultsDisplays[i]);
                    changes.Add(builder + " result display types");
                }
            }

            // default browse
            compare_nullable_strings(Base.Default_BrowseBy, Compared.Default_BrowseBy, "default browse", changes);

            // metadata browse

            // oai-pmh flag
            if (Base.OAI_Enabled != Compared.OAI_Enabled)
            {
                if (Compared.OAI_Enabled)
                    changes.Add("OAI-PMH enabled");
                else
                    changes.Add("OAI-PMH disabled");
            }

            // oai-pmh description
            compare_nullable_strings(Base.OAI_Metadata, Compared.OAI_Metadata, "additional OAI-PMH metadata", changes);

            // child pages

            return changes;
        }
        /// <summary> Adds the title, item, and page counts to this item aggregation object </summary>
        /// <param name="Aggregation"> Mostly built item aggregation object </param>
        /// <param name="Tracer">Trace object keeps a list of each method executed and important milestones in rendering</param>
        /// <returns> TRUE if successful, otherwise FALSE </returns>
        /// <remarks> This method calls the stored procedure 'SobekCM_Get_Item_Aggregation2'. </remarks>
        public static bool Get_Item_Aggregation_Counts(Complete_Item_Aggregation Aggregation, Custom_Tracer Tracer)
        {
            if (Tracer != null)
            {
                Tracer.Add_Trace("Engine_Database.Get_Item_Aggregation_Counts", "Add the title, item, and page count to the item aggregation object");
            }

            try
            {
                // Build the parameter list
                EalDbParameter[] paramList = new EalDbParameter[3];
                paramList[0] = new EalDbParameter("@code", Aggregation.Code);
                paramList[1] = new EalDbParameter("@include_counts", true);
                paramList[2] = new EalDbParameter("@is_robot", false);

                // Define a temporary dataset
                DataSet tempSet = EalDbAccess.ExecuteDataset( DatabaseType, Connection_String, CommandType.StoredProcedure, "SobekCM_Get_Item_Aggregation2", paramList);

                // Add the counts for this item aggregation
                if (tempSet.Tables.Count > 4)
                {
                    add_counts(Aggregation, tempSet.Tables[4]);
                }

                // Return the built argument set
                return true;
            }
            catch (Exception ee)
            {
                if (Tracer != null)
                {
                    Tracer.Add_Trace("Engine_Database.Get_Item_Aggregation_Counts", "Exception caught during database work", Custom_Trace_Type_Enum.Error);
                    Tracer.Add_Trace("Engine_Database.Get_Item_Aggregation_Counts", ee.Message, Custom_Trace_Type_Enum.Error);
                    Tracer.Add_Trace("Engine_Database.Get_Item_Aggregation_Counts", ee.StackTrace, Custom_Trace_Type_Enum.Error);
                }
                return false;
            }
        }