CmsConfig.cs

// Copyright © 2007 Jeffrey Bazinet, http://www.vwd-cms.com/
using System;
using System.Data;
using System.Configuration;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
using System.Web.Configuration;
using System.Collections.Generic;
using System.Web.Caching;
using System.Reflection;
using System.Text;
using System.Collections;
using System.Xml;
using System.Collections.Specialized;

namespace VwdCms.Configuration
{
    public class CmsConfig
    {
        private CmsHost _currentHost = null;
        public const string ConfigFile = "vwdcms.config";
        public const string CacheKey = "__vwdcms_config";
        public const string ValueDelimiter = ";";

        public DateTime LoadedDate = DateTime.MinValue;
        public CmsSettings CmsSettings = null;
        public VwdCms.Configuration.SiteMapSettings SiteMapSettings = null;
        public VwdCms.Configuration.MembershipSettings MembershipSettings = null;
        
        public Dictionarystring, Exception Exceptions = new Dictionarystring, Exception();
        public Dictionarystring, CmsHost Hosts = new Dictionarystring, CmsHost();
        public Dictionarystring, ManagedFolder ManagedFolders = new Dictionarystring, ManagedFolder();
        public Dictionarystring, FileExtension FileExtensions = new Dictionarystring, FileExtension();
        public Dictionarystring, Language Languages = new Dictionarystring, Language();
        public Dictionarystring, SiteSetting SiteSettings = new Dictionarystring, SiteSetting();
        public Dictionarystring, MasterPageConfig MasterPages = new Dictionarystring, MasterPageConfig();
        public Dictionarystring, PageRedirect PageRedirects = new Dictionarystring, PageRedirect();
        public Dictionarystring, SmtpAccount SmtpAccounts = new Dictionarystring, SmtpAccount();
        public Dictionarystring, SqlInstaller SqlInstallers = new Dictionarystring, SqlInstaller();
        public Dictionarystring, NetworkLogin NetworkLogins = new Dictionarystring, NetworkLogin();

        public CmsHost CurrentHost
        {
            get
            {
                CmsHost host = _currentHost;

                if (host == null)
                {
                    // get domain of the current host
                    HttpContext context = HttpContext.Current;
                    Uri uri = context.Request.Url;
                    string domain = uri.Host.ToLower();
                    string appPath = context.Request.ApplicationPath.ToLower();
                    if (!appPath.StartsWith("/"))
                    {
                        appPath = "/" + appPath;
                    }
                    string key = domain + appPath;
                    if (this.Hosts.ContainsKey(key))
                    {
                        host = this.Hosts[key];
                    }
                    else
                    {
                        host = new CmsHost(this, key);
                        this.Hosts.Add(key, host);
                    }
                    _currentHost = host;
                }
                return host;
            }
        }

        public ManagedFolder GetManagedFolder(string vpFolder)
        {
            ManagedFolder mf = null;
            if (!string.IsNullOrEmpty(vpFolder))
            {
                vpFolder = vpFolder.ToLower();

                // check to see if the actual folder is managed
                if (this.ManagedFolders.ContainsKey(vpFolder))
                {
                    mf = this.ManagedFolders[vpFolder];
                }

                // if this folder is not managed, check to see if 
                // any of its parent folders are managed
                if (mf == null)
                {
                    string[] folders = vpFolder.Split('/');
                    int i = 0;
                    int j = 0;
                    string path = "/";

                    for ( i = folders.Length-1; i = 0; i--)
                    {
                        if (!string.IsNullOrEmpty(folders[i]))
                        {
                            path = string.Empty;
                            for (j = 0; j = i; j++)
                            {
                                path = folders[j] + "/";
                            }

                            if (!string.IsNullOrEmpty(path))
                            {
                                if (this.ManagedFolders.ContainsKey(path))
                                {
                                    mf = this.ManagedFolders[path];
                                    if (mf != null)
                                    {
                                        break;
                                    }
                                }
                            }
                        }
                    }
                }
            }
            return mf;
        }

        public static CmsConfig Current
        {
            get
            {
                object cmscfg = null;
                long ticks = DateTime.Now.Ticks + (TimeSpan.TicksPerSecond * 2);

                while (cmscfg == null  DateTime.Now.Ticks  ticks)
                {
                    cmscfg = HttpContext.Current.Cache[CacheKey];

                    if (cmscfg == null)
                    {
                        cmscfg = LoadAndCacheCatchExceptions();
                    }
                    if (cmscfg == null)
                    {
                        System.Threading.Thread.Sleep(10);
                    }
                }

                return (CmsConfig)cmscfg;
            }
        }

        public static CmsConfig LoadAndCacheThrowExceptions()
        {
            return LoadAndCache();
        }

        private static CmsConfig LoadAndCacheCatchExceptions()
        {
            CmsConfig cfg = null;
            try
            {
                cfg = LoadAndCache();
            }
            catch (Exception ex)
            {
                if (cfg != null)
                {
                    cfg.Exceptions.Add("Exception" + cfg.Exceptions.Count.ToString(), ex);
                }
                else
                {
                    throw ex;
                }
            }
            return cfg;
        }

        public static void DuplicateItem(CmsConfig cfg, string collectionName, string key)
        {
            try
            {
                string msg = "A {0} item with the key '{1}' already exists in the collection";
                msg = string.Format(msg, collectionName, key);
                Exception ex = new Exception(msg);
                string exceptionKey = "Exception" + cfg.Exceptions.Count.ToString();
                cfg.Exceptions.Add(exceptionKey, ex);
            }
            catch { }
        }

        private static CmsConfig LoadAndCache()
        {
            HttpContext context = HttpContext.Current;
            CmsConfig cmscfg = null;

            cmscfg = new CmsConfig();
            cmscfg.LoadedDate = DateTime.Now;

            // load the data from an xml document 

            string filepath = context.Request.ApplicationPath + "/" + ConfigFile;
            filepath = context.Server.MapPath(filepath);

            System.Xml.XmlDocument xdoc = new System.Xml.XmlDocument();
            xdoc.Load(filepath);

            // <configuration> 
            System.Xml.XmlNode docnode = (System.Xml.XmlNode)xdoc.DocumentElement;

            if (docnode != null  docnode.ChildNodes.Count  0)
            {
                System.Xml.XmlNode node = null;
                string name = "./";

                //<hosts>
                cmscfg.Hosts.Clear();
                node = docnode.SelectSingleNode(name + "hosts");
                CmsHost host = null;
                string key = null;
                if (node != null  node.ChildNodes.Count  0)
                {
                    foreach (System.Xml.XmlNode n in node.ChildNodes)
                    {
                        if (n != null  n.NodeType == System.Xml.XmlNodeType.Element)
                        {
                            host = new CmsHost(cmscfg, n);
                            key = host.Key;
                            if (cmscfg.Hosts.ContainsKey(key))
                            {
                                DuplicateItem(cmscfg, "Host", key);
                            }
                            else
                            {
                                cmscfg.Hosts.Add(key, host);
                            }
                        }
                    }
                }
                // get CurrentHost to set _currentHost before caching
                host = cmscfg.CurrentHost;

                //<cmssettings>
                node = docnode.SelectSingleNode(name + "cmssettings");
                cmscfg.CmsSettings = new CmsSettings(cmscfg, node);

                //<languages>
                cmscfg.Languages.Clear();
                node = docnode.SelectSingleNode(name + "languages");
                Language lang = null;
                if (node != null  node.ChildNodes.Count  0)
                {
                    foreach (System.Xml.XmlNode n in node.ChildNodes)
                    {
                        if (n != null  n.NodeType == System.Xml.XmlNodeType.Element)
                        {
                            lang = new Language(cmscfg, n);
                            key = lang.ID;
                            if (cmscfg.Languages.ContainsKey(key))
                            {
                                DuplicateItem(cmscfg, "Lanuage", key);
                            }
                            else
                            {
                                cmscfg.Languages.Add(key, lang);
                            }
                        }
                    }
                }

                //<sitesettings>
                cmscfg.SiteSettings.Clear();
                node = docnode.SelectSingleNode(name + "sitesettings");
                SiteSetting ss = null;
                if (node != null  node.ChildNodes.Count  0)
                {
                    foreach (System.Xml.XmlNode n in node.ChildNodes)
                    {
                        if (n != null  n.NodeType == System.Xml.XmlNodeType.Element)
                        {
                            ss = new SiteSetting(cmscfg, n);
                            key = ss.Key;
                            if (cmscfg.SiteSettings.ContainsKey(key))
                            {
                                DuplicateItem(cmscfg, "SiteSetting", key);
                            }
                            else
                            {                            
                                cmscfg.SiteSettings.Add(key, ss);
                            }
                        }
                    }
                }

                //<fileextensions>
                cmscfg.FileExtensions.Clear();
                node = docnode.SelectSingleNode(name + "fileextensions");
                FileExtension fe = null;
                if (node != null  node.ChildNodes.Count  0)
                {
                    foreach (System.Xml.XmlNode n in node.ChildNodes)
                    {
                        if (n != null  n.NodeType == System.Xml.XmlNodeType.Element)
                        {
                            fe = new FileExtension(cmscfg, n);
                            key = fe.Extension;
                            if (cmscfg.FileExtensions.ContainsKey(key))
                            {
                                DuplicateItem(cmscfg, "FileExtension", key);
                            }
                            else
                            {
                                cmscfg.FileExtensions.Add(key, fe);
                            }
                        }
                    }
                }

                //<managedfolders>
                cmscfg.ManagedFolders.Clear();
                node = docnode.SelectSingleNode(name + "managedfolders");
                ManagedFolder mf = null;
                if (node != null  node.ChildNodes.Count  0)
                {
                    foreach (System.Xml.XmlNode n in node.ChildNodes)
                    {
                        if (n != null  n.NodeType == System.Xml.XmlNodeType.Element)
                        {
                            mf = new ManagedFolder(cmscfg, n);
                            key = mf.Folder;
                            if (cmscfg.ManagedFolders.ContainsKey(key))
                            {
                                DuplicateItem(cmscfg, "ManagedFolder", key);
                            }
                            else
                            {
                                cmscfg.ManagedFolders.Add(key, mf);
                            }
                        }
                    }
                }

                //<masterpages>
                cmscfg.MasterPages.Clear();
                node = docnode.SelectSingleNode(name + "masterpages");
                MasterPageConfig mpc = null;
                if (node != null  node.ChildNodes.Count  0)
                {
                    foreach (System.Xml.XmlNode n in node.ChildNodes)
                    {
                        if (n != null  n.NodeType == System.Xml.XmlNodeType.Element)
                        {
                            mpc = new MasterPageConfig(cmscfg, n);
                            key = mpc.File;
                            if (cmscfg.MasterPages.ContainsKey(key))
                            {
                                DuplicateItem(cmscfg, "MasterPage", key);
                            }
                            else
                            {
                                cmscfg.MasterPages.Add(key, mpc);
                            }
                        }
                    }
                }

                //<pageredirects>
                cmscfg.PageRedirects.Clear();
                node = docnode.SelectSingleNode(name + "pageredirects");
                PageRedirect pr = null;
                if (node != null  node.ChildNodes.Count  0)
                {
                    foreach (System.Xml.XmlNode n in node.ChildNodes)
                    {
                        if (n != null  n.NodeType == System.Xml.XmlNodeType.Element)
                        {
                            pr = new PageRedirect(cmscfg, n);
                            key = pr.Key;
                            if (cmscfg.PageRedirects.ContainsKey(key))
                            {
                                DuplicateItem(cmscfg, "PageRedirect", key);
                            }
                            else
                            {
                                cmscfg.PageRedirects.Add(key, pr);
                            }
                        }
                    }
                }

                //<smtpaccounts>
                cmscfg.SmtpAccounts.Clear();
                node = docnode.SelectSingleNode(name + "smtpaccounts");
                SmtpAccount sa = null;
                if (node != null  node.ChildNodes.Count  0)
                {
                    foreach (System.Xml.XmlNode n in node.ChildNodes)
                    {
                        if (n != null  n.NodeType == System.Xml.XmlNodeType.Element)
                        {
                            sa = new SmtpAccount(cmscfg, n);
                            key = sa.Key;
                            if (cmscfg.SmtpAccounts.ContainsKey(key))
                            {
                                DuplicateItem(cmscfg, "SmtpAccount", key);
                            }
                            else
                            {
                                cmscfg.SmtpAccounts.Add(key, sa);
                            }
                        }
                    }
                }

                //<sqlinstallers>
                cmscfg.SqlInstallers.Clear();
                node = docnode.SelectSingleNode(name + "sqlinstallers");
                SqlInstaller si = null;
                if (node != null  node.ChildNodes.Count  0)
                {
                    foreach (System.Xml.XmlNode n in node.ChildNodes)
                    {
                        if (n != null  n.NodeType == System.Xml.XmlNodeType.Element)
                        {
                            si = new SqlInstaller(cmscfg, n);
                            key = si.Key;
                            if (cmscfg.SqlInstallers.ContainsKey(key))
                            {
                                DuplicateItem(cmscfg, "SqlInstaller", key);
                            }
                            else
                            {
                                cmscfg.SqlInstallers.Add(key, si);
                            }
                        }
                    }
                }

                //<sitemapsettings>
                node = docnode.SelectSingleNode(name + "sitemapsettings");
                if (node != null  node.ChildNodes.Count  0)
                {
                    cmscfg.SiteMapSettings = new VwdCms.Configuration.SiteMapSettings(cmscfg, node);
                }
                else
                {
                    cmscfg.SiteMapSettings = new VwdCms.Configuration.SiteMapSettings(cmscfg);
                }

                //<membershipsettings>
                node = docnode.SelectSingleNode(name + "membershipsettings");
                if (node != null  node.ChildNodes.Count  0)
                {
                    cmscfg.MembershipSettings = new VwdCms.Configuration.MembershipSettings(cmscfg, node);
                }
                else
                {
                    cmscfg.MembershipSettings = new VwdCms.Configuration.MembershipSettings(cmscfg);
                }

                //<networklogins>
                cmscfg.NetworkLogins.Clear();
                node = docnode.SelectSingleNode(name + "networklogins");
                NetworkLogin nl = null;
                if (node != null  node.ChildNodes.Count  0)
                {
                    foreach (System.Xml.XmlNode n in node.ChildNodes)
                    {
                        if (n != null  n.NodeType == System.Xml.XmlNodeType.Element)
                        {
                            nl = new NetworkLogin(cmscfg, n);
                            key = nl.Key;
                            if (cmscfg.NetworkLogins.ContainsKey(key))
                            {
                                DuplicateItem(cmscfg, "NetworkLogin", key);
                            }
                            else
                            {
                                cmscfg.NetworkLogins.Add(key, nl);
                            }
                        }
                    }
                }


                // ****************************************************************
                // Cache the ConfigurationManager object
                // ****************************************************************

                if (HttpContext.Current.Cache[CacheKey] != null)
                {
                    HttpContext.Current.Cache.Remove(CacheKey);
                }

                CacheDependency dep = new CacheDependency(filepath);

                object cachedObj = null;
                int count = 0;

                // make sure the config object is successfully cached by checking the cache after 
                // adding it, and retrying if it is not there
                while (cachedObj == null  count  20)
                {
                    HttpContext.Current.Cache.Insert(CacheKey, cmscfg, dep, Cache.NoAbsoluteExpiration,
                    Cache.NoSlidingExpiration, CacheItemPriority.NotRemovable, null);

                    count++;
                    cachedObj = HttpContext.Current.Cache[CacheKey];
                    if (cachedObj == null)
                    {
                        System.Threading.Thread.Sleep(100);
                    }
                    else
                    {
                        break;
                    }
                }
            }

            if (!cmscfg.CurrentHost.Persistent)
            {
                CmsHost host = cmscfg.CurrentHost;
                HostComponent comp = null;

                comp = new HostComponent(host, "vwdcmsadmin");
                comp.Type = "sqlinstaller";
                comp.Status = "notinstalled";
                host.Components.Add("vwdcmsadmin", comp);

                comp = new HostComponent(host, "vwdcmsforum");
                comp.Type = "sqlinstaller";
                comp.Status = "notinstalled";
                host.Components.Add("vwdcmsforum", comp);

                comp = new HostComponent(host, "vwdcmsmembers");
                comp.Type = "sqlinstaller";
                comp.Status = "notinstalled";
                host.Components.Add("vwdcmsmembers", comp);

                cmscfg.Save();
            }

            return cmscfg;
        }

        public void Save()
        {
            HttpContext context = HttpContext.Current;
            string filepath = context.Request.ApplicationPath + "/" + ConfigFile;
            filepath = context.Server.MapPath(filepath);

            // load the data 
            System.Xml.XmlDocument xdoc = new System.Xml.XmlDocument();
            xdoc.PreserveWhitespace = true;
            
            xdoc.Load(filepath);
            System.Xml.XmlNode docnode = (System.Xml.XmlNode)xdoc.DocumentElement;

            // Hosts
            UpdateXml(docnode, "hosts", "host", this.Hosts);

            // CmsSettings
            UpdateXml(docnode, "cmssettings", this.CmsSettings);

            // SiteSettings
            UpdateXml(docnode, "sitesettings", "sitesetting", this.SiteSettings);

            // FileExtensions
            UpdateXml(docnode, "fileextensions", "fileextension", this.FileExtensions);

            // ManagedFolders
            UpdateXml(docnode, "managedfolders", "managedfolder", this.ManagedFolders);

            // MasterPages
            UpdateXml(docnode, "masterpages", "masterpage", this.MasterPages);

            // PageRedirects
            UpdateXml(docnode, "pageredirects", "pageredirect", this.PageRedirects);

            // SmtpAccounts
            UpdateXml(docnode, "smtpaccounts", "smtpaccount", this.SmtpAccounts);

            // SqlInstallers
            UpdateXml(docnode, "sqlinstallers", "sqlinstaller", this.SqlInstallers);

            // SiteMapSettings
            UpdateXml(docnode, "sitemapsettings/pageextensions", "extension", this.SiteMapSettings.PageExtensions);
            UpdateXml(docnode, "sitemapsettings/hiddenfolders", "folder", this.SiteMapSettings.HiddenFolders);
            UpdateXml(docnode, "sitemapsettings/hiddenpages", "page", this.SiteMapSettings.HiddenPages);

            // MembershipSettings
            UpdateXml(docnode, "membershipsettings", this.MembershipSettings);

            // NetworkLogins
            UpdateXml(docnode, "networklogins", "networklogin", this.NetworkLogins);

            // save the config file
            if (!CmsConfig.Current.CurrentHost.DemoMode)
            {
                xdoc.Save(filepath);
            }
        }

        private void UpdateXml(XmlNode docnode, string collectionName, string tagName, Dictionarystring, string dictionary)
        {
            // Note: this method needs to maintain the format of the xml document which 
            // includes preserving white space and comments

            XmlNode colNode = null;
            XmlNode objNode = null;
            string name = "./";
            string key = null;
            XmlSignificantWhitespace xsws = null;
            string indent = string.Empty;
            colNode = docnode.SelectSingleNode(name + collectionName);

            if (colNode != null)
            {
                // this dictionary contains simple string values

                // get the indentation level for the collection node
                if (colNode.PreviousSibling != null 
                    (colNode.PreviousSibling.NodeType == XmlNodeType.Whitespace ||
                    colNode.PreviousSibling.NodeType == XmlNodeType.SignificantWhitespace))
                {
                    indent = colNode.PreviousSibling.InnerText;
                }
                else
                {
                    indent = "\r\n\t";
                }

                // step 1: remove values from colNode that do not exist in the collection
                XmlNode prevNode = colNode.LastChild;
                objNode = prevNode;
                while (objNode != null)
                {
                    prevNode = objNode.PreviousSibling;
                    if (objNode.NodeType == XmlNodeType.Element)
                    {
                        if (!dictionary.ContainsKey(objNode.InnerText))
                        {
                            while (objNode.NextSibling != null  
                                (objNode.NextSibling.NodeType == XmlNodeType.Whitespace || 
                                objNode.NextSibling.NodeType == XmlNodeType.SignificantWhitespace))
                            {
                                colNode.RemoveChild(objNode.NextSibling);
                            }
                            colNode.RemoveChild(objNode);
                        }
                    }
                    objNode = prevNode;
                }

                // step 2: insert values into the colNode that exist in the collection but not in colNode
                foreach (KeyValuePairstring, string kvp in dictionary)
                {
                    key = kvp.Key; // note: kvp.Key and kvp.Value should be identical
                    objNode = colNode.SelectSingleNode(name + tagName + "[text()='" + key + "']");

                    // if the node is found then there is no need to update it
                    // if it is not found, create a new node for this value
                    if (objNode == null)
                    {
                        xsws = docnode.OwnerDocument.CreateSignificantWhitespace("\t");
                        colNode.AppendChild(xsws);
                        objNode = docnode.OwnerDocument.CreateNode(XmlNodeType.Element, tagName, null);
                        objNode.InnerText = key;
                        colNode.AppendChild(objNode);
                        xsws = docnode.OwnerDocument.CreateSignificantWhitespace(indent);
                        colNode.AppendChild(xsws);
                    }
                }
            }
        }

        private void UpdateXml(XmlNode colNode, IDictionary collection)
        {
            UpdateXml(colNode, colNode.OwnerDocument.DocumentElement, null, null, collection);
        }

        private void UpdateXml(XmlNode docnode, string collectionName, string tagName, IDictionary collection)
        {
            UpdateXml(null, docnode, collectionName, tagName, collection);
        }

        private void UpdateXml(XmlNode colNode, XmlNode docnode, string collectionName, string tagName, IDictionary collection)
        {
            // note: some of the complexity of this method comes from a need 
            // to maintain the format of the xml document which 
            // includes preserving white space and comments

            //XmlNode colNode = null;
            XmlNode objNode = null;
            XmlNode prpNode = null;

            string name = "./";

            object obj = null;
            Type type = null;
            string key = null;
            string prpName = null;
            string prpValue = null;
            BindingFlags flags = BindingFlags.Instance | BindingFlags.Public;
            PropertyInfo[] props = null;
            PropertyInfo prpKey = null;
            PropertyInfo prpTagName = null;
            Attribute notPersitedAtt = null;
            Attribute notUpdatedAtt = null;
            bool newNode = false;
            XmlSignificantWhitespace xsws = null;
            IDictionary prpCol = null;

            if (colNode == null)
            {
                colNode = docnode.SelectSingleNode(name + collectionName);
            }

            if (colNode != null)
            {
                // this collectoin contains more complex objects
                // *** future work: this code only handles new items and 
                // updated items, but does not handle deletes
                foreach (DictionaryEntry de in collection)
                {
                    newNode = false;
                    obj = de.Value;
                    type = obj.GetType();
                    prpKey = type.GetProperty("Key", flags);
                    key = Convert.ToString(prpKey.GetValue(obj, null));
                    if(string.IsNullOrEmpty(tagName))
                    {
                        prpTagName = type.GetProperty("XmlTagName", flags);
                        tagName = Convert.ToString(prpTagName.GetValue(obj, null));
                    }
                    objNode = colNode.SelectSingleNode(name + tagName + "[key='" + key + "']");

                    if (objNode == null)
                    {
                        xsws = docnode.OwnerDocument.CreateSignificantWhitespace("\t");
                        colNode.AppendChild(xsws);
                        objNode = docnode.OwnerDocument.CreateNode(XmlNodeType.Element, tagName, null);
                        xsws = docnode.OwnerDocument.CreateSignificantWhitespace("\r\n");
                        objNode.AppendChild(xsws);
                        colNode.AppendChild(objNode);
                        newNode = true;
                    }
                    props = type.GetProperties(flags);
                    object objVal = null;
                    foreach (PropertyInfo prp in props)
                    {
                        notPersitedAtt = Attribute.GetCustomAttribute(prp, typeof(NotPersistedAttribute));
                        if (notPersitedAtt == null)
                        {
                            notUpdatedAtt = Attribute.GetCustomAttribute(prp, typeof(NotUpdatedAttribute));
                            if (notUpdatedAtt == null)
                            {
                                prpName = prp.Name.ToLower();
                                if (newNode || prpName != "key") // do not update the key in the config file
                                {
                                    prpNode = objNode.SelectSingleNode(prpName);
                                    objVal = prp.GetValue(obj, null);
                                    prpCol = objVal as IDictionary;
                                    if (prpCol != null)
                                    {
                                        if (prpNode == null)
                                        {
                                            xsws = docnode.OwnerDocument.CreateSignificantWhitespace("\t");
                                            objNode.AppendChild(xsws);
                                            prpNode = docnode.OwnerDocument.CreateNode(XmlNodeType.Element, prpName, null);
                                            xsws = docnode.OwnerDocument.CreateSignificantWhitespace("\r\n\t\t\t");
                                            prpNode.AppendChild(xsws);
                                            objNode.AppendChild(prpNode);
                                            xsws = docnode.OwnerDocument.CreateSignificantWhitespace("\r\n");
                                            objNode.AppendChild(xsws);
                                        }
                                        // this is a collection property
                                        UpdateXml(prpNode, prpCol);
                                    }
                                    else
                                    {
                                    if (prpNode == null)
                                    {
                                        xsws = docnode.OwnerDocument.CreateSignificantWhitespace("\t\t\t");
                                        objNode.AppendChild(xsws);
                                        prpNode = docnode.OwnerDocument.CreateNode(XmlNodeType.Element, prpName, null);
                                        objNode.AppendChild(prpNode);
                                        xsws = docnode.OwnerDocument.CreateSignificantWhitespace("\r\n");
                                        objNode.AppendChild(xsws);
                                    }

                                        prpValue = Convert.ToString(objVal);
                                        prpNode.InnerText = prpValue;
                                    }
                                }
                            }
                        }
                    }
                    if (newNode)
                    {
                        xsws = docnode.OwnerDocument.CreateSignificantWhitespace("\t\t");
                        objNode.AppendChild(xsws);
                        xsws = docnode.OwnerDocument.CreateSignificantWhitespace("\r\n\t");
                        colNode.AppendChild(xsws);
                    }
                }
            }
        }

        private void UpdateXml(XmlNode docnode, string nodeName, object obj)
        {
            string name = "./";
            XmlNode objNode = docnode.SelectSingleNode(name + nodeName);
            XmlNode prpNode = null;

            Type type = null;
            string prpName = null;
            string prpValue = null;
            BindingFlags flags = BindingFlags.Instance | BindingFlags.Public;
            PropertyInfo[] props = null;
            Attribute notPersitedAtt = null;
            Attribute notUpdatedAtt = null;

            type = obj.GetType();
            
            props = type.GetProperties(flags);
            foreach (PropertyInfo prp in props)
            {
                notPersitedAtt = Attribute.GetCustomAttribute(prp, typeof(NotPersistedAttribute));
                if (notPersitedAtt == null)
                {
                    notUpdatedAtt = Attribute.GetCustomAttribute(prp, typeof(NotUpdatedAttribute));
                    if (notUpdatedAtt == null)
                    {
                        prpName = prp.Name.ToLower();
                        prpValue = Convert.ToString(prp.GetValue(obj, null));
                        prpNode = objNode.SelectSingleNode(prpName);
                        if (prpNode != null)
                        {
                            prpNode.InnerText = prpValue;
                        }
                    }
                }
            }
        }

        public static string GetChildNodeInnerText(System.Xml.XmlNode node, string childNodeName, CmsConfig cmsconfig)
        {
            string text = string.Empty;

            try
            {
                System.Xml.XmlNode n = node.SelectSingleNode(childNodeName);
                if (n != null  n.NodeType == System.Xml.XmlNodeType.Element)
                {
                    text = n.InnerText;
                }
            }
            catch (Exception ex)
            {
                if (cmsconfig != null  cmsconfig.Exceptions != null)
                {
                    string exId = "Exception" + cmsconfig.Exceptions.Count.ToString();
                    string msg = "Configuration Node Not Found: '" + childNodeName + "'\r\n"
                        + "Configuration Node: " + node.OuterXml;
                    Exception ex2 = new Exception(msg, ex);
                    cmsconfig.Exceptions.Add(exId, ex2);
                }
            }
            return text;
        }

        public static string GetCollectionString(StringCollection col, int itemsPerLine, int indentationLevel)
        {
            StringBuilder sb = new StringBuilder();
            int count = 0;
            int items = 0;
            const string delim = ";";
            const string crlf = "\r\n";
            string indent = string.Empty;
            if (indentationLevel  0)
            {
                indent = indent.PadLeft(indentationLevel, '\t');
            }
            string lastIndent = string.Empty;
            if (indentationLevel - 1  0)
            {
                lastIndent = lastIndent.PadLeft(indentationLevel - 1, '\t');
            }

            if (col != null  col.Count  0)
            {
                sb.Append(crlf);
                sb.Append(indent);
                foreach (string item in col)
                {
                    if (!string.IsNullOrEmpty(item))
                    {
                        items++;
                        sb.Append(item);
                        sb.Append(delim);
                        count++;
                        if (itemsPerLine  0  count = itemsPerLine)
                        {
                            sb.Append(crlf);
                            if (items  col.Count - 1)
                            {
                                sb.Append(indent);
                            }
                            count = 0;
                        }
                    }
                }
                if (count != 0)
                {
                    sb.Append(crlf);
                }
                sb.Append(lastIndent);
            }
            return sb.ToString();
        }
    }
}